You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 295f805
Browse filesBrowse the repository at this point in the historyBrowse files
feat: add manual_soc_max, a discharge-direction ceiling sibling to manual_soc (#1578)
manual_soc already lets an automation set a minimum SoC floor for a
specific time - Predbat plans charging to reach it. There was no
equivalent for the opposite direction: forcing the battery down to a
ceiling by a deadline, which is what #1578 actually needs (a periodic
calibration discharge finishing right before a known cheap import slot,
e.g. an Octopus Intelligent Go midnight slot).
manual_soc_max mirrors manual_soc's existing mechanism exactly, just as
a ceiling: it merges into a new all_active_keep_max per-minute dict
(fetch.py), and the optimiser treats being above the ceiling as a cost
penalty priced at the export rate (prediction.py) - the same "keep"
scoring approach already used for the floor, just inverted. Because
it's a real cost signal integrated into the normal search rather than a
fixed-duration forced export, the optimiser is free to use the energy
against load or export depending on what's actually cheapest at each
moment, which should make better use of the energy than a blunt forced
discharge automation.
A ceiling below the floor at the same minute is a contradiction (most
likely a stale override left over from a previous automation run) - the
floor wins as the safety-relevant constraint, and the conflicting
ceiling is dropped with a warning rather than handing the optimiser two
penalties pulling opposite ways.
This touches the hot loop that has both a pure-Python and a C++ kernel
implementation (prediction_kernel.cpp) for performance - mirrored the
new ceiling logic there too and bumped both parity revisions per the
kernel's own documented rule, so a stale prebuilt kernel binary falls
back to the Python engine instead of silently disagreeing with it.
Verified locally: rebuilt the kernel and the full parity suite passes,
including a new randomized ceiling scenario added to its existing
seeded sweep (constructed to consume no additional RNG draws, so the
existing seeded scenarios are unaffected).
Also covered: the web plan page's SOC dropdown gained a second "Maximum
SOC" control (web.py/web_helper.py), plan-status markers mirroring the
existing floor indicator (execute.py/output.py), and the export-window
merge guard already applied to the floor is now applied to the ceiling
too (plan.py) so two export windows can't be silently combined across a
ceiling deadline.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# A ceiling below the floor at the same minute is a contradiction (e.g. a leftover manual_soc
975
+
# override never cleared) - the floor wins as the safety-relevant constraint, so drop the
976
+
# conflicting ceiling rather than hand the optimiser two penalties pulling opposite ways.
977
+
forminuteinlist(self.all_active_keep_max.keys()):
978
+
floor_value=self.all_active_keep.get(minute, 0)
979
+
iffloor_value>self.all_active_keep_max[minute]:
980
+
self.log("Warn: manual_soc_max target {}% at minute {} is below the manual_soc/alert floor {}% for the same minute - ignoring the ceiling there".format(self.all_active_keep_max[minute], minute, floor_value))
0 commit comments