Add dynamic additional load forecasts#3885
Add dynamic additional load forecasts#3885Scholdan wants to merge 25 commits intospringfall2008:mainfrom
Conversation
|
I found two edge cases while reviewing this and pushed fixes in
I added regression coverage for both cases and ran:
|
There was a problem hiding this comment.
I re-checked the PR after c851bf93.
The two issues I found earlier are now fixed and covered by tests:
- Partial-duration loads now keep the configured total energy in the planner.
- Sanitized Home Assistant entity names now map back to the original forecast names for service updates and delete buttons.
I did not find any new blocking issues in the updated diff.
Checks looked good:
coverage/./run_all --test additional_load_forecastcoverage/./run_pre_commit
There was a problem hiding this comment.
Pull request overview
This PR adds support for named “additional house load forecasts” so Predbat can account for known future appliance/load events in its forward plan, including a new flexible scheduling mode that selects the best run window using the full prediction metric. It also documents the new configuration/API and adds targeted unit tests.
Changes:
- Add
house_load_additional_forecastYAML config plus runtime one-shot updates viaselect.predbat_load_forecast_delta_apiandpredbat.update_load_forecast_delta. - Integrate additional load deltas into plan load-step data, with flexible loads selected using prediction-metric scoring and published to HA as binary sensors (+ delete buttons for dynamic entries).
- Add documentation and a dedicated test suite covering fixed/flexible semantics, weighting, expiry, delete, and restart persistence.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/manual-api.md | Documents how to create/update one-shot additional load forecasts via HA select, including flexible scheduling examples. |
| docs/apps-yaml.md | Documents static house_load_additional_forecast configuration, weighting, fixed vs flexible semantics, and API updates. |
| apps/predbat/userinterface.py | Wires HA select/service/button events to update/delete additional load forecasts and trigger refresh. |
| apps/predbat/unit_test.py | Registers the new additional load forecast test suite. |
| apps/predbat/tests/test_additional_load_forecast.py | Adds comprehensive tests for additional load forecasts (fixed/flexible/API/service/delete/expiry/restart). |
| apps/predbat/predbat.py | Adds delete_state_wrapper() and initializes new additional-load-related runtime state on reset. |
| apps/predbat/plan.py | Adds prediction-metric candidate scoring/selection for flexible additional loads and injects adjustments into plan load step data. |
| apps/predbat/output.py | Adds textual plan summary lines for planned/suggested/running additional loads. |
| apps/predbat/ha.py | Adds HA state deletion support and extends api_call() to support HTTP DELETE. |
| apps/predbat/fetch.py | Implements parsing, merging, scheduling, publishing, expiry, and entity lifecycle for additional load forecasts. |
| apps/predbat/config.py | Adds the new API select (load_forecast_delta_api) and config schema for house_load_additional_forecast. |
| .cspell/custom-dictionary-workspace.txt | Adds delayedstart for spellchecking in docs/examples. |
| def delete_state(self, entity_id): | ||
| """ | ||
| Delete a state from Home Assistant. | ||
| """ | ||
| self.db_mirror_list.pop(entity_id, None) | ||
| self.state_data.pop(entity_id.lower(), None) | ||
| if self.ha_key: | ||
| self.api_call("/api/states/{}".format(entity_id), delete=True) |
| if not name and entity_id: | ||
| marker = "_load_forecast_delta_" | ||
| if marker in entity_id: | ||
| name = entity_id.split(marker, 1)[1] |
|
I simplified the dynamic update side of this PR. Changes:
I kept the remaining behavior intact: static YAML forecasts, dynamic one-shot forecasts, flexible scheduling lifecycle, restart metadata, Validation:
|
Summary
This PR adds named additional house load forecasts so Predbat can include known future appliance/load events in the forward plan. The main use case is loads that are not well represented by historical usage, for example a dishwasher, cooking, hot water, or heating demand.
Fixes #2917.
It supports both static
apps.yamlconfiguration and dynamic one-shot requests from Home Assistant automations.What This Adds
Static YAML Load Forecasts
Adds
house_load_additional_forecastentries inapps.yaml, keyed byname.Supported fields include:
nameenabledmode:fixedorflexiblestart_timeend_timedurationenergy: total kWh across the whole loadslot_energy: advanced kWh per Predbat plan slotweighting: optional profile shaping across the durationStatic YAML entries are intended for recurring or known configuration-driven load injection. They do not get delete buttons and are edited or removed through
apps.yaml.Dynamic Home Assistant API Forecasts
Adds/uses
select.predbat_load_forecast_delta_apifor runtime one-shot forecasts from Home Assistant automations.Example:
These dynamic forecasts are one-shot requests:
binary_sensor.predbat_load_forecast_delta_dishwasherbutton.predbat_load_forecast_delta_dishwasher_deleteA service path is also supported via
predbat.update_load_forecast_deltafor integrations that prefer a service-style update.Published HA Attributes
Each named load forecast publishes useful attributes including:
enabledmodeenergyslot_energydurationload_modeplan_interval_minutesslotstotal_energyrequested_startrequested_endsuggested_startsuggested_endselection_reasonselection_lockedcandidate_countsourceauto_expireexpires_attarget_timesThese attributes make the selected plan inspectable in Home Assistant and usable from automations.
Flexible Load Scheduling
Adds
mode=flexiblefor appliance loads that can run anytime within a deadline.Flexible semantics:
start_timemeans earliest allowed startend_timemeans the load must be done by this timeend_timemeans the remaining forecast horizonstart_timediffers by source:Predbat chooses the flexible start time using the full prediction metric, rather than just the raw import rate. This means selection considers the current plan, battery state, PV forecast, import/export prices, losses, and other predicted load.
One-Shot Lifecycle
Dynamic flexible forecasts now follow the practical appliance lifecycle:
Before
suggested_start, the selected window is suggestion-only. Predbat publishessuggested_startandsuggested_end, but does not publish committedtarget_times,slots, ortotal_energyfor the load sensor yet. This keeps the request movable and allows later planning runs to choose a better window if the wider prediction metric changes.Once
suggested_starthas passed, the load is treated as committed/running because a real appliance such as a dishwasher cannot normally be moved after it has started. At that pointselection_lockedbecomes true, future replans no longer move it, and the sensor publishes only the remaining futuretarget_timesuntil auto-expiry atsuggested_end.Requested Start Drift Fix
This PR fixes an issue found during testing where dynamic API loads with omitted
start_timehadrequested_startdrift forward on each refresh.The fix stamps
_requested_start_minuteswhen a one-shot API/service request is first parsed or created. This keepsrequested_startstable across replans while still allowing a fresh start time when the same command is sent again.A follow-up edge case is also handled: if stale selected flexible metadata exists before the frozen requested start, the selected start is clamped to
requested_start, sosuggested_startcannot be earlier than the published requested window.Textual Plan
The textual plan now includes confirmed additional loads once they have target slots, for example:
Pending flexible loads are not included until Predbat has selected an actual window.
Documentation
Docs were added/updated in:
docs/apps-yaml.mddocs/manual-api.mdThe docs cover:
house_load_additional_forecastconfigurationenergyvsslot_energystart_timebehaviour vs rolling YAML behavioursuggested_startautomation, and manual-start cleanup automationValidation
Ran successfully:
The targeted test coverage includes:
start_time/durationstart_time/end_timeenergydistributionslot_energysuggested_startsuggested_startsuggested_startsuggested_endstart_timefreezestart_timerolling behaviourrequested_start