Skip to content

Commit b027416

Browse files
bakerkjclaude
andcommitted
feat(sensor): round native values to cut recorder volume
Metric sensors previously reported full-precision rolling-average floats, so the recorder wrote a new states row every publish cycle (zero dedup). Round the value the recorder sees: loads / capacity-adjusted loads to 1 decimal, MHz (and max_mhz) to the nearest 50 MHz; epp/epb pass through. MHz uses a step rather than decimals because the value is a 4-digit average that wanders tens of MHz per cycle, so decimal rounding never dedupes and sub-50 MHz precision is noise. Measured against ~2 days of production data this dedupes ~33% of cpu_capacity states rows (15m windows compress most: mhz_15m -80%, *_load_15m -42..49%; 1m windows resist by design). Affects states only, not statistics. Load suggested_display_precision lowered 2 -> 1 so the UI does not render a fake trailing digit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 933d2f3 commit b027416

1 file changed

Lines changed: 35 additions & 3 deletions

File tree

custom_components/cpu_capacity/sensor.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def _build_descriptions(
6060
native_unit_of_measurement=PERCENTAGE,
6161
state_class=SensorStateClass.MEASUREMENT,
6262
icon="mdi:gauge",
63-
suggested_display_precision=2,
63+
suggested_display_precision=1,
6464
entity_registry_enabled_default=enabled_by_default,
6565
)
6666
)
@@ -74,7 +74,7 @@ def _build_descriptions(
7474
native_unit_of_measurement=PERCENTAGE,
7575
state_class=SensorStateClass.MEASUREMENT,
7676
icon="mdi:speedometer",
77-
suggested_display_precision=2,
77+
suggested_display_precision=1,
7878
entity_registry_enabled_default=enabled_by_default,
7979
)
8080
)
@@ -154,6 +154,35 @@ def _round_summary_value(key: str, value: Any) -> Any:
154154
return value
155155

156156

157+
# Frequency is reported as a kHz-derived rolling average in the low thousands of
158+
# MHz; it wanders by tens of MHz every publish, so decimal rounding never lets
159+
# the recorder dedupe and sub-50 MHz precision is noise. Quantize to this step
160+
# instead.
161+
_MHZ_NATIVE_STEP = 50
162+
163+
164+
def _round_native_value(metric_key: str, value: Any) -> Any:
165+
"""Round the value the recorder sees so unchanged samples are deduped.
166+
167+
Loads keep one decimal (sub-percent detail on the responsive 1m windows is
168+
preserved); MHz is quantized to ``_MHZ_NATIVE_STEP``. Non-numeric metrics
169+
(``epp`` string, ``epb`` integer) pass through unchanged.
170+
"""
171+
if value is None or isinstance(value, bool):
172+
return value
173+
if not isinstance(value, (int, float)):
174+
return value
175+
176+
number = float(value)
177+
if metric_key == "max_mhz" or metric_key.startswith("mhz_"):
178+
return int(round(number / _MHZ_NATIVE_STEP) * _MHZ_NATIVE_STEP)
179+
if metric_key.startswith("load_pct_") or metric_key.startswith(
180+
"capacity_adjusted_load_pct_"
181+
):
182+
return round(number, 1)
183+
return value
184+
185+
157186
class CpuCapacityBaseSensor(CoordinatorEntity[CpuCapacityCoordinator], SensorEntity):
158187
_attr_has_entity_name = True
159188
_attr_should_poll = False
@@ -198,7 +227,10 @@ def native_value(self) -> Any:
198227
cpu_data = self._cpu_data
199228
if cpu_data is None:
200229
return None
201-
return cpu_data.get(self.entity_description.metric_key)
230+
return _round_native_value(
231+
self.entity_description.metric_key,
232+
cpu_data.get(self.entity_description.metric_key),
233+
)
202234

203235
@property
204236
def available(self) -> bool:

0 commit comments

Comments
 (0)