From 0ca8301666de030b2693d05992ee6ffdbcb6fdf2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:57:19 +0000 Subject: [PATCH 1/5] Initial plan From ec1e35a5aad762919d9359cc5121dcd5c449b3d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Mar 2026 22:03:35 +0000 Subject: [PATCH 2/5] Fix Axle API key validation: proper attribute init and clear error messages for list-type key" Co-authored-by: springfall2008 <48591903+springfall2008@users.noreply.github.com> --- apps/predbat/axle.py | 21 ++++++++++++++++++++- apps/predbat/tests/test_axle.py | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/apps/predbat/axle.py b/apps/predbat/axle.py index e3357d790..2ce97bea9 100644 --- a/apps/predbat/axle.py +++ b/apps/predbat/axle.py @@ -29,10 +29,24 @@ class AxleAPI(ComponentBase): def initialize(self, api_key, pence_per_kwh, automatic): """Initialize the AxleAPI component""" + if isinstance(api_key, list): + self.log("Error: AxleAPI: axle_api_key is configured as a list but must be a plain string.") + self.log("Error: AxleAPI: Incorrect format in apps.yaml: axle_api_key:") + self.log("Error: AxleAPI: - 'your-key-here'") + self.log("Error: AxleAPI: Correct format: axle_api_key: 'your-key-here'") + if api_key: + api_key = api_key[0] + self.log("Warn: AxleAPI: Using first element of the list as a fallback. Please fix your configuration.") + else: + api_key = None + if not isinstance(api_key, str) or not api_key: + self.log("Error: AxleAPI: axle_api_key is missing or invalid. Axle Energy integration will not function correctly.") + api_key = None self.api_key = api_key self.pence_per_kwh = pence_per_kwh self.automatic = automatic self.failures_total = 0 + self.history_loaded = False self.event_history = [] # List of past events self.current_event = { # Current event "start_time": None, @@ -40,7 +54,7 @@ def initialize(self, api_key, pence_per_kwh, automatic): "import_export": None, "pence_per_kwh": None, } - self.updated_at: None # Last updated moved out to separate attribute to not pollute triggering on change of current_event + self.updated_at = None # Last updated moved out to separate attribute to not pollute triggering on change of current_event def load_event_history(self): """ @@ -185,6 +199,11 @@ async def fetch_axle_event(self): """ Fetch the latest VPP event from Axle Energy API """ + if not self.api_key: + self.log("Error: AxleAPI: Cannot fetch event - axle_api_key is not set or invalid. Please check your apps.yaml configuration.") + self.failures_total += 1 + return + self.log("AxleAPI: Fetching latest VPP event data") url = "https://api.axle.energy/vpp/home-assistant/event" diff --git a/apps/predbat/tests/test_axle.py b/apps/predbat/tests/test_axle.py index 44982d967..8e87e979c 100644 --- a/apps/predbat/tests/test_axle.py +++ b/apps/predbat/tests/test_axle.py @@ -114,6 +114,7 @@ def test_axle(my_predbat=None): # Sub-test registry - each entry is (key, function, description) sub_tests = [ ("initialization", _test_axle_initialization, "Axle API initialization"), + ("list_api_key", _test_axle_list_api_key_validation, "List API key validation and error logging"), ("active_event", _test_axle_fetch_with_active_event, "Fetch with active event"), ("duplicate_event", _test_axle_duplicate_event_detection, "Duplicate event detection"), ("notify_config", _test_axle_fetch_with_notify_config, "Notification config control"), @@ -174,6 +175,7 @@ def _test_axle_initialization(my_predbat=None): assert axle.pence_per_kwh == 150, "Pence per kWh not set correctly" assert axle.failures_total == 0, "Failures should be 0 on init" assert axle.last_updated_timestamp is None, "Last updated should be None on init" + assert axle.updated_at is None, "updated_at should be None on init" assert axle.current_event["start_time"] is None, "Current event should be None on init" assert axle.event_history == [], "Event history should be empty on init" assert axle.history_loaded is False, "History should not be loaded on init" @@ -182,6 +184,31 @@ def _test_axle_initialization(my_predbat=None): return False +def _test_axle_list_api_key_validation(my_predbat=None): + """Test that a list-type API key (incorrect YAML format) is handled with clear error logging""" + print("Test: Axle API list API key validation") + + # Case 1: api_key is a non-empty list (most common misconfiguration) + axle = MockAxleAPI() + axle.initialize(api_key=["correct_key_as_list"], pence_per_kwh=100, automatic=False) + + assert axle.api_key == "correct_key_as_list", "Should use first element of list as fallback" + assert any("list" in msg.lower() for msg in axle.log_messages), "Should log error about list type" + assert any("axle_api_key" in msg for msg in axle.log_messages), "Should mention axle_api_key in error" + assert any("fallback" in msg.lower() for msg in axle.log_messages), "Should log fallback warning" + + # Case 2: api_key is an empty list + axle2 = MockAxleAPI() + axle2.initialize(api_key=[], pence_per_kwh=100, automatic=False) + + assert axle2.api_key is None, "Empty list should result in None api_key" + assert any("list" in msg.lower() for msg in axle2.log_messages), "Should log error about list type" + assert any("missing or invalid" in msg.lower() for msg in axle2.log_messages), "Should log invalid key error" + + print(" ✓ List API key validation works correctly") + return False + + def _test_axle_fetch_with_active_event(my_predbat=None): """Test fetching an active VPP event from API""" print("Test: Axle API fetch with active event") From c343ae54cd6a05529d1ee47dd8bbfc3e1fae2288 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:42:17 +0000 Subject: [PATCH 3/5] Refine api_key validation in AxleAPI initialization Updated error handling for api_key to ensure it is a string and not a list or number. --- apps/predbat/axle.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/apps/predbat/axle.py b/apps/predbat/axle.py index 2ce97bea9..2d93dda03 100644 --- a/apps/predbat/axle.py +++ b/apps/predbat/axle.py @@ -29,18 +29,8 @@ class AxleAPI(ComponentBase): def initialize(self, api_key, pence_per_kwh, automatic): """Initialize the AxleAPI component""" - if isinstance(api_key, list): - self.log("Error: AxleAPI: axle_api_key is configured as a list but must be a plain string.") - self.log("Error: AxleAPI: Incorrect format in apps.yaml: axle_api_key:") - self.log("Error: AxleAPI: - 'your-key-here'") - self.log("Error: AxleAPI: Correct format: axle_api_key: 'your-key-here'") - if api_key: - api_key = api_key[0] - self.log("Warn: AxleAPI: Using first element of the list as a fallback. Please fix your configuration.") - else: - api_key = None if not isinstance(api_key, str) or not api_key: - self.log("Error: AxleAPI: axle_api_key is missing or invalid. Axle Energy integration will not function correctly.") + self.log("Error: AxleAPI: axle_api_key is missing or invalid, you must set it to a string (not a list or number). Axle Energy integration will not function correctly.") api_key = None self.api_key = api_key self.pence_per_kwh = pence_per_kwh From 1d4f642e15070c11d7eb799a0760099679ce3bfa Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:44:06 +0000 Subject: [PATCH 4/5] Update test_axle.py --- apps/predbat/tests/test_axle.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/predbat/tests/test_axle.py b/apps/predbat/tests/test_axle.py index 8e87e979c..2402e1ce0 100644 --- a/apps/predbat/tests/test_axle.py +++ b/apps/predbat/tests/test_axle.py @@ -192,18 +192,13 @@ def _test_axle_list_api_key_validation(my_predbat=None): axle = MockAxleAPI() axle.initialize(api_key=["correct_key_as_list"], pence_per_kwh=100, automatic=False) - assert axle.api_key == "correct_key_as_list", "Should use first element of list as fallback" - assert any("list" in msg.lower() for msg in axle.log_messages), "Should log error about list type" - assert any("axle_api_key" in msg for msg in axle.log_messages), "Should mention axle_api_key in error" - assert any("fallback" in msg.lower() for msg in axle.log_messages), "Should log fallback warning" + assert axle.api_key == None, "Should failed to allow a list as an API key" # Case 2: api_key is an empty list axle2 = MockAxleAPI() axle2.initialize(api_key=[], pence_per_kwh=100, automatic=False) assert axle2.api_key is None, "Empty list should result in None api_key" - assert any("list" in msg.lower() for msg in axle2.log_messages), "Should log error about list type" - assert any("missing or invalid" in msg.lower() for msg in axle2.log_messages), "Should log invalid key error" print(" ✓ List API key validation works correctly") return False From 5f4aa7a336c88110f8537154093f8ec733c5f362 Mon Sep 17 00:00:00 2001 From: Trefor Southwell <48591903+springfall2008@users.noreply.github.com> Date: Tue, 10 Mar 2026 08:48:12 +0000 Subject: [PATCH 5/5] Update apps/predbat/tests/test_axle.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/predbat/tests/test_axle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/predbat/tests/test_axle.py b/apps/predbat/tests/test_axle.py index 2402e1ce0..4ee595730 100644 --- a/apps/predbat/tests/test_axle.py +++ b/apps/predbat/tests/test_axle.py @@ -192,7 +192,7 @@ def _test_axle_list_api_key_validation(my_predbat=None): axle = MockAxleAPI() axle.initialize(api_key=["correct_key_as_list"], pence_per_kwh=100, automatic=False) - assert axle.api_key == None, "Should failed to allow a list as an API key" + assert axle.api_key is None, "Should failed to allow a list as an API key" # Case 2: api_key is an empty list axle2 = MockAxleAPI()