From 3a55519b74033e65ea82ccb80f2d6c85487d69ee Mon Sep 17 00:00:00 2001 From: James Mackay Date: Mon, 12 Oct 2026 07:39:01 +0100 Subject: [PATCH] fix(config-flow): the setup menu was unreachable under real HA (t57 UNIT 0a) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t61 F1 (BLOCKER) + F2 + F3 + F5, all reproduced against a real FlowManager before fixing. The OPTIONS flow was verified good and is untouched by this commit; the SETUP flow was broken in three independent ways. F1 — `_show_setup_menu` passed `errors=` to `async_show_menu`, which has no such parameter (HA 2026.10.1: step_id / menu_options / sort / description_placeholders). Every path that went near the menu therefore raised `TypeError` the moment the first form was submitted or ADB pairing was skipped. The path map, driven end to end: FOUR of the five ordinary paths died before create_entry ("ADB skip", "ADB paired + protocol off", "ADB skip + protocol skip", "pair ADB"); only "protocol only" survived, and it never showed the menu — so a new entry could only be created on that one path and the sub-screens were unreachable. F2 — the menu's rows broke the rule this repo documents: HA dispatches a row to `async_step_` AND dispatches a submitted form by its OWN step_id, so a row named `adb` with a handler called `async_step_adb_pair` is an UnknownStep, and a row named `receiver` rendering `step_id="setup_receiver"` is a form that cannot be submitted. The rows are now their handler names (`receiver`, `connection`, `adb_pair`, `protocol_pair`, `finish`) and the two sub-screens render their row id. There is also a `_paired_adb` guard now, so a completed ADB pairing stops offering that row — the rule the protocol row already followed. F3 — both menus keyed their row labels under a bare `menu`, which core does not read (56 core strings files use `menu_options`), so every row fell back to its raw id in the browser. Renamed in both strings files. F5 — the per-command script merge is field-wise now: a `script_` key that is present and empty removes that command, one with a value sets it, and a key the submission does not carry is untouched — the same rule as every other option. The wholesale rebuild depended on the page always being complete, so a partial submission silently wiped the commands it omitted. THE SYSTEMIC HALF (F1), closed as asked: `ha_api_drift.HA_CALL_OWNERS` now covers the flow-display APIs (`async_show_menu`, `async_show_progress`, `async_show_progress_done`, plus the existing form/abort/entry owners), so the drift engine resolves their REAL signatures and flags an unknown keyword. `tests/test_ha_api_drift.py` carries a pre-fix source using `errors=` to prove the new owner is non-vacuous. Every harness stub now mirrors the real signature (no `errors` on a menu) and a stub-level test pins that parameter set, so a stub cannot go back to accepting anything. On the pre-fix tree the drift engine reports exactly this: config_flow.py:898: unknown-kwarg: async_show_menu() has no 'errors' parameter; real signature: (self, *, step_id=None, menu_options, sort, description_placeholders) -> _FlowResultT TESTS: tests/test_config_flow_real_ha.py gains a SETUP section that drives the real flow — the first form, the ADB skip, the pairing guard, EVERY menu row (dispatch AND re-submittable step_id), an accepted sub-screen submission, a full connection+receiver+finish walk merging into one entry, the menu's pairing note, and the five-path map as a parametrised test. 11 of those fail on the pre-fix tree; the drift engine failure above is the 12th. The stub suite additionally pins the menu stub's signature and the `menu_options` key in both strings files. Also in this commit (t57 F4): the real-HA file claimed entity-valued pages could not be driven there, which was false (it has always submitted `switch.soundbar`). The claim is gone and the scripts page is driven through SET (two scripts selected, real entity ids) → CLEAR (one selector emptied) → SAVE → REOPEN, so the SET half is pinned in the real flow and not only against the selector objects. --- .../nvidia_shield/config_flow.py | 121 ++++-- custom_components/nvidia_shield/strings.json | 118 +++--- .../nvidia_shield/translations/en.json | 118 +++--- tests/ha_api_drift.py | 10 +- tests/test_config_flow_pairing.py | 8 +- tests/test_config_flow_protocol.py | 8 +- tests/test_config_flow_real_ha.py | 383 ++++++++++++++++-- tests/test_config_flow_ux.py | 116 +++++- tests/test_ha_api_drift.py | 23 ++ tests/test_standalone_setup.py | 16 +- 10 files changed, 713 insertions(+), 208 deletions(-) diff --git a/custom_components/nvidia_shield/config_flow.py b/custom_components/nvidia_shield/config_flow.py index 92e1b1f..47aa294 100644 --- a/custom_components/nvidia_shield/config_flow.py +++ b/custom_components/nvidia_shield/config_flow.py @@ -875,29 +875,66 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._setup_extra_options = {} return self._setup_extra_options - def _show_setup_menu(self, errors=None): - """The setup menu: connection, receiver, and finishing the entry (t56). + def _show_setup_menu(self): + """The setup menu: receiver, connection, pairing, finish (t56). - Home Assistant's standard menu step: it renders every option as a row - and dispatches the choice to ``async_step_