Files
goodwe-addon/goodwe_controller/DECISIONS/SAFETY-128-late-readings.md
T
glenn schrooyenandClaude c70eeb6916
checks / unit (pull_request) Successful in 23m1s
infra-pin / fresh (pull_request) Successful in 1s
SAFETY-129 round 3: the real reason for the 16 s limit, a slow-start check, the row's tail
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-07 00:12:33 +01:00

6.0 KiB

SAFETY-128 - a reading that does not answer the last command is not current

Status: FIXED on release/1.0 (after 0.20.0). Code: app/lateness.py, control.compute(hold=, hold_w=), peakguard.cap_floor(), Controller._readings_late(), Controller.read_inputs() (feeds the fits). Tests: test_safety128.py (tools/safety128_rig.py, TEST-23's plant).

Cause (SAFETY-127 report). Home Assistant can deliver readings late but still changing. Every gate judges arrival (SAFETY-01) or a value that stops changing (SAFETY-18, SAFETY-37), so a late reading passes them all. The loop then corrects, each cycle, an error its last command has already fixed: a loop with a dead time far longer than its cycle, which swings and grows. The peak guard projects the quarter from the same late figures and pushes to the inverter limit in its last seconds (OPT-34 rig F-2).

Rule.

  1. Find late readings (Answer.watch(), every 2 s, from the 1 s reads).
    • Batteries (Delay): fit one d so that each battery's reading minus its own request at t - d (averaged over 2 s for the battery's lag and the read) is flattest. The house does not move that reading, so a repeating load cannot line up with it (review round 1, B1). Needs 5 new readings in 60 s and the request and the reading each to move 300 W. Late from 9.5 s, after 2 late fits in a row. A late fit must explain 75 % and beat every fit up to 6.5 s by 2x; the shortest delay that fits about as well (within 2x, or within 2 500 W^2) is taken instead, since a swing's period fits again. A slow battery (3-10 s lag) answering at once gives no late fit; at a 15-20 s lag some fits read late (see "What it costs"). No late battery fit counts for 60 s after a battery gave nothing under its ask: an emptied battery lined its old movement up as 85 s late.
    • Meter (MeterFit, round 1's fit): meter(t) + request(t - d) is the house alone at the right d. Quick, but confounded by the house. Needs 16 new values in 60 s, so only a meter of about 3.5 s or faster: at 5 s it gave false delays. Late over 8 s. At a slower meter (a 5 s P1) the same fit runs over 120 s and calls only a delay over 16 s late, on 2 fits in a row, and never ends a verdict (SAFETY-129). Alone it reads 35-90 s on healthy runs, a few times in a row; only the slow-start and battery-fit check below keeps those from a verdict. 10 s late is not found: it cannot be told from a 10 s slow battery, and it does not swing at 5 s.
    • A meter fit says late only with evidence from the batteries' side too: a request more than 100 W from the battery reading took 3 s or more to move a reading towards it (SLOW_START_S; on time they start within 0-1 s in the recorded house at 1 and 2 s ticks), and no battery fit in the last 60 s said on time (under 9.5 s). An ask during which a battery gave nothing under its ask is no slow evidence. A repeating load fools the meter fit but not these. No meter verdict in the first 150 s.
    • On time again on a battery fit under 6.5 s, or a meter fit within 8 s unless a battery fit said late (hysteresis). With no fit for 120 s the verdict is dropped.
  2. Hold. While late, a request more than 100 W from the battery reading is pending until that reading comes within 100 W of it, or half way. Meanwhile the loop may not lead the reading by more than 100 W that way, and the peak guard's floor is capped at the request that stands. The loop is not held while a battery gives nothing under its ask or refuses (SAFETY-52/94).
  3. Late verdict. Pending for LATE_S (23 s) with no reading moving 100 W towards it: control holds, then after stale_input_s goes to 0 W ("readings late"), as for a missing input.
  4. Seen. While late an amber row says "Home Assistant readings show requests about N seconds late, so control holds the batteries." (NL too). N is the batteries' fitted delay: Home Assistant's lag plus the battery's own answer. While only the meter has shown it the row gives no figure. One WARNING (READINGS_LATE) per episode and one line when it ends.

With readings on time no verdict was reached in any reviewer scenario (owner-shaped house, ovens 20/40 to 60/60 s, clouds, a 300 W/s battery, 6-8 s dead times, HA jitter 0-4 s, an EV following the meter, capped batteries, HA restarts, meter pauses; 1, 2 and 5 s meters) nor in 2 days of the recorded house at 1, 2 and 5 s ticks, so the commands are the same as without the gate. The fits run all the time; the pull request has the counts.

What it costs.

  • Found 36-68 s after a lag starts at 1-2 s meters; at a 5 s meter 30 s late is found within 100 s after it starts (88 s measured) (the loop first moves 100 W after 60 s, SAFETY-129) with the export above -200 W, and 10 s late is not found. Until then control is as before. In a quiet house the "readings late" row stays about 2 minutes longer after the lag ends (126 s against 32 s on base); no harm measured, and it clears as early as on base when the load changes.
  • A battery that answers slowly is read as late readings and held: a dead time of 10 s or more (8 s reads 7-8; 10 s reads 10 at 1-2 s meters, 9 at 5 s), or a 15-20 s lag (review round 2). The GoodWe measured settles in 6 s (freshness.PLANT_SETTLE_S); the row then blames Home Assistant wrongly.
  • The row can stay up to 180 s after the readings are on time on a steady site. While only the meter has shown the delay it gives no figure.
  • A battery that cannot follow while the readings are late (at a limit, not reading 0 W) is held, then set to 0 W after 38 s.
  • The fits cost a few ms every 2 s (x86, measured after 3 idle days); the history they read is bounded (60 s of readings, 150 s of requests).

Left out. The setpoint -> Home Assistant round trip: the fitted delay gives the figure from readings the add-on already has. The hold's step-and-pull-back at extreme lag and the arbitrage charge's sizing on late readings (charge_room_w()) are follow-up cards.