Ryan Malloy 4781f4d276
Some checks are pending
Validate / HACS validation (push) Waiting to run
Validate / Hassfest (push) Waiting to run
panel: trigger initial loadList from discover, prefer loaded entries
Two bugs surfaced when smoke-testing against a real OmniPro II:

1. Empty list after page load. _discoverViaList ran fire-and-forget;
   connectedCallback then synchronously checked _entryId (still null
   because await hadn't resolved) and skipped _loadList. The panel
   rendered "No programs match the current filters" forever — until
   the next 5-second poll tick, which never fires because
   _startRefreshTimer was also gated on the same null check.

   Fix: have _discoverViaList itself trigger _loadList and
   _startRefreshTimer after _entryId lands. The connectedCallback /
   updated paths can stay gated on _entryId; the discover path now
   takes ownership of "do the initial load too."

2. Dev installs with both a working entry and a setup_retry entry
   (mock container down, real panel up) had the panel pick the
   setup_retry one first and surface "panel not configured" on every
   call. Fix: prefer entries with state === "loaded" in the discover
   step, falling back to first entry only when none are loaded.

Also: screenshot.py drops the seed-via-WS step (was unsafe — would
write Programs to whatever entry is loaded, including real panels).
Updates the in-page click helpers to walk the shadow DOM recursively
instead of hardcoding HA's host-element path, so detail/editor
screenshots work on the actual depth-8 element location.

Smoke test against real panel: 154 programs render correctly with
structured English, BEDTIME / OPEN BIG GAR / Zone 133 events all
decoded, B. GAR MAN DOOR [SECURE] live-state badge visible.
Detail panel + editor mode both function end-to-end.
2026-05-16 17:48:17 -06:00
..

Omni Programs side panel — frontend

Lit/TypeScript source for the HA side panel registered by websocket.py:async_register_side_panel. The build output (../www/panel.js) is committed so end-users don't need Node installed.

Edit / rebuild

cd custom_components/omni_pca/frontend
npm install         # one-time
npm run build       # one-shot — drops a fresh ../www/panel.js
npm run watch       # rebuild on change (use during HA dev)

The build script (build.mjs) bundles the entry point + Lit + all imports into a single ESM file at ../www/panel.js. Source maps are inlined in --watch mode and stripped in production builds. Output is ~34 KB minified.

Layout

File Purpose
src/omni-panel-programs.ts The custom-element entry point. Defines <omni-panel-programs> (matching the panel_custom registration).
src/token-renderer.ts Token stream → Lit TemplateResult. Each TokenKind gets distinctive styling; REF tokens become buttons that dispatch a click.
src/types.ts TS interfaces mirroring the Phase-B websocket wire shapes. Short keys (k/t/ek/ei/s) match websocket.py:_tokens_to_json.

Wire contract

The panel calls three websocket commands (all defined in ../websocket.py):

  • omni_pca/programs/list — paginated, filterable summaries.
  • omni_pca/programs/get — full structured-English detail for one slot.
  • omni_pca/programs/fire — sends Command.EXECUTE_PROGRAM over the wire.

The frontend doesn't subscribe to push events; live-state badges refresh on a low-frequency poll (REFRESH_MS = 5000). That's a deliberate scope choice — switching to per-entity event subscription is a follow-up if the polling overhead becomes visible on huge installs.