Files
goodwe-addon/goodwe_controller/DECISIONS/OPT-34-the-night-charge-keeps-its-level.md
T
glenn schrooyenandClaude 6263a57b6b
checks / unit (pull_request) Successful in 21m5s
infra-pin / fresh (pull_request) Successful in 2s
OPT-34 fix (rig F-1): a replaced evening forecast does not come back
After a fresh today reading in the night (02:00 on the rig) went stale
(05:00), Hold.stand_in(as_today=True) served the 17:05 "tomorrow" figure
again until 06:00: the plan went back to 95 %, the latch took it, and
Leftover solar charged back up. read_sum() now skips that stand-in once
every today sensor has given a fresh reading for the day
(Hold.replaced()); the stale newer reading follows the OPT-27 rules.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 12:26:05 +01:00

70 lines
4.6 KiB
Markdown

# OPT-34 - the night charge keeps its level when the kept forecast ends
**Status: done on `release/1.0`, not released.** Tests: `test_opt34.py`.
**The defect** (OPT-27 rig test, F-1). The kept evening figure ended at midnight + 3 h.
With the sensors silent from 17:05 the Leftover solar battery's level went with
it in the middle of the charge: A ended at 49.8 % against 79.9 % with a fresh
figure. Whether Forecast.Solar refreshes after midnight on a live site is not
known, so the add-on does not depend on it.
**The rule.**
1. *A charge under way keeps its level* (`Controller._latch_charge()`). While
there is a forecast (fresh or kept), each battery that charges from the grid in the
cheap run takes its planned level into a latch (store key `night_charge_latch`).
With no forecast, `plan_targets()` gives the latched level, and the wide
no-forecast gap does not refuse the next slice of the run (`weather_held()`,
as TARIFF-24 does for the run under way). While a
forecast is there the latch follows the plan, up or down as a fresh figure moves
it. It lasts through every cheap run on and one price slot after the last, and it
ends at once when its end time passes, whatever the run state (it is also
ignored at a restart if it has ended). A battery that is switched off or
changed to another mode leaves it.
*Night bound (manager's call, 2026-10-31):* a run carries the latch on only if
it starts by the bell start (06:00 local) after the latch began; that run
finishes, plus one slot. Chosen over a fixed 12 h because the night tariff
(22:00-07:00) needs the 06:00-07:00 slice and 12 h from a late start would
reach into the day. With dynamic prices a chain of cheap slots cannot carry a
night's level into the next day.
2. *The evening's tomorrow figure is today's until the bell starts*, 06:00 local
(`BELL_FROM_H`, `solarfc._bell_start()`), not until midnight + 3 h.
Nothing has been produced yet, so it is still the whole day.
3. *Up to 15 h old for that case only* (`KEPT_MORNING_MAX_AGE_S`): a 17:05
reading is 12 h 55 min old at 06:00. It must have been at most 12 h old at
midnight, so a 09:05 reading, unusable since 21:05, does not come back for five
minutes after midnight. Same-day figures and the daytime rule keep 12 h and 3 h.
4. A fresh figure replaces the kept one at once, as before.
*Rig F-1 fix:* once every today sensor has given a fresh reading after
midnight, the evening figure is no stand-in for that day any more
(`Hold.replaced()`). When that reading goes stale (3 h), it is kept or not
as any today reading (OPT-27), so it is "no forecast" at night and the latch
keeps the level it set; the older 17:05 figure never comes back. Rig: A
stopped at 69.5 % after a brighter 02:00 figure, then charged back to 94.4 %
from 05:15 when the 17:05 figure returned. Test: `test_opt34.py` F-1.
**Step 1 alone is enough for the owner's shape, steps 2-3 are still needed.**
With the window put back to midnight + 3 h the owner's night still ends as a
fresh figure ends it (A 86.9 %, B 95.0 %), because the charge had started at
22:00. But a charge that has not started by 03:00 (the price dips at 04:00, or
the headroom is taken) has no level to latch: A ends at 38 % against 78 %. The
plan, the screens and a restart also need the figure itself.
**Screens.** "Solar forecast read {at}, kept for tonight." is still true until
06:00 and unchanged. After the figure ends, with a charge still going on: "No
solar forecast now. The charge under way goes on to its planned level." /
"Nu geen zonneprognose. De lopende lading gaat door tot het geplande niveau."
(`home_words.solar.latched`, only while a cheap run is on and a latched battery is below its level).
**Not used.** A figure over 15 h old; a morning figure at night (12 h); a figure
for another day; a bad value or unit; and no latch for a charge that has not
started, for a battery that is not charging, or after the cheap runs have ended
(plus one slot).
**Not changed.** Charge sizing, the peak guard, Cheap hours, modes off, arbitrage.
A fresh figure that LOWERS the level during a charge still lowers it, as before.
**Mutants** (all red in `test_opt34.py`): no latch; window back to midnight + 3 h;
15 h without a limit; no "usable at midnight" rule; the latch without the narrow
gap, without its save, or not kept through the next run; the window 6 h after
midnight instead of 06:00 local (DST); 15 h cut to 12 h; the new screen line off or shown without a cheap run; no expiry check; no pruning of a changed mode; no night bound; a left-the-plan battery keeping its level; no `replaced()` check (F-1); `replaced()` with one of several today sensors (any, not all).