Metadata-Version: 2.4
Name: hxtpy
Version: 1.0.7
Summary: Official HxTP/3.0 Python SDK — Protocol reference engine, AI integration layer, automation glue, provisioning toolkit.
Project-URL: Homepage, https://github.com/hestialabs/hxtpy
Project-URL: Documentation, https://docs.hestialabs.in/
Project-URL: Repository, https://github.com/hestialabs/hxtpy
Project-URL: Issues, https://github.com/hestialabs/hxtpy/issues
Author-email: Hestia Labs <contact@hestialabs.in>
License-Expression: MIT
License-File: LICENSE
Keywords: automation,embedded,hmac,hxtpy,iot,mqtt,protocol,security
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: websockets>=12.0
Provides-Extra: all
Requires-Dist: gmqtt>=0.6.0; extra == 'all'
Requires-Dist: mypy>=1.8; extra == 'all'
Requires-Dist: pytest-asyncio>=0.23; extra == 'all'
Requires-Dist: pytest>=8.0; extra == 'all'
Requires-Dist: ruff>=0.3; extra == 'all'
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.3; extra == 'dev'
Provides-Extra: mqtt
Requires-Dist: gmqtt>=0.6.0; extra == 'mqtt'
Description-Content-Type: text/markdown

# HXTP Python SDK

[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://python.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![PyPI](https://img.shields.io/pypi/v/hxtpy.svg)](https://pypi.org/project/hxtpy/)

## Installation

```bash
pip install hxtpy
```

With MQTT transport support:

```bash
pip install hxtpy[mqtt]
```

With development dependencies:

```bash
pip install hxtpy[all]
```

## Quick Start

### Async Client

```python
import asyncio
from hxtpy.client import HxTPClient

async def main():
    client = HxTPClient(
        url="wss://api.hestialabs.in/ws",
        tenant_id="your-tenant-uuid",
        device_id="your-device-uuid",
        secret="64-char-hex-secret",
    )

    await client.connect()

    @client.on_message
    def handle(msg):
        print(f"Received: {msg}")

    response = await client.send_command({
        "action": "set_pin",
        "params": {"pin": 13, "value": 1},
    })
    print(f"Sent: {response}")

    await client.disconnect()

asyncio.run(main())
```

### Sync Client

```python
from hxtpy.client import SyncHxTPClient

client = SyncHxTPClient(
    url="wss://api.hestialabs.in/ws",
    tenant_id="your-tenant-uuid",
    device_id="your-device-uuid",
    secret="64-char-hex-secret",
)

client.connect()
response = client.send_command({
    "action": "set_pin",
    "params": {"pin": 13, "value": 1},
})
client.disconnect()
```

### Core Protocol Engine (No Networking)

```python
from hxtpy.core import build_canonical, parse_canonical, validate_canonical
from hxtpy.crypto import sign_hmac_sha256, sha256_hex, generate_nonce

# Build canonical string
canonical = build_canonical({
    "version": "HxTP/3.0",
    "message_type": "command",
    "device_id": "device-uuid",
    "tenant_id": "tenant-uuid",
    "timestamp": 1708444800,
    "message_id": "msg-uuid",
    "nonce": "random-hex-nonce",
})

# Sign
secret = bytes.fromhex("a" * 64)
signature = sign_hmac_sha256(secret, canonical)

# Verify
from hxtpy.crypto import constant_time_equal
expected = sign_hmac_sha256(secret, canonical)
assert constant_time_equal(signature, expected)
```

### Validation Pipeline

```python
from hxtpy.validation import validate_message

result = validate_message(msg, secret_hex="your-secret", now_ms=None)
if not result.ok:
    print(f"Rejected: {result.code} — {result.reason}")
```

## License

MIT License — Copyright (c) 2026 Hestia Labs
