UX-15: setup-only mode, and the wizard fixes F1, F2, meter, step 1

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>
This commit is contained in:
2026-09-28 08:48:21 +02:00
co-authored by Claude Opus 5.5
parent 4de96752ff
commit 159d7461d8
20 changed files with 1075 additions and 71 deletions
+10 -5
View File
@@ -94,11 +94,13 @@
function render() {
renderPill();
const structured = S.now && S.plan && !S.home_error;
banner($("#home-banner"), S.level, S.banner, structured ? S.state.sub : "", S.level === "bad" ? "alert" : null);
// UX-15: setup-only mode commands nothing and has no control to offer.
const setup = S.setup_only === true;
banner($("#home-banner"), S.level, S.banner, structured ? S.state.sub : setup ? t("setup_only.description") : "", S.level === "bad" ? "alert" : null);
["#c-now", "#c-plan", "#c-peak", "#c-attn"].forEach((id) => { $(id).hidden = !structured; });
if (structured) { renderNow(); renderPlan(); renderPeak(); renderAttention(); }
renderControl(structured);
renderDetails(!structured);
renderControl(structured, setup);
renderDetails(!structured && !setup);
renderBatteries();
}
@@ -270,12 +272,15 @@
badge.textContent = n; badge.hidden = !n;
badge.classList.toggle("bad", list.some((a) => a.severity === "alarm"));
}
function renderControl(structured) {
function renderControl(structured, setup) {
const running = structured ? S.control.running : null;
const btn = $("#btn-ctl");
btn.textContent = running === false ? t("control.start") : t("control.stop");
btn.className = "btn" + (running === false ? " primary" : "");
$("#ctl-text").textContent = structured ? S.control.text : "";
btn.hidden = !!setup;
$("#btn-setup").hidden = !setup;
$("#c-ctl .more").hidden = !!setup;
$("#ctl-text").textContent = structured ? S.control.text : setup ? t("setup_only.control") : "";
$("#hint").textContent = S.hint || "";
const docs = $("#docs-link");
if (structured && S.control.docs_url) { docs.href = S.control.docs_url; docs.hidden = false; } else docs.hidden = true;