A new install names no inverter. When that is the startup gate's only refusal, the add-on now serves the setup wizard in setup-only mode instead of exiting 78. It builds no controller, MQTT client or meter task, its HomeAssistant is read-only (every service call refuses before sending), Controller refuses a read-only HomeAssistant, and /action answers 409. Any other refusal still exits 78. DECISIONS/UX-15-setup-only-mode.md. Wizard fixes from the UX-13 P4 test: - F1: /config/validate converts what it can, so a conversion error blocks its own step and the live checks still run; save 422 carries steps. - F2: saves send the model the page opened (base); only keys this page changed are written onto the blob stored now. - A meter entity missing from Home Assistant is red on the last step. - Step 1 says the reading went down after a skip that followed a fall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
5.6 KiB
Markdown
102 lines
5.6 KiB
Markdown
# UX-13 P4 — the setup wizard, and when it opens by itself
|
|
|
|
**Status: DECIDED 2026-09-28 (P4 dev; the design and mockup were approved
|
|
2026-09-27).** Card 6ab979852f3eca02fb40f69e. Builds on
|
|
[[UX-13-one-layout-table]]. Design note: `goodwe-session/UX-13-design.md` §1, §3.6.
|
|
|
|
## 1. What the wizard is
|
|
|
|
The Setup tab draws the Installer view's page model one step at a time: a
|
|
field is on step *n* when its `uilayout.Field` says `wizard=n`. It uses P2's
|
|
controls, `/config/validate` and `/config/save`. No route was added.
|
|
`uilayout.py` holds what a field cannot say: the kettle table (`KETTLE`), the
|
|
optional step and the shadow switch (`SHADOW`). Step 5 has no battery modes
|
|
yet (OPT-11); it says they come later.
|
|
|
|
A step moves on when no refusal is anchored to one of its shown fields
|
|
(`uipage.step_problems`). A refusal on a hidden field, a field on no step or
|
|
no field at all blocks the last step, which lists every refusal.
|
|
|
|
## 2. When it opens by itself (`uipage.setup_needed`)
|
|
|
|
The page opens `#setup` only when it is opened with no route, and only when
|
|
`/config/model` gives a reason:
|
|
|
|
- **`no_power_request`**: the stored options name no inverter to control.
|
|
- **`first_start`**: this boot found no `/data/state.json` (`Store.fresh`), the
|
|
Store has no `setup_done` and no `auto` key, and control is not started.
|
|
`auto` is the key Start/Stop writes. Neither the startup gate's exit 78 nor
|
|
setup-only mode (UX-15) writes the Store, so a new install's first good boot
|
|
is fresh.
|
|
|
|
"Never started" alone is not enough: both TEST-23 add-ons have `auto_start`
|
|
off and no `auto` key, and one has run in shadow for weeks (review of PR #146,
|
|
R1). So a Store read from a file is an old site, and it is never sent to the
|
|
wizard. A new install that restarts before the page is opened is not sent
|
|
either, and its Setup tab still works. A site that runs control, or ever
|
|
started or stopped it, is never sent. Without a Store the answer is no. The wizard's save writes
|
|
`setup_done` (an ISO time) to the Store. A save from the Installer view does
|
|
not.
|
|
|
|
**Decided since, in [[UX-15-setup-only-mode]] (owner, 2026-09-28):** a
|
|
brand-new install ships `setpoint_entity: ""`, and it no longer exits 78. When
|
|
naming no inverter is the gate's only refusal, the add-on serves the page in
|
|
setup-only mode, which commands nothing, and the wizard opens with
|
|
`no_power_request`. Setup-only mode writes no Store file, so the first-run
|
|
rule above still holds for an install set up on the Configuration tab.
|
|
|
|
## 3. The kettle test and the sign checks
|
|
|
|
Step 1 reads the meter's entities now (`GET ha/entities?entity=`, one
|
|
`hass.state()` read, in W) before and after a kettle is switched on. The
|
|
change, not the sign, decides: a rise of at least 500 W keeps `meter_invert`
|
|
off, a fall turns it on. For a net entity or import and export, a fall cannot
|
|
be fixed by an option, so the step is not passed and the page says what to
|
|
check. A meter the add-on reads itself (HomeWizard, MQTT) cannot be read by
|
|
the page, so the test is skipped and Home shows the direction after the
|
|
restart. The installer may skip the test, because a cloud can fake a fall;
|
|
the last step then warns, and after a fall it says the reading went down.
|
|
|
|
The battery and solar signs cannot be forced from the page, which never
|
|
commands an inverter. The last step reports each battery's reading with its
|
|
direction ("reads 800 W charging now, so check the inverter shows the same")
|
|
and warns when a solar entity reads below zero without its "reads negative"
|
|
switch. It also warns when meter + solar + battery, all known, add up to a
|
|
house using less than -300 W: a battery sensor reversed while giving power.
|
|
An idle or charging battery cannot be judged that way.
|
|
|
|
## 4. The last step
|
|
|
|
`validate.setup_checks()` sits beside the rules because it names option keys.
|
|
It reads nothing itself: `web.py` passes a Home Assistant entity list at most
|
|
5 s old, the running options, the running meter reading and today's price
|
|
count. Tiers are ok, info, warn and bad. A power request entity missing from
|
|
Home Assistant is **bad**, because a write to it is answered 200 (SAFETY-77)
|
|
and only the entity list can tell. So is Home Assistant not answering, since
|
|
the list is then unknown. The wizard does not save while anything is
|
|
bad. It then saves through P2's path (`setup: "done"`) and asks for the
|
|
restart as the second, confirmed request, with a dialog that names the 0 W.
|
|
The shadow switch is on by default and writes `shadow_mode: on`, which holds
|
|
every inverter at 0 W after a restart even if control is started. Off keeps the
|
|
stored value, or `auto` if that was `on`. The switch goes only on the model the
|
|
wizard posts, never into the page model the Installer view also saves.
|
|
|
|
## 5. The stale save lists only what moved (P2 test, finding a)
|
|
|
|
`/config/model` remembers the last 16 blobs it handed out, by fingerprint. On a
|
|
stale save the add-on compares that blob with what is stored now, so the banner
|
|
lists only the other surface's changes. `changed` (stored against the posted
|
|
blob) is unchanged for the classic page's "theirs → mine" list. When the
|
|
fingerprint was not handed out by this process, it falls back to `changed`.
|
|
|
|
## 6. How it is held
|
|
|
|
- test_setup.py: the steps and tables, the kettle logic (and the page's copy of
|
|
it), step blocking, the first-run rule on new and TEST-23 sites, the live
|
|
checks, the save through the P2 path, the stale fix, and the words.
|
|
- tools/ux13_wizard_blobs.py produces every blob the wizard can save from a new
|
|
install and three TEST-23 sites (88 accepted by the shipped validator).
|
|
UX-15 added the shipped defaults as a start (setup-only mode). At P4
|
|
all 88 were accepted by Supervisor 2026.08.0's `AppOptions`, offline, 78
|
|
keys each.
|