✨ feat: site-level total_energy sensors from official QUANTITY/HOUR buckets #71

Merged
mat merged 3 commits from site-total-energy into main 2026-08-23 13:52:49 +00:00
Owner

Adds the site-level cumulative energy sensors needed to drive the HA Energy dashboard from whole-site figures, fed exclusively by Comwatt's official hourly buckets.

What it adds

6 new sensors per site — <site>_production_total_energy, _consumption_, _injection_, _withdrawal_, _charge_, _discharge_ — with device_class: energy, state_class: total_increasing, unit Wh, so they are directly selectable in the Energy dashboard.

Where the values come from

get_site_time_series(site_id, "QUANTITY", "HOUR", None, "DAY", 8) — the server's official hourly Wh buckets for the whole site (verified live: 191 buckets over 8 days). Each bucket newer than the site's high-water mark is folded in exactly once.

  • No client-side integration between buckets, by design. The total advances in hourly steps as the server publishes each completed hour. This is a deliberate product decision: no computed/interpolated value, only what the API returns. Real-time is already covered by the *_power site sensors and the per-device *_total_energy entities.
  • Seeded with ~8 days of official history on the first successful fetch, so the Energy dashboard shows data immediately.
  • Before the first successful fetch the sensors stay unknown (never 0), which avoids a false 8-day jump in the statistics.

Cost and robustness

  • +1 request per site per ~55 min — reuses ENERGY_MIN_FETCH_INTERVAL_S, the same gate cadence as the per-device QUANTITY path. On a gate-closed poll or a failed fetch, the last known totals are republished unchanged.
  • Persisted across restarts in the existing comwatt.energy_state store under a reserved __sites__ key (additive; _STORE_VERSION stays 1, old stores load cleanly).
  • TOTAL_INCREASING contract protected: totals only ever advance, and negative server values are rejected with a warning (corrupt upstream data would otherwise push the counter backwards and break HA statistics).
  • The per-site high-water mark means a metric whose series is short loses that bucket permanently — now logged at debug level so it stays observable instead of silently under-counting.

Tests

103 total (93 baseline + 10 new): sensor creation/classes, bucket accumulation across polls, 8-day seeding, restart restore without re-folding, API-failure republication, reserved store key, negative-value rejection, short-series isolation. ruff + mypy clean.

Notes

  • __init__.py is untouched: the existing load/save lifecycle (setup, end of poll, FINAL_WRITE, unload) already covers the new site state.
  • Trivial textual conflict expected with #70 (which renames _delta → _power in sensor.py); merge #70 first.
  • Part of the #42 follow-up work.
Adds the site-level cumulative energy sensors needed to drive the HA **Energy dashboard** from whole-site figures, fed exclusively by Comwatt's official hourly buckets. ## What it adds 6 new sensors per site — `<site>_production_total_energy`, `_consumption_`, `_injection_`, `_withdrawal_`, `_charge_`, `_discharge_` — with `device_class: energy`, `state_class: total_increasing`, unit Wh, so they are directly selectable in the Energy dashboard. ## Where the values come from `get_site_time_series(site_id, "QUANTITY", "HOUR", None, "DAY", 8)` — the server's **official** hourly Wh buckets for the whole site (verified live: 191 buckets over 8 days). Each bucket newer than the site's high-water mark is folded in exactly once. - **No client-side integration between buckets, by design.** The total advances in **hourly steps** as the server publishes each completed hour. This is a deliberate product decision: no computed/interpolated value, only what the API returns. Real-time is already covered by the `*_power` site sensors and the per-device `*_total_energy` entities. - **Seeded with ~8 days of official history** on the first successful fetch, so the Energy dashboard shows data immediately. - Before the first successful fetch the sensors stay `unknown` (never `0`), which avoids a false 8-day jump in the statistics. ## Cost and robustness - **+1 request per site per ~55 min** — reuses `ENERGY_MIN_FETCH_INTERVAL_S`, the same gate cadence as the per-device QUANTITY path. On a gate-closed poll or a failed fetch, the last known totals are republished unchanged. - Persisted across restarts in the existing `comwatt.energy_state` store under a reserved `__sites__` key (additive; `_STORE_VERSION` stays 1, old stores load cleanly). - `TOTAL_INCREASING` contract protected: totals only ever advance, and **negative server values are rejected with a warning** (corrupt upstream data would otherwise push the counter backwards and break HA statistics). - The per-site high-water mark means a metric whose series is short loses that bucket permanently — now **logged at debug level** so it stays observable instead of silently under-counting. ## Tests 103 total (93 baseline + 10 new): sensor creation/classes, bucket accumulation across polls, 8-day seeding, restart restore without re-folding, API-failure republication, reserved store key, negative-value rejection, short-series isolation. ruff + mypy clean. ## Notes - `__init__.py` is untouched: the existing load/save lifecycle (setup, end of poll, `FINAL_WRITE`, unload) already covers the new site state. - Trivial textual conflict expected with #70 (which renames `_delta` → `_power` in `sensor.py`); merge #70 first. - Part of the #42 follow-up work.
🐛 fix: log skipped site buckets and reject negative values
All checks were successful
Validate / lint-ruff (pull_request) Successful in 7s
Validate / test-pytest (pull_request) Successful in 3m14s
Validate / type-check-mypy (pull_request) Successful in 3m14s
Validate / lint-ruff (push) Successful in 7s
Validate / test-pytest (push) Successful in 3m11s
Validate / type-check-mypy (push) Successful in 3m12s
bbbdff65dc
mat changed title from WIP: feat: site-level total_energy sensors from official QUANTITY/HOUR buckets to ✨ feat: site-level total_energy sensors from official QUANTITY/HOUR buckets 2026-08-23 13:42:27 +00:00
🔀 merge main into site-total-energy (resolve _delta/_power conflict)
All checks were successful
Validate / lint-ruff (push) Successful in 7s
Validate / test-pytest (push) Successful in 3m12s
Validate / type-check-mypy (push) Successful in 3m16s
Validate / lint-ruff (pull_request) Successful in 7s
Validate / test-pytest (pull_request) Successful in 3m13s
Validate / type-check-mypy (pull_request) Successful in 3m17s
f208bccfe5
mat merged commit 29bdb4c76f into main 2026-08-23 13:52:49 +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!71
No description provided.