Python library for reading Thai national ID cards using smartcard readers.
- Features
- What's New in v0.3.0
- Quick Start
- Flutter Library Development
- Prerequisites
- Installation
- Usage
- Data Fields
- Dependencies
- Troubleshooting
- Project Structure
- Credits
- License
- ✅ Full Card Data Extraction: Read all information from Thai ID cards
- 📸 Photo Support: Extract and save card photos (JPEG format)
- 🏥 NHSO Health Insurance: Read National Health Security Office data
- 🔖 Laser ID Support: Read laser-engraved ID from cards
- 🔒 Data Validation: Automatic CID checksum validation and date parsing
- 📅 Date Conversion: Buddhist Era to Gregorian calendar conversion
- 🏛️ Structured Data Models: Name and Address parsed into structured objects
- 🎨 Modern Web UI: Streamlit-based debug interfaces
- 🐍 Type-Safe: Full type hints and Pydantic models
- 🔍 Error Handling: Comprehensive exception handling and system checks
- 📦 Zero Config: Auto-detects readers and connects to cards
- 🚀 Fast: Efficient APDU command implementation
- 🎨 Consolidated Modern Interface: Merged debug interfaces into a single sidebar-based layout
- 📱 Better Organization: Expandable reader list with status indicators in sidebar
- ✨ Quick Copy Buttons: One-click copy for CID, names, and address fields
- 🌓 Dark Gradient Theme: Modern, production-ready appearance with card-based design
- ⚡ Event-Driven Monitoring: New PCSCMonitor with real-time PC/SC state change detection
- 🔄 Auto-Read by Default: Reliable automatic card reading on insertion
- 🔧 Improved Reliability: Better handling of reader availability and connection states
- 🐛 Field Mapping Fixes: Corrected web app field names to match API model
- 📚 Organized Structure: Moved documentation files to
notes/directory - 📝 Updated Guides: Comprehensive documentation for all interfaces
- 🧪 Test Coverage: Added event-driven monitoring tests
# 1. Install system dependencies
sudo apt-get install -y pcscd libpcsclite-dev python3-dev swig
# 2. Install Python dependencies
uv sync --group dev
# 3. Run the web interface
uv run streamlit run debug/app.pyThen open http://localhost:8501 in your browser, insert your Thai ID card, and click "Scan Readers" → "Connect" → "Read Card".
We are actively working on a Flutter version of this library for cross-platform mobile support (iOS/Android). The development is in progress at playground/thai_idcard_reader_test/.
- Package: Uses the
ccidFlutter package for smartcard reading - Platform Support: iOS (13.0+) and Android with USB OTG smartcard readers
- Architecture: Mirrors the Python implementation using APDU commands over PC/SC
- CI/CD: Automated iOS builds via GitHub Actions and Fastlane
cd playground/thai_idcard_reader_test/
# Install dependencies
flutter pub get
# Run on Android device
flutter run
# For iOS builds, see the CI/CD workflow or use GitHub ActionsSee the Flutter project README for detailed setup instructions, hardware requirements, and iOS cloud build configuration.
Note: Thai National ID cards do NOT support NFC - external USB OTG (Android) or MFi-certified (iOS) smartcard readers are required.
iOS Testing Limitation: The author currently lacks access to a Mac, making iOS testing challenging. However, the project includes GitHub Actions CI/CD for automated iOS builds. Community contributions and testing on iOS devices are highly appreciated!
This project is inspired by and based on:
- Thai National ID Card Reader Gist by bouroo
- lab-python3-th-idcard by pstudiodev1
This project requires the following system packages:
pcscd- PC/SC Smart Card Daemonlibpcsclite-dev- PC/SC development filespython3-dev- Python development headersswig- Interface compiler for Python bindings
Install them using:
sudo apt-get update
sudo apt-get install -y pcscd libpcsclite-dev python3-dev swigOr if you have mise installed:
mise run install-depsThis project uses uv for Python package management.
# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install Python dependencies
uv syncOr with mise:
mise run setupConnect your smartcard reader and insert a Thai ID card, then run:
uv run python thai-idcard.pyOr:
mise run runThe script will:
- Detect available smartcard readers
- Connect to the first reader automatically
- Read personal data from the Thai ID card including:
- Citizen ID
- Thai/English full name
- Date of birth
- Gender
- Card issuer
- Issue/Expiry dates
- Address
- Photo (saved as
{CID}.jpg)
For a modern web-based interface with visual feedback:
# Modern interface with visual feedback
uv run streamlit run debug/app.pyFeatures:
- 🔍 Visual reader detection and selection
- 🔌 Connection status monitoring
- 📖 Interactive card reading with progress bars
- 📸 Photo preview and download
- 💾 Export data as JSON, CSV, or photo
- 🐛 Real-time debug logging (full interface)
See debug/README.md for detailed documentation.
from pythaiidcard import ThaiIDCardReader
# Basic usage - auto-connect and read card
reader = ThaiIDCardReader()
with reader.card_session():
card = reader.read_card(include_photo=True)
print(f"Name: {card.english_fullname}")
print(f"CID: {card.cid}")
print(f"Age: {card.age}")
print(f"Expires: {card.expire_date}")
print(f"Valid: {not card.is_expired}")
# Save photo
if card.photo:
card.save_photo() # Saves as {cid}.jpg
# Advanced usage - manual control
from pythaiidcard.reader import ThaiIDCardReader
# List available readers
readers = ThaiIDCardReader.list_readers()
for reader_info in readers:
print(f"Reader {reader_info.index}: {reader_info.name}")
print(f" ATR: {reader_info.atr}")
print(f" Connected: {reader_info.connected}")
# Connect to specific reader
reader = ThaiIDCardReader(reader_index=0)
reader.connect()
# Read without photo for faster operation
card = reader.read_card(include_photo=False)
# Read with progress callback
def on_photo_progress(current, total):
print(f"Reading photo: {current}/{total}")
card = reader.read_card(
include_photo=True,
photo_progress_callback=on_photo_progress
)
reader.disconnect()
# Access card data - Structured models
# Names are parsed into structured Name objects
print(f"Thai First Name: {card.thai_name.first_name}")
print(f"Thai Last Name: {card.thai_name.last_name}")
print(f"English Prefix: {card.english_name.prefix}") # "Mr.", "Mrs.", etc.
print(f"English Full Name: {card.english_name.full_name}")
# Address is parsed into structured Address object
print(f"House Number: {card.address_info.house_no}")
print(f"Moo: {card.address_info.moo}")
print(f"Soi: {card.address_info.soi}")
print(f"Street: {card.address_info.street}")
print(f"Subdistrict: {card.address_info.subdistrict}")
print(f"District: {card.address_info.district}")
print(f"Province: {card.address_info.province}")
print(f"Full Address: {card.address_info.address}")
# Backward compatibility - original string properties still work
print(f"Thai Name: {card.thai_fullname}") # Returns full name string
print(f"English Name: {card.english_fullname}") # Returns full name string
print(f"Address: {card.address}") # Returns full address string
# Other computed properties
print(f"Gender: {card.gender_text}") # "Male" or "Female"
print(f"Issue Date: {card.issue_date}")
print(f"Days until expiry: {card.days_until_expiry}")
# Export as JSON
import json
card_json = card.model_dump_json(indent=2)
print(card_json)
# Read NHSO (National Health Security Office) data
nhso_data = reader.read_nhso_data()
print(f"Main Hospital: {nhso_data.main_hospital_name}")
print(f"Insurance Type: {nhso_data.main_inscl}")
print(f"Expiry Date: {nhso_data.expire_date}")
print(f"Is Expired: {nhso_data.is_expired}")
# Read Laser ID (laser-engraved ID on card)
laser_id = reader.read_laser_id()
print(f"Laser ID: {laser_id}")The library extracts and parses card data into structured models:
| Field | Type | Description |
|---|---|---|
cid |
str |
13-digit citizen identification number (validated with checksum) |
thai_name |
Name |
Structured Thai name (prefix, first_name, middle_name, last_name) |
english_name |
Name |
Structured English name (prefix, first_name, middle_name, last_name) |
date_of_birth |
date |
Birth date (Buddhist Era → Gregorian converted) |
gender |
str |
Gender code ("1"=Male, "2"=Female) |
card_issuer |
str |
Issuing organization |
issue_date |
date |
Card issue date |
expire_date |
date |
Card expiration date |
address_info |
Address |
Structured address (house_no, moo, soi, street, subdistrict, district, province) |
photo |
bytes |
JPEG photo data (optional) |
Automatically parses names from Thai ID card format (prefix#firstname#middlename#lastname):
| Field | Type | Description |
|---|---|---|
prefix |
str |
Name prefix (e.g., "นาย", "นาง", "Mr.", "Mrs.") |
first_name |
str |
First name |
middle_name |
str |
Middle name (may be empty) |
last_name |
str |
Last name |
full_name |
str |
Computed full name with proper spacing |
Automatically parses Thai address format with proper component separation:
| Field | Type | Description |
|---|---|---|
house_no |
str |
House number (บ้านเลขที่) |
moo |
str |
Village/Moo number (หมู่ที่) |
soi |
str |
Soi/Lane (ซอย) |
street |
str |
Street/Road (ถนน) |
subdistrict |
str |
Subdistrict/Tambon (ตำบล) or แขวง (Bangkok) |
district |
str |
District/Amphoe (อำเภอ) or เขต (Bangkok) |
province |
str |
Province (จังหวัด) |
address |
str |
Computed full address with Thai prefixes |
| Property | Type | Description |
|---|---|---|
age |
int |
Current age calculated from date of birth |
gender_text |
str |
Gender as text ("Male" or "Female") |
is_expired |
bool |
Whether the card has expired |
days_until_expiry |
int |
Days remaining until card expires |
thai_fullname |
str |
Full Thai name (backward compatibility) |
english_fullname |
str |
Full English name (backward compatibility) |
address |
str |
Full address string (backward compatibility) |
National Health Security Office health insurance data:
| Field | Type | Description |
|---|---|---|
main_inscl |
str |
Main insurance classification code |
sub_inscl |
str |
Sub insurance classification code |
main_hospital_name |
str |
Main registered hospital name |
sub_hospital_name |
str |
Sub hospital name |
paid_type |
str |
Payment type code |
issue_date |
date |
NHSO registration issue date |
expire_date |
date |
NHSO registration expiry date |
update_date |
date |
Last update date |
change_hospital_amount |
str |
Number of hospital changes allowed |
Computed Properties:
is_expired(bool): Whether the NHSO registration has expireddays_until_expiry(int): Days remaining until NHSO registration expires
Thai ID cards also contain a laser-engraved ID that can be read separately:
laser_id = reader.read_laser_id() # Returns stringThis is a unique identifier laser-engraved on the physical card.
- pyscard (>=2.3.0) - Python smartcard library for PC/SC interface
- Pillow (>=11.3.0) - Python imaging library for photo handling
- pydantic (>=2.0) - Data validation and settings management
- python-dateutil (>=2.8.2) - Date parsing utilities
- streamlit (>=1.28.0) - Web interface framework (optional)
- ruff (>=0.8.4) - Linting and formatting
The library will automatically check for required system dependencies on Linux systems with apt package manager. If dependencies are missing, you'll see a helpful error message with installation instructions.
Example error:
SystemDependencyError: Missing required system dependencies:
✗ PC/SC Smart Card Daemon (pcscd)
✗ PC/SC Lite development library (libpcsclite-dev)
To install missing dependencies, run:
sudo apt-get update && sudo apt-get install -y pcscd libpcsclite-dev python3-dev swig
To skip the dependency check (if you know dependencies are installed via other means):
from pythaiidcard import ThaiIDCardReader
reader = ThaiIDCardReader(skip_system_check=True)Install the system dependencies listed above, particularly libpcsclite-dev.
- Ensure your smartcard reader is connected
- Check that the
pcscdservice is running:sudo systemctl status pcscd - Start it if needed:
sudo systemctl start pcscd
Add your user to the scard group:
sudo usermod -a -G scard $USERThen log out and log back in.
pythaiidcard/
├── pythaiidcard/ # Main library package
│ ├── __init__.py # Package initialization
│ ├── reader.py # ThaiIDCardReader implementation
│ ├── models.py # Pydantic data models
│ ├── constants.py # APDU commands and response codes
│ ├── exceptions.py # Custom exceptions
│ ├── utils.py # Utility functions
│ └── system_check.py # System dependency checking
├── debug/ # Debug interface
│ ├── app.py # Modern compact interface
│ └── README.md # Debug interface documentation
├── thai-idcard.py # Legacy CLI script
├── pyproject.toml # Project configuration
└── README.md # This file
- ThaiIDCardReader: Main class for reading Thai ID cards
- ThaiIDCard: Pydantic model with validated card data
- Name: Structured name model (prefix, first_name, middle_name, last_name)
- Address: Structured address model (house_no, moo, soi, street, subdistrict, district, province)
- NHSOData: Health insurance data from National Health Security Office
- CardReaderInfo: Information about available card readers
- System Check: Automatic validation of system dependencies
- Debug Interfaces: Streamlit-based web UIs for testing and debugging
See LICENSE file for details.

