From 6002377d4f7389f3f7415f3395d8481eff0ea502 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 22 Sep 2022 14:15:22 +0200 Subject: [PATCH] Convert UnitConverter protocol to a class (#78934) * Convert UnitConverter protocl to a class * Remove logic change * Use TypeVar * Remove NORMALIZED_UNIT from pressure * Reduce size of PR * Reduce some more * Once more * Once more * Remove DEVICE_CLASS --- homeassistant/components/number/__init__.py | 8 +-- .../components/recorder/statistics.py | 22 ++++--- homeassistant/components/sensor/__init__.py | 17 +++--- homeassistant/components/sensor/recorder.py | 21 ++++--- homeassistant/helpers/typing.py | 12 +--- homeassistant/util/unit_conversion.py | 60 +++++++++++++++++++ 6 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 homeassistant/util/unit_conversion.py diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index f3e9a1d9da1..0012a4b77ff 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -28,8 +28,8 @@ from homeassistant.helpers.config_validation import ( # noqa: F401 from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity -from homeassistant.helpers.typing import ConfigType, UnitConverter -from homeassistant.util import temperature as temperature_util +from homeassistant.helpers.typing import ConfigType +from homeassistant.util.unit_conversion import BaseUnitConverter, TemperatureConverter from .const import ( ATTR_MAX, @@ -70,8 +70,8 @@ class NumberMode(StrEnum): SLIDER = "slider" -UNIT_CONVERTERS: dict[str, UnitConverter] = { - NumberDeviceClass.TEMPERATURE: temperature_util, +UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = { + NumberDeviceClass.TEMPERATURE: TemperatureConverter, } # mypy: disallow-any-generics diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index d1d9dd2a658..bb38f35cad2 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -29,7 +29,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.storage import STORAGE_DIR -from homeassistant.helpers.typing import UNDEFINED, UndefinedType, UnitConverter +from homeassistant.helpers.typing import UNDEFINED, UndefinedType from homeassistant.util import ( dt as dt_util, energy as energy_util, @@ -38,6 +38,14 @@ from homeassistant.util import ( temperature as temperature_util, volume as volume_util, ) +from homeassistant.util.unit_conversion import ( + BaseUnitConverter, + EnergyConverter, + PowerConverter, + PressureConverter, + TemperatureConverter, + VolumeConverter, +) from .const import DOMAIN, MAX_ROWS_TO_PURGE, SupportedDialect from .db_schema import Statistics, StatisticsMeta, StatisticsRuns, StatisticsShortTerm @@ -179,12 +187,12 @@ STATISTIC_UNIT_TO_UNIT_CLASS: dict[str | None, str] = { volume_util.NORMALIZED_UNIT: "volume", } -STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, UnitConverter] = { - energy_util.NORMALIZED_UNIT: energy_util, - power_util.NORMALIZED_UNIT: power_util, - pressure_util.NORMALIZED_UNIT: pressure_util, - temperature_util.NORMALIZED_UNIT: temperature_util, - volume_util.NORMALIZED_UNIT: volume_util, +STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = { + energy_util.NORMALIZED_UNIT: EnergyConverter, + power_util.NORMALIZED_UNIT: PowerConverter, + pressure_util.NORMALIZED_UNIT: PressureConverter, + temperature_util.NORMALIZED_UNIT: TemperatureConverter, + volume_util.NORMALIZED_UNIT: VolumeConverter, } # Convert energy power, pressure, temperature and volume statistics from the diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 530769f2873..e15bd851952 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -56,11 +56,12 @@ from homeassistant.helpers.config_validation import ( # noqa: F401 from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity -from homeassistant.helpers.typing import ConfigType, StateType, UnitConverter -from homeassistant.util import ( - dt as dt_util, - pressure as pressure_util, - temperature as temperature_util, +from homeassistant.helpers.typing import ConfigType, StateType +from homeassistant.util import dt as dt_util, pressure as pressure_util +from homeassistant.util.unit_conversion import ( + BaseUnitConverter, + PressureConverter, + TemperatureConverter, ) from .const import CONF_STATE_CLASS # noqa: F401 @@ -207,9 +208,9 @@ STATE_CLASS_TOTAL: Final = "total" STATE_CLASS_TOTAL_INCREASING: Final = "total_increasing" STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass] -UNIT_CONVERTERS: dict[str, UnitConverter] = { - SensorDeviceClass.PRESSURE: pressure_util, - SensorDeviceClass.TEMPERATURE: temperature_util, +UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = { + SensorDeviceClass.PRESSURE: PressureConverter, + SensorDeviceClass.TEMPERATURE: TemperatureConverter, } UNIT_RATIOS: dict[str, dict[str, float]] = { diff --git a/homeassistant/components/sensor/recorder.py b/homeassistant/components/sensor/recorder.py index 9bd068aa469..c7ace30af6e 100644 --- a/homeassistant/components/sensor/recorder.py +++ b/homeassistant/components/sensor/recorder.py @@ -47,7 +47,6 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import entity_sources -from homeassistant.helpers.typing import UnitConverter from homeassistant.util import ( dt as dt_util, energy as energy_util, @@ -56,6 +55,14 @@ from homeassistant.util import ( temperature as temperature_util, volume as volume_util, ) +from homeassistant.util.unit_conversion import ( + BaseUnitConverter, + EnergyConverter, + PowerConverter, + PressureConverter, + TemperatureConverter, + VolumeConverter, +) from . import ( ATTR_LAST_RESET, @@ -76,12 +83,12 @@ DEFAULT_STATISTICS = { STATE_CLASS_TOTAL_INCREASING: {"sum"}, } -UNIT_CONVERTERS: dict[str, UnitConverter] = { - SensorDeviceClass.ENERGY: energy_util, - SensorDeviceClass.POWER: power_util, - SensorDeviceClass.PRESSURE: pressure_util, - SensorDeviceClass.TEMPERATURE: temperature_util, - SensorDeviceClass.GAS: volume_util, +UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = { + SensorDeviceClass.ENERGY: EnergyConverter, + SensorDeviceClass.POWER: PowerConverter, + SensorDeviceClass.PRESSURE: PressureConverter, + SensorDeviceClass.TEMPERATURE: TemperatureConverter, + SensorDeviceClass.GAS: VolumeConverter, } UNIT_CONVERSIONS: dict[str, dict[str, Callable]] = { diff --git a/homeassistant/helpers/typing.py b/homeassistant/helpers/typing.py index a6b4862f03b..0e3edab71b0 100644 --- a/homeassistant/helpers/typing.py +++ b/homeassistant/helpers/typing.py @@ -1,7 +1,7 @@ """Typing Helpers for Home Assistant.""" from collections.abc import Mapping from enum import Enum -from typing import Any, Optional, Protocol, Union +from typing import Any, Optional, Union import homeassistant.core @@ -27,16 +27,6 @@ class UndefinedType(Enum): UNDEFINED = UndefinedType._singleton # pylint: disable=protected-access -class UnitConverter(Protocol): - """Define the format of a conversion utility.""" - - VALID_UNITS: tuple[str, ...] - NORMALIZED_UNIT: str - - def convert(self, value: float, from_unit: str, to_unit: str) -> float: - """Convert one unit of measurement to another.""" - - # The following types should not used and # are not present in the core code base. # They are kept in order not to break custom integrations diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py new file mode 100644 index 00000000000..bc0ec09794f --- /dev/null +++ b/homeassistant/util/unit_conversion.py @@ -0,0 +1,60 @@ +"""Typing Helpers for Home Assistant.""" +from __future__ import annotations + +from collections.abc import Callable + +from . import ( + energy as energy_util, + power as power_util, + pressure as pressure_util, + temperature as temperature_util, + volume as volume_util, +) + + +class BaseUnitConverter: + """Define the format of a conversion utility.""" + + NORMALIZED_UNIT: str + VALID_UNITS: tuple[str, ...] + convert: Callable[[float, str, str], float] + + +class EnergyConverter(BaseUnitConverter): + """Utility to convert energy values.""" + + NORMALIZED_UNIT = energy_util.NORMALIZED_UNIT + VALID_UNITS = energy_util.VALID_UNITS + convert = energy_util.convert + + +class PowerConverter(BaseUnitConverter): + """Utility to convert power values.""" + + NORMALIZED_UNIT = power_util.NORMALIZED_UNIT + VALID_UNITS = power_util.VALID_UNITS + convert = power_util.convert + + +class PressureConverter(BaseUnitConverter): + """Utility to convert pressure values.""" + + NORMALIZED_UNIT = pressure_util.NORMALIZED_UNIT + VALID_UNITS = pressure_util.VALID_UNITS + convert = pressure_util.convert + + +class TemperatureConverter(BaseUnitConverter): + """Utility to convert temperature values.""" + + NORMALIZED_UNIT = temperature_util.NORMALIZED_UNIT + VALID_UNITS = temperature_util.VALID_UNITS + convert = temperature_util.convert + + +class VolumeConverter(BaseUnitConverter): + """Utility to convert volume values.""" + + NORMALIZED_UNIT = volume_util.NORMALIZED_UNIT + VALID_UNITS = volume_util.VALID_UNITS + convert = volume_util.convert