✨ feat: auto-migrate stale Wh statistics on site power sensors #72

Merged
mat merged 5 commits from migrate-site-stats into main 2026-08-23 14:23:47 +00:00
Owner

Stacked on #70. Makes the unit correction of #70 transparent for users: no manual step in Developer Tools.

Why

#70 corrects the 6 site sensors from Wh to W (they were instantaneous watts mislabelled as watt-hours). On HA ≥ 2025.12 that leaves stale Wh long-term statistics behind, which raise a units_changed repair per entity and suppress statistics compilation until the old statistics are deleted by hand. No conversion is possible (watts and watt-hours are different physical dimensions — and the stored values were wrong anyway), so they must be deleted.

What it does

On config entry setup, best-effort and idempotent:

  1. skips entirely if the recorder component is not loaded;
  2. resolves the 6 site power entity_ids through the entity registry (site_{site_id}_{key}), so user renames are handled and nothing else can ever be targeted;
  3. reads their statistics metadata via async_list_statistic_ids;
  4. clears only the stale ones via get_instance(hass).async_clear_statistics(...).

Same pattern as duke_energy and ista_ecotrend in HA core. Raw history states are kept; only long-term statistics restart. Any failure is logged and never blocks setup.

Safety: which statistics can be deleted

Only those whose recorded unit is not a power unit at all:

if meta.get("unit_of_measurement") not in PowerConverter.VALID_UNITS
recorded unit outcome
Wh (the stale ones) deleted
W kept
kW, MW, mW, GW, TW (user display override) kept

This matters: a naive != "W" comparison would have deleted the valid statistics of any user who overrode the display unit to kW — and would have done so on every restart, because the recompiled statistics would again be in kW. Verified in the HA source: _normalize_states sets statistics_unit = state_unit for a first-seen sensor, and SensorEntity.unit_of_measurement gives highest priority to the user's chosen unit. A dedicated regression test covers the six power units.

The device *_total_energy entities (legitimately in Wh) have different unique_ids and can never be touched.

Also

Restores "after_dependencies": ["recorder"] in the manifest — introduced by #53, silently lost during the manifest rewrite in 81f6fcc, and required again now that __init__.py imports the recorder.

Tests

101 total (95 baseline + 6 new): stale Wh cleared, all-W idle, user-overridden power units kept, no recorder, failure does not break setup, fresh install no-op. ruff + mypy clean.

Merge order

Merge #70 first, then this PR (its base should be retargeted to main once #70 is merged). The migration note in #70's description can then be replaced by "handled automatically".

Stacked on #70. Makes the unit correction of #70 **transparent for users**: no manual step in Developer Tools. ## Why #70 corrects the 6 site sensors from `Wh` to `W` (they were instantaneous watts mislabelled as watt-hours). On HA ≥ 2025.12 that leaves stale Wh long-term statistics behind, which raise a `units_changed` repair per entity and **suppress statistics compilation** until the old statistics are deleted by hand. No conversion is possible (watts and watt-hours are different physical dimensions — and the stored values were wrong anyway), so they must be deleted. ## What it does On config entry setup, best-effort and idempotent: 1. skips entirely if the `recorder` component is not loaded; 2. resolves the 6 site power `entity_id`s through the **entity registry** (`site_{site_id}_{key}`), so user renames are handled and nothing else can ever be targeted; 3. reads their statistics metadata via `async_list_statistic_ids`; 4. clears only the stale ones via `get_instance(hass).async_clear_statistics(...)`. Same pattern as `duke_energy` and `ista_ecotrend` in HA core. Raw history states are kept; only long-term statistics restart. Any failure is logged and **never blocks setup**. ## Safety: which statistics can be deleted Only those whose recorded unit **is not a power unit at all**: ```python if meta.get("unit_of_measurement") not in PowerConverter.VALID_UNITS ``` | recorded unit | outcome | |---|---| | `Wh` (the stale ones) | deleted | | `W` | kept | | `kW`, `MW`, `mW`, `GW`, `TW` (user display override) | kept | This matters: a naive `!= "W"` comparison would have deleted the **valid** statistics of any user who overrode the display unit to kW — and would have done so on *every restart*, because the recompiled statistics would again be in kW. Verified in the HA source: `_normalize_states` sets `statistics_unit = state_unit` for a first-seen sensor, and `SensorEntity.unit_of_measurement` gives highest priority to the user's chosen unit. A dedicated regression test covers the six power units. The device `*_total_energy` entities (legitimately in Wh) have different unique_ids and can never be touched. ## Also Restores `"after_dependencies": ["recorder"]` in the manifest — introduced by #53, silently lost during the manifest rewrite in `81f6fcc`, and required again now that `__init__.py` imports the recorder. ## Tests 101 total (95 baseline + 6 new): stale Wh cleared, all-W idle, **user-overridden power units kept**, no recorder, failure does not break setup, fresh install no-op. ruff + mypy clean. ## Merge order Merge #70 first, then this PR (its base should be retargeted to `main` once #70 is merged). The migration note in #70's description can then be replaced by "handled automatically".
🐛 fix: only clear statistics whose unit is not a power unit
All checks were successful
Validate / lint-ruff (pull_request) Successful in 7s
Validate / test-pytest (pull_request) Successful in 3m9s
Validate / type-check-mypy (pull_request) Successful in 3m15s
Validate / lint-ruff (push) Successful in 7s
Validate / test-pytest (push) Successful in 3m9s
Validate / type-check-mypy (push) Successful in 3m12s
eb128abe75
mat changed title from WIP: feat: auto-migrate stale Wh statistics on site power sensors to ✨ feat: auto-migrate stale Wh statistics on site power sensors 2026-08-23 13:42:28 +00:00
mat changed target branch from fix-site-unit to main 2026-08-23 14:09:41 +00:00
🔀 merge main into migrate-site-stats
All checks were successful
Validate / lint-ruff (push) Successful in 7s
Validate / test-pytest (push) Successful in 3m13s
Validate / type-check-mypy (push) Successful in 3m17s
Validate / lint-ruff (pull_request) Successful in 7s
Validate / test-pytest (pull_request) Successful in 3m12s
Validate / type-check-mypy (pull_request) Successful in 3m15s
72ff769403
mat merged commit 3240eb7764 into main 2026-08-23 14:23:47 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
mat/homeassistant-comwatt!72
No description provided.