OPT-34 round 2: the latch follows the plan, up or down, and drops what left it
While a forecast is present and the cheap run holds, the night-charge latch is rebuilt from the plan: a latched battery takes its current level whatever its role this tick (a fresh figure may lower it below the SoC), one that left the plan is dropped, a new one comes in only while it charges from the grid. Test: a fresh brighter figure lowers A below its SoC, the sensors go quiet, A does not charge back up. CHANGELOG summary says two fixes. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
This commit is contained in:
co-authored by
Claude
parent
412e47f111
commit
eee7616022
@@ -2392,9 +2392,11 @@ class Controller:
|
||||
charge that has started keeps its planned level when the forecast goes
|
||||
mid-charge (the kept figure ends and the sensors are still silent).
|
||||
|
||||
While there is a forecast, fresh or kept, each battery that charges
|
||||
from the grid in the cheap run takes its planned level into the latch;
|
||||
a fresh figure moves it up or down, as it moves the plan. While there
|
||||
While there is a forecast, fresh or kept, the latch is rebuilt each
|
||||
cycle from the plan: a battery that charges from the grid in the cheap
|
||||
run comes in with its planned level, and stays in it at whatever level
|
||||
the plan gives it, up or down as a fresh figure moves it (also when it
|
||||
has stopped, at a lowered level), until it leaves the plan. While there
|
||||
is none, `plan_targets()` gives the latched levels, so the charge goes
|
||||
on to them, and `weather_held()` keeps the narrow price gap. The latch
|
||||
lasts through every cheap run on and one price slot after the last
|
||||
@@ -2403,20 +2405,24 @@ class Controller:
|
||||
tick = self.mode_tick
|
||||
run = None if runs is None else runs.cheap
|
||||
in_run = run is not None and run.holds(now)
|
||||
if lt is not None:
|
||||
if in_run:
|
||||
lt = (max(lt[0], run.end + timedelta(
|
||||
seconds=CHARGE_LATCH_GRACE_S)), lt[1])
|
||||
elif now >= lt[0]:
|
||||
lt = None
|
||||
if (in_run and self.solar_fc is not None and targets
|
||||
and tick is not None and tick.action == modetrade.CHARGE):
|
||||
lv = dict(lt[1]) if lt is not None else {}
|
||||
lv.update({n: float(targets[n]) for n, r in tick.roles.items()
|
||||
if r == modetrade.CHARGE and n in targets})
|
||||
if lv:
|
||||
until = run.end + timedelta(seconds=CHARGE_LATCH_GRACE_S)
|
||||
lt = (max(until, lt[0]) if lt is not None else until, lv)
|
||||
until = None if run is None else run.end + timedelta(
|
||||
seconds=CHARGE_LATCH_GRACE_S)
|
||||
if lt is not None and not in_run and now >= lt[0]:
|
||||
lt = None
|
||||
if in_run and self.solar_fc is not None:
|
||||
# Rebuilt from the plan: a battery already latched follows its
|
||||
# level up or down whatever its role this tick (a fresh figure may
|
||||
# lower it below the SoC), one that left the plan is dropped, and
|
||||
# a new one comes in only while it charges from the grid.
|
||||
tg = targets or {}
|
||||
lv = {n: float(tg[n]) for n in (lt[1] if lt else {}) if n in tg}
|
||||
if tick is not None and tick.action == modetrade.CHARGE:
|
||||
lv.update({n: float(tg[n]) for n, r in tick.roles.items()
|
||||
if r == modetrade.CHARGE and n in tg})
|
||||
lt = (max(until, lt[0]) if lt is not None else until, lv) \
|
||||
if lv else None
|
||||
elif lt is not None and in_run:
|
||||
lt = (max(lt[0], until), lt[1])
|
||||
if lt != old:
|
||||
self._charge_latch = lt
|
||||
self.store.set(CHARGE_LATCH_KEY, None if lt is None else {
|
||||
|
||||
Reference in New Issue
Block a user