Add speed to units system (#58437)

* Use speed units in unit system

* Use more obvious conversion factor for unit system speed test

* Use wind_speed instead of speed, use m/s
This commit is contained in:
rianadon 2021-11-18 07:08:42 -08:00 committed by GitHub
parent 3dc0b9537c
commit 7cc7bbb76d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 1 deletions

View file

@ -686,6 +686,7 @@ PRESSURE: Final = "pressure"
VOLUME: Final = "volume" VOLUME: Final = "volume"
TEMPERATURE: Final = "temperature" TEMPERATURE: Final = "temperature"
SPEED: Final = "speed" SPEED: Final = "speed"
WIND_SPEED: Final = "wind_speed"
ILLUMINANCE: Final = "illuminance" ILLUMINANCE: Final = "illuminance"
WEEKDAYS: Final[list[str]] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] WEEKDAYS: Final[list[str]] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]

View file

@ -17,6 +17,8 @@ from homeassistant.const import (
PRESSURE, PRESSURE,
PRESSURE_PA, PRESSURE_PA,
PRESSURE_PSI, PRESSURE_PSI,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS, TEMP_CELSIUS,
TEMP_FAHRENHEIT, TEMP_FAHRENHEIT,
TEMPERATURE, TEMPERATURE,
@ -24,10 +26,12 @@ from homeassistant.const import (
VOLUME, VOLUME,
VOLUME_GALLONS, VOLUME_GALLONS,
VOLUME_LITERS, VOLUME_LITERS,
WIND_SPEED,
) )
from homeassistant.util import ( from homeassistant.util import (
distance as distance_util, distance as distance_util,
pressure as pressure_util, pressure as pressure_util,
speed as speed_util,
temperature as temperature_util, temperature as temperature_util,
volume as volume_util, volume as volume_util,
) )
@ -42,6 +46,8 @@ PRESSURE_UNITS = pressure_util.VALID_UNITS
VOLUME_UNITS = volume_util.VALID_UNITS VOLUME_UNITS = volume_util.VALID_UNITS
WIND_SPEED_UNITS = speed_util.VALID_UNITS
TEMPERATURE_UNITS: tuple[str, ...] = (TEMP_FAHRENHEIT, TEMP_CELSIUS) TEMPERATURE_UNITS: tuple[str, ...] = (TEMP_FAHRENHEIT, TEMP_CELSIUS)
@ -49,6 +55,8 @@ def is_valid_unit(unit: str, unit_type: str) -> bool:
"""Check if the unit is valid for it's type.""" """Check if the unit is valid for it's type."""
if unit_type == LENGTH: if unit_type == LENGTH:
units = LENGTH_UNITS units = LENGTH_UNITS
elif unit_type == WIND_SPEED:
units = WIND_SPEED_UNITS
elif unit_type == TEMPERATURE: elif unit_type == TEMPERATURE:
units = TEMPERATURE_UNITS units = TEMPERATURE_UNITS
elif unit_type == MASS: elif unit_type == MASS:
@ -71,6 +79,7 @@ class UnitSystem:
name: str, name: str,
temperature: str, temperature: str,
length: str, length: str,
wind_speed: str,
volume: str, volume: str,
mass: str, mass: str,
pressure: str, pressure: str,
@ -81,6 +90,7 @@ class UnitSystem:
for unit, unit_type in ( for unit, unit_type in (
(temperature, TEMPERATURE), (temperature, TEMPERATURE),
(length, LENGTH), (length, LENGTH),
(wind_speed, WIND_SPEED),
(volume, VOLUME), (volume, VOLUME),
(mass, MASS), (mass, MASS),
(pressure, PRESSURE), (pressure, PRESSURE),
@ -97,6 +107,7 @@ class UnitSystem:
self.mass_unit = mass self.mass_unit = mass
self.pressure_unit = pressure self.pressure_unit = pressure
self.volume_unit = volume self.volume_unit = volume
self.wind_speed_unit = wind_speed
@property @property
def is_metric(self) -> bool: def is_metric(self) -> bool:
@ -130,6 +141,14 @@ class UnitSystem:
pressure, from_unit, self.pressure_unit 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: def volume(self, volume: float | None, from_unit: str) -> float:
"""Convert the given volume to this unit system.""" """Convert the given volume to this unit system."""
if not isinstance(volume, Number): if not isinstance(volume, Number):
@ -146,6 +165,7 @@ class UnitSystem:
PRESSURE: self.pressure_unit, PRESSURE: self.pressure_unit,
TEMPERATURE: self.temperature_unit, TEMPERATURE: self.temperature_unit,
VOLUME: self.volume_unit, VOLUME: self.volume_unit,
WIND_SPEED: self.wind_speed_unit,
} }
@ -153,6 +173,7 @@ METRIC_SYSTEM = UnitSystem(
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_METRIC,
TEMP_CELSIUS, TEMP_CELSIUS,
LENGTH_KILOMETERS, LENGTH_KILOMETERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS, VOLUME_LITERS,
MASS_GRAMS, MASS_GRAMS,
PRESSURE_PA, PRESSURE_PA,
@ -162,6 +183,7 @@ IMPERIAL_SYSTEM = UnitSystem(
CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_IMPERIAL,
TEMP_FAHRENHEIT, TEMP_FAHRENHEIT,
LENGTH_MILES, LENGTH_MILES,
SPEED_MILES_PER_HOUR,
VOLUME_GALLONS, VOLUME_GALLONS,
MASS_POUNDS, MASS_POUNDS,
PRESSURE_PSI, PRESSURE_PSI,

View file

@ -14,6 +14,7 @@ from homeassistant.const import (
LENGTH_METERS, LENGTH_METERS,
MASS_GRAMS, MASS_GRAMS,
PRESSURE_PA, PRESSURE_PA,
SPEED_KILOMETERS_PER_HOUR,
TEMP_CELSIUS, TEMP_CELSIUS,
VOLUME_LITERS, VOLUME_LITERS,
) )
@ -34,7 +35,13 @@ from tests.common import (
def _set_up_units(hass): def _set_up_units(hass):
"""Set up the tests.""" """Set up the tests."""
hass.config.units = UnitSystem( 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,
) )

View file

@ -9,10 +9,12 @@ from homeassistant.const import (
MASS_GRAMS, MASS_GRAMS,
PRESSURE, PRESSURE,
PRESSURE_PA, PRESSURE_PA,
SPEED_METERS_PER_SECOND,
TEMP_CELSIUS, TEMP_CELSIUS,
TEMPERATURE, TEMPERATURE,
VOLUME, VOLUME,
VOLUME_LITERS, VOLUME_LITERS,
WIND_SPEED,
) )
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM, UnitSystem
@ -27,6 +29,7 @@ def test_invalid_units():
SYSTEM_NAME, SYSTEM_NAME,
INVALID_UNIT, INVALID_UNIT,
LENGTH_METERS, LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS, VOLUME_LITERS,
MASS_GRAMS, MASS_GRAMS,
PRESSURE_PA, PRESSURE_PA,
@ -37,6 +40,7 @@ def test_invalid_units():
SYSTEM_NAME, SYSTEM_NAME,
TEMP_CELSIUS, TEMP_CELSIUS,
INVALID_UNIT, INVALID_UNIT,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS, VOLUME_LITERS,
MASS_GRAMS, MASS_GRAMS,
PRESSURE_PA, PRESSURE_PA,
@ -48,6 +52,7 @@ def test_invalid_units():
TEMP_CELSIUS, TEMP_CELSIUS,
LENGTH_METERS, LENGTH_METERS,
INVALID_UNIT, INVALID_UNIT,
VOLUME_LITERS,
MASS_GRAMS, MASS_GRAMS,
PRESSURE_PA, PRESSURE_PA,
) )
@ -57,6 +62,18 @@ def test_invalid_units():
SYSTEM_NAME, SYSTEM_NAME,
TEMP_CELSIUS, TEMP_CELSIUS,
LENGTH_METERS, LENGTH_METERS,
SPEED_METERS_PER_SECOND,
INVALID_UNIT,
MASS_GRAMS,
PRESSURE_PA,
)
with pytest.raises(ValueError):
UnitSystem(
SYSTEM_NAME,
TEMP_CELSIUS,
LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS, VOLUME_LITERS,
INVALID_UNIT, INVALID_UNIT,
PRESSURE_PA, PRESSURE_PA,
@ -67,6 +84,7 @@ def test_invalid_units():
SYSTEM_NAME, SYSTEM_NAME,
TEMP_CELSIUS, TEMP_CELSIUS,
LENGTH_METERS, LENGTH_METERS,
SPEED_METERS_PER_SECOND,
VOLUME_LITERS, VOLUME_LITERS,
MASS_GRAMS, MASS_GRAMS,
INVALID_UNIT, INVALID_UNIT,
@ -79,6 +97,8 @@ def test_invalid_value():
METRIC_SYSTEM.length("25a", LENGTH_KILOMETERS) METRIC_SYSTEM.length("25a", LENGTH_KILOMETERS)
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.temperature("50K", TEMP_CELSIUS) METRIC_SYSTEM.temperature("50K", TEMP_CELSIUS)
with pytest.raises(TypeError):
METRIC_SYSTEM.wind_speed("50km/h", SPEED_METERS_PER_SECOND)
with pytest.raises(TypeError): with pytest.raises(TypeError):
METRIC_SYSTEM.volume("50L", VOLUME_LITERS) METRIC_SYSTEM.volume("50L", VOLUME_LITERS)
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -89,6 +109,7 @@ def test_as_dict():
"""Test that the as_dict() method returns the expected dictionary.""" """Test that the as_dict() method returns the expected dictionary."""
expected = { expected = {
LENGTH: LENGTH_KILOMETERS, LENGTH: LENGTH_KILOMETERS,
WIND_SPEED: SPEED_METERS_PER_SECOND,
TEMPERATURE: TEMP_CELSIUS, TEMPERATURE: TEMP_CELSIUS,
VOLUME: VOLUME_LITERS, VOLUME: VOLUME_LITERS,
MASS: MASS_GRAMS, MASS: MASS_GRAMS,
@ -142,6 +163,29 @@ def test_length_to_imperial():
assert IMPERIAL_SYSTEM.length(5, METRIC_SYSTEM.length_unit) == 3.106855 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(): def test_pressure_same_unit():
"""Test no conversion happens if to unit is same as from unit.""" """Test no conversion happens if to unit is same as from unit."""
assert METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit) == 5 assert METRIC_SYSTEM.pressure(5, METRIC_SYSTEM.pressure_unit) == 5
@ -172,6 +216,7 @@ def test_pressure_to_imperial():
def test_properties(): def test_properties():
"""Test the unit properties are returned as expected.""" """Test the unit properties are returned as expected."""
assert METRIC_SYSTEM.length_unit == LENGTH_KILOMETERS 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.temperature_unit == TEMP_CELSIUS
assert METRIC_SYSTEM.mass_unit == MASS_GRAMS assert METRIC_SYSTEM.mass_unit == MASS_GRAMS
assert METRIC_SYSTEM.volume_unit == VOLUME_LITERS assert METRIC_SYSTEM.volume_unit == VOLUME_LITERS