From 6eebd4882b220441cf47ee9bf3398680d57fb5dd Mon Sep 17 00:00:00 2001 From: "Claude (SAFETY-125 review)" Date: Thu, 29 Oct 2026 12:56:01 +0100 Subject: [PATCH] SAFETY-125 review: guard row and Home line within UX-08 limits; N=1 maintenance named - 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 --- goodwe_controller/CHANGELOG.md | 2 +- ...TY-125-grid-charge-under-the-month-peak.md | 5 +++-- goodwe_controller/DOCS.md | 2 +- goodwe_controller/app/homeview.py | 2 ++ goodwe_controller/app/messages.py | 21 ++++++++++++------- goodwe_controller/app/ui_text/nl.json | 4 ++-- goodwe_controller/test_safety125.py | 15 +++++++------ 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/goodwe_controller/CHANGELOG.md b/goodwe_controller/CHANGELOG.md index 814e159..b62cb91 100644 --- a/goodwe_controller/CHANGELOG.md +++ b/goodwe_controller/CHANGELOG.md @@ -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 diff --git a/goodwe_controller/DECISIONS/SAFETY-125-grid-charge-under-the-month-peak.md b/goodwe_controller/DECISIONS/SAFETY-125-grid-charge-under-the-month-peak.md index 1457c0b..7ec515d 100644 --- a/goodwe_controller/DECISIONS/SAFETY-125-grid-charge-under-the-month-peak.md +++ b/goodwe_controller/DECISIONS/SAFETY-125-grid-charge-under-the-month-peak.md @@ -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. diff --git a/goodwe_controller/DOCS.md b/goodwe_controller/DOCS.md index c35b20e..3c23bdb 100644 --- a/goodwe_controller/DOCS.md +++ b/goodwe_controller/DOCS.md @@ -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. | diff --git a/goodwe_controller/app/homeview.py b/goodwe_controller/app/homeview.py index d0c5c7c..c416a0e 100644 --- a/goodwe_controller/app/homeview.py +++ b/goodwe_controller/app/homeview.py @@ -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)), diff --git a/goodwe_controller/app/messages.py b/goodwe_controller/app/messages.py index 09fdeec..9ae3607 100644 --- a/goodwe_controller/app/messages.py +++ b/goodwe_controller/app/messages.py @@ -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)) diff --git a/goodwe_controller/app/ui_text/nl.json b/goodwe_controller/app/ui_text/nl.json index 191c2f9..b92abea 100644 --- a/goodwe_controller/app/ui_text/nl.json +++ b/goodwe_controller/app/ui_text/nl.json @@ -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."}, diff --git a/goodwe_controller/test_safety125.py b/goodwe_controller/test_safety125.py index 063dd80..bedb326 100644 --- a/goodwe_controller/test_safety125.py +++ b/goodwe_controller/test_safety125.py @@ -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(