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
39 lines
2.5 KiB
JSON
39 lines
2.5 KiB
JSON
{
|
|
"groups": {
|
|
"meter": {"name": "Meter", "description": "Where the grid reading comes from."},
|
|
"inverters": {"name": "Inverters and batteries", "description": "The inverters the add-on controls, and your solar."},
|
|
"tariff": {"name": "Tariff and prices", "description": "What you pay and are paid per kWh."},
|
|
"safety": {"name": "Safety limits", "description": "The peak limit, starting and stopping, and the safety stops."},
|
|
"maintenance": {"name": "Maintenance", "description": "The monthly battery cycle and the log."},
|
|
"tuning": {"name": "Control tuning", "description": "For support only, because these were measured on real hardware."}
|
|
},
|
|
"parts": {
|
|
"meter.timing": {"name": "Timing"},
|
|
"inverters.solar": {"name": "Solar"},
|
|
"tariff.selling": {"name": "Charging and selling on price"},
|
|
"tariff.feed": {"name": "Price feed details"},
|
|
"safety.peak": {"name": "Peak limit"},
|
|
"safety.start": {"name": "Starting and stopping"},
|
|
"safety.stops": {"name": "Safety stops", "description": "Measured on real hardware, so leave them unchanged."},
|
|
"maintenance.support": {"name": "Support"}
|
|
},
|
|
"fields": {
|
|
"solar": {"name": "Solar inverters", "description": "Each solar inverter's power sensor, which the add-on records but does not use for control."},
|
|
"contract": {"name": "Your contract", "description": "How your supplier prices each kWh you take from the grid."},
|
|
"region": {"name": "Region", "description": "Fills in the grid costs, levies and VAT for the year shown, so check them on your bill."},
|
|
"fallback": {"name": "Price when the feed is down", "description": "Used when the price source does not answer."},
|
|
"fixed_price": {"name": "Price per kWh, all in", "description": "Energy, supplier costs, grid costs, levies and VAT together."},
|
|
"day_price": {"name": "Day price, weekdays 07:00 to 22:00", "description": "All in, as on your bill."},
|
|
"night_price": {"name": "Night and weekend price", "description": "All in, as on your bill."}
|
|
},
|
|
"choices": {
|
|
"contract": {"none": "No prices", "fixed": "Fixed price", "daynight": "Day and night", "dynamic": "Dynamic, hourly", "custom": "Other time windows"},
|
|
"fallback": {"none": "None", "fixed": "One price", "daynight": "Day and night", "custom": "Other time windows"},
|
|
"region": {"flanders": "Flanders", "own": "I enter my own figure"}
|
|
},
|
|
"applies": {
|
|
"restart": {"name": "Applies after a restart"},
|
|
"now": {"name": "Applies right away"}
|
|
}
|
|
}
|