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>
70 lines
4.6 KiB
Markdown
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).
|