Skip to content

Migrate from jomnilinkII or pyomnilink

If you’ve been using jomnilinkII (Java) or pyomnilink (Python) and want to switch to omni-pca, here’s what changes and why.

  • Your panel hardware and firmware — no changes needed.
  • The ControllerKeyomni-pca uses the same persistent 32-hex key your existing client uses. Pull it the same way (PC Access UI, .pca file, keypad).
  • Network details — same IP, same port (4369 by default).
  • The user codes / area mode semantics / zone numbering — these are panel-side, identical across all clients.

This is the headline. Per the quirks explainer, PC Access does two things that no other public Omni-Link client we checked implements:

  1. Session key XOR mix. The AES-128 key for the session is not the persistent ControllerKey. It’s ControllerKey[0:11] || (ControllerKey[11:16] XOR SessionID[0:5]).
  2. Per-block XOR pre-whitening. The first two bytes of every 16-byte AES block are XORed with the packet’s sequence number before encryption.

If your existing client is currently working against your panel, then one of these is true:

  • The panel firmware is configured to allow unencrypted sessions (ProtocolVersion=V1 over a serial-or-modem path), and the existing client is using that path.
  • Your panel is older firmware that didn’t ship the quirks (we don’t have a confirmed cutoff version, but pre-3.0 likely doesn’t have them).
  • The existing client does implement the quirks but didn’t document them in source — possible but we haven’t found one.

omni-pca always uses the quirks, so it works against modern firmware out of the box. If your panel is older and rejects connections, file an issue — we can add a protocol_quirks=False option.

The shape is different. omni-pca is async; the others are sync (or event-loop-based with a different idiom).

# pyomnilink
from omnilink2 import OmniLink2
omni = OmniLink2('192.168.1.9', 4369, 'CONTROLLER_KEY_HEX')
omni.connect()
info = omni.system_information()
print(info)
omni.close()
# 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('CONTROLLER_KEY_HEX')) as panel:
info = await panel.get_system_information()
print(info)
asyncio.run(main())

Method names mostly translate directly:

pyomnilink / jomnilinkIIomni-pca
system_information()get_system_information()
system_status()get_system_status()
object_properties(type, idx)get_object_properties(ObjectType.X, idx)
extended_status(type, start, end)get_extended_status(ObjectType.X, start, end)
command(cmd, p1, p2)execute_command(Command.X, p1, p2)
unit_on(idx)turn_unit_on(idx)
subscribe_unsolicited(cb)async for ev in panel.events(): ...

All omni-pca methods are typed and return parsed dataclasses, not raw byte tuples.

pyomnilink and jomnilinkII typically use a callback model: register a function, get raw events. omni-pca exposes events as a typed async iterator with 26 parsed subclasses:

async for event in panel.events():
match event:
case ZoneStateChanged(zone_index=idx, new_state=state):
...
case ArmingChanged(area_index=idx, new_mode=mode):
...
case AlarmActivated(area_index=idx, alarm_type=kind):
...

Subclassing pattern matching catches everything and lets the type checker yell when you handle an event with the wrong field name.

If you’re using HA, swap the integration:

  1. Remove the existing Omni integration from Settings → Devices & Services.
  2. Drop in custom_components/omni_pca/ — see the quickstart.
  3. Add the new integration; entity IDs will change (the new naming convention is <platform>.omni_pro_ii_<object_name>).

If you have automations referring to the old entity IDs, search-and- replace; the matching of zones/units/areas to underlying panel objects is otherwise identical.

  • Battle-tested years of production useomni-pca is fresh code (released 2026-05-10). The protocol layer has 351 unit + integration tests against a faithful mock, but the wider OSS world hasn’t kicked the tyres yet.
  • Some opcode coverageomni-pca v1.0 doesn’t have a path for Programs discovery yet. Most other features are covered.
  • Async + typed API — better Python ergonomics, mypy-friendly.
  • Two protocol quirks correctly implemented — works against firmware that rejects naive AES-ECB clients.
  • Faithful mock panel — develop and test without touching real hardware.
  • Modern HA integration — config flow, discovery, push events, diagnostics, services. No YAML configuration.
  • Full byte-level documentation — see protocol reference, file format, and the JOURNEY for how it was derived.

If you’ve migrated and something works in jomnilinkII / pyomnilink that doesn’t in omni-pca, that’s an opcode we haven’t reverse- engineered yet (or implemented in a model). Open an issue with:

  • Panel model + firmware version
  • The specific pyomnilink / jomnilinkII call you were making
  • Any captured packets if you have them

The protocol decompilation is exhaustive — if PC Access does something, we can implement it.