Metadata-Version: 2.4
Name: topdata-sdk
Version: 0.1.0
Summary: A production-ready asynchronous Python SDK for Topdata facial reader devices (AiFace, Catraca Fit, Revolution, Box, Inner Ponto 4, Inner Acesso 2). WebSocket server model — the device connects to you.
Author-email: Tulio Amancio <root@tsuriu.com.br>
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/libandpackages/topdata-sdk
Project-URL: Source, https://gitlab.com/libandpackages/topdata-sdk
Project-URL: Bug Tracker, https://gitlab.com/libandpackages/topdata-sdk/-/issues
Keywords: topdata,facial-reader,access-control,biometrics,iot,async,sdk,websocket
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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.10
Description-Content-Type: text/markdown
Requires-Dist: websockets>=12.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: Pillow>=10.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"

# Topdata Python SDK

A production-ready asynchronous Python SDK for **Topdata facial reader devices** (AiFace, Catraca Fit, Revolution, Box, Inner Ponto 4, Inner Acesso 2).

> ⚠️ **Inverted connection model**: Unlike traditional device SDKs where your software connects _to_ the device, Topdata devices connect _to_ your server. This SDK runs a WebSocket server that accepts incoming connections from the readers.

## Installation

```bash
pip install topdata-sdk
```

## Quick Start

```python
import asyncio
from topdata import TopdataServer, TopdataDeviceSession, LogEvent

async def main():
    server = TopdataServer(host="0.0.0.0", port=7792)

    async def on_connected(session: TopdataDeviceSession):
        print(f"✅ Device connected: {session.serial_number}")
        print(f"   Model: {session.device_info.modelname}")
        print(f"   Firmware: {session.device_info.firmware}")
        print(f"   Users: {session.device_info.useduser}/{session.device_info.usersize}")

    async def on_disconnected(sn: str):
        print(f"❌ Device disconnected: {sn}")

    async def on_event(session: TopdataDeviceSession, event: LogEvent):
        for record in event.record:
            print(f"🔔 Access event from {session.serial_number}:")
            print(f"   User: {record.enrollid} ({record.name})")
            print(f"   Time: {record.time}")
            print(f"   Mode: {record.mode} (8=face, 3=card, 2=password)")
            print(f"   Event: {record.event}")

        # For online mode, return access decision:
        # return {"access": True, "message": "Welcome!"}
        return None  # Offline mode — no access decision needed

    server.on_device_connected = on_connected
    server.on_device_disconnected = on_disconnected
    server.on_event = on_event

    await server.start()
    print("🚀 Topdata server listening on ws://0.0.0.0:7792/pub/chat")
    print("   Configure your device: MENU → REDE → SERVIDOR → IP/Porta")

    # Keep running
    try:
        await asyncio.Future()  # Run forever
    except KeyboardInterrupt:
        await server.stop()

asyncio.run(main())
```

## User Management

Once a device is connected, you can manage users through the session object:

```python
async def on_connected(session: TopdataDeviceSession):
    # Create a user (without photo)
    await session.set_user(
        enrollid=1001,
        name="João Silva",
        admin=0,       # 0=user, 1=admin, 2=super
        card=25565535,  # Wiegand 10 format
        password=1234,
    )

    # Add a facial photo
    with open("joao.jpg", "rb") as f:
        photo_data = f.read()
    await session.set_user_photo(enrollid=1001, image_data=photo_data, name="João Silva")

    # List all users
    users = await session.get_user_list()
    print(f"Device has {len(users)} user records")

    # Get user details
    info = await session.get_user_info(enrollid=1001)
    print(f"User: {info.name}, has face: {info.faceflag}")

    # Delete a user
    await session.delete_user(enrollid=1001)
```

## Image Requirements

Photos sent to the device must meet these requirements:
- **Format**: JPEG only
- **File size**: < 150 KB
- **Resolution**: 240×320 to 800×1280 px (recommended: 480×640)
- **Content**: Single person, vertical face, no mask/hat/sunglasses

The SDK **automatically validates and normalizes** images: oversized files are downscaled to 480×640 and re-compressed.

## Wiegand Utilities

```python
from topdata import wiegand10_to_wiegand26, wiegand26_to_wiegand10

# Wiegand 10 (facility=255, card=65535) → Wiegand 26 integer
w26 = wiegand10_to_wiegand26(255, 65535)  # → 16776959

# Reverse
facility, card = wiegand26_to_wiegand10(16776959)  # → (255, 65535)
```

## Device Configuration

```python
async def configure_device(session: TopdataDeviceSession):
    # Set online mode (server decides access)
    await session.set_device_info(server_verify=1)

    # Set volume and door open time
    await session.set_device_info(volume=8, door_opentime=5)

    # Disable device during bulk operations
    await session.disable()
    # ... do bulk operations ...
    await session.enable()
```

## Protocol Reference

| Command | Direction | Description |
|---|---|---|
| `reg` | device → server | Handshake (serial, capabilities) |
| `sendlog` | device → server | Access event (face/card/password recognition) |
| `senduser` | device → server | User registered at device |
| `enabledevice` | server → device | Re-enable recognition |
| `disabledevice` | server → device | Suspend recognition |
| `getuserlist` | server → device | List users (paginated) |
| `getuserinfo` | server → device | Get user details |
| `setuserinfo` | server → device | Create/update user |
| `deleteuser` | server → device | Delete user data |
| `cleanuser` | server → device | Delete ALL users |
| `setdevinfo` | server → device | Configure device parameters |
| `setdevlock` | server → device | Configure card format & time zones |
| `setuserlock` | server → device | Per-user time restrictions |
| `getalllog` | server → device | Fetch access logs (paginated) |
| `cleanlog` | server → device | Delete all logs |

## Architecture

```
┌──────────────────┐     WebSocket      ┌──────────────────┐
│  Topdata Device  │ ──── connects to ──→│  TopdataServer   │
│  (Facial Reader) │                     │  (Your App)      │
│                  │ ← reg ──────────── │                  │
│                  │ ── ret:reg ───────→ │                  │
│                  │                     │                  │
│                  │ ← sendlog ──────── │ on_event()       │
│                  │ ── ret:sendlog ───→ │                  │
│                  │                     │                  │
│                  │ ── cmd:setuserinfo →│                  │
│                  │ ← ret:setuserinfo ─ │                  │
└──────────────────┘                     └──────────────────┘
```

## License

MIT
