Score a forecast only when the relay matched its plan in time, not just in share
Reproduced: a forecast planning 15 minutes of heat was scored although the relay was on for 12 (the 0.4.1 rule allowed 20 % per 15-minute block), and the same on-time placed elsewhere in a block passed as well. - The relay's on-time per poll interval, integrated at its own switch times, is compared with the plan block by block up to each horizon: the deviation is the time the relay spent in another state than planned. Unobserved relay time counts as possible deviation. - Matched (at most 2 minutes, worst case): Forecast Error figures. Approximate (at most 5 minutes, at most 2 of them unobserved): separate approximate_stats. Counterfactual (more than 5 minutes seen): counted, not scored. Incomplete otherwise, by reason: relay history, assumed inputs at forecast time (an unknown window or electrical heat), no poll near the due time, or a missing reading. - 2 minutes of unplanned boiler heat move a room by roughly half a 0.1 °C sensor step for the learned heat coefficients (0.05-0.12 °C per 5 minutes); 5 minutes by about one step. - Stored ledger statistics are versioned (2); 0.4.1 statistics used the looser rule and are dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2cfba024ff
commit
0b82a8e26c
5 files changed
+267
-83
No files matched your search
@@ -895,9 +895,16 @@ class HeatingMPCCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
_LOGGER.warning("MPC prediction failed: %s", err)
|
||||
status["status"] = "prediction_error"
|
||||
return status
|
||||
# Inputs the forecast had to assume: such forecasts are not compared with reality.
|
||||
uncertain = [f"window: {name}" for room in vals
|
||||
for name in (vent_inputs.get(room) or {}).get("unknown_windows", [])]
|
||||
uncertain += [f"electrical heat: {self.zones[room].name}" for room in vals
|
||||
if internal[room] is None and room in self.extended
|
||||
and ZoneModel(self.learners[room], self.extended[room]).uses_extended]
|
||||
self._forecasts.record(now.timestamp(), vals, output["trajectory_15min"], output["plan_15min"],
|
||||
self._revision(), {
|
||||
"strategy": "mpc_plan", "outside": outside, "wind": weather.get("wind"),
|
||||
"uncertain_inputs": uncertain,
|
||||
"internal_half_life_minutes": float(self.config.get(CONF_INTERNAL_HALF_LIFE, 30)),
|
||||
"window_open_minutes": float(self.config[CONF_WINDOW_OPEN_MINUTES]),
|
||||
"internal_kw": {room: internal[room] for room in vals},
|
||||
|
||||
Reference in new issue
Block a user