Skip to content

Sigenergy: let Axle keep the inverter during its events (option B for #4454) - #4456

Closed
mgazza wants to merge 1 commit into
mainfrom
feat/sigenergy-axle-owns-during-events
Closed

Sigenergy: let Axle keep the inverter during its events (option B for #4454)#4456
mgazza wants to merge 1 commit into
mainfrom
feat/sigenergy-axle-owns-during-events

Conversation

@mgazza

@mgazza mgazza commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Option B of two mutually-exclusive alternatives for #4454. Only one of this and #4455 should merge.

What this does

Lets Axle keep the Sigenergy inverter for the duration of its events, by finishing a feature that already exists.

Predbat already has an axle_control option: when it is set and an Axle event is active, Fetch.fetch_config_options() raises set_read_only_axle so Predbat stands down and lets Axle drive. That flag was only ever consumed by execute.py:63 and output.py:955, so it never reached the Sigenergy component.

The result is that the stand-down does not work on Sigenergy. VPP mode and the NorthBound Interface are mutually exclusive, and _manage_vpp_registration() reads the user-facing switch.predbat_set_read_only rather than the effective read-only state — so it never learns Axle has the floor and pulls the system back into VPP, evicting Axle mid-dispatch.

Observed live 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.

Changes

  • _axle_has_control() reads set_read_only_axle from the parent (defensively, so unit tests without a ComponentBase parent still work).
  • Leave the operating mode exactly as it is during an event. 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 would hand control to the owner's app rather than to Axle. Logged once per event, not per cycle.
  • Suppress battery commands for the same window, so Predbat is not issuing charge/discharge at an inverter Axle is dispatching.
  • Reclaim VPP when the event ends, and log the resume.
  • Stop reporting a system in NBI as pending_approval — same downstream false "approve in app" banner as Sigenergy: make Predbat's ownership of the inverter deliberate (option A for #4454) #4455 fixes.

Trade-off

Behaviour is unchanged unless axle_control is enabled — without it Predbat keeps ownership and reclaims VPP exactly as before. That makes this safe to merge, but it also means it does nothing on its own: axle_control is currently not set for any of our fleet, so adopting this position also requires a config/template change.

It also means Predbat's own plan for that window becomes redundant — Predbat still ingests the Axle session as an export window via load_axle_slot() but won't execute it. Worth deciding in #4454 whether axle_control should also suppress that planning.

Tests

4 new tests in tests/test_sigenergy.py: mode-untouched in both NBI and VPP during an event, controls suppressed, resume-on-event-end, and NBI-not-pending-approval. Full Sigenergy suite 72 pass / 0 fail; axle, fetch_config_options and execute suites pass; unit_test.py --quick passes with 0 failures. run_pre_commit clean.

…vents

Predbat already has an axle_control option: when it is set and an Axle event is
active, Fetch.fetch_config_options() raises set_read_only_axle so Predbat stands
down and lets Axle drive the battery. That flag was only ever consumed by
execute.py and output.py, so it never reached the Sigenergy component.

The result on Sigenergy is that the stand-down does not work. VPP mode (Predbat)
and the NorthBound Interface (Axle) are mutually exclusive, and
_manage_vpp_registration reads the user-facing switch.predbat_set_read_only
rather than the effective read-only state — so it never learned Axle had the
floor and pulled the system back into VPP, evicting Axle mid-dispatch.

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.

This wires the existing flag through:

- _axle_has_control() reads set_read_only_axle from the parent
- 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, so Predbat is not issuing
  charge/discharge at an inverter Axle is dispatching
- when the event ends, VPP is reclaimed on the next check and that is logged
- 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 is unchanged unless axle_control is enabled — without it Predbat keeps
ownership and reclaims VPP exactly as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mgazza

mgazza commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Customer-impact tracking and fleet scope: Predictive-Cloud-Ltd/predbat-saas#1591 (private). Alternative approach: #4455.

@mgazza

mgazza commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #4457.

These two were opened as mutually-exclusive alternatives — Predbat keeps the inverter, or Axle does. That framing was wrong. A code review found that this PR's faster reclaim actively defeats the axle_control option, which meant option A had to honour axle_control regardless — and once it does, it contains option B. They were complementary all along, not alternatives.

The real either/or was never in the code: it is the policy question of whether axle_control should be enabled, which #4454 now tracks separately.

#4457 combines both, plus fixes for the three review findings (the stale set_read_only_axle flag on restart, the reclaim defeating axle_control, and the contention marker being cleared before it was ever published).

@mgazza mgazza closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant