UX-13 P0: layout table, page<->options derivation, warning tier, entity list
The foundation for the new settings page. No visible UI change. - app/uilayout.py: screen -> group -> part -> field for every option in config.yaml (67 fields, 11 derived, 0 hidden), with control, unit, display scale, step, entity kind, show conditions, Simple/wizard placement and "applies after a restart". Ranges stay in the schema, words in translations and the new app/ui_text/en.json. - app/uimodel.py: options_to_ui()/ui_to_options() for the derived keys (one inverter vs the list, solar rows, static_profile from simple prices, a year-stamped region preset for the levies figure, and an OPT-11 hook for arbitrage_enabled that changes nothing yet). Unchanged derivations write the stored values back, so the round trip is exact. - validate.py: validate.Note, a warn/info tier. Each logged warning is also a note when a list is passed; config_notes() adds checks that need live data (missing entity, a kW sensor read as W, a limit below this month's peak). /config/validate returns them as "notes". No refusal changed: 261 recorded validator calls, 0 differ (tools/ux13_validator_calls.py). - GET ha/entities: Home Assistant's entity list for the picker, read through hass.states(), cached 30 s, 10 s timeout. When HA is down it answers 200 with the old list marked old, or an empty list that says why. - DECISIONS/UX-13-one-layout-table.md amends UX-02's "no option key in UI code": keys may appear in uilayout.py and uimodel.py only, both tested. - Tests: test_uilayout, test_uimodel (round trip over the recorded calls, the TEST-23 option files and config.yaml defaults), test_haentities, a warning-tier part in test_validate; test_messages scans notes and ui_text; test_pv and test_selfconfig part 7 widened on purpose. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
652097abde
commit
15627c38a6
@@ -1808,6 +1808,47 @@ check("an UNREADABLE shadow_mode resolves to SHADOW, not to live - the "
|
||||
and shadow.engaged({"shadow_mode": ""}, control_started=True) is True
|
||||
and shadow.mode({"shadow_mode": "garbage"}) == "on")
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
print("\nUX-13 - the warning tier: notes for the settings page, never refusals")
|
||||
from app.validate import Note, WATT_ENTITY_KEYS, config_notes # noqa: E402
|
||||
|
||||
_warned = base(integrator_max_w=3000, maintenance_discharge_w=2500,
|
||||
price_provider="generic_dayahead", static_profile="")
|
||||
_notes = []
|
||||
_with = validate_startup_config(_warned, -5000, 5000, notes=_notes)
|
||||
check("a config with three logged warnings starts, and gives three notes, "
|
||||
"each filed under the setting it names",
|
||||
_with == [] and validate_startup_config(_warned, -5000, 5000) == []
|
||||
and sorted(n.keys for n in _notes) == [
|
||||
("integrator_max_w",), ("maintenance_discharge_w",),
|
||||
("static_profile",)]
|
||||
and all(n.tier == "warn" and f"({n.keys[0]})" in n.text for n in _notes))
|
||||
_refused = base(setpoint_entity="", integrator_max_w=3000)
|
||||
_n2 = []
|
||||
check("collecting notes changes no refusal",
|
||||
validate_startup_config(_refused, notes=_n2)
|
||||
== validate_startup_config(_refused) != [] and len(_n2) == 1)
|
||||
_rows = {"number.goodwe_setpoint": {"unit": None},
|
||||
"sensor.soc": {"unit": "%"}, "sensor.batt": {"unit": "kW"}}
|
||||
_cn = config_notes(base(), entities=_rows)
|
||||
check("a battery power sensor in kW is warned about: the add-on takes it as "
|
||||
"watts", [n.keys for n in _cn] == [("batt_entity",)]
|
||||
and "kW" in _cn[0].text)
|
||||
_cn = config_notes(base(meter_entity="sensor.grid"), entities={})
|
||||
check("an entity Home Assistant does not have is named, and only the ones "
|
||||
"this meter source and roster shape read",
|
||||
sorted(n.keys[0] for n in _cn) == ["batt_entity", "meter_entity",
|
||||
"setpoint_entity", "soc_entity"])
|
||||
check("with no entity list (Home Assistant down) those checks are skipped",
|
||||
config_notes(base(), entities=None) == [])
|
||||
check("a peak limit below this month's peak is warned about",
|
||||
[n.keys for n in config_notes(base(peak_cap_w=2500),
|
||||
month_peak_w=2900)] == [("peak_cap_w",)]
|
||||
and config_notes(base(peak_cap_w=3500), month_peak_w=2900) == [])
|
||||
check("pv.py converts solar units itself, so no solar key is read as watts",
|
||||
not [k for k in WATT_ENTITY_KEYS if k.startswith("pv_")]
|
||||
and isinstance(_cn[0], Note))
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
print()
|
||||
if fails:
|
||||
|
||||
Reference in New Issue
Block a user