Homepage redesign: lead with the HA integration, then the library

The previous homepage opened with prose ('What it is') and listed three
generic 'where to start' cards. New version is more concrete:

- Hero block with three call-to-action buttons (Add to HA, Decode .pca,
  Source on Gitea) using Starlight's hero frontmatter
- '## In Home Assistant' section opens with what users actually get,
  followed by a 2x2 grid of clickable HA screenshots (integrations
  list, integration page, device controls, dev-tools states) — each
  thumbnail links to the relevant detail page with a short caption
- '## In Python' section: 15-line OmniClient + events() example so
  the library shape is immediately legible, plus four bullets
  highlighting opcode coverage / typed dataclasses / mock controller
  / async-first
- '## Two non-public protocol quirks' kept verbatim — still the
  headline RE finding
- '## Read the in-depth content' replaces the three generic cards
  with six specific ones routing to install how-to, dev-stack
  tutorial, protocol reference, quirks explainer, file format,
  and journey

Build clean, 23 pages. Local container rebuilt + recreated. The
new sections render: In Home Assistant, In Python, Two non-public
protocol quirks, Read the in-depth content.
This commit is contained in:
Ryan Malloy 2026-05-10 18:18:44 -06:00
parent 17cd57f1bf
commit f7be0f7b18

View File

@ -2,69 +2,159 @@
title: HAI Omni Pro II — omni-pca title: HAI Omni Pro II — omni-pca
description: Reverse-engineered Python library and Home Assistant integration for HAI/Leviton Omni Pro II home automation panels. description: Reverse-engineered Python library and Home Assistant integration for HAI/Leviton Omni Pro II home automation panels.
template: doc template: doc
hero:
tagline: |
Async Python library and a drop-in Home Assistant integration for the
HAI/Leviton Omni-Link II protocol — clean-room reverse-engineered from
PC Access 3.17, complete with two non-public crypto quirks no other
public client implements.
actions:
- text: Add to Home Assistant
link: /how-to/install-in-home-assistant/
icon: right-arrow
variant: primary
- text: Decode your .pca file
link: /tutorials/decrypt-your-pca/
icon: external
variant: secondary
- text: Source on Gitea
link: https://git.supported.systems/warehack.ing/omni-pca
icon: external
variant: minimal
--- ---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
import { Image } from 'astro:assets'; import { Image } from 'astro:assets';
import wordmark from '../../assets/manual/omnipro-ii-wordmark.png'; import wordmark from '../../assets/manual/omnipro-ii-wordmark.png';
import shotIntegrations from '../../assets/screenshots/02-integrations-list.png';
import shotConfig from '../../assets/screenshots/03-omni-pca-config.png';
import shotDevice from '../../assets/screenshots/04-panel-device.png';
import shotStates from '../../assets/screenshots/06-developer-states.png';
<Image <Image
src={wordmark} src={wordmark}
alt="OmniPro II Automation" alt="OmniPro II Automation"
loading="eager" loading="eager"
style="max-width: 480px; width: 100%; height: auto; margin: 1rem 0 2rem; filter: invert(1); opacity: 0.9;" style="max-width: 420px; width: 100%; height: auto; margin: 0.5rem 0 2rem; filter: invert(1); opacity: 0.9;"
/> />
## What it is ## In Home Assistant
`omni-pca` is an async Python library and a matching Home Assistant custom One device per panel. Typed entities for every named object the controller
component for HAI / Leviton **Omni Pro II**, **Omni IIe**, **Omni LTe**, and knows about — alarm areas, lights and outputs, binary zones with bypass,
**Lumina** panels. It speaks Omni-Link II straight to the controller over TCP, thermostats with HVAC modes, programs and panel-button macros, plus a
opens an encrypted session, and surfaces the panel's typed object model — zones, single `event` entity that relays the panel's typed push-event stream
units, areas, thermostats, buttons, programs, codes, messages — plus the into HA automations. Push updates arrive within one TCP round-trip; a
unsolicited push-event stream the panel emits on state changes. 30-second poll backstops anything that didn't push.
The protocol layer was built from a clean-room decompilation of HAI's PC Access <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0.75rem; margin: 1.5rem 0;">
3.17. Every opcode, byte layout, and crypto step is cited back to the source <a href="/how-to/install-in-home-assistant/" style="display: block;">
line in the decompiled C# (`clsOmniLinkConnection.cs`, `clsHAC.cs`, etc.). <Image src={shotIntegrations} alt="HA Devices and Services dashboard with HAI/Leviton Omni Panel listed alongside built-in integrations" style="width: 100%; height: auto; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px;" />
<div style="font-size: 0.85rem; opacity: 0.75; margin-top: 0.5rem;">Discoverable in the integrations list →</div>
</a>
<a href="/reference/ha-entities/" style="display: block;">
<Image src={shotConfig} alt="omni_pca integration page showing Custom integration, version 2026.5.10, 1 device with 38 entities" style="width: 100%; height: auto; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px;" />
<div style="font-size: 0.85rem; opacity: 0.75; margin-top: 0.5rem;">Entity catalogue · 8 platforms →</div>
</a>
<a href="/reference/ha-entities/#what-it-looks-like-in-ha" style="display: block;">
<Image src={shotDevice} alt="Omni Pro II device page in HA with lights, areas, thermostats, and panel buttons all live" style="width: 100%; height: auto; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px;" />
<div style="font-size: 0.85rem; opacity: 0.75; margin-top: 0.5rem;">Per-device controls grid →</div>
</a>
<a href="/how-to/automate-on-alarm/" style="display: block;">
<Image src={shotStates} alt="HA Developer Tools showing alarm_control_panel.omni_pro_ii_main with full attribute payload" style="width: 100%; height: auto; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px;" />
<div style="font-size: 0.85rem; opacity: 0.75; margin-top: 0.5rem;">Real entity state from the panel →</div>
</a>
</div>
## In Python
The library underneath is intentionally async-first and typed end-to-end:
```python
import asyncio
from 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 `SystemEvent`
subclasses, no untyped bytes leaking past the framing layer.
- **Stateful mock controller** for offline development. The same `MockPanel`
class powers the integration tests and the docker dev stack.
- **Async-first** — `OmniClient` is an async context manager, `events()` is
an async iterator, no callback soup.
## Two non-public protocol quirks ## Two non-public protocol quirks
The wire protocol — as actually implemented in PC Access 3.17 — has two The wire protocol — as actually implemented in PC Access 3.17 — has two
non-public quirks that public Omni-Link clients miss. Without them the panel quirks that public Omni-Link clients miss. Without them the panel will
will accept your TCP connection, complete the unencrypted handshake, and then accept your TCP connection, complete the unencrypted handshake, and then
silently drop you on the first encrypted message: silently drop you on the first encrypted message:
1. **Session key XOR mix.** The AES-128 session key is *not* the panel's 1. **Session key XOR mix.** The AES-128 session key is *not* the panel's
`ControllerKey` directly. Bytes `[11..16)` of the ControllerKey are XORed `ControllerKey` directly. Bytes `[11..16)` of the ControllerKey are
with a 5-byte `SessionID` nonce that the controller sends in XORed with a 5-byte `SessionID` nonce that the controller sends in
`ControllerAckNewSession`. Bytes `[0..11)` are the ControllerKey verbatim. `ControllerAckNewSession`. Bytes `[0..11)` are the ControllerKey
2. **Per-block XOR pre-whitening before AES.** Before each 16-byte block is verbatim.
AES-encrypted, its first two bytes are XORed with the packet's 16-bit 2. **Per-block XOR pre-whitening before AES.** Before each 16-byte block
sequence number (high byte first). The same mask is applied to *every* is AES-encrypted, its first two bytes are XORed with the packet's
block of the packet. Decrypt reverses it. 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` Both are unambiguous in the decompiled C# (`clsOmniLinkConnection.cs:1886-1892`
and `:396-401`). Neither appears in `jomnilinkII`, `pyomnilink`, or any and `:396-401`). Neither appears in `jomnilinkII`, `pyomnilink`, or any
third-party Omni-Link writeup we found. See [the quirks third-party Omni-Link writeup we found. See
explainer](/explanation/quirks/) for the full story. [the quirks explainer](/explanation/quirks/) for why they exist and the
full visual breakdown.
## Where to start ## Read the in-depth content
<CardGrid> <CardGrid>
<LinkCard <LinkCard
title="Decode your .pca file" title="Install in Home Assistant"
href="/start/quickstart/" href="/how-to/install-in-home-assistant/"
description="Pull the panel's IP, port, and AES-128 ControllerKey out of an encrypted .pca config export — no panel hardware required." description="Two install paths (HACS + manual), config-flow walkthrough, common errors, reauth flow."
/> />
<LinkCard <LinkCard
title="Protocol reference" title="Spin up the dev stack"
href="/tutorials/dev-stack/"
description="Real HA + a faithful mock panel in Docker. Click around the integration without touching real hardware."
/>
<LinkCard
title="Omni-Link II protocol reference"
href="/reference/protocol/" href="/reference/protocol/"
description="Byte-level Omni-Link II handshake, packet layouts, key derivation, steady-state encryption, sequence numbers, and teardown." description="Byte-level handshake diagrams, every packet type visualized, key derivation, steady-state encryption."
/>
<LinkCard
title="The two non-public quirks, in depth"
href="/explanation/quirks/"
description="Why the session key isn't the ControllerKey, and the per-block whitening that breaks every naive AES-ECB client."
/>
<LinkCard
title=".pca file format"
href="/reference/file-format/"
description="Borland-Pascal LCG XOR cipher, three keys, and the on-disk layout so you can decode your config without PC Access."
/> />
<LinkCard <LinkCard
title="The Journey" title="The Journey"
href="/journey/" href="/journey/"
description="Chronological retrospective of the reverse-engineering work — pile of binaries to 351 passing tests in a few days." description="Chronological retrospective from a pile of binaries off a Mac volume to 351 passing tests in a couple of days."
/> />
</CardGrid> </CardGrid>