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:
@@ -20,7 +20,8 @@ import json
|
||||
from .price import FLEMISH_TAXES_COMPONENTS_EUR_KWH
|
||||
from .static_tariff import ProfileInvalid, parse_static_profile
|
||||
|
||||
__all__ = ["options_to_ui", "ui_to_options", "REGION_PRESETS",
|
||||
__all__ = ["options_to_ui", "ui_to_options", "ui_to_options_partial",
|
||||
"REGION_PRESETS",
|
||||
"arbitrage_from_batteries", "DeriveError", "COUNT", "GROWN"]
|
||||
|
||||
|
||||
@@ -32,6 +33,7 @@ class DeriveError(ValueError):
|
||||
def __init__(self, text, code="", field=""):
|
||||
super().__init__(text)
|
||||
self.code, self.field = code, field
|
||||
self.keys = () # the option keys the failed derivation writes
|
||||
|
||||
|
||||
# The page model's inverter count, which show conditions read, and what adding
|
||||
@@ -337,14 +339,31 @@ def options_to_ui(options: dict) -> dict:
|
||||
def ui_to_options(model: dict) -> dict:
|
||||
"""The options blob for a page model. Raises DeriveError on a model no blob
|
||||
can express, so the page says so instead of saving something else."""
|
||||
out, errors = ui_to_options_partial(model)
|
||||
if errors:
|
||||
raise errors[0]
|
||||
return out
|
||||
|
||||
|
||||
def ui_to_options_partial(model: dict):
|
||||
"""(blob, [DeriveError]): every derivation that can be made, and the
|
||||
stored values (`keep`) for each one that cannot. For the checks only, so a
|
||||
step can be blocked by its own field and the rest still checked (UX-15
|
||||
F1). Never saved: `ui_to_options()` raises instead."""
|
||||
keep = model.get("keep") or {}
|
||||
out = {k: copy.deepcopy(v) for k, v in model.items()
|
||||
if k not in _MODEL_ONLY and k not in _OWNED
|
||||
and not k.startswith("ui.")}
|
||||
errors = []
|
||||
for keys, decode, encode in DERIVATIONS:
|
||||
was = decode(keep)
|
||||
if all(model.get(name) == value for name, value in was.items()):
|
||||
out.update({k: copy.deepcopy(keep[k]) for k in keys if k in keep})
|
||||
else:
|
||||
continue
|
||||
try:
|
||||
out.update(encode(model, keep))
|
||||
return out
|
||||
except DeriveError as err:
|
||||
err.keys = keys
|
||||
errors.append(err)
|
||||
out.update({k: copy.deepcopy(keep[k]) for k in keys if k in keep})
|
||||
return out, errors
|
||||
|
||||
Reference in New Issue
Block a user