The Setup tab draws the Installer view's page model one step at a time, from each field's `wizard` step in app/uilayout.py: meter (with the kettle test), inverters and batteries, solar (optional), prices, peak limit (battery modes: "comes in a later update", OPT-11), check and start. It reuses P2's controls and saves through /config/validate and /config/save. No route was added. - First run: /config/model says `setup_needed` when the stored options name no power request entity, or on a new install (no `setup_done` and no `auto` in the Store, control not started). The page opens #setup only when opened with no route. The wizard's save writes `setup_done`. - Kettle test: GET ha/entities?entity= reads one entity now, in W. The change decides: up keeps meter_invert off, down turns it on; a net or import/export meter that goes down blocks the step with what to check. - Last step: validate.setup_checks() beside the refusals: the meter reads, each power request entity exists in HA (bad if not, SAFETY-77), battery levels and power with direction, solar sign, prices. Saves with shadow_mode on by default, then a confirmed restart naming the 0 W. - P2 test finding (a): a stale save lists only the keys the other surface moved (the blob handed out on open, by fingerprint, against stored now). - test_setup.py; tools/ux13_wizard_blobs.py makes the 88 blobs the wizard can save; all accepted by Supervisor 2026.08.0 AppOptions (offline). - DECISIONS/UX-13-setup-wizard.md; DOCS "Setup, for a new install". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p
94 lines
4.9 KiB
Markdown
94 lines
4.9 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`**: the Store has no `setup_done` and no `auto` key, and
|
|
control is not started. `auto` is the key Start/Stop writes, and
|
|
config.yaml ships `auto_start` off (the same "fresh install" reading as
|
|
app/shadow.py's `auto` default), so this is a site that has never run or
|
|
been stopped by hand and never finished the wizard.
|
|
|
|
A site that runs control, or ever started or stopped it, is never sent to the
|
|
wizard. 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.
|
|
|
|
**Not decided here, for the owner:** the add-on exits 78 before it serves the
|
|
page when the stored options name no power request entity, which is what a
|
|
brand-new install ships (config.yaml's `setpoint_entity: ""`). So on a truly
|
|
new install the installer fills in "Inverter setpoint entity" on the
|
|
Configuration tab first, and the wizard opens on the next page visit.
|
|
Serving a setup-only page from a refused configuration would change the CFG-01
|
|
startup gate and UX-02 §4 ("the panel is not available when it is most
|
|
needed"), so it is left as an open question.
|
|
|
|
## 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; the last step then warns.
|
|
|
|
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.
|
|
|
|
## 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. 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`.
|
|
|
|
## 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). At P4
|
|
all 88 were accepted by Supervisor 2026.08.0's `AppOptions`, offline, 78
|
|
keys each.
|