Skip to content

feat: accept an external consumption forecast as a time-series entity - #437

Draft
johanzander wants to merge 4 commits into
mainfrom
feat/issue-428-consumption-forecast-series
Draft

feat: accept an external consumption forecast as a time-series entity#437
johanzander wants to merge 4 commits into
mainfrom
feat/issue-428-consumption-forecast-series

Conversation

@johanzander

Copy link
Copy Markdown
Owner

Summary

  • Adds ha_consumption_series: a new consumption-forecast strategy reading a user-authored HA time-series entity (raw_today/raw_tomorrow attributes of timestamped {start, value} records), mirroring the existing price-manager entity pattern rather than solar's hour-indexed parser (which can't represent sub-hourly data).
  • BESS still does not model loads itself — no EV/weather/occupancy modelling was added. The user's own HA template or automation builds the shaped series; BESS only normalizes 15- or 60-minute record spacing onto the DP's quarter-hour grid.
  • A missing, stale, malformed, or horizon-short series raises an explicit ConsumptionForecastUnavailableError — this strategy never silently falls back to a flat profile, unlike ha_statistics's fixed-value fallback on insufficient data.
  • Renames the scalar sensor strategy's misleading "Consumption Forecast" UI category to "Consumption Average (48h Rolling)" (it was never a real forecast) and adds a new "Consumption Forecast Series" category for the new entity.
  • Adds an example HA template (baseline + a scheduled EV block) to docs/USER_GUIDE.md so the seam is usable without starting from a blank YAML file.

Root cause

From the issue: every existing consumption strategy is either a constant or an in-house statistical model. ha_statistics's trimmed mean deliberately discounts one-off spikes (the right call for outlier robustness) but as a side effect is permanently blind to a known, predictable load like an EV session — nothing puts that load back into the forecast. Solar has an external-forecast seam (Solcast); consumption had none.

Fix

  • HomeAssistantAPIController.get_consumption_forecast_series() / get_consumption_forecast_series_tomorrow() (core/bess/ha_api_controller.py) — fetch and normalize the entity, mirroring price_manager.py's raw_today/raw_tomorrow parsing.
  • BatterySystemManager._get_consumption_forecast dispatches to it for the new strategy; _extend_consumption_predictions (extracted from the existing tomorrow-horizon-extension logic) uses the entity's real raw_tomorrow data instead of repeating today's pattern when the DP horizon spans midnight — the same treatment solar's _fetch_tomorrow_solar_forecast already gets.
  • Health-check coverage (sensor_collector.check_prediction_health) and the strategy-comparison endpoint (get_consumption_forecast_comparison) both cover the new strategy.
  • Frontend: new radio option + description in HomeFormSection.tsx, sensor category rename + addition in sensorDefinitions.ts, updated SensorConfigSection.tsx detection logic, ConsumptionForecastComparison.tsx chart labels/colors.

Test plan

  • ./scripts/quality-check.sh passes locally (fast suite: 1399 passed, 15 skipped; frontend: 103 passed; Black/Ruff/TypeScript/ESLint clean)
  • .venv/bin/pytest -m slow passes (394 passed, 3 skipped)
  • New unit tests: entity parsing (happy path, hourly upsampling, stale/malformed/short-horizon/unsupported-interval failure modes), strategy dispatch + no-silent-fallback, health-check integration, and an end-to-end test that runs a shaped EV-load series through the real DP optimizer and confirms it produces a different (correct) plan than a flat series
  • Local E2E verification via docker-compose.ci.yml (real backend + mock-HA): configured the new strategy and a shaped entity via the real PATCH /api/settings API, confirmed GET /api/consumption-forecast-comparison returns the correct shaped hourly profile (EV hour at 16.0 kWh vs 1.2 kWh baseline) through the live system, confirmed the health check reports OK with the exact shaped series and then a clear ERROR (no silent fallback) when the entity's raw_today data was removed, and confirmed the new frontend strings are present in the actual served JS bundle

Closes #428

johanzander and others added 4 commits July 30, 2026 23:46
…#428)

BESS optimizes against forecasts but never modelled loads itself — for
consumption every existing strategy was either a constant or an in-house
statistical model, none able to express a known, shaped load (an EV
session, weather-driven aircon) the user can predict but a trimmed-mean
baseline deliberately discounts as an outlier.

Adds `ha_consumption_series`: a new strategy reading a user-authored HA
entity with `raw_today`/`raw_tomorrow` timestamped {start, value}
attributes, mirroring the existing price-manager pattern rather than
solar's hour-indexed parser (which structurally can't represent
sub-hourly data). Accepts 15- or 60-minute record spacing, normalized
onto the DP's quarter-hour grid; a missing, stale, malformed, or
horizon-short series is an explicit ConsumptionForecastUnavailableError,
never a silent fallback to a flat profile — unlike ha_statistics's
fixed-value fallback on insufficient data.

Also renames the scalar `sensor` strategy's misleading "Consumption
Forecast" UI category to "Consumption Average (48h Rolling)", since it
was never a real forecast, and adds an example HA template so the seam
is usable without starting from a blank YAML file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ption-forecast-series

# Conflicts:
#	CHANGELOG.md
…ption-forecast-series

# Conflicts:
#	CHANGELOG.md
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.

Accept an external consumption forecast as a time-series entity

1 participant