Finish migration of recorder to unit conversion (#78985)

This commit is contained in:
epenet 2022-09-26 03:14:18 +02:00 committed by GitHub
parent 3ad5d799c6
commit c1bc26b413
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable, Iterable, MutableMapping from collections.abc import Iterable, MutableMapping
import datetime import datetime
import itertools import itertools
import logging import logging
@ -23,27 +23,7 @@ from homeassistant.components.recorder.models import (
StatisticMetaData, StatisticMetaData,
StatisticResult, StatisticResult,
) )
from homeassistant.const import ( from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_UNIT_OF_MEASUREMENT
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
ENERGY_KILO_WATT_HOUR,
ENERGY_MEGA_WATT_HOUR,
ENERGY_WATT_HOUR,
POWER_KILO_WATT,
POWER_WATT,
PRESSURE_BAR,
PRESSURE_HPA,
PRESSURE_INHG,
PRESSURE_KPA,
PRESSURE_MBAR,
PRESSURE_PA,
PRESSURE_PSI,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
TEMP_KELVIN,
VOLUME_CUBIC_FEET,
VOLUME_CUBIC_METERS,
)
from homeassistant.core import HomeAssistant, State from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import entity_sources from homeassistant.helpers.entity import entity_sources
@ -84,47 +64,6 @@ UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = {
SensorDeviceClass.GAS: VolumeConverter, SensorDeviceClass.GAS: VolumeConverter,
} }
UNIT_CONVERSIONS: dict[str, dict[str, Callable]] = {
# Convert energy to kWh
SensorDeviceClass.ENERGY: {
ENERGY_KILO_WATT_HOUR: lambda x: x
/ EnergyConverter.UNIT_CONVERSION[ENERGY_KILO_WATT_HOUR],
ENERGY_MEGA_WATT_HOUR: lambda x: x
/ EnergyConverter.UNIT_CONVERSION[ENERGY_MEGA_WATT_HOUR],
ENERGY_WATT_HOUR: lambda x: x
/ EnergyConverter.UNIT_CONVERSION[ENERGY_WATT_HOUR],
},
# Convert power to W
SensorDeviceClass.POWER: {
POWER_WATT: lambda x: x / PowerConverter.UNIT_CONVERSION[POWER_WATT],
POWER_KILO_WATT: lambda x: x / PowerConverter.UNIT_CONVERSION[POWER_KILO_WATT],
},
# Convert pressure to Pa
# Note: PressureConverter.convert is bypassed to avoid redundant error checking
SensorDeviceClass.PRESSURE: {
PRESSURE_BAR: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_BAR],
PRESSURE_HPA: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_HPA],
PRESSURE_INHG: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_INHG],
PRESSURE_KPA: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_KPA],
PRESSURE_MBAR: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_MBAR],
PRESSURE_PA: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_PA],
PRESSURE_PSI: lambda x: x / PressureConverter.UNIT_CONVERSION[PRESSURE_PSI],
},
# Convert temperature to °C
# Note: TemperatureConverter.convert is bypassed to avoid redundant error checking
SensorDeviceClass.TEMPERATURE: {
TEMP_CELSIUS: lambda x: x,
TEMP_FAHRENHEIT: TemperatureConverter.fahrenheit_to_celsius,
TEMP_KELVIN: TemperatureConverter.kelvin_to_celsius,
},
# Convert volume to cubic meter
SensorDeviceClass.GAS: {
VOLUME_CUBIC_METERS: lambda x: x,
VOLUME_CUBIC_FEET: lambda x: x
/ VolumeConverter.UNIT_CONVERSION[VOLUME_CUBIC_FEET],
},
}
# Keep track of entities for which a warning about decreasing value has been logged # Keep track of entities for which a warning about decreasing value has been logged
SEEN_DIP = "sensor_seen_total_increasing_dip" SEEN_DIP = "sensor_seen_total_increasing_dip"
WARN_DIP = "sensor_warn_total_increasing_dip" WARN_DIP = "sensor_warn_total_increasing_dip"
@ -212,9 +151,9 @@ def _normalize_states(
entity_id: str, entity_id: str,
) -> tuple[str | None, str | None, list[tuple[float, State]]]: ) -> tuple[str | None, str | None, list[tuple[float, State]]]:
"""Normalize units.""" """Normalize units."""
state_unit = None state_unit: str | None = None
if device_class not in UNIT_CONVERSIONS: if device_class not in UNIT_CONVERTERS:
# We're not normalizing this device class, return the state as they are # We're not normalizing this device class, return the state as they are
fstates = [] fstates = []
for state in entity_history: for state in entity_history:
@ -250,6 +189,7 @@ def _normalize_states(
state_unit = fstates[0][1].attributes.get(ATTR_UNIT_OF_MEASUREMENT) state_unit = fstates[0][1].attributes.get(ATTR_UNIT_OF_MEASUREMENT)
return state_unit, state_unit, fstates return state_unit, state_unit, fstates
converter = UNIT_CONVERTERS[device_class]
fstates = [] fstates = []
for state in entity_history: for state in entity_history:
@ -259,7 +199,7 @@ def _normalize_states(
continue continue
state_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state_unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
# Exclude unsupported units from statistics # Exclude unsupported units from statistics
if state_unit not in UNIT_CONVERSIONS[device_class]: if state_unit not in converter.VALID_UNITS:
if WARN_UNSUPPORTED_UNIT not in hass.data: if WARN_UNSUPPORTED_UNIT not in hass.data:
hass.data[WARN_UNSUPPORTED_UNIT] = set() hass.data[WARN_UNSUPPORTED_UNIT] = set()
if entity_id not in hass.data[WARN_UNSUPPORTED_UNIT]: if entity_id not in hass.data[WARN_UNSUPPORTED_UNIT]:
@ -272,7 +212,14 @@ def _normalize_states(
) )
continue continue
fstates.append((UNIT_CONVERSIONS[device_class][state_unit](fstate), state)) fstates.append(
(
converter.convert(
fstate, from_unit=state_unit, to_unit=converter.NORMALIZED_UNIT
),
state,
)
)
return UNIT_CONVERTERS[device_class].NORMALIZED_UNIT, state_unit, fstates return UNIT_CONVERTERS[device_class].NORMALIZED_UNIT, state_unit, fstates
@ -655,7 +602,7 @@ def list_statistic_ids(
): ):
continue continue
if device_class not in UNIT_CONVERSIONS: if device_class not in UNIT_CONVERTERS:
result[state.entity_id] = { result[state.entity_id] = {
"has_mean": "mean" in provided_statistics, "has_mean": "mean" in provided_statistics,
"has_sum": "sum" in provided_statistics, "has_sum": "sum" in provided_statistics,
@ -667,10 +614,11 @@ def list_statistic_ids(
} }
continue continue
if state_unit not in UNIT_CONVERSIONS[device_class]: converter = UNIT_CONVERTERS[device_class]
if state_unit not in converter.VALID_UNITS:
continue continue
statistics_unit = UNIT_CONVERTERS[device_class].NORMALIZED_UNIT statistics_unit = converter.NORMALIZED_UNIT
result[state.entity_id] = { result[state.entity_id] = {
"has_mean": "mean" in provided_statistics, "has_mean": "mean" in provided_statistics,
"has_sum": "sum" in provided_statistics, "has_sum": "sum" in provided_statistics,
@ -721,7 +669,7 @@ def validate_statistics(
) )
metadata_unit = metadata[1]["unit_of_measurement"] metadata_unit = metadata[1]["unit_of_measurement"]
if device_class not in UNIT_CONVERSIONS: if device_class not in UNIT_CONVERTERS:
if state_unit != metadata_unit: if state_unit != metadata_unit:
# The unit has changed # The unit has changed
validation_result[entity_id].append( validation_result[entity_id].append(
@ -761,8 +709,8 @@ def validate_statistics(
if ( if (
state_class in STATE_CLASSES state_class in STATE_CLASSES
and device_class in UNIT_CONVERSIONS and device_class in UNIT_CONVERTERS
and state_unit not in UNIT_CONVERSIONS[device_class] and state_unit not in UNIT_CONVERTERS[device_class].VALID_UNITS
): ):
# The unit in the state is not supported for this device class # The unit in the state is not supported for this device class
validation_result[entity_id].append( validation_result[entity_id].append(