From 825f9502adab0feb10f880e73ff5a4310e6ff557 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 3 Oct 2022 10:09:55 +0200 Subject: [PATCH] Align temperature conversion with other converters (#79521) * Align temperature conversion with other converters * Add comments and docstring * Align tests --- homeassistant/components/alexa/handlers.py | 4 +- homeassistant/util/temperature.py | 6 +- homeassistant/util/unit_conversion.py | 96 +++++++++------------- tests/util/test_unit_conversion.py | 5 +- 4 files changed, 48 insertions(+), 63 deletions(-) diff --git a/homeassistant/components/alexa/handlers.py b/homeassistant/components/alexa/handlers.py index 73410ba06d2..b4c842dd5b5 100644 --- a/homeassistant/components/alexa/handlers.py +++ b/homeassistant/components/alexa/handlers.py @@ -819,7 +819,9 @@ def temperature_from_object(hass, temp_obj, interval=False): # convert to Celsius if absolute temperature temp -= 273.15 - return TemperatureConverter.convert(temp, from_unit, to_unit, interval=interval) + if interval: + return TemperatureConverter.convert_interval(temp, from_unit, to_unit) + return TemperatureConverter.convert(temp, from_unit, to_unit) @HANDLERS.register(("Alexa.ThermostatController", "SetTargetTemperature")) diff --git a/homeassistant/util/temperature.py b/homeassistant/util/temperature.py index e08e1207e06..9173fbc5eee 100644 --- a/homeassistant/util/temperature.py +++ b/homeassistant/util/temperature.py @@ -43,6 +43,6 @@ def convert( "unit_conversion.TemperatureConverter instead", error_if_core=False, ) - return TemperatureConverter.convert( - temperature, from_unit, to_unit, interval=interval - ) + if interval: + return TemperatureConverter.convert_interval(temperature, from_unit, to_unit) + return TemperatureConverter.convert(temperature, from_unit, to_unit) diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index cb066901b37..6d502ee6e6d 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -1,8 +1,6 @@ """Typing Helpers for Home Assistant.""" from __future__ import annotations -from abc import abstractmethod - from homeassistant.const import ( ENERGY_KILO_WATT_HOUR, ENERGY_MEGA_WATT_HOUR, @@ -88,20 +86,6 @@ class BaseUnitConverter: NORMALIZED_UNIT: str VALID_UNITS: set[str] - @classmethod - @abstractmethod - def convert(cls, value: float, from_unit: str, to_unit: str) -> float: - """Convert one unit of measurement to another.""" - - @classmethod - @abstractmethod - def get_unit_ratio(cls, from_unit: str, to_unit: str) -> float: - """Get unit ratio between units of measurement.""" - - -class BaseUnitConverterWithUnitConversion(BaseUnitConverter): - """Define the format of a conversion utility.""" - _UNIT_CONVERSION: dict[str, float] @classmethod @@ -133,7 +117,7 @@ class BaseUnitConverterWithUnitConversion(BaseUnitConverter): return cls._UNIT_CONVERSION[from_unit] / cls._UNIT_CONVERSION[to_unit] -class DistanceConverter(BaseUnitConverterWithUnitConversion): +class DistanceConverter(BaseUnitConverter): """Utility to convert distance values.""" UNIT_CLASS = "distance" @@ -160,7 +144,7 @@ class DistanceConverter(BaseUnitConverterWithUnitConversion): } -class EnergyConverter(BaseUnitConverterWithUnitConversion): +class EnergyConverter(BaseUnitConverter): """Utility to convert energy values.""" UNIT_CLASS = "energy" @@ -177,7 +161,7 @@ class EnergyConverter(BaseUnitConverterWithUnitConversion): } -class MassConverter(BaseUnitConverterWithUnitConversion): +class MassConverter(BaseUnitConverter): """Utility to convert mass values.""" UNIT_CLASS = "mass" @@ -200,7 +184,7 @@ class MassConverter(BaseUnitConverterWithUnitConversion): } -class PowerConverter(BaseUnitConverterWithUnitConversion): +class PowerConverter(BaseUnitConverter): """Utility to convert power values.""" UNIT_CLASS = "power" @@ -215,7 +199,7 @@ class PowerConverter(BaseUnitConverterWithUnitConversion): } -class PressureConverter(BaseUnitConverterWithUnitConversion): +class PressureConverter(BaseUnitConverter): """Utility to convert pressure values.""" UNIT_CLASS = "pressure" @@ -244,7 +228,7 @@ class PressureConverter(BaseUnitConverterWithUnitConversion): } -class SpeedConverter(BaseUnitConverterWithUnitConversion): +class SpeedConverter(BaseUnitConverter): """Utility to convert speed values.""" UNIT_CLASS = "speed" @@ -281,47 +265,49 @@ class TemperatureConverter(BaseUnitConverter): TEMP_FAHRENHEIT, TEMP_KELVIN, } - _UNIT_RATIO = { + _UNIT_CONVERSION = { TEMP_CELSIUS: 1.0, TEMP_FAHRENHEIT: 1.8, TEMP_KELVIN: 1.0, } @classmethod - def convert( - cls, value: float, from_unit: str, to_unit: str, *, interval: bool = False - ) -> float: - """Convert a temperature from one unit to another.""" + def convert(cls, value: float, from_unit: str, to_unit: str) -> float: + """Convert a temperature from one unit to another. + + eg. 10°C will return 50°F + + For converting an interval between two temperatures, please use + `convert_interval` instead. + """ + # We cannot use the implementation from BaseUnitConverter here because the temperature + # units do not use the same floor: 0°C, 0°F and 0K do not align if from_unit == to_unit: return value if from_unit == TEMP_CELSIUS: if to_unit == TEMP_FAHRENHEIT: - return cls._celsius_to_fahrenheit(value, interval) + return cls._celsius_to_fahrenheit(value) if to_unit == TEMP_KELVIN: - return cls._celsius_to_kelvin(value, interval) + return cls._celsius_to_kelvin(value) raise HomeAssistantError( UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit, cls.UNIT_CLASS) ) if from_unit == TEMP_FAHRENHEIT: if to_unit == TEMP_CELSIUS: - return cls._fahrenheit_to_celsius(value, interval) + return cls._fahrenheit_to_celsius(value) if to_unit == TEMP_KELVIN: - return cls._celsius_to_kelvin( - cls._fahrenheit_to_celsius(value, interval), interval - ) + return cls._celsius_to_kelvin(cls._fahrenheit_to_celsius(value)) raise HomeAssistantError( UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit, cls.UNIT_CLASS) ) if from_unit == TEMP_KELVIN: if to_unit == TEMP_CELSIUS: - return cls._kelvin_to_celsius(value, interval) + return cls._kelvin_to_celsius(value) if to_unit == TEMP_FAHRENHEIT: - return cls._celsius_to_fahrenheit( - cls._kelvin_to_celsius(value, interval), interval - ) + return cls._celsius_to_fahrenheit(cls._kelvin_to_celsius(value)) raise HomeAssistantError( UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit, cls.UNIT_CLASS) ) @@ -330,40 +316,40 @@ class TemperatureConverter(BaseUnitConverter): ) @classmethod - def _fahrenheit_to_celsius(cls, fahrenheit: float, interval: bool = False) -> float: + def convert_interval(cls, interval: float, from_unit: str, to_unit: str) -> float: + """Convert a temperature interval from one unit to another. + + eg. a 10°C interval (10°C to 20°C) will return a 18°F (50°F to 68°F) interval + + For converting a temperature value, please use `convert` as this method + skips floor adjustment. + """ + # We use BaseUnitConverter implementation here because we are only interested + # in the ratio between the units. + return super().convert(interval, from_unit, to_unit) + + @classmethod + def _fahrenheit_to_celsius(cls, fahrenheit: float) -> float: """Convert a temperature in Fahrenheit to Celsius.""" - if interval: - return fahrenheit / 1.8 return (fahrenheit - 32.0) / 1.8 @classmethod - def _kelvin_to_celsius(cls, kelvin: float, interval: bool = False) -> float: + def _kelvin_to_celsius(cls, kelvin: float) -> float: """Convert a temperature in Kelvin to Celsius.""" - if interval: - return kelvin return kelvin - 273.15 @classmethod - def _celsius_to_fahrenheit(cls, celsius: float, interval: bool = False) -> float: + def _celsius_to_fahrenheit(cls, celsius: float) -> float: """Convert a temperature in Celsius to Fahrenheit.""" - if interval: - return celsius * 1.8 return celsius * 1.8 + 32.0 @classmethod - def _celsius_to_kelvin(cls, celsius: float, interval: bool = False) -> float: + def _celsius_to_kelvin(cls, celsius: float) -> float: """Convert a temperature in Celsius to Kelvin.""" - if interval: - return celsius return celsius + 273.15 - @classmethod - def get_unit_ratio(cls, from_unit: str, to_unit: str) -> float: - """Get unit ratio between units of measurement.""" - return cls._UNIT_RATIO[from_unit] / cls._UNIT_RATIO[to_unit] - -class VolumeConverter(BaseUnitConverterWithUnitConversion): +class VolumeConverter(BaseUnitConverter): """Utility to convert volume values.""" UNIT_CLASS = "volume" diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index ca70af2e53f..ec839a6575c 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -452,10 +452,7 @@ def test_temperature_convert_with_interval( value: float, from_unit: str, expected: float, to_unit: str ) -> None: """Test conversion to other units.""" - assert ( - TemperatureConverter.convert(value, from_unit, to_unit, interval=True) - == expected - ) + assert TemperatureConverter.convert_interval(value, from_unit, to_unit) == expected @pytest.mark.parametrize(