Ryan Malloy e8ed7d1b89 HA Phase A: rebuild coordinator + binary_sensor on v1.0 client + JOURNEY.md
custom_components/omni_pca/coordinator.py — full rewrite:
- Long-lived OmniClient for entry lifetime
- One-shot discovery: system info + zone/unit/area/thermostat/button names
  via list_*_names + per-index get_object_properties
- Periodic poll (30s default): get_extended_status for zones/units/thermostats,
  get_object_status for areas, skip empty discoveries
- Background _run_event_listener task consuming client.events(), patches
  state in-place and async_set_updated_data on push:
    ZoneStateChanged    -> patch zone_status raw byte
    UnitStateChanged    -> patch unit_status state, preserve brightness
    ArmingChanged       -> patch area_status mode + last_user
    AlarmActivated/Cleared -> trigger refresh
    AcLost/Restored, BatteryLow/Restored -> recorded for sensors
- InvalidEncryptionKeyError/HandshakeError -> ConfigEntryAuthFailed (HA reauth)
- OmniConnectionError/RequestTimeoutError -> UpdateFailed + drop client
- Event task cancelled in async_shutdown

custom_components/omni_pca/binary_sensor.py — full rewrite:
- OmniZoneBinarySensor per discovered zone (device class from zone type:
  smoke/water/freeze use latched-alarm bit; doors/motion use current condition)
- OmniZoneBypassedBinarySensor per zone (DIAGNOSTIC, PROBLEM)
- OmniSystemAcBinarySensor (POWER, prefers AcLost/AcRestored push)
- OmniSystemBatteryBinarySensor (BATTERY)
- OmniSystemTroubleBinarySensor (PROBLEM)

custom_components/omni_pca/helpers.py — pure functions extracted for testing:
- device_class_for_zone_type, is_binary_zone_type, use_latched_alarm_for_zone,
  prettify_name. 61 unit tests in tests/test_ha_helpers.py.

docs/JOURNEY.md — 4383-word raw chronological retrospective of the whole
arc from binary archive to working library. 18 dated sections including
the 2191-byte magic-number header validation moment, the two non-public
protocol quirks, the offline-panel comedy. Source material for future
writeups (intentionally raw, not polished).

264 tests pass (was 203, +61 helper tests). Ruff clean across all dirs.
2026-05-10 14:48:50 -06:00
2026-05-10 12:46:26 -06:00
2026-05-10 12:46:26 -06:00

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 for the full byte-level spec.

Quick start (library)

uv add omni-pca
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:

uvx --from omni-pca omni-pca decode-pca path/to/Your.pca --field controller_key

Without a panel — mock controller

For testing, the library ships a minimal Omni controller emulator:

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): 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.

Description
Async Python library and Home Assistant integration for HAI/Leviton Omni Pro II / Omni IIe / Omni LTe / Lumina panels. Reverse-engineered from PC Access 3.17.
Readme MIT 1.4 MiB
2026-05-10 23:49:48 +00:00
Languages
Python 99.8%
Makefile 0.2%