2016-07-31 20:24:49 +00:00
|
|
|
"""Unit system helper class and methods."""
|
2021-03-17 20:46:07 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2016-07-31 20:24:49 +00:00
|
|
|
from numbers import Number
|
2017-06-08 13:53:12 +00:00
|
|
|
|
2016-07-31 20:24:49 +00:00
|
|
|
from homeassistant.const import (
|
2021-11-19 08:18:44 +00:00
|
|
|
ACCUMULATED_PRECIPITATION,
|
2019-12-09 15:42:10 +00:00
|
|
|
CONF_UNIT_SYSTEM_IMPERIAL,
|
|
|
|
CONF_UNIT_SYSTEM_METRIC,
|
|
|
|
LENGTH,
|
2021-11-19 08:18:44 +00:00
|
|
|
LENGTH_INCHES,
|
2019-07-31 19:25:30 +00:00
|
|
|
LENGTH_KILOMETERS,
|
2019-12-09 15:42:10 +00:00
|
|
|
LENGTH_MILES,
|
2021-11-19 08:18:44 +00:00
|
|
|
LENGTH_MILLIMETERS,
|
2019-12-09 15:42:10 +00:00
|
|
|
MASS,
|
2019-07-31 19:25:30 +00:00
|
|
|
MASS_GRAMS,
|
|
|
|
MASS_KILOGRAMS,
|
|
|
|
MASS_OUNCES,
|
|
|
|
MASS_POUNDS,
|
|
|
|
PRESSURE,
|
2019-12-09 15:42:10 +00:00
|
|
|
PRESSURE_PA,
|
|
|
|
PRESSURE_PSI,
|
2021-11-18 15:08:42 +00:00
|
|
|
SPEED_METERS_PER_SECOND,
|
|
|
|
SPEED_MILES_PER_HOUR,
|
2019-12-09 15:42:10 +00:00
|
|
|
TEMP_CELSIUS,
|
|
|
|
TEMP_FAHRENHEIT,
|
2019-07-31 19:25:30 +00:00
|
|
|
TEMPERATURE,
|
|
|
|
UNIT_NOT_RECOGNIZED_TEMPLATE,
|
2019-12-09 15:42:10 +00:00
|
|
|
VOLUME,
|
|
|
|
VOLUME_GALLONS,
|
|
|
|
VOLUME_LITERS,
|
2021-11-18 15:08:42 +00:00
|
|
|
WIND_SPEED,
|
2019-12-09 15:42:10 +00:00
|
|
|
)
|
2022-10-14 10:06:14 +00:00
|
|
|
from homeassistant.helpers.frame import report
|
2021-12-23 19:14:47 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
from .unit_conversion import (
|
|
|
|
DistanceConverter,
|
|
|
|
PressureConverter,
|
|
|
|
SpeedConverter,
|
|
|
|
TemperatureConverter,
|
|
|
|
VolumeConverter,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
LENGTH_UNITS = DistanceConverter.VALID_UNITS
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-09-27 06:16:03 +00:00
|
|
|
MASS_UNITS: set[str] = {MASS_POUNDS, MASS_OUNCES, MASS_KILOGRAMS, MASS_GRAMS}
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
PRESSURE_UNITS = PressureConverter.VALID_UNITS
|
2019-03-24 17:37:31 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
VOLUME_UNITS = VolumeConverter.VALID_UNITS
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
WIND_SPEED_UNITS = SpeedConverter.VALID_UNITS
|
2021-11-18 15:08:42 +00:00
|
|
|
|
2022-09-27 06:16:03 +00:00
|
|
|
TEMPERATURE_UNITS: set[str] = {TEMP_FAHRENHEIT, TEMP_CELSIUS}
|
2016-07-31 20:24:49 +00:00
|
|
|
|
|
|
|
|
2022-09-22 19:02:31 +00:00
|
|
|
def _is_valid_unit(unit: str, unit_type: str) -> bool:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Check if the unit is valid for it's type."""
|
|
|
|
if unit_type == LENGTH:
|
|
|
|
units = LENGTH_UNITS
|
2021-11-19 08:18:44 +00:00
|
|
|
elif unit_type == ACCUMULATED_PRECIPITATION:
|
|
|
|
units = LENGTH_UNITS
|
2021-11-18 15:08:42 +00:00
|
|
|
elif unit_type == WIND_SPEED:
|
|
|
|
units = WIND_SPEED_UNITS
|
2016-07-31 20:24:49 +00:00
|
|
|
elif unit_type == TEMPERATURE:
|
|
|
|
units = TEMPERATURE_UNITS
|
|
|
|
elif unit_type == MASS:
|
|
|
|
units = MASS_UNITS
|
|
|
|
elif unit_type == VOLUME:
|
|
|
|
units = VOLUME_UNITS
|
2019-03-24 17:37:31 +00:00
|
|
|
elif unit_type == PRESSURE:
|
|
|
|
units = PRESSURE_UNITS
|
2016-07-31 20:24:49 +00:00
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return unit in units
|
|
|
|
|
|
|
|
|
2018-07-20 08:45:20 +00:00
|
|
|
class UnitSystem:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""A container for units of measure."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
name: str,
|
2021-07-20 12:13:51 +00:00
|
|
|
temperature: str,
|
|
|
|
length: str,
|
2021-11-18 15:08:42 +00:00
|
|
|
wind_speed: str,
|
2021-07-20 12:13:51 +00:00
|
|
|
volume: str,
|
|
|
|
mass: str,
|
|
|
|
pressure: str,
|
2021-11-19 08:18:44 +00:00
|
|
|
accumulated_precipitation: str,
|
2019-07-31 19:25:30 +00:00
|
|
|
) -> None:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Initialize the unit system object."""
|
2019-09-04 03:36:04 +00:00
|
|
|
errors: str = ", ".join(
|
2019-07-31 19:25:30 +00:00
|
|
|
UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit, unit_type)
|
2021-07-19 13:57:06 +00:00
|
|
|
for unit, unit_type in (
|
2021-11-19 08:18:44 +00:00
|
|
|
(accumulated_precipitation, ACCUMULATED_PRECIPITATION),
|
2019-07-31 19:25:30 +00:00
|
|
|
(temperature, TEMPERATURE),
|
|
|
|
(length, LENGTH),
|
2021-11-18 15:08:42 +00:00
|
|
|
(wind_speed, WIND_SPEED),
|
2019-07-31 19:25:30 +00:00
|
|
|
(volume, VOLUME),
|
|
|
|
(mass, MASS),
|
|
|
|
(pressure, PRESSURE),
|
2021-07-19 13:57:06 +00:00
|
|
|
)
|
2022-09-22 19:02:31 +00:00
|
|
|
if not _is_valid_unit(unit, unit_type)
|
2019-09-04 03:36:04 +00:00
|
|
|
)
|
2016-07-31 20:24:49 +00:00
|
|
|
|
|
|
|
if errors:
|
|
|
|
raise ValueError(errors)
|
|
|
|
|
2022-10-14 10:06:14 +00:00
|
|
|
self._name = name
|
2021-11-19 08:18:44 +00:00
|
|
|
self.accumulated_precipitation_unit = accumulated_precipitation
|
2016-07-31 20:24:49 +00:00
|
|
|
self.temperature_unit = temperature
|
|
|
|
self.length_unit = length
|
|
|
|
self.mass_unit = mass
|
2019-03-24 17:37:31 +00:00
|
|
|
self.pressure_unit = pressure
|
2016-07-31 20:24:49 +00:00
|
|
|
self.volume_unit = volume
|
2021-11-18 15:08:42 +00:00
|
|
|
self.wind_speed_unit = wind_speed
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-10-14 10:06:14 +00:00
|
|
|
@property
|
|
|
|
def name(self) -> str:
|
|
|
|
"""Return the name of the unit system."""
|
|
|
|
report(
|
|
|
|
"accesses the `name` property of the unit system. "
|
|
|
|
"This is deprecated and will stop working in Home Assistant 2023.1. "
|
|
|
|
"Please adjust to use instance check instead.",
|
|
|
|
error_if_core=False,
|
|
|
|
)
|
|
|
|
return self._name
|
|
|
|
|
2016-07-31 20:24:49 +00:00
|
|
|
@property
|
2018-07-13 10:24:51 +00:00
|
|
|
def is_metric(self) -> bool:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Determine if this is the metric unit system."""
|
2022-10-14 11:44:18 +00:00
|
|
|
report(
|
|
|
|
"accesses the `is_metric` property of the unit system. "
|
|
|
|
"This is deprecated and will stop working in Home Assistant 2023.1. "
|
|
|
|
"Please adjust to use instance check instead.",
|
|
|
|
error_if_core=False,
|
|
|
|
)
|
|
|
|
return self is METRIC_SYSTEM
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2021-07-20 12:13:51 +00:00
|
|
|
def temperature(self, temperature: float, from_unit: str) -> float:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Convert the given temperature to this unit system."""
|
|
|
|
if not isinstance(temperature, Number):
|
2020-01-03 13:47:06 +00:00
|
|
|
raise TypeError(f"{temperature!s} is not a numeric value.")
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2022-09-28 12:37:22 +00:00
|
|
|
return TemperatureConverter.convert(
|
|
|
|
temperature, from_unit, self.temperature_unit
|
|
|
|
)
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2021-07-20 12:13:51 +00:00
|
|
|
def length(self, length: float | None, from_unit: str) -> float:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Convert the given length to this unit system."""
|
|
|
|
if not isinstance(length, Number):
|
2020-01-03 13:47:06 +00:00
|
|
|
raise TypeError(f"{length!s} is not a numeric value.")
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2019-07-16 22:11:38 +00:00
|
|
|
# type ignore: https://github.com/python/mypy/issues/7207
|
2022-09-28 12:37:22 +00:00
|
|
|
return DistanceConverter.convert( # type: ignore[unreachable]
|
2019-07-31 20:08:31 +00:00
|
|
|
length, from_unit, self.length_unit
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2021-11-19 08:18:44 +00:00
|
|
|
def accumulated_precipitation(self, precip: float | None, from_unit: str) -> float:
|
|
|
|
"""Convert the given length to this unit system."""
|
|
|
|
if not isinstance(precip, Number):
|
|
|
|
raise TypeError(f"{precip!s} is not a numeric value.")
|
|
|
|
|
|
|
|
# type ignore: https://github.com/python/mypy/issues/7207
|
2022-09-28 12:37:22 +00:00
|
|
|
return DistanceConverter.convert( # type: ignore[unreachable]
|
2021-11-19 08:18:44 +00:00
|
|
|
precip, from_unit, self.accumulated_precipitation_unit
|
|
|
|
)
|
|
|
|
|
2021-07-20 12:13:51 +00:00
|
|
|
def pressure(self, pressure: float | None, from_unit: str) -> float:
|
2019-03-24 17:37:31 +00:00
|
|
|
"""Convert the given pressure to this unit system."""
|
|
|
|
if not isinstance(pressure, Number):
|
2020-01-03 13:47:06 +00:00
|
|
|
raise TypeError(f"{pressure!s} is not a numeric value.")
|
2019-03-24 17:37:31 +00:00
|
|
|
|
2019-07-16 22:11:38 +00:00
|
|
|
# type ignore: https://github.com/python/mypy/issues/7207
|
2022-09-28 12:37:22 +00:00
|
|
|
return PressureConverter.convert( # type: ignore[unreachable]
|
2019-07-31 20:08:31 +00:00
|
|
|
pressure, from_unit, self.pressure_unit
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-03-24 17:37:31 +00:00
|
|
|
|
2021-11-18 15:08:42 +00:00
|
|
|
def wind_speed(self, wind_speed: float | None, from_unit: str) -> float:
|
|
|
|
"""Convert the given wind_speed to this unit system."""
|
|
|
|
if not isinstance(wind_speed, Number):
|
|
|
|
raise TypeError(f"{wind_speed!s} is not a numeric value.")
|
|
|
|
|
|
|
|
# type ignore: https://github.com/python/mypy/issues/7207
|
2022-09-28 12:37:22 +00:00
|
|
|
return SpeedConverter.convert(wind_speed, from_unit, self.wind_speed_unit) # type: ignore[unreachable]
|
2021-11-18 15:08:42 +00:00
|
|
|
|
2021-07-20 12:13:51 +00:00
|
|
|
def volume(self, volume: float | None, from_unit: str) -> float:
|
2018-10-11 08:55:22 +00:00
|
|
|
"""Convert the given volume to this unit system."""
|
|
|
|
if not isinstance(volume, Number):
|
2020-01-03 13:47:06 +00:00
|
|
|
raise TypeError(f"{volume!s} is not a numeric value.")
|
2018-10-11 08:55:22 +00:00
|
|
|
|
2019-07-16 22:11:38 +00:00
|
|
|
# type ignore: https://github.com/python/mypy/issues/7207
|
2022-09-28 12:37:22 +00:00
|
|
|
return VolumeConverter.convert(volume, from_unit, self.volume_unit) # type: ignore[unreachable]
|
2018-10-11 08:55:22 +00:00
|
|
|
|
2021-07-20 12:13:51 +00:00
|
|
|
def as_dict(self) -> dict[str, str]:
|
2016-07-31 20:24:49 +00:00
|
|
|
"""Convert the unit system to a dictionary."""
|
|
|
|
return {
|
|
|
|
LENGTH: self.length_unit,
|
2021-11-19 08:18:44 +00:00
|
|
|
ACCUMULATED_PRECIPITATION: self.accumulated_precipitation_unit,
|
2016-07-31 20:24:49 +00:00
|
|
|
MASS: self.mass_unit,
|
2019-03-24 17:37:31 +00:00
|
|
|
PRESSURE: self.pressure_unit,
|
2016-07-31 20:24:49 +00:00
|
|
|
TEMPERATURE: self.temperature_unit,
|
2019-07-31 19:25:30 +00:00
|
|
|
VOLUME: self.volume_unit,
|
2021-11-18 15:08:42 +00:00
|
|
|
WIND_SPEED: self.wind_speed_unit,
|
2016-07-31 20:24:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
METRIC_SYSTEM = UnitSystem(
|
|
|
|
CONF_UNIT_SYSTEM_METRIC,
|
|
|
|
TEMP_CELSIUS,
|
|
|
|
LENGTH_KILOMETERS,
|
2021-11-18 15:08:42 +00:00
|
|
|
SPEED_METERS_PER_SECOND,
|
2019-07-31 19:25:30 +00:00
|
|
|
VOLUME_LITERS,
|
|
|
|
MASS_GRAMS,
|
|
|
|
PRESSURE_PA,
|
2021-11-19 08:18:44 +00:00
|
|
|
LENGTH_MILLIMETERS,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2016-07-31 20:24:49 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
IMPERIAL_SYSTEM = UnitSystem(
|
|
|
|
CONF_UNIT_SYSTEM_IMPERIAL,
|
|
|
|
TEMP_FAHRENHEIT,
|
|
|
|
LENGTH_MILES,
|
2021-11-18 15:08:42 +00:00
|
|
|
SPEED_MILES_PER_HOUR,
|
2019-07-31 19:25:30 +00:00
|
|
|
VOLUME_GALLONS,
|
|
|
|
MASS_POUNDS,
|
|
|
|
PRESSURE_PSI,
|
2021-11-19 08:18:44 +00:00
|
|
|
LENGTH_INCHES,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|