fix(axle): warn and continue when Axle component is unhealthy#4236
Open
mgazza wants to merge 2 commits into
Open
fix(axle): warn and continue when Axle component is unhealthy#4236mgazza wants to merge 2 commits into
mgazza wants to merge 2 commits into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.pystill 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
Fetch.axle_component_healthy(), using the same "active but not alive" definition already used for thecomponents_healthydashboard sensor (predbat.py).self.axle_sessions = []) when unhealthy.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
tests/test_axle.pywith unit tests for the predicate: healthy / not-configured / active-and-alive → left untouched; active-but-not-alive → ignored and warns.unit_test.py --test axle→ 29/29 pass;-k fetch_configpasses; Black / flake8 (250) / cspell clean.🤖 Generated with Claude Code