Align temperature conversion with other converters (#79521)
* Align temperature conversion with other converters * Add comments and docstring * Align testspull/79527/head
parent
47b40e1e61
commit
825f9502ad
|
@ -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"))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue