Two new pages distilled from HAI's official OmniPro II Installation
Manual 3-2 + Product Specifications datasheet (Quadomated mirror).
reference/hardware-specs.md
Capacity ceilings (176 zones, 511 units, 8 areas, 64 thermostats,
128 buttons, 1500 programs, 99 codes, 128 messages, etc.), digital
communicator + network features, electrical specs with the per-output
current caps and the 24-hour battery-standby derating numbers,
physical dimensions, environmental ranges, listings, languages, and
part-number lookup. The numbers here are the source of every cap in
clsCapOMNI_PRO_II.cs and the upper bounds for the protocol's range
fields, so anyone debugging "why does my panel NAK at index N" should
start here.
explanation/zone-unit-numbering.md
Appendix C of the install manual, transcribed and explained: how the
panel maps physical hardware to its 1-176 zone and 1-511 unit
address spaces. Documents the four overlapping families that share
the unit number line -- X-10 (1-256, by house code), ALC bus
(parallel address space within those slots), physical outputs
(257-392), and panel flags (393-511) -- which is why the working
panel reports units at index 257+ (sprinkler outputs on the first
17A00 expansion enclosure) and 393+ (named flags) even though only
a dozen lights are wired up.
Closes a real debugging mystery from Phase 2/3 of the v1+UDP work:
OmniClientV1's long-form RequestUnitStatus path (BE u16 start/end)
exists specifically to address units > 255, which only happens
because of this firmware-fixed address layout.
astro.config.mjs
Slot both new pages into the existing Reference and Explanation
sidebar groups.
The auto-extracted manual SVGs were unusable PDF text-glyph soup. These
are fresh, theme-aware (currentColor everywhere, accent via the
--sl-color-accent CSS var), and built to teach.
src/assets/diagrams/handshake-sequence.svg
Sequence diagram with CLIENT and CONTROLLER swim lanes, five steps:
ClientRequestNewSession -> ControllerAckNewSession (carries SessionID)
-> derive SessionKey (inline note) -> ClientRequestSecureSession
(encrypted, accent-coloured) -> ControllerAckSecureSession (encrypted)
-> first OmniLink2Message. Plaintext arrows in currentColor, encrypted
arrows in accent.
src/assets/diagrams/packet-structure.svg
Bytes-on-the-wire box diagram: outer Packet header (seq u16 + type +
reserved + encrypted payload) decomposed below into the inner Message
(start byte 0x21, length, opcode, data, CRC u16 LE). Plain vs encrypted
fields colour-coded with a legend.
src/assets/diagrams/session-key-derivation.svg
Quirk #1 visual. Three rows of byte cells: ControllerKey (16 bytes,
with bytes 0..10 in plain colour and 11..15 highlighted), SessionID
(5 bytes), and the resulting SessionKey with the XOR boundary
visible. XOR operator in the accent colour to draw the eye.
src/assets/diagrams/per-block-whitening.svg
Quirk #2 visual. seq pill at the top, three blocks below (block 1,
block 2, block N) each showing 16 byte cells with the first two
highlighted in accent and labelled with the seq XOR mask. Drives home
that it's the SAME mask on EVERY block.
src/assets/diagrams/architecture.svg
Three groups (LIBRARY, HA INTEGRATION, TEST SURFACE) with boxes
inside. Library shows the four protocol-layer modules + connection +
client + models + events. HA shows coordinator + 8 platforms. Test
surface shows MockPanel (accent-coloured), HA test harness, e2e tests,
unit tests. One accent-coloured arrow runs from OmniConnection across
to MockPanel labelled 'TCP/4369 (encrypted)'.
src/assets/diagrams/pca-file-format.svg
Key chain: hardcoded keyPC01 -> decrypts PCA01.CFG (boxes for the
CFG fields including the highlighted pca_key) -> arrow showing the
extracted pca_key -> decrypts the .pca file (boxes for PCA03 magic,
account info, model byte, body, and the highlighted ControllerKey)
-> caption 'feeds session-key derivation (quirk #1)'.
Wired in via inline-SVG-via-?raw-import + set:html (so currentColor
adapts to the theme). Required converting four pages to .mdx:
reference/protocol.mdx + handshake + packet diagrams
reference/file-format.mdx + pca-file-format diagram
explanation/quirks.mdx + session-key + whitening diagrams
explanation/architecture.mdx + architecture diagram
Two MDX paper cuts during conversion: bare '<100ms' and '<50ms' in
architecture.mdx confused the JSX parser; backticked them as .
Build: 23 pages clean. Verified inline SVG ships in the rendered HTML
(grep for SVG title IDs returns 2/2 hits per relevant page). Container
rebuilt + redeployed. Protocol page is now 92750 bytes (was ~63000),
quirks page 84156 (was ~63000).
src/content/docs/ — twelve pages totalling ~18,800 words, ported from
the omni-pca repo's docs and reference material:
index.mdx (377 w) landing page with three CardGrid links
start/quickstart.md (572 w) three flows: decode .pca / talk to
panel / install in HA
reference/protocol.md (2525 w) byte-level Omni-Link II spec, full
packet+message layouts, the two
non-public quirks, opcode tables
reference/file-format.md (1593 w) XOR-LCG cipher, key derivation,
PCA01.CFG schema, .pca PCA03 header
reference/library-api.md (1170 w) module-by-module Python API summary
reference/ha-entities.md (1070 w) per-platform entity catalogue
reference/ha-services.md (567 w) seven services + automation YAML
explanation/quirks.md (1448 w) the headline RE essay — session-key
XOR mix + per-block whitening, why
no public client documents them
explanation/architecture.md (1123 w) library + HA + mock + tests
explanation/pc-access-bug.md (1131 w) LargeVocabulary off-by-N
journey.md (6194 w) chronological retrospective ported
from omni-pca/docs/JOURNEY.md
changelog.md (1213 w) full 2026.5.10 release notes
Dockerfile — pinned node:lts-alpine and caddy:latest (registry-1
.docker.io was returning 'tls: internal error' on node:25-alpine and
caddy:2-alpine pulls; the pinned tags are cached locally and work).
TODO comment notes to bump back to node:25 once registry stabilises.
.gitignore — added .env / .env.local just in case.
Build: 13 pages built clean in 1.83s, sitemap + Pagefind search index
emitted. Container runs at hai-omni-docs-docs (caddy network), accepts
requests with Host: hai-omni-pro-ii.warehack.ing, returns rendered
Starlight HTML with title/description meta intact. Once DNS for
hai-omni-pro-ii.warehack.ing points at the host, the site is live.
Astro 6 + Starlight 0.39 documentation site for omni-pca, organised
around the Diatáxis framework (Tutorials / How-to / Reference /
Explanation), plus a chronological Journey page and Changelog.
Theme: muted slate-blue with amber accents. astro-icon + lucide
preinstalled. Astro telemetry and Starlight devToolbar both off.
Deployment: multi-stage Dockerfile (node:25-alpine builder ->
caddy:2-alpine runtime), inner Caddy serves static dist on :80,
outer caddy-docker-proxy on the host terminates TLS for
hai-omni-pro-ii.warehack.ing.