From f71980a6347f1993201891722bfc0a41cd5ea2e6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Jul 2021 12:56:34 -0700 Subject: [PATCH] Create stats for all sensors that have % unit and are measurement (#53576) --- homeassistant/components/sensor/recorder.py | 48 ++++++++++++--------- tests/components/sensor/test_recorder.py | 1 + 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/sensor/recorder.py b/homeassistant/components/sensor/recorder.py index b5a38cfeec1..afcfe2f228d 100644 --- a/homeassistant/components/sensor/recorder.py +++ b/homeassistant/components/sensor/recorder.py @@ -23,6 +23,7 @@ from homeassistant.const import ( DEVICE_CLASS_POWER, ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR, + PERCENTAGE, POWER_KILO_WATT, POWER_WATT, PRESSURE_BAR, @@ -44,7 +45,7 @@ from . import ATTR_LAST_RESET, DOMAIN _LOGGER = logging.getLogger(__name__) -DEVICE_CLASS_STATISTICS = { +DEVICE_CLASS_OR_UNIT_STATISTICS = { DEVICE_CLASS_BATTERY: {"mean", "min", "max"}, DEVICE_CLASS_ENERGY: {"sum"}, DEVICE_CLASS_HUMIDITY: {"mean", "min", "max"}, @@ -52,6 +53,7 @@ DEVICE_CLASS_STATISTICS = { DEVICE_CLASS_POWER: {"mean", "min", "max"}, DEVICE_CLASS_PRESSURE: {"mean", "min", "max"}, DEVICE_CLASS_TEMPERATURE: {"mean", "min", "max"}, + PERCENTAGE: {"mean", "min", "max"}, } # Normalized units which will be stored in the statistics table @@ -102,13 +104,19 @@ def _get_entities(hass: HomeAssistant) -> list[tuple[str, str]]: entity_ids = [] for state in all_sensors: - device_class = state.attributes.get(ATTR_DEVICE_CLASS) - state_class = state.attributes.get(ATTR_STATE_CLASS) - if not state_class or state_class != STATE_CLASS_MEASUREMENT: + if state.attributes.get(ATTR_STATE_CLASS) != STATE_CLASS_MEASUREMENT: continue - if not device_class or device_class not in DEVICE_CLASS_STATISTICS: - continue - entity_ids.append((state.entity_id, device_class)) + + if ( + key := state.attributes.get(ATTR_DEVICE_CLASS) + ) in DEVICE_CLASS_OR_UNIT_STATISTICS: + entity_ids.append((state.entity_id, key)) + + if ( + key := state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + ) in DEVICE_CLASS_OR_UNIT_STATISTICS: + entity_ids.append((state.entity_id, key)) + return entity_ids @@ -158,12 +166,12 @@ def _time_weighted_average( def _normalize_states( - entity_history: list[State], device_class: str, entity_id: str + entity_history: list[State], key: str, entity_id: str ) -> tuple[str | None, list[tuple[float, State]]]: """Normalize units.""" unit = None - if device_class not in UNIT_CONVERSIONS: + if key not in UNIT_CONVERSIONS: # We're not normalizing this device class, return the state as they are fstates = [ (float(el.state), el) for el in entity_history if _is_number(el.state) @@ -182,15 +190,15 @@ def _normalize_states( fstate = float(state.state) unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) # Exclude unsupported units from statistics - if unit not in UNIT_CONVERSIONS[device_class]: + if unit not in UNIT_CONVERSIONS[key]: if entity_id not in WARN_UNSUPPORTED_UNIT: WARN_UNSUPPORTED_UNIT.add(entity_id) _LOGGER.warning("%s has unknown unit %s", entity_id, unit) continue - fstates.append((UNIT_CONVERSIONS[device_class][unit](fstate), state)) + fstates.append((UNIT_CONVERSIONS[key][unit](fstate), state)) - return DEVICE_CLASS_UNITS[device_class], fstates + return DEVICE_CLASS_UNITS[key], fstates def compile_statistics( @@ -209,14 +217,14 @@ def compile_statistics( hass, start - datetime.timedelta.resolution, end, [i[0] for i in entities] ) - for entity_id, device_class in entities: - wanted_statistics = DEVICE_CLASS_STATISTICS[device_class] + for entity_id, key in entities: + wanted_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key] if entity_id not in history_list: continue entity_history = history_list[entity_id] - unit, fstates = _normalize_states(entity_history, device_class, entity_id) + unit, fstates = _normalize_states(entity_history, key, entity_id) if not fstates: continue @@ -288,8 +296,8 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) - statistic_ids = {} - for entity_id, device_class in entities: - provided_statistics = DEVICE_CLASS_STATISTICS[device_class] + for entity_id, key in entities: + provided_statistics = DEVICE_CLASS_OR_UNIT_STATISTICS[key] if statistic_type is not None and statistic_type not in provided_statistics: continue @@ -302,14 +310,14 @@ def list_statistic_ids(hass: HomeAssistant, statistic_type: str | None = None) - native_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - if device_class not in UNIT_CONVERSIONS: + if key not in UNIT_CONVERSIONS: statistic_ids[entity_id] = native_unit continue - if native_unit not in UNIT_CONVERSIONS[device_class]: + if native_unit not in UNIT_CONVERSIONS[key]: continue - statistics_unit = DEVICE_CLASS_UNITS[device_class] + statistics_unit = DEVICE_CLASS_UNITS[key] statistic_ids[entity_id] = statistics_unit return statistic_ids diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 998cc93e629..58614e86a0e 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -44,6 +44,7 @@ TEMPERATURE_SENSOR_ATTRIBUTES = { @pytest.mark.parametrize( "device_class,unit,native_unit,mean,min,max", [ + (None, "%", "%", 16.440677, 10, 30), ("battery", "%", "%", 16.440677, 10, 30), ("battery", None, None, 16.440677, 10, 30), ("humidity", "%", "%", 16.440677, 10, 30),