Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@
VOLUME: Final = "volume"
TEMPERATURE: Final = "temperature"
SPEED: Final = "speed"
WIND_SPEED: Final = "wind_speed"
ILLUMINANCE: Final = "illuminance"

WEEKDAYS: Final[list[str]] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
Expand Down
22 changes: 22 additions & 0 deletions homeassistant/util/unit_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@
PRESSURE,
PRESSURE_PA,
PRESSURE_PSI,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
TEMPERATURE,
UNIT_NOT_RECOGNIZED_TEMPLATE,
VOLUME,
VOLUME_GALLONS,
VOLUME_LITERS,
WIND_SPEED,
)
from homeassistant.util import (
distance as distance_util,
pressure as pressure_util,
speed as speed_util,
temperature as temperature_util,
volume as volume_util,
)
Expand All @@ -42,13 +46,17 @@

VOLUME_UNITS = volume_util.VALID_UNITS

WIND_SPEED_UNITS = speed_util.VALID_UNITS

TEMPERATURE_UNITS: tuple[str, ...] = (TEMP_FAHRENHEIT, TEMP_CELSIUS)


def is_valid_unit(unit: str, unit_type: str) -> bool:
"""Check if the unit is valid for it's type."""
if unit_type == LENGTH:
units = LENGTH_UNITS
elif unit_type == WIND_SPEED:
units = WIND_SPEED_UNITS
elif unit_type == TEMPERATURE:
units = TEMPERATURE_UNITS
elif unit_type == MASS:
Expand All @@ -71,6 +79,7 @@ def __init__(
name: str,
temperature: str,
length: str,
wind_speed: str,
volume: str,
mass: str,
pressure: str,
Expand All @@ -81,6 +90,7 @@ def __init__(
for unit, unit_type in (
(temperature, TEMPERATURE),
(length, LENGTH),
(wind_speed, WIND_SPEED),
(volume, VOLUME),
(mass, MASS),
(pressure, PRESSURE),
Expand All @@ -97,6 +107,7 @@ def __init__(
self.mass_unit = mass
self.pressure_unit = pressure
self.volume_unit = volume
self.wind_speed_unit = wind_speed

@property
def is_metric(self) -> bool:
Expand Down Expand Up @@ -130,6 +141,14 @@ def pressure(self, pressure: float | None, from_unit: str) -> float:
pressure, from_unit, self.pressure_unit
)

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
return speed_util.convert(wind_speed, from_unit, self.wind_speed_unit) # type: ignore

def volume(self, volume: float | None, from_unit: str) -> float:
"""Convert the given volume to this unit system."""
if not isinstance(volume, Number):
Expand All @@ -146,13 +165,15 @@ def as_dict(self) -> dict[str, str]:
PRESSURE: self.pressure_unit,
TEMPERATURE: self.temperature_unit,
VOLUME: self.volume_unit,
WIND_SPEED: self.wind_speed_unit,
}


METRIC_SYSTEM = UnitSystem(
CONF_UNIT_SYSTEM_METRIC,
TEMP_CELSIUS,
LENGTH_KILOMETERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS,
MASS_GRAMS,
PRESSURE_PA,
Expand All @@ -162,6 +183,7 @@ def as_dict(self) -> dict[str, str]:
CONF_UNIT_SYSTEM_IMPERIAL,
TEMP_FAHRENHEIT,
LENGTH_MILES,
SPEED_MILES_PER_HOUR,
VOLUME_GALLONS,
MASS_POUNDS,
PRESSURE_PSI,
Expand Down
9 changes: 8 additions & 1 deletion tests/helpers/test_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
LENGTH_METERS,
MASS_GRAMS,
PRESSURE_PA,
SPEED_KILOMETERS_PER_HOUR,
TEMP_CELSIUS,
VOLUME_LITERS,
)
Expand All @@ -34,7 +35,13 @@
def _set_up_units(hass):
"""Set up the tests."""
hass.config.units = UnitSystem(
"custom", TEMP_CELSIUS, LENGTH_METERS, VOLUME_LITERS, MASS_GRAMS, PRESSURE_PA
"custom",
TEMP_CELSIUS,
LENGTH_METERS,
SPEED_KILOMETERS_PER_HOUR,
VOLUME_LITERS,
MASS_GRAMS,
PRESSURE_PA,
)


Expand Down
45 changes: 45 additions & 0 deletions tests/util/test_unit_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
MASS_GRAMS,
PRESSURE,
PRESSURE_PA,
SPEED_METERS_PER_SECOND,
TEMP_CELSIUS,
TEMPERATURE,
VOLUME,
VOLUME_LITERS,
WIND_SPEED,
)
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem

Expand All @@ -27,6 +29,7 @@ def test_invalid_units():
SYSTEM_NAME,
INVALID_UNIT,
LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS,
MASS_GRAMS,
PRESSURE_PA,
Expand All @@ -37,6 +40,7 @@ def test_invalid_units():
SYSTEM_NAME,
TEMP_CELSIUS,
INVALID_UNIT,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS,
MASS_GRAMS,
PRESSURE_PA,
Expand All @@ -48,6 +52,18 @@ def test_invalid_units():
TEMP_CELSIUS,
LENGTH_METERS,
INVALID_UNIT,
VOLUME_LITERS,
MASS_GRAMS,
PRESSURE_PA,
)

with pytest.raises(ValueError):
UnitSystem(
SYSTEM_NAME,
TEMP_CELSIUS,
LENGTH_METERS,
SPEED_METERS_PER_SECOND,
INVALID_UNIT,
MASS_GRAMS,
PRESSURE_PA,
)
Expand All @@ -57,6 +73,7 @@ def test_invalid_units():
SYSTEM_NAME,
TEMP_CELSIUS,
LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS,
INVALID_UNIT,
PRESSURE_PA,
Expand All @@ -67,6 +84,7 @@ def test_invalid_units():
SYSTEM_NAME,
TEMP_CELSIUS,
LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS,
MASS_GRAMS,
INVALID_UNIT,
Expand All @@ -79,6 +97,8 @@ def test_invalid_value():
METRIC_SYSTEM.length("25a", LENGTH_KILOMETERS)
with pytest.raises(TypeError):
METRIC_SYSTEM.temperature("50K", TEMP_CELSIUS)
with pytest.raises(TypeError):
METRIC_SYSTEM.wind_speed("50km/h", SPEED_METERS_PER_SECOND)
with pytest.raises(TypeError):
METRIC_SYSTEM.volume("50L", VOLUME_LITERS)
with pytest.raises(TypeError):
Expand All @@ -89,6 +109,7 @@ def test_as_dict():
"""Test that the as_dict() method returns the expected dictionary."""
expected = {
LENGTH: LENGTH_KILOMETERS,
WIND_SPEED: SPEED_METERS_PER_SECOND,
TEMPERATURE: TEMP_CELSIUS,
VOLUME: VOLUME_LITERS,
MASS: MASS_GRAMS,
Expand Down Expand Up @@ -142,6 +163,29 @@ def test_length_to_imperial():
assert IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit) == 3.106855


def test_wind_speed_unknown_unit():
"""Test wind_speed conversion with unknown from unit."""
with pytest.raises(ValueError):
METRIC_SYSTEM.length(5, "turtles")


def test_wind_speed_to_metric():
"""Test length conversion to metric system."""
assert METRIC_SYSTEM.wind_speed(100, METRIC_SYSTEM.wind_speed_unit) == 100
# 1 m/s is about 2.237 mph
assert METRIC_SYSTEM.wind_speed(
2237, IMPERIAL_SYSTEM.wind_speed_unit
) == pytest.approx(1000, abs=0.1)


def test_wind_speed_to_imperial():
"""Test wind_speed conversion to imperial system."""
assert IMPERIAL_SYSTEM.wind_speed(100, IMPERIAL_SYSTEM.wind_speed_unit) == 100
assert IMPERIAL_SYSTEM.wind_speed(
1000, METRIC_SYSTEM.wind_speed_unit
) == pytest.approx(2237, abs=0.1)


def test_pressure_same_unit():
"""Test no conversion happens if to unit is same as from unit."""
assert METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit) == 5
Expand Down Expand Up @@ -172,6 +216,7 @@ def test_pressure_to_imperial():
def test_properties():
"""Test the unit properties are returned as expected."""
assert METRIC_SYSTEM.length_unit == LENGTH_KILOMETERS
assert METRIC_SYSTEM.wind_speed_unit == SPEED_METERS_PER_SECOND
assert METRIC_SYSTEM.temperature_unit == TEMP_CELSIUS
assert METRIC_SYSTEM.mass_unit == MASS_GRAMS
assert METRIC_SYSTEM.volume_unit == VOLUME_LITERS
Expand Down