custom_components/omni_pca/services.yaml — declares 7 services with config_entry selectors so HA's UI gives users a panel picker: bypass_zone, restore_zone, execute_program, show_message, clear_message, acknowledge_alerts, send_command (raw escape hatch) custom_components/omni_pca/services.py — async handlers wired via async_setup_services on entry setup; idempotent across multiple entries. Each handler validates entry_id, looks up the right coordinator, calls the matching OmniClient method. CommandFailedError wrapped to HomeAssistantError; unknown Command codes raise ServiceValidationError. async_unload_services removes them when the last entry unloads. custom_components/omni_pca/diagnostics.py — async_get_config_entry_ diagnostics dumps a redacted snapshot for bug reports: panel model + firmware, discovered/live counts per object type, sha256-hashed zone/ unit/area names (so uniqueness is visible without leaking PII), last event class, controller key REDACTED via async_redact_data. custom_components/omni_pca/__init__.py — wires async_setup_services on entry setup and async_unload_services on the last entry unload. custom_components/omni_pca/README.md — full entity table, service list, example automation, troubleshooting section, link to JOURNEY.md. Top-level README — entity rundown updated to reflect the full v1.0 surface (was: 'binary_sensor for zones'). 331 tests still pass; ruff clean across src/ tests/ custom_components/. hacs.json already in place from initial scaffold.
73 lines
2.6 KiB
Markdown
73 lines
2.6 KiB
Markdown
# omni-pca
|
|
|
|
Async Python client for HAI/Leviton Omni-Link II home automation panels — Omni Pro II, Omni IIe, Omni LTe, Lumina.
|
|
|
|
Includes a Home Assistant custom component (`custom_components/omni_pca/`).
|
|
|
|
## Status
|
|
|
|
**Alpha.** Built from a full reverse-engineering of HAI's PC Access 3.17 (the Windows installer/programmer app). The protocol layer captures two non-public quirks that public Omni-Link clients miss:
|
|
|
|
1. **Session key is not the ControllerKey.** Last 5 bytes are XORed with a controller-supplied SessionID nonce.
|
|
2. **Per-block XOR pre-whitening before AES.** First two bytes of every 16-byte block are XORed with the packet's sequence number.
|
|
|
|
See [`docs/PROTOCOL.md`](docs/PROTOCOL.md) for the full byte-level spec.
|
|
|
|
## Quick start (library)
|
|
|
|
```bash
|
|
uv add omni-pca
|
|
```
|
|
|
|
```python
|
|
import asyncio
|
|
from omni_pca import OmniClient
|
|
|
|
async def main():
|
|
async with OmniClient(
|
|
host="192.168.1.9",
|
|
port=4369,
|
|
controller_key=bytes.fromhex("6ba7b4e9b4656de3cd7edd4c650cdb09"),
|
|
) as panel:
|
|
info = await panel.get_system_info()
|
|
print(info.model_name, info.firmware_version)
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
## Quick start (Home Assistant)
|
|
|
|
Copy `custom_components/omni_pca/` into your HA `config/custom_components/`, restart HA, then add the integration via Settings → Devices & Services. You'll need:
|
|
|
|
- Panel IP / hostname
|
|
- TCP port (default 4369)
|
|
- ControllerKey as 32 hex chars
|
|
|
|
Get the ControllerKey from your `.pca` file using the included parser:
|
|
|
|
```bash
|
|
uvx --from omni-pca omni-pca decode-pca path/to/Your.pca --field controller_key
|
|
```
|
|
|
|
The integration creates one HA device per panel plus typed entities for every named object on the controller: `alarm_control_panel` for areas, `light` for units, `binary_sensor`/`switch` for zones (state + bypass), `climate` for thermostats, `sensor` for analog zones and panel telemetry, `button` for panel macros, and `event` for the typed push-notification stream. See [`custom_components/omni_pca/README.md`](custom_components/omni_pca/README.md) for the entity table and service list.
|
|
|
|
## Without a panel — mock controller
|
|
|
|
For testing, the library ships a minimal Omni controller emulator:
|
|
|
|
```python
|
|
from omni_pca.mock_panel import MockPanel
|
|
|
|
async with MockPanel(controller_key=...).serve(port=14369):
|
|
# connect a real OmniClient to localhost:14369 — works end-to-end
|
|
...
|
|
```
|
|
|
|
## Versioning
|
|
|
|
Date-based ([CalVer](https://calver.org/)): `YYYY.M.D`. Bumped on backwards-incompatible changes.
|
|
|
|
## Acknowledgments
|
|
|
|
This client is independent and not affiliated with Leviton or HAI. Protocol details derived from clean-room analysis of the publicly-distributed PC Access installer.
|