SAFETY-112: say plainly that control waits for Home Assistant to start
checks / unit (pull_request) Successful in 36m38s
infra-pin / fresh (pull_request) Successful in 2s

The Supervisor proxy answers 502 to every add-on read, write and websocket
until Core is RUNNING (proxy.py api()/websocket() -> check_api_state()), so
the add-on cannot control while a slow integration holds HA's start, even
with its entities live. Measured on a safety112 repro stack with the
2026.08.0 proxy code: first write 1.0-4.4 s after RUNNING, 238-300 s after
live readings. Not routed around (DECISIONS/SAFETY-112-...).

While the API does not answer, the first status row is red and is the
banner ("Waiting for Home Assistant to finish starting"), Home shows a
"waiting" state in EN/NL instead of "Needs you now", and the log adds a
"still waiting" line every 5 minutes and the wait's length at the end.
A safety stop, a stalled loop or shadow mode keeps its own headline.
Control, writes, heartbeat and the stand-down are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 01:07:51 +02:00
co-authored by Claude Opus 5.5
parent 3050fa5208
commit d94cb0c263
11 changed files with 518 additions and 19 deletions
+15
View File
@@ -436,6 +436,17 @@ def banner_shadow(latched: bool) -> str:
return "Shadow mode: the battery is not controlled. Inverters stay at 0 W."
def banner_ha_waiting() -> str:
"""SAFETY-112: the Supervisor passes nothing on until Home Assistant has
finished starting, so the add-on can neither read nor write."""
return "Waiting for Home Assistant to finish starting"
def row_ha_waiting() -> str:
return ("Home Assistant is not answering, perhaps it is still starting. "
"Control resumes by itself when it answers.")
# Rows: control and the meter. "Control" is what the add-on does, as in
# "start control" (STYLE.md section 4).
def row_controller_starting() -> str:
@@ -1055,6 +1066,10 @@ def status_samples() -> list:
("banner_setup_only", None, banner_setup_only()),
("row_setup_only", AMBER, row_setup_only()),
("banner_shadow", None, banner_shadow(True)),
("banner_not_ready", None, banner_not_ready(
{"text": row_ha_waiting(), "banner": banner_ha_waiting()})),
("banner_ha_waiting", None, banner_ha_waiting()),
("row_ha_waiting", RED, row_ha_waiting()),
("row_controller_starting", GREEN, row_controller_starting()),
("row_controller_ran", GREEN, row_controller_ran(2)),
("row_controller_stalled", RED, row_controller_stalled(45, 30)),