Never publish a plan computed from inputs that have since changed
Confirmed: the check run after the optimizer compared only zone sensors, outdoor temperature, thermostat and relay. A window opening, a UPS load change or a wind change during the calculation went unnoticed; nothing prevented an older update that finished late from overwriting a newer result; and an update in flight during unload still saved. - Every input a prediction uses is captured as a normalized snapshot (zone and outdoor temperatures, wind speed/direction/gust, heat-source power, window states, relay state and switch time, thermostat mode and target) and compared after the calculation with tolerances (0.25 °C, 50 W, 1 m/s, 2 m/s gusts, 20° direction around the circle; exact for relay, thermostat and windows). A meaningful change discards the plan and lists the inputs in changed_inputs; noise does not. No immediate recalculation: an off-schedule poll would break the 5-minute learning interval; the next regular poll computes a fresh plan. - Updates are numbered; one overtaken by a newer update, or running when the entry unloads, keeps the newer data and skips saving and optimizing. Configuration changes reload the entry, so they are covered. - A per-entry storage lock makes a reloaded coordinator restore only after the previous one's save finished, and integration removal waits for it too, so a late save cannot recreate a deleted model file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
099c63cd02
commit
d73e32a4b9
5 files changed
+236
-27
No files matched your search
@@ -12,7 +12,7 @@ from homeassistant.helpers.event import async_track_state_change_event
|
||||
|
||||
from .const import CONF_HEATER, CONF_ROOMS, CONF_WEIGHTS, DOMAIN, CONF_OUTSIDE, WEATHER_SOURCES
|
||||
from .weather_sources import sources_for
|
||||
from .coordinator import HeatingMPCCoordinator
|
||||
from .coordinator import HeatingMPCCoordinator, storage_lock
|
||||
from .zones import SUBENTRY_ZONE, ZONE_KEY, ZONE_NAME, legacy_zone_data
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.BINARY_SENSOR]
|
||||
@@ -62,7 +62,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Remove the learned model when this integration instance is deleted."""
|
||||
store = Store(hass, 1, f"{DOMAIN}.{entry.entry_id}")
|
||||
await store.async_remove()
|
||||
# Wait for a save still in progress, which would otherwise recreate the file.
|
||||
async with storage_lock(hass, entry.entry_id):
|
||||
await store.async_remove()
|
||||
hass.data.get(f"{DOMAIN}_storage_locks", {}).pop(entry.entry_id, None)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
Reference in new issue
Block a user