HAI Omni Pro II — omni-pca
In Home Assistant
Section titled “In Home Assistant”One device per panel. Typed entities for every named object the controller
knows about — alarm areas, lights and outputs, binary zones with bypass,
thermostats with HVAC modes, programs and panel-button macros, plus a
single event entity that relays the panel’s typed push-event stream
into HA automations. Push updates arrive within one TCP round-trip; a
30-second poll backstops anything that didn’t push.




In Python
Section titled “In Python”The library underneath is intentionally async-first and typed end-to-end:
import asynciofrom omni_pca import OmniClient
async def main() -> None: async with OmniClient( host="192.168.1.9", port=4369, controller_key=bytes.fromhex("6ba7b4e9b4656de3cd7edd4c650cdb09"), ) as panel: info = await panel.get_system_information() print(info.model_name, info.firmware_version)
async for event in panel.events(): print(event) # ZoneStateChanged, ArmingChanged, AlarmActivated, …
asyncio.run(main())What you get from the library:
- Full opcode coverage — 104 v1 + 83 v2 message types, byte-exact to the decompiled C# enums.
- 21 typed status/properties dataclasses, 26 typed
SystemEventsubclasses, no untyped bytes leaking past the framing layer. - Stateful mock controller for offline development. The same
MockPanelclass powers the integration tests and the docker dev stack. - Async-first —
OmniClientis an async context manager,events()is an async iterator, no callback soup.
Two non-public protocol quirks
Section titled “Two non-public protocol quirks”The wire protocol — as actually implemented in PC Access 3.17 — has two quirks that public Omni-Link clients miss. Without them the panel will accept your TCP connection, complete the unencrypted handshake, and then silently drop you on the first encrypted message:
- Session key XOR mix. The AES-128 session key is not the panel’s
ControllerKeydirectly. Bytes[11..16)of the ControllerKey are XORed with a 5-byteSessionIDnonce that the controller sends inControllerAckNewSession. Bytes[0..11)are the ControllerKey verbatim. - Per-block XOR pre-whitening before AES. Before each 16-byte block is AES-encrypted, its first two bytes are XORed with the packet’s 16-bit sequence number (high byte first). The same mask is applied to every block of the packet. Decrypt reverses it.
Both are unambiguous in the decompiled C# (clsOmniLinkConnection.cs:1886-1892
and :396-401). Neither appears in jomnilinkII, pyomnilink, or any
third-party Omni-Link writeup we found. See
the quirks explainer for why they exist and the
full visual breakdown.