4 Commits

Author SHA1 Message Date
6f92671cf2 panel: preserve out-of-range object refs in editor dropdowns
Some checks are pending
Validate / HACS validation (push) Waiting to run
Validate / Hassfest (push) Waiting to run
Real-world programs reference object indexes well past the coordinator's
discovery range — typical example from a live OmniPro II: a program
that's "Turn ON Unit 33025" where the unit number is a raw byte value
from undecoded extended-output addressing. The discovered units bucket
only covers slots up to ~511, so 33025 doesn't match any entry.

Before this commit the dropdown silently fell through to the first
known unit (e.g. ROOM ONE), making it look like the user had selected
that unit. The underlying draft.pr2 stayed at 33025, but a user who
glanced at the form and clicked Save would either preserve the original
(if they didn't touch the select) or accidentally clobber it with the
first list item (if they did).

Fix: _bucketWithPreserve prepends a synthesized option
"(undiscovered <kind> <idx> — preserve original)" when the current
value isn't represented. Applies in all four picker sites:

  * Action object picker (Unit / Zone / Area / Button for action commands)
  * EVENT trigger Button picker
  * EVENT trigger Zone picker
  * EVENT trigger Unit picker

The synthesized entry sits at the top of the list (visually distinct)
and is the selected default. Picking any other entry from the dropdown
then becomes an explicit choice — no more silent coercion.

Smoke-tested against the real panel: slot #1 (WHEN OPEN BIG GAR →
Turn ON Unit 33025) now shows "#33025 (undiscovered unit 33025 —
preserve original)" as the selected Unit option. Screenshots updated.
2026-05-16 19:55:16 -06:00
4781f4d276 panel: trigger initial loadList from discover, prefer loaded entries
Some checks are pending
Validate / HACS validation (push) Waiting to run
Validate / Hassfest (push) Waiting to run
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
116591be90 dev: refresh integration screenshots (2026-05-10 + 2026-05-11) 2026-05-14 02:32:54 -06:00
f6a09592f1 Live demo: HA in docker discovers + drives mock panel, screenshots captured
dev/screenshot.py — end-to-end automated demo:
  * onboards HA via /api/onboarding (user creation + auth_code flow)
  * subsequent runs log in via /auth/login_flow with saved credentials
  * adds the omni_pca config entry via /api/config/config_entries/flow
  * uses HA's template REST endpoint to discover the panel device_id
  * launches a headless chromium via playwright with prefetched auth tokens
  * captures 6 deep-linked screenshots:
      01-overview.png            — Lovelace
      02-integrations-list.png   — HAI/Leviton sitting next to HA's built-ins
      03-omni-pca-config.png     — '1 device · 38 entities', custom integration
      04-panel-device.png        — Omni Pro II device page with full controls
      05-entities-omni.png       — config_entry filtered entities table
      06-developer-states.png    — alarm_control_panel.omni_pro_ii_main with
                                   raw_mode_name=OFF, code_arm_required=true,
                                   etc. proving real entity state from mock

dev/docker-compose.yml — mock-panel command rewritten:
  * Mounts only src/ and run_mock_panel.py (read-only) instead of the full
    project so uv doesn't try to recreate the host's .venv on a RO mount
  * Installs cryptography via uv pip install --system
  * PYTHONPATH set to /tmp/mock/src so omni_pca imports work without a
    package install

dev/artifacts/screenshots/2026-05-10/ — six PNGs from the run.

.gitignore — adds dist/ for build artifacts.

Confirmed end-to-end: HA discovered the integration via mDNS hint
(showed up in the onboarding wizard's compatible-devices step), the
config flow connected to the mock over host.docker.internal:14369,
materialized 38 entities across 8 platforms (alarm_control_panel,
binary_sensor, button, climate, event, light, sensor, switch), and
displayed everything in the device + entity registries with friendly
names and attributes intact. The integration name hash is 38 entities
because the mock seeds 5 zones (binary + bypass) + 4 units + 2 areas +
2 thermostats + 3 buttons + 3 system-level binary sensors + 2 system
sensors + 6 thermostat sensors + 1 event entity = 38 (matches HA UI).
2026-05-10 16:17:33 -06:00