Skip to content

Fix: Graceful Startup Config Retries & ML Load Fallback#4216

Open
rholligan wants to merge 17 commits into
springfall2008:mainfrom
rholligan:fix/auto-config-startup-retry
Open

Fix: Graceful Startup Config Retries & ML Load Fallback#4216
rholligan wants to merge 17 commits into
springfall2008:mainfrom
rholligan:fix/auto-config-startup-retry

Conversation

@rholligan

Copy link
Copy Markdown

This PR resolves several startup initialization bugs when dealing with slower Home Assistant environments and unexpected ML model outputs.

Changes:

  • Auto-Config Retries: Fixed a bug where unmatched regex strings (re:sensor...) in apps.yaml lists/dicts were destructively overwritten with None. They now retain their original string and are cleanly retried.
  • Startup Entity Buffer: predbat.py now waits 10 minutes from startup before finalizing auto_config (hard-disabling unmatched entities), allowing slower HA environments time to load all integrations.
  • ML Load NaN Safety: Added safe_float to utils.py and applied it to the ML component to prevent InvalidJSONError crashes when the ML model occasionally outputs NaN or Inf.
  • Tests & CI: Added 100% test coverage for the new retry/fallback logic. Also squashed several long-standing cross-platform test bugs (Unicode emojis and CRLF line-endings) that caused unit_test.py to crash on Windows environments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets Predbat startup resilience by (1) improving auto_config handling of regex-based entity configuration during slow Home Assistant startups, and (2) hardening ML load forecasting publication/consumption against non-JSON-compliant floats, alongside several cross-platform test-output cleanups.

Changes:

  • Add safe_float() and apply it to ML load stats publishing to avoid crashes when values become NaN/Inf.
  • Rework regex resolution and startup retry timing for auto_config, plus add new tests intended to cover the retry/fallback paths.
  • Normalize many unit-test prints/comments to avoid Unicode/CRLF issues on Windows.

Reviewed changes

Copilot reviewed 52 out of 53 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
apps/predbat/utils.py Adds safe_float() helper for sanitizing floats.
apps/predbat/userinterface.py Changes regex resolution and auto_config retry bookkeeping.
apps/predbat/unit_test.py Registers new test modules in the test runner.
apps/predbat/tests/test_temperature.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_storage.py Comment-only ASCII normalization.
apps/predbat/tests/test_solis.py Comment-only ASCII normalization.
apps/predbat/tests/test_solcast.py Comment-only ASCII normalization.
apps/predbat/tests/test_sigenergy.py Comment/docstring ASCII normalization.
apps/predbat/tests/test_rate_replicate_missing_slots.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_rate_min_forward_calc.py Comment-only ASCII normalization.
apps/predbat/tests/test_rate_add_io_slots.py Comment-only ASCII normalization.
apps/predbat/tests/test_open_meteo.py Docstring/comment ASCII normalization.
apps/predbat/tests/test_ohme.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_octopus_url.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_octopus_read_response_retry.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_octopus_misc.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_octopus_download_rates.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_octopus_day_night_rates.py Docstring/comment ASCII normalization + status text changes.
apps/predbat/tests/test_ml_load_fallback.py New tests for ML load fallback behaviour.
apps/predbat/tests/test_minute_data.py Comment-only ASCII normalization.
apps/predbat/tests/test_minute_array.py Comment-only ASCII normalization.
apps/predbat/tests/test_load_ml.py Test output ASCII normalization (and some comment tweaks).
apps/predbat/tests/test_kraken.py Comment-only ASCII normalization.
apps/predbat/tests/test_integer_config.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_hainterface_websocket.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_hainterface_state.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_hainterface_service.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_hainterface_lifecycle.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_hainterface_api.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_ge_cloud.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_gateway.py Docstring/comment ASCII normalization.
apps/predbat/tests/test_futurerate_auto.py Docstring ASCII normalization.
apps/predbat/tests/test_find_charge_window.py Docstring/comment ASCII normalization.
apps/predbat/tests/test_find_battery_size.py Comment-only ASCII normalization.
apps/predbat/tests/test_fill_load_from_power.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_fetch_pv_forecast.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_fetch_config_options.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_download.py Fixes temp file writing to be bytes-mode (Windows-friendly).
apps/predbat/tests/test_db_manager.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_component_base.py Comment-only ASCII normalization.
apps/predbat/tests/test_compare.py Comment-only ASCII normalization.
apps/predbat/tests/test_carbon.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_battery_curve_keys.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_balance_inverters.py Replaces Unicode status markers with ASCII text.
apps/predbat/tests/test_auto_config.py New tests for auto_config regex retry behaviour.
apps/predbat/tests/open_meteo_live.py Comment-only ASCII normalization.
apps/predbat/predbat.py Changes startup auto_config finalization and adds a 10-minute retry window.
apps/predbat/load_ml_component.py Uses safe_float() for ML stats publishing to avoid JSON serialization crashes.
apps/predbat/fetch.py Adds “inactive ML forecast” early return fallback to historical data.
Comments suppressed due to low confidence (1)

apps/predbat/userinterface.py:1099

  • auto_config(final=False) does nothing when an arg doesn’t match (it neither disables the arg nor records it in unmatched_args). Since predbat.py only retries auto_config when unmatched_args is non-empty, unmatched regex args won’t be retried during startup, which contradicts the intended “retry for 10 minutes then final-disable” behaviour.

Comment on lines 1045 to +1049
for item_value in arg_value:
item_matched, item_value = self.resolve_arg_re(arg, item_value, state_keys)
if not item_matched:
self.log("Warn: Regular argument {} expression {} failed to match - disabling this item".format(arg, item_value))
new_list.append(None)
else:
new_list.append(item_value)
self.log("Warn: Regular argument {} expression {} failed to match - will retry".format(arg, item_value))
new_list.append(item_value)
Comment on lines 1054 to +1058
item_value = arg_value[item_name]
item_matched, item_value = self.resolve_arg_re(arg, item_value, state_keys)
if not item_matched:
self.log("Warn: Regular argument {} expression {} failed to match - disabling this item".format(arg, item_value))
new_dict[item_name] = None
else:
new_dict[item_name] = item_value
self.log("Warn: Regular argument {} expression {} failed to match - will retry".format(arg, item_value))
new_dict[item_name] = item_value
Comment on lines 24 to 26
from datetime import datetime, timezone, timedelta
import math
from component_base import ComponentBase
Comment on lines +38 to +60
# Test 1: Active status proceeds
fetch.prefix = "active_test"
try:
fetch.fetch_ml_load_forecast(now_utc)
print("FAIL: ML Load forecast should have raised AttributeError on active path")
failed = True
except AttributeError:
# It bypassed the early return and hit unmocked attributes, which means it works
print("PASS: ML Load forecast active status proceeds past early return")

# Test 2: Inactive status falls back
fetch.prefix = "inactive_test"
fetch.load_ml_forecast = {}
try:
fetch.fetch_ml_load_forecast(now_utc)
if fetch.load_ml_forecast:
print("FAIL: ML Load forecast should NOT be populated when inactive")
failed = True
else:
print("PASS: ML Load forecast ignored when inactive (fallback)")
except AttributeError:
print("FAIL: ML Load forecast did NOT fall back early and hit AttributeError")
failed = True
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.

2 participants