Skip to content

Commit de706f3

Browse files
authored
Merge pull request #202 from SpanPanel/2_0_4a_attribute_recorder
Exclude high-churn energy sensor attrs from recorder (#197)
2 parents 7180a52 + 283fec5 commit de706f3

3 files changed

Lines changed: 52 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ All notable changes to this project will be documented in this file.
3333

3434
- **PV nameplate capacity unit** — Corrected the PV nameplate capacity sensor unit to watts.
3535

36+
- **Recorder database growth** — Energy sensors still expose grace-period and dip-compensation diagnostics, plus circuit `tabs` and `voltage`, on the entity, but
37+
those attributes are no longer written to the recorder, which greatly reduces churn in the `state_attributes` table (#197).
38+
3639
## [2.0.3] - 3/2026
3740

3841
**Important** 2.0.1 cautions still apply — read those carefully if not already on 2.0.1 BEFORE proceeding:

custom_components/span_panel/sensor_base.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@
3737
# Sentinel value to distinguish "never synced" from "circuit name is None"
3838
_NAME_UNSET: object = object()
3939

40+
# Keys from Span energy sensors' extra_state_attributes that we omit from the recorder
41+
# (SpanEnergySensorBase: panel-wide and circuit energy entities). High-churn grace/dip
42+
# diagnostics dominated DB growth (#197). tabs and voltage are merged in by circuit
43+
# subclasses; they stay on the live entity for Developer tools and automations.
44+
_ENERGY_SENSOR_UNRECORDED_ATTRIBUTES: frozenset[str] = frozenset(
45+
{
46+
"energy_offset",
47+
"grace_period_remaining",
48+
"last_dip_delta",
49+
"last_valid_changed",
50+
"last_valid_state",
51+
"tabs",
52+
"using_grace_period",
53+
"voltage",
54+
}
55+
)
56+
4057

4158
def _parse_numeric_state(state: State | None) -> tuple[float | None, datetime | None]:
4259
"""Extract a numeric value and naive timestamp from a restored HA state.
@@ -445,8 +462,14 @@ class SpanEnergySensorBase[T: SensorEntityDescription, D](SpanSensorBase[T, D],
445462
- Grace period tracking for offline scenarios
446463
- State restoration across HA restarts via RestoreSensor mixin
447464
- Automatic persistence of last_valid_state and last_valid_changed
465+
466+
High-churn diagnostic attributes are listed in ``extra_state_attributes`` for
467+
the UI but omitted from recorder history via ``_unrecorded_attributes`` so the
468+
database is not flooded with unique attribute blobs on every energy update.
448469
"""
449470

471+
_unrecorded_attributes = _ENERGY_SENSOR_UNRECORDED_ATTRIBUTES
472+
450473
def __init__(
451474
self,
452475
data_coordinator: SpanPanelCoordinator,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Energy sensors exclude volatile attributes from recorder history (#197)."""
2+
3+
from homeassistant.components.sensor import SensorEntity
4+
5+
from custom_components.span_panel.sensor_base import SpanEnergySensorBase
6+
7+
8+
def test_span_energy_sensor_combined_unrecorded_includes_high_churn_attributes() -> None:
9+
"""Recorder must not persist grace-period / dip diagnostic attributes."""
10+
11+
combined = getattr(SpanEnergySensorBase, "_Entity__combined_unrecorded_attributes")
12+
for key in (
13+
"energy_offset",
14+
"grace_period_remaining",
15+
"last_dip_delta",
16+
"last_valid_changed",
17+
"last_valid_state",
18+
"tabs",
19+
"using_grace_period",
20+
"voltage",
21+
):
22+
assert key in combined, f"missing unrecorded key: {key}"
23+
24+
assert SensorEntity._entity_component_unrecorded_attributes <= combined, (
25+
"sensor component exclusions (e.g. options) must remain"
26+
)

0 commit comments

Comments
 (0)