- a 502 or refused read turns waiting on, the next good read off, and a read timeout never (two mutants survived without this) - the flag changes no write with live readings, not only with 502 ones - the page script's amber dot and batteries-tab banner for "waiting" - DECISIONS: about 5 minutes per start stage, not "at most" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.9 KiB
SAFETY-112 - control waits for Home Assistant to finish starting
Status: for review. Tests: test_safety112.py, test_messages.py.
Control is unchanged. The add-on now says plainly that it is waiting.
Cause
The Supervisor's proxy refuses every add-on request with 502 until Core's
state is RUNNING. The add-on reads every battery reading and writes every
power request through that proxy, so it cannot control until then. Readings
that are already live inside Home Assistant do not help.
Source, Supervisor 2026.08.0 (the image the live site ran at CFG-04):
supervisor/api/proxy.py,APIProxy.api()(REST, used for reads andnumber.set_value) andAPIProxy.websocket()both start withif not await self.sys_homeassistant.api.check_api_state(): raise HTTPBadGateway.supervisor/homeassistant/api.py,check_api_state()returnsstate.core_state == "RUNNING" or state.offline_db_migration. The state comes from Core's/api/core/state.- Upstream
mainhas the same gate in both handlers (read 2026-10-13). - A Core restart restarts only the Core container
(
homeassistant/core.py,restart()). The add-on keeps running.
The add-on itself has no wait. Nothing in app/ reads Core's state or waits
for an event. The loop reads its inputs every second, a 502 gives None,
and missing inputs give 0 W. P1 backoff is not involved: the live site reads
the meter straight from the HomeWizard (homewizard_local), not through Home
Assistant.
Measured
Repro stack safety112-*, all removed afterwards:
- Home Assistant 2026.9.4, as live.
- A test integration whose
async_setupsleeps, so Core staysNOT_RUNNING. - A fake meter and two fake inverters that update every 2 s from load.
- The Supervisor's own
APIProxyandHomeAssistantAPIfrom the 2026.08.0 image. Only the add-on token lookup, Core's access token and the container check are stubbed. - The add-on on
meter_source: homewizard_localagainst a fake HomeWizard.
Times are seconds after docker restart of Home Assistant:
| run | Core NOT_RUNNING |
entities live in HA | Core RUNNING |
proxy 502 | first write | wait from live readings |
|---|---|---|---|---|---|---|
base 3050fa5, N=2 |
13.1 | 17.1 | 253.1 | 0.9-252.7 (1 132 requests) | 254.7 | 237.6 |
base 3050fa5, N=1 |
13.1 | 18.1 | 256.1 | 1.5-255.1 (573 requests) | 257.2 | 239.1 |
| this change, N=2 | 12.2 | 17.2 | 313.2 | 1.4-312.5 (1 406 requests) | 317.6 | 300.4 |
| this change, N=1 | 12.2 | 17.2 | 255.2 | 1.2-254.1 (571 requests) | 257.1 | 240.0 |
The first write comes 1.0-4.4 s after RUNNING, so the whole wait is the
proxy's. With a 400 s sleep Core moved on at 300.88 s on its own (bootstrap's
stage timeout), so one slow integration holds a start stage for about 5
minutes. The whole start can take longer: the live one took 414 s.
Before this change the page said "Not ready — Battery level: no usable reading", which sends the reader to Developer Tools for an entity that is fine.
Rule
- No way around the Supervisor. The add-on still talks to Home Assistant only through the proxy.
- While the proxy does not answer on the API (
hass.OUTAGE.waiting()), the first status row is red: "Home Assistant is not answering, perhaps it is still starting. Control resumes by itself when it answers." The banner reads "Not ready — Waiting for Home Assistant to finish starting". The Home pill reads "Waiting for Home Assistant", in English and Dutch. - A safety stop, a stalled control loop or shadow mode keeps its own banner and state over the wait, because none of them clears by itself.
- The log has one WARNING when Home Assistant stops answering, an INFO line every 5 minutes while it is down, and an INFO line with the length of the wait when it answers again.
- Only the API counts. The meter websocket alone does not, because the meter has its own row.
Why it is safe
The change is text only. cycle(), the writes, the heartbeat, the stand-down
on shutdown and every watchdog are untouched. test_safety112.py part 3 shows
the same targets and writes with the flag on and off, for N=1 and N=2.
Open, for the owner
The add-on cannot control while Home Assistant starts. These ways around it were not taken:
- Talk to Core directly with a long-lived token (
http://homeassistant:8123). Core's own API answers whileNOT_RUNNING. In the repro/api/core/stateanswered from 12-13 s, and a directnumber.set_value10 s intoNOT_RUNNINGreturned 200 and set the value. The cost is a secret stored at every client site, whichhass.pyexists to avoid. - Talk to the control board directly (ESPHome native API), so control does not need Home Assistant at all. This is a large change.
- Remove the slow start at the site. On 2026-10-12 the Solis integration's discovery held the start. Fixing or disabling it shortens the wait there, but not on a client site with another slow integration.