Metadata-Version: 2.4
Name: zk-sdk
Version: 0.1.0
Summary: A production-ready Python SDK for ZKTeco standalone access control and time attendance devices (supporting biometrics, RFID, relay control, live capture, and logical access rules).
Author-email: Tulio Amancio <root@tsuriu.com.br>
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/libandpackages/zk-sdk
Project-URL: Source, https://gitlab.com/libandpackages/zk-sdk
Project-URL: Bug Tracker, https://gitlab.com/libandpackages/zk-sdk/-/issues
Keywords: zk,zkteco,pyzk,access-control,biometrics,attendance,rfid,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Hardware
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyzk>=0.9
Requires-Dist: pydantic>=2.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: twine>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: tomli; python_version < "3.11" and extra == "dev"
Dynamic: license-file

# ZKTeco Python SDK (`zk-sdk`)

A production-ready, type-hinted, synchronous and asynchronous Python SDK for ZKTeco standalone access control and time attendance devices (Bio Inox Plus, iFace, K-Series, IN01, etc.) built on top of `pyzk` and `pydantic` v2.

---

## Highlights

- 🔄 **Dual Engine**: High-level synchronous (`ZKClient`) and asynchronous non-blocking (`AsyncZKClient`) interfaces.
- 🔒 **Device Safety**: Keypad and display locking during sensitive operations (`_device_locked`) to prevent race conditions.
- 🛡️ **Pydantic v2 Type Safety**: Strict data models (`User`, `FingerprintTemplate`, `AttendanceRecord`, `DeviceInfo`, `CapacityInfo`).
- 👤 **Full User Management**: Create, update, delete, list, filter by department (`group_id`), and search by RFID card.
- 👆 **Biometrics (Fingerprints)**: Read, write, delete templates, remote enrollment, user backup, and High-Rate bulk restore (`HR_save_usertemplates`).
- ⚡️ **Real-Time Live Capture**: Real-time event streaming with background worker threads and async coroutine dispatch.
- ⏰ **Software Access Rules**: Integrated logical Access Rules engine (`AccessRulesRegistry`, `TimeWindow`, `TimeZoneRule`, `Area`) with JSON persistence to evaluate live access events in software.
- 🚪 **Relay & Hardware Actions**: Remote door unlocking, audio voice test prompts, device restart, poweroff, and memory clearing.

---

## Installation

```bash
pip install zk-sdk
```

Or install with development dependencies:

```bash
pip install zk-sdk[dev]
```

---

## Quick Start

### Synchronous Usage (`ZKClient`)

```python
from zk_sdk import ZKClient

# Automatic connection and cleanup using context manager
with ZKClient("192.168.1.201", port=4370, timeout=10, force_udp=True) as client:
    # 1. Device Info
    info = client.get_info()
    print(f"Connected to {info.platform} - Firmware: {info.firmware_version}")

    # 2. List Users
    users = client.list_users()
    print(f"Total registered users: {len(users)}")
    for user in users:
        print(f"UID={user.uid} | ID={user.user_id} | Name={user.name} | Dept={user.group_id}")

    # 3. Add or update user
    client.add_user(
        uid=101,
        user_id="101",
        name="John Doe",
        password="123",
        card=987654,
        department="Engineering",
    )

    # 4. Pulse Relay (Unlock door for 5 seconds)
    client.unlock_door(seconds=5)
```

### Asynchronous Usage (`AsyncZKClient`)

For modern async frameworks (FastAPI, aio-pika, UniHub, Celery async):

```python
import asyncio
from zk_sdk import AsyncZKClient

async def main():
    async with AsyncZKClient("192.168.1.201") as client:
        # Get memory capacity
        cap = await client.get_capacity()
        print(f"Users: {cap.users}/{cap.users_cap} | Fingers: {cap.fingers}/{cap.fingers_cap}")

        # Fetch attendance logs
        logs = await client.get_attendance(limit=10)
        for log in logs:
            print(f"User: {log.user_id} at {log.timestamp} (Punch: {log.punch})")

if __name__ == "__main__":
    asyncio.run(main())
```

---

## Key Capabilities

### 1. Biometrics & Fingerprint Templates

```python
with ZKClient("192.168.1.201") as client:
    # Read all stored templates
    templates = client.get_all_templates()
    print(f"Found {len(templates)} biometric templates.")

    # Backup a user and their enrolled templates
    user, user_templates = client.backup_user_with_templates(uid=101)

    # Remote enrollment (device prompts the user to place finger on sensor)
    success = client.enroll_user(uid=101, temp_id=0)
    if success:
        print("Fingerprint enrolled successfully!")

    # Delete single fingerprint template (finger 0) without removing the user
    client.delete_user_template(uid=101, temp_id=0)
```

### 2. Real-Time Live Capture & Logical Access Rules

Standalone ZKTeco devices do not enforce complex time schedules natively on hardware. The SDK provides an in-software rules registry to evaluate punches in real-time:

```python
from zk_sdk import ZKClient, AccessRulesRegistry

# Define rules
rules = AccessRulesRegistry(storage_path="access_rules.json")
rules.define_time_zone("Commercial", [(None, "08:00", "18:00")])  # Daily 8h - 18h
rules.define_area("Office", time_zone="Commercial")
rules.assign_user_to_area(user_id="101", area_name="Office")
rules.save()

# Start live capture with rule enforcement
with ZKClient("192.168.1.201", rules=rules) as client:
    def on_event(event):
        print(f"Punch from {event.attendance.user_id}: Allowed={event.allowed} ({event.verdict})")

    client.watch_live_events(on_event=on_event, enforce_rules=True)
```

### 3. Hardware Controls & Audio

```python
with ZKClient("192.168.1.201") as client:
    # Unlock door relay for 3 seconds
    client.unlock_door(seconds=3)

    # Play voice message (0 = "Thank you", 2 = "Access denied")
    client.test_voice(index=0)

    # Synchronize terminal clock with system time
    client.sync_time()

    # Reboot terminal
    client.restart_device()
```

---

## Architecture & Compatibility

- **Protocol**: Standalone UDP/TCP ZK Protocol (port 4370) via `pyzk`.
- **Concurrency**: `AsyncZKClient` executes synchronous pyzk calls safely via `asyncio.to_thread` with mutual exclusion locking.
- **Legacy Compatibility**: `ZKAdapter` is provided as an alias for `ZKClient` for seamless drop-in compatibility with legacy scripts.

---

## License

MIT License - Tsuriu Tech / AccessHub.
