Skip to content

fix(axle): warn and continue when Axle component is unhealthy#4236

Open
mgazza wants to merge 2 commits into
mainfrom
fix/axle-unhealthy-warn-continue
Open

fix(axle): warn and continue when Axle component is unhealthy#4236
mgazza wants to merge 2 commits into
mainfrom
fix/axle-unhealthy-warn-continue

Conversation

@mgazza

@mgazza mgazza commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Make an unhealthy / erroring Axle Energy VPP component warn and continue instead of influencing (and thrashing) battery control.

Problem

When the Axle component errors on every run — e.g. Axle rotated their API keys and a customer's key expired — fetch.py still trusted Axle:

  • fetch_axle_sessions() injected Axle VPP sessions as forced import/export slots into the plan (load_axle_slot).
  • fetch_axle_active() could flip PredBat into read-only mode, handing battery control to the (broken) VPP.

Neither path checked component health, so stale / inconsistent Axle state flip-flopped read-only mode and injected phantom forced slots — thrashing charge/discharge. Observed on a live Intelligent Octopus Go customer: ~5-minutely battery charge↔discharge, SOC 98%→49%, and peak-rate grid import during an active IOG car session, for ~2 days until the API key was regenerated.

Fix

  • Add Fetch.axle_component_healthy(), using the same "active but not alive" definition already used for the components_healthy dashboard sensor (predbat.py).
  • Gate both Axle influence paths on it:
    • Skip loading Axle sessions (self.axle_sessions = []) when unhealthy.
    • Never enter Axle read-only control when unhealthy.
  • Logs a single Warn: and continues with the normal tariff plan — the integration degrades to absent rather than actively wrong (fail safe).

A healthy or entirely absent Axle component is unaffected.

Tests

  • Extends tests/test_axle.py with unit tests for the predicate: healthy / not-configured / active-and-alive → left untouched; active-but-not-alive → ignored and warns.
  • unit_test.py --test axle29/29 pass; -k fetch_config passes; Black / flake8 (250) / cspell clean.

🤖 Generated with Claude Code

mgazza and others added 2 commits July 11, 2026 23:23
A failed Axle Energy VPP integration (e.g. an expired/rotated API key that errors on every run) previously still influenced the plan: fetch.py trusted fetch_axle_sessions() and the fetch_axle_active() read-only handoff even when the component was in an error state. With stale/inconsistent Axle state this flip-flopped read-only mode and injected phantom forced import/export slots, thrashing battery charge/discharge on IOG tariffs (observed live: ~5-minutely charge<->discharge, battery 98%->49%, peak-rate import during an active IOG car session).

Add Fetch.axle_component_healthy() (same 'active but not alive' definition as the components_healthy dashboard sensor) and gate both Axle influence paths on it: skip loading Axle sessions and never enter Axle read-only control when the component is unhealthy. The integration degrades to absent (warn and continue) and PredBat plans normally on the tariff. Adds unit tests for the predicate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…th guard

test_fetch_config_options runs against the shared my_predbat instance, whose
`components` was left as a bare _MockComponents by an earlier test (no is_active).
The new axle_control path now calls axle_component_healthy() → components.is_active
("axle"), so Test 2 crashed with AttributeError.

Inject a minimal _FakeComponents (is_active/is_alive) for the duration of the test
and restore the original in teardown. Axle is present+healthy here so the control
path is exercised; the unhealthy warn-and-continue path is covered in test_axle.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mgazza mgazza requested a review from springfall2008 July 11, 2026 22:47
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