Align temperature conversion with other converters (#79521)

* Align temperature conversion with other converters

* Add comments and docstring

* Align tests
pull/79527/head
epenet 2022-10-03 10:09:55 +02:00 committed by GitHub
parent 47b40e1e61
commit 825f9502ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 63 deletions

View File

@ -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"))

View File

@ -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)

View File

@ -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"

View File

@ -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(