# v0.2.1 source audit — 2026-10-26 Scope: repository implementation, offline tests and documented HA APIs. No HAOS, physical relay, thermostat, deployment or gas-consumption testing was performed. The manifest remains 0.2.1; these are unreleased reliability fixes, not a release acceptance declaration. Milestone 0 remains open. ## Findings and repairs | Area | Baseline finding | Repair / evidence | |---|---|---| | Lifecycle, high | Config flow VERSION=2 had no migration hook; the weather helper's legacy fallback alone cannot migrate HA entries. | Migrate v1 data and options independently through HA's update API, retaining options precedence and existing prioritized selections. Reject newer config versions. | | Lifecycle, medium | Removed room forecast entities remained in the registry after options reload. | Remove only obsolete room forecast IDs for this integration and entry; retain all other entities and compatible IDs. | | Lifecycle, medium | Raised platform setup left the coordinator in hass.data. | Remove that reference on failure. HA's own partially loaded platform cleanup still requires real runtime testing. | | Persistence, high | Malformed roots/counters could crash restore; finite but nonphysical coefficients and invalid covariance could be accepted. | Validate shapes, coefficient bounds, symmetric positive-definite covariance, integer counts and finite error totals; reset malformed rooms atomically. | | Learning, high | Global ON/OFF counts increased even when fitting rejected outliers and could qualify a newly added room using other rooms' history. | Persist accepted ON/OFF counts per room; readiness requires every room to qualify. Retain compatible legacy coefficients/counts but collect fresh excitation evidence. | | Learning, high | A 4–7 minute interval was treated as exactly 5 minutes, including intervals containing relay transitions. | Accept 270–330 seconds, normalize temperature change to five minutes, compute lag using actual elapsed time; reject gaps/backward time, source changes and relay changes since the previous poll (including on/off round trips). | | Inputs, high | Thermostat targets were interpreted as Celsius even in a Fahrenheit HA installation. | Normalize using thermostat temperature_unit when present, otherwise HA's configured temperature unit. | | Inputs, medium | Invalid weights or malformed unit metadata could cause crashes/nonfinite arithmetic. | Reject invalid weights with unavailable proposal; reject non-string units as unusable sources. | | Prediction, high | Inputs could change while saving or optimizing, yet the old plan could be published as ready. | Compare captured HA State snapshots before publishing; discard results when any watched state changes. | | Prediction, medium | A 30/45-minute pure optimizer horizon returned its endpoint as a 60-minute forecast. | Return no 60-minute forecast for horizons below 60. UI already limits horizons to at least 60. Replay tests check the actual +60 and endpoint values. | | Prediction, medium | NaN weights/lag and negative/nonfinite optimizer parameters were not rejected consistently. | Add finite/range validation and regression tests. | The initial 10 tests passed. The expanded suite contains 33 offline tests covering model/weather logic plus coordinator and integration entry points using small HA boundary doubles. The doubles check our calls and state handling; they do not exercise HA scheduling, Store internals, entity registration or actual config forms. The test service-call spy raises if invoked. Source inspection found no actuator service calls, service registration, relay automations or network model inference. ## Remaining gaps, in priority order 1. **Freshness and alignment:** numeric but stale room/outdoor states are still accepted. Define configurable freshness limits and reporting timestamps in the UI, distinguish unchanged values from missing reports, expose rejection reasons, and test fallback/recovery. Missing units still assume canonical units for compatibility. Optional weather source lists lack uniform maximum-size checks. 2. **Real HA test harness and HAOS acceptance:** config/options form submission, reload listener/poller teardown, disabled entities, partial platform setup failure, actual storage failures, restart/uninstall and registry cleanup need tests against a declared supported HA version. Add CI, translation/schema and packaging checks. Existing en/ro flow strings are unchanged by this patch. 3. **Sampling and identification:** polling detects a relay change through last_changed and skips that interval; it does not integrate the relay duty cycle. Restart/gaps reset the lag proxy to the current command, which is only an approximation. Use timestamped relay events and aligned sensor observations before claiming robust radiator identification. Rejecting transition intervals can slow learning. The jitter correction is a local linear approximation. 4. **Persistence and identity:** Store envelope version stays 1; the additive model payload is schema_version=2. Legacy payloads remain readable. Unsupported payload schemas are ignored; comprehensive future-version downgrade protection remains open. Renamed room entity IDs are treated as replacements (fresh model), not automatically matched to entity-registry identity. Same-ID physical sensor relocation and systematic weather provider offsets remain undetected. 5. **Forecast validity:** forecasts remain counterfactual MPC plans with constant outdoor temperature. Five-minute residual MAE is not held-out multi-horizon accuracy. No actual-policy forecast, recorded trajectories, confidence estimate, identifiability test or persistence benchmark exists. Prediction updates remain five-minute polls; a later manual change does not instantly refresh the diagnostic. 6. **Resource use:** optimization is offloaded, but no Pi 5 benchmark exists. Model state is written on each eligible interval (roughly 288 writes/day). Rejected-fit reasons and aggregate failure counters are not yet exposed. ## Compatibility and safe trial Back up HA before installing. Replace the component directory and restart Core; keep Generic Thermostat as the only relay writer. Compatible room coefficients and forecast unique IDs remain stable. Older saved models need at least 30 newly accepted ON and 30 OFF samples per room before becoming ready again, plus the existing 200-total-sample condition. No artificial heating cycles are generated. Observe status, normalized target, per-room model counts and weather provenance. Test manual heat/off and preset changes, a missing sensor, an options reload, room removal and a restart. The proposed-heat entity must remain diagnostic only. Use a disposable config entry for uninstall testing because deletion removes its models. For rollback, restore the component and HA backup together; older code ignores the new per-room counters and retains its original defects. No installation was done as part of this audit. ## Verification commands - `python3 -m unittest discover -s tests -v` — 33 tests pass (Python 3.9.6). - `PYTHONPYCACHEPREFIX=/tmp/heating-mpc-audit-pycache python3 -m compileall -q custom_components/heating_mpc` — passes. - `git diff --check` — passes. The initial unredirected compile check failed because macOS's Python cache path was outside the writable sandbox; the temporary-cache rerun succeeded. No full HA suite, type checker, linter, HAOS or physical-device checks were run. API references used to check migration and entity-registry calls: [HA config entries](https://developers.home-assistant.io/docs/config_entries_index/), [config migration](https://developers.home-assistant.io/docs/config_entries_config_flow_handler/#config-entry-migration), [entity registry](https://contributors.home-assistant.io/core/integrations/entity-registry/). ## Follow-up: configurable freshness The freshness portion of finding 1 is now addressed: room/weather report-age limits, stale-source fallback, recovery without cross-gap learning and per-source quality reasons are implemented. Defaults are 30/120 minutes and both limits are editable in setup/Options with English and Romanian labels. This supersedes the original audit's statement that stale numeric readings are accepted. Physical measurement timestamps, source alignment, cached-provider detection and HAOS validation remain open. See README for compatibility and safe trial details. The suite now has 42 passing offline tests. Compilation with the temporary cache and `git diff --check` pass. The API basis is HA's [state timestamp documentation](https://www.home-assistant.io/docs/configuration/state_object/). ## Follow-up: HA Core harness and CI Added a separate `tests/ha` pytest suite using the real Home Assistant 2025.12.0 package and matching fixtures, pinned for Python 3.13. All 14 tests pass locally, alongside the existing 42 offline tests. The full `scripts/check.sh` checks and `pip check` passed. The suite exercises the actual config manager, config/options schemas, registry, scheduler and Store API, with mocked disk I/O and simulated sensor/reference entities. A service-call guard covers every test. No actuator calls were observed. Tests include cold setup with stored data/manual off, but not a full HA process restart or HAOS hardware validation. Added a Forgejo/Gitea Actions workflow and lightweight package/translation checks. Remote runner execution, a modern-HA version matrix, dedicated lint/type checks, full process restart, partial platform failure and real disk-failure testing remain open. The test harness needed a pycares 4.11.0 pin for compatibility with HA's DNS dependency. Integration runtime dependencies and behavior are unchanged.