SAFETY-125 review: guard row and Home line within UX-08 limits; N=1 maintenance named
checks / unit (pull_request) Successful in 56m45s
infra-pin / fresh (pull_request) Successful in 1s

- The over-the-limit rows ran over STYLE's 15 words (EN 16/19, NL 16/21)
  and two Dutch Home sentences over 12 words; they were not in the samples,
  so test_messages/test_ux46 never saw them. Now:
  "Peak guard: grid charge limited, at this rate 5 814 W, over 5 413 W" /
  "Piekbewaking: laden beperkt, aan dit tempo 5 814 W, boven 5 413 W",
  "...idle, at this rate ..., over ..." / "...in rust, aan dit tempo ...",
  NL Home "Laadt minder van het net om onder 5,4 kW te blijven." and
  "Zou minder van het net laden om onder 5,4 kW te blijven."
  The rows and the line are added to the samples. The under row is unchanged.
- CHANGELOG known issues and the DECISIONS note: at N=1 the maintenance
  charge is not sized to the room, so SAFETY-99 still applies to it.
- DOCS: the peak guard row's new words.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Claude (SAFETY-125 review)
2026-10-29 12:56:01 +01:00
parent 88ab8a50e3
commit 6eebd4882b
7 changed files with 29 additions and 22 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ Contents of this release, in merge order since 0.19.1 (`2932f3a`):
### Known issues
The list under `## 0.19.0` below still applies. The SAFETY-99 line there now
covers the guard's discharge only, not a grid charge.
covers the guard's discharge, and the maintenance charge with one inverter.
### If you are crossing from a version older than 0.10.0
@@ -5,5 +5,6 @@
A grid charge is sized by `peakguard.charge_room_w()`, the guard's headroom
with the seconds since a late meter reading counted at that reading (the
projection leaves them out, SAFETY-99, the bill does not), and the guard's own
floor is unchanged. Evidence (repro on 0.19.1, sweep, digests): the SAFETY-125
pull request.
floor is unchanged. Not at N=1: the maintenance charge there is not sized to
the room (SAFETY-84), so SAFETY-99 still applies to it. Evidence (repro on
0.19.1, sweep, digests): the SAFETY-125 pull request.
+1 -1
View File
@@ -3597,7 +3597,7 @@ for a new install*.
| Meter reading may be stuck | See *Meter reading may be stuck* under Notifications. |
| Month peak | This month's highest 15-minute average, when it happened, and how close it is to your peak limit. Amber from 80 %. |
| 15-minute periods measured this month | How many 15-minute periods the add-on has measured and saved this month. |
| Peak guard, Load too high for battery, Battery at its peak reserve | What the add-on does to keep your peak down. *Idle, heading for …* is where this 15-minute period is heading. *Waiting for meter readings* after a restart means the battery does not charge until the add-on can measure the period again. A red peak row is an alarm while control keeps running, so the banner shows its title without *Not ready*. |
| Peak guard, Load too high for battery, Battery at its peak reserve | What the add-on does to keep your peak down. *Idle, heading for …* is where this 15-minute period is heading. *At this rate … over …* means it would go over at the current rate. With *grid charge limited*, a charge from the grid is cut back so it does not. *Waiting for meter readings* after a restart means the battery does not charge until the add-on can measure the period again. A red peak row is an alarm while control keeps running, so the banner shows its title without *Not ready*. |
| Battery level, Battery power, Grid power | The reading of that entity. *No usable reading* means the entity is missing, or its reading is too old. Check it in Developer Tools before replacing it. |
| Inverter control | Whether the inverter can be controlled, and how much it can charge and give. |
| *name*: battery power reading stopped changing | The reading did not change while the add-on asked for a change. It may be stuck, or the battery may be refusing. The add-on sets the inverters to 0 W until it changes. |
+2
View File
@@ -1453,6 +1453,8 @@ def _samples() -> list:
("plan", line_period(1240, 3500, sh)),
("plan", line_period(None, 3500, sh)),
("plan", line_period(1240, 3500, sh, unmeasured=True)),
("plan", line_period(5814, 5413, sh)),
("plan", line_period(5814, 5413, sh, cut=True)),
("plan", control_text(True, sh)),
("plan", control_text(False, sh))]
out += [("plan", run_grid_why(0.1412)), ("plan", run_grid_amount(1.23)),
+13 -8
View File
@@ -1174,18 +1174,18 @@ def row_peak_guard_idle(projected_w, limit_w, month_peak: bool,
cut: bool = False) -> str:
"""The projection first and the limit second (SAFETY-38's ordered pair).
SAFETY-125: over the limit at this rate it says so, and `cut` that a
grid charge is cut back to the room left."""
grid charge is limited to the room left."""
if float(projected_w) > float(limit_w):
doing, doet = (("grid charge limited", "laden beperkt") if cut
else ("idle", "in rust"))
return Bi(f"Peak guard: {doing}, at this rate {watts(projected_w)}, "
f"over {watts(limit_w)}",
f"Piekbewaking: {doet}, aan dit tempo "
f"{watts(projected_w)}, boven {watts(limit_w)}")
limit = (f"this month's {watts(limit_w)} peak" if month_peak
else f"the {watts(limit_w)} peak limit")
grens = (f"de maandpiek van {watts(limit_w)}" if month_peak
else f"de piekgrens van {watts(limit_w)}")
if float(projected_w) > float(limit_w):
doing, doet = (("cutting the grid charge", "schroeft het laden van "
"het net terug") if cut else ("idle", "in rust"))
return Bi(f"Peak guard: {doing}, at this rate {watts(projected_w)}, "
f"over {limit}",
f"Piekbewaking: {doet}, aan dit tempo "
f"{watts(projected_w)}, boven {grens}")
return Bi(f"Peak guard: idle, heading for {watts(projected_w)}, under "
f"{limit}",
f"Piekbewaking: in rust, richting {watts(projected_w)}, onder "
@@ -1573,6 +1573,11 @@ def status_samples() -> list:
("row_periods_measured", GREEN, row_periods_measured("unreadable")),
("row_peak_guard_idle", GREEN, row_peak_guard_idle(1300, 2500, True)),
("row_peak_guard_idle", GREEN, row_peak_guard_idle(1300, 2500, False)),
("row_peak_guard_idle", GREEN, row_peak_guard_idle(5814, 5413, True)),
("row_peak_guard_idle", GREEN, row_peak_guard_idle(5814, 5413, True,
cut=True)),
("row_peak_guard_idle", GREEN, row_peak_guard_idle(3814, 3500, False,
cut=True)),
("row_peak_guard_quiet", GREEN, row_peak_guard_quiet()),
("row_peak_guard_unmeasured", AMBER, row_peak_guard_unmeasured()),
*(("row_peak_guard_blind", AMBER, row_peak_guard_blind(k))
+2 -2
View File
@@ -352,8 +352,8 @@
"period": "Dit kwartier gaat richting {kw}.",
"period_unmeasured": "Een deel van dit kwartier is niet gemeten.",
"period_over": "Aan dit tempo komt dit kwartier op {kw}.",
"charge_cut": "Het laden van het net wordt teruggeschroefd om onder {kw} te blijven.",
"charge_cut_would": "Het laden van het net zou teruggeschroefd worden om onder {kw} te blijven.",
"charge_cut": "Laadt minder van het net om onder {kw} te blijven.",
"charge_cut_would": "Zou minder van het net laden om onder {kw} te blijven.",
"prices": {"now": "Prijs nu {eur} per kWh, alles inbegrepen.", "today": "Vandaag: laagste {lo} om {lo_at}, hoogste {hi} om {hi_at}.", "none": "Geen prijzen voor vandaag."},
"tomorrow": {"known": "De prijzen van morgen zijn binnen.", "unknown": "De prijzen van morgen komen meestal na 13:00."},
"region": {"preset": "Heffingen en btw: {region}, cijfers van {year}.", "own": "Heffingen en btw: uw eigen bedrag."},
+7 -8
View File
@@ -355,9 +355,9 @@ for name, kw, aim, kw_text in (("month peak banked", dict(peak=PEAK),
f"{_u[3] if present(_u) else _u!s} / "
f"{_u[4] if present(_u) else _u!s}")
check(f"{name}: over the limit at this rate the guard row never says "
f"'under'; it says the grid charge is cut",
f"'under'; it says the grid charge is limited",
present(_o) and all(
"under" not in s[3] and "Peak guard: cutting the grid charge, "
"under" not in s[3] and "Peak guard: grid charge limited, "
"at this rate" in s[3] and " over " in s[3] for s in over))
check(f"{name}: ...and the month-peak line says the rate, and that the "
f"grid charge is cut back to stay under {kw_text}",
@@ -382,17 +382,16 @@ _cut = messages.row_peak_guard_idle(5814.0, 5413.0, month_peak=True,
_ok = messages.row_peak_guard_idle(5200.0, 5413.0, month_peak=True)
print(f" {_en}\n {_cut}\n {_ok}")
check("over, nothing cut: 'idle, at this rate ..., over ...'",
_en == "Peak guard: idle, at this rate 5 814 W, over this month's "
"5 413 W peak")
_en == "Peak guard: idle, at this rate 5 814 W, over 5 413 W")
check("over, a grid charge cut",
_cut == "Peak guard: cutting the grid charge, at this rate 5 814 W, "
"over this month's 5 413 W peak")
_cut == "Peak guard: grid charge limited, at this rate 5 814 W, "
"over 5 413 W")
check("under: the 0.19.1 sentence, unchanged",
_ok == "Peak guard: idle, heading for 5 200 W, under this month's "
"5 413 W peak")
check("the Dutch forms say the same",
_cut.nl == "Piekbewaking: schroeft het laden van het net terug, aan "
"dit tempo 5 814 W, boven de maandpiek van 5 413 W"
_cut.nl == "Piekbewaking: laden beperkt, aan dit tempo 5 814 W, "
"boven 5 413 W"
and _en.nl.startswith("Piekbewaking: in rust, aan dit tempo"))
check("the month-peak line in shadow mode says 'would'",
homeview.line_period(5814.0, 5413.0, True, cut=True).endswith(