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
fix(sigenergy): stop Predbat and Axle fighting over the inverter
A Sigenergy accepts one controller at a time, and VPP mode (Predbat) and the
NorthBound Interface (Axle's dispatch channel) are mutually exclusive. Today
Predbat wins that contest silently, on a 5 minute timer, and the axle_control
option that is supposed to let Axle win does not work here at all.
Observed on a live system on 2026-08-06: Axle took the inverter into Northbound
Integration at 19:35:18 and it was back in VPP at 19:40:12 — exactly one
SIGENERGY_POLL_INTERVAL — while the log only said "controls skipped until
onboard is approved".
Make axle_control actually work on Sigenergy:
- _axle_has_control() evaluates axle_control and the Axle event sensor LIVE
rather than reading Fetch's cached set_read_only_axle. That flag is only
refreshed by the 5 minute prediction loop and is still False from reset()
when this phase-1 component makes its first run — precisely the case that
matters, a restart during a live event, where the cached flag would have had
Predbat reclaim VPP and kill the dispatch.
- during an event the operating mode is left exactly as it is. If Axle has moved
the system to NBI it stays there; if the event has started but Axle has not
switched yet, do not drop to MSC either — that hands control to the owner's
app rather than to Axle.
- battery commands are suppressed for the same window.
- VPP is reclaimed when the event ends, and that is logged.
And where Predbat does keep ownership, make it deliberate rather than a race:
- reclaim VPP every minute instead of every 5. This also lets an event start or
end be picked up promptly now that ownership is evaluated here. set_operating_
mode is an MQTT publish that only fires when the mode is wrong, so it costs
nothing against the REST rate limit.
- log the reclaim naming the controller being displaced.
- record last_contended_by on the status sensor, published on the same minute
cadence and never cleared. Contention is usually shorter than a publish
interval, so a marker reset on recovery would almost never be seen; "has this
happened" is the useful signal, and in_vpp already answers "right now".
- stop reporting a system in NBI as pending_approval. The SaaS UI renders that
as an amber "approve this in the Sigenergy app" banner, so every Axle event
told the user to approve something that needed no approval.
Behaviour with axle_control unset is unchanged except for the faster reclaim,
the clearer logs and the status fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Axle owns the inverter for the duration of its event. Leave the mode exactly as
2233
+
# it is: if Axle has already moved the system to NBI it stays there, and if the
2234
+
# event has started but Axle has not switched yet, do not pull it to MSC either —
2235
+
# that would hand control to the owner's app rather than to Axle.
2236
+
ifself._axle_has_control():
2237
+
ifnotself._axle_standoff_logged.get(system_id):
2238
+
self.log("SigenergyAPI: Axle VPP event active — leaving system {} in {} and standing down until the event ends".format(system_id, SIGENERGY_MODE_NAMES.get(self.current_mode.get(system_id, -1), "Unknown")))
self.log("SigenergyAPI: System {} is not in VPP mode ({}) — switching to VPP to enable controls".format(system_id, SIGENERGY_MODE_NAMES.get(current, "Unknown")))
0 commit comments