omni-pca/dev/docker-compose.yml
Ryan Malloy 4ba8c2043e
Some checks failed
Validate / HACS validation (push) Has been cancelled
Validate / Hassfest (push) Has been cancelled
grafana: dashboard bundle + dev-stack integration
Adds a self-contained omni-pca/grafana/ bundle (InfluxDB v2 + Grafana
with pre-provisioned datasource and dashboard) plus dev-stack wiring
so iterating against the mock or real panel is one docker-compose-up.

The dashboard has four rows plus an insights row:

  System health   AC, battery, trouble, 24h event count
  Security        area arming state, recent events table, zone trips
  Climate         thermostat temperatures, HVAC mode
  Activity        event rate by type, top toggled units
  Insights        active zone bypasses, button press log, event distribution

Color-coded event_type tags persist across panels (alarms red, restores
green, batteries orange, etc.); explicit no-purple palette per CLAUDE.md.

The bundle is portable: any HA install can use it by running grafana/
docker compose up -d and pasting ha-snippet.yaml into configuration.yaml.
For the dev stack, dev/docker-compose.yml mounts the same provisioning
files so dev and prod stay in lockstep.

Verified end-to-end against the real Our House.pca panel (192.168.1.9):
the dashboard fills with live zone trips, X-10 unit toggles, and
push-event traffic within 30s of HA bootup.
2026-05-17 23:43:01 -06:00

176 lines
6.1 KiB
YAML

# Local dev stack: real Home Assistant talking to a MockPanel running on
# the host. Lets you click around the UI and grab screenshots without a
# physical Omni controller.
#
# make dev-up # start
# make dev-logs # tail HA logs
# make dev-down # stop and clean
#
# On every container start the HA service pip-installs the local
# `omni-pca` library from ../ into site-packages (the version pinned in
# the integration manifest isn't on PyPI yet, and we want our latest
# v1/ subpackage available either way). Source changes in src/omni_pca
# require a ``docker compose restart homeassistant`` to take effect.
#
# Once running, open http://localhost:8123 and:
# 1. Onboard with any name / location.
# 2. Settings -> Devices & Services -> Add Integration ->
# "HAI/Leviton Omni Panel".
# 3. Use one of:
# Mock panel (TCP):
# host host.docker.internal
# port 14369
# transport TCP
# controller_key 000102030405060708090a0b0c0d0e0f
# Real panel (UDP, v1 wire protocol):
# host <panel IP, e.g. 192.168.1.9>
# port 4369
# transport UDP
# controller_key <32 hex chars from the panel's .pca file>
services:
mock-panel:
image: ghcr.io/astral-sh/uv:python3.14-bookworm-slim
working_dir: /tmp/mock
volumes:
- ../src:/tmp/mock/src:ro
- ./run_mock_panel.py:/tmp/mock/run_mock_panel.py:ro
# Mount the captured .pca fixtures read-only so the mock can
# optionally seed its state from a real export. Set
# OMNI_PCA_FIXTURE in dev/.env (or pass on the command line) to
# activate; left unset, the mock uses the hard-coded sample.
- /home/kdm/home-auto/HAI:/fixtures:ro
environment:
PYTHONPATH: /tmp/mock/src
OMNI_PCA_FIXTURE: ${OMNI_PCA_FIXTURE:-}
command:
- sh
- -c
- "uv pip install --system --quiet cryptography && python /tmp/mock/run_mock_panel.py --host 0.0.0.0 --port 14369"
ports:
- "14369:14369"
networks:
- default
homeassistant:
image: ghcr.io/home-assistant/home-assistant:2026.5
container_name: omni-pca-dev-ha
depends_on:
- mock-panel
volumes:
- ./ha-config:/config
- ../custom_components/omni_pca:/config/custom_components/omni_pca:ro
# Make the whole library project (pyproject + src/ + dist/) available
# so the entrypoint override below can pip-install from local source
# before /init starts. This gives HA real dist-info for
# ``omni-pca==2026.5.10`` (which isn't on PyPI yet) and ensures the
# v1 subpackage is present.
- ../:/opt/omni-pca-src:ro
# Keep 8123 mapped on localhost for direct access during development;
# public traffic comes in via caddy-docker-proxy on the `caddy` net.
ports:
- "8123:8123"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- TZ=America/Boise
networks:
- default
- caddy
labels:
caddy: juliet.warehack.ing
caddy.reverse_proxy: "{{upstreams 8123}}"
# HA uses WebSockets for the frontend (lovelace state updates,
# config flow, etc.) so we need the streaming-friendly settings
# from CLAUDE.md, otherwise caddy closes the socket every ~15s.
caddy.reverse_proxy.flush_interval: "-1"
caddy.reverse_proxy.transport: http
caddy.reverse_proxy.transport.read_timeout: "0"
caddy.reverse_proxy.transport.write_timeout: "0"
caddy.reverse_proxy.transport.keepalive: 5m
caddy.reverse_proxy.transport.keepalive_idle_conns: "10"
caddy.reverse_proxy.stream_timeout: 24h
caddy.reverse_proxy.stream_close_delay: 5s
# HA's image entrypoint is /init (s6-overlay). We pre-install our
# local library against site-packages so HA's manifest-requirement
# check finds it, then exec /init normally.
entrypoint:
- sh
- -c
- |
set -e
pip install --quiet --no-deps --upgrade /opt/omni-pca-src
exec /init
# InfluxDB v2 + Grafana stack — kept inline rather than `extends:`-ing
# ../grafana/docker-compose.yml so this file stays self-contained and
# the named volumes get scoped to this compose project. The bundle
# compose stays the canonical ship-to-users version; we share its
# provisioning files via the volume mount on the grafana service.
influxdb:
image: influxdb:2.7-alpine
container_name: omni-pca-dev-influxdb
restart: unless-stopped
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUX_USERNAME:-admin}
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUX_PASSWORD}
DOCKER_INFLUXDB_INIT_ORG: omni-pca
DOCKER_INFLUXDB_INIT_BUCKET: ha
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUX_TOKEN}
DOCKER_INFLUXDB_INIT_RETENTION: 30d
volumes:
- influxdb-data:/var/lib/influxdb2
- influxdb-config:/etc/influxdb2
ports:
- "8086:8086"
networks:
- default
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8086/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 10s
grafana:
image: grafana/grafana:11.4.0
container_name: omni-pca-dev-grafana
restart: unless-stopped
depends_on:
influxdb:
condition: service_healthy
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
GF_AUTH_ANONYMOUS_ENABLED: "false"
GF_USERS_ALLOW_SIGN_UP: "false"
GF_LOG_LEVEL: warn
INFLUX_URL: http://influxdb:8086
INFLUX_TOKEN: ${INFLUX_TOKEN}
volumes:
- grafana-data:/var/lib/grafana
- ../grafana/provisioning:/etc/grafana/provisioning:ro
ports:
- "3000:3000"
networks:
- default
- caddy
labels:
caddy: grafana-omni.juliet.warehack.ing
caddy.reverse_proxy: "{{upstreams 3000}}"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/health || exit 1"]
interval: 10s
timeout: 3s
retries: 5
start_period: 15s
volumes:
influxdb-data:
influxdb-config:
grafana-data:
networks:
caddy:
external: true