Skip to content

Spin up the dev stack

Two Docker containers, one Makefile target, and you’re looking at a real Home Assistant UI driving a faithful Omni Pro II emulator over TCP. Useful for clicking through the integration before you point it at your real panel, for screenshots, and for catching protocol regressions when you change the library.

  • Docker + docker compose (compose v2).
  • ~500 MB of free disk for the HA + builder images.
  • Port 8123 free on localhost.
  • The omni-pca source repo cloned locally (git clone https://git.supported.systems/warehack.ing/omni-pca).
Terminal window
cd omni-pca/dev/
make dev-up

Two containers come up:

  • omni-pca-dev-ha — Home Assistant 2026.5, mounting ../custom_components/omni_pca read-only so HA loads the integration from your working tree.
  • dev-mock-panel-1 — a long-running mock panel on port 14369 with a populated state: 5 zones, 4 units, 2 areas, 2 thermostats, 3 buttons, two valid user codes (1234 and 5678).
Terminal window
make dev-logs # tail HA + mock logs
make dev-down # stop everything
make dev-reset # wipe HA state and start fresh

Wait ~30 seconds for HA to finish booting. You’ll see the usual http.log running line in the logs when it’s ready.

Open http://localhost:8123/ in a browser. HA’s first-run wizard takes ~60 seconds:

  1. Set up a user account — anything works for local testing.
  2. Pick a location (or skip it).
  3. The wizard’s “We found compatible devices” step will already list HAI/Leviton Omni Panel — that’s our manifest.json getting picked up automatically. You can finish the wizard either way; we’ll add the integration manually next.

Settings → Devices & Services → Add Integration, search for HAI/Leviton Omni Panel, then fill in:

FieldValue
Hosthost.docker.internal
Port14369
Controller Key000102030405060708090a0b0c0d0e0f

Submit. Within 5 seconds you should see one device — Omni Pro II — with ~38 entities discovered:

Omni Pro II device page after the dev-stack mock seeds 5 zones, 4 units, 2 areas, 2 thermostats, and 3 buttons

Try a few things from the HA UI:

  • Toggle a light. Living Lamp’s switch flips the mock’s unit-state byte; the entity updates instantly via the synthesized UnitStateChanged push event.
  • Arm an area. Click Main → Arm Away, enter 1234. The area transitions to armed_away and the mock pushes ArmingChanged.
  • Use the wrong code. Same flow with 9999 — HA toasts a service error and the area stays disarmed. The mock validated the code server-side and responded Nak.
  • Trigger a panel button. Press Good Morning — mock acks the EXECUTE_BUTTON command (no state change to observe, but check the logs).
  • Open Developer Tools → States. Filter for omni_pro_ii and watch attributes mutate as you interact with entities elsewhere in the UI.

Edit custom_components/omni_pca/binary_sensor.py (or anywhere in the integration), then:

Terminal window
docker compose restart homeassistant

HA picks up the change in ~10 seconds. The mounted volume is :ro so you can’t accidentally write back from the container.

The HA container is the real upstream Home Assistant image. The mock container is the same MockPanel class our integration tests use, exposed via a tiny script (dev/run_mock_panel.py). HA opens an encrypted session to the mock over real TCP — full handshake, per-block whitening, real AES, real CRC. If the protocol agrees end-to-end, every entity materialises; if anything’s off (a missing opcode handler, a wrong byte offset), it shows up as a missing entity or an unavailable state.

This is the same loop the project itself uses — every code change runs against the mock first. Bringing your own real panel online is the next tutorial.