mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 22:57:17 +00:00
Add Beaufort to wind_speed (#105795)
* Add Beaufort to wind_speed * Add Bft to UnitOfSpeed * Update tests with Bft * Remove check for unit * Fix test_deprecated_constants * Test depricated constant Beaufort * Fix test_unit_system.py for Beaufort * Remove _DEPRECATED_SPEED_FEET_BEAUFORT * Remove maxsize from lru_cache * Update test_deprecated_constants * Update comment * Add missing docstring * Apply suggestions from code review --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
f3eb292c2d
commit
385b29bdf5
@ -344,6 +344,7 @@ class SensorDeviceClass(StrEnum):
|
|||||||
- SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h`
|
- SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h`
|
||||||
- USCS / imperial: `in/d`, `in/h`, `ft/s`, `mph`
|
- USCS / imperial: `in/d`, `in/h`, `ft/s`, `mph`
|
||||||
- Nautical: `kn`
|
- Nautical: `kn`
|
||||||
|
- Beaufort: `Beaufort`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SULPHUR_DIOXIDE = "sulphur_dioxide"
|
SULPHUR_DIOXIDE = "sulphur_dioxide"
|
||||||
@ -431,6 +432,7 @@ class SensorDeviceClass(StrEnum):
|
|||||||
- SI /metric: `m/s`, `km/h`
|
- SI /metric: `m/s`, `km/h`
|
||||||
- USCS / imperial: `ft/s`, `mph`
|
- USCS / imperial: `ft/s`, `mph`
|
||||||
- Nautical: `kn`
|
- Nautical: `kn`
|
||||||
|
- Beaufort: `Beaufort`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -1214,6 +1214,7 @@ CONCENTRATION_PARTS_PER_BILLION: Final = "ppb"
|
|||||||
class UnitOfSpeed(StrEnum):
|
class UnitOfSpeed(StrEnum):
|
||||||
"""Speed units."""
|
"""Speed units."""
|
||||||
|
|
||||||
|
BEAUFORT = "Beaufort"
|
||||||
FEET_PER_SECOND = "ft/s"
|
FEET_PER_SECOND = "ft/s"
|
||||||
METERS_PER_SECOND = "m/s"
|
METERS_PER_SECOND = "m/s"
|
||||||
KILOMETERS_PER_HOUR = "km/h"
|
KILOMETERS_PER_HOUR = "km/h"
|
||||||
|
@ -334,6 +334,7 @@ class SpeedConverter(BaseUnitConverter):
|
|||||||
UnitOfSpeed.KNOTS: _HRS_TO_SECS / _NAUTICAL_MILE_TO_M,
|
UnitOfSpeed.KNOTS: _HRS_TO_SECS / _NAUTICAL_MILE_TO_M,
|
||||||
UnitOfSpeed.METERS_PER_SECOND: 1,
|
UnitOfSpeed.METERS_PER_SECOND: 1,
|
||||||
UnitOfSpeed.MILES_PER_HOUR: _HRS_TO_SECS / _MILE_TO_M,
|
UnitOfSpeed.MILES_PER_HOUR: _HRS_TO_SECS / _MILE_TO_M,
|
||||||
|
UnitOfSpeed.BEAUFORT: 1,
|
||||||
}
|
}
|
||||||
VALID_UNITS = {
|
VALID_UNITS = {
|
||||||
UnitOfVolumetricFlux.INCHES_PER_DAY,
|
UnitOfVolumetricFlux.INCHES_PER_DAY,
|
||||||
@ -345,8 +346,73 @@ class SpeedConverter(BaseUnitConverter):
|
|||||||
UnitOfSpeed.KNOTS,
|
UnitOfSpeed.KNOTS,
|
||||||
UnitOfSpeed.METERS_PER_SECOND,
|
UnitOfSpeed.METERS_PER_SECOND,
|
||||||
UnitOfSpeed.MILES_PER_HOUR,
|
UnitOfSpeed.MILES_PER_HOUR,
|
||||||
|
UnitOfSpeed.BEAUFORT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache
|
||||||
|
def converter_factory(
|
||||||
|
cls, from_unit: str | None, to_unit: str | None
|
||||||
|
) -> Callable[[float], float]:
|
||||||
|
"""Return a function to convert a speed from one unit to another."""
|
||||||
|
if from_unit == to_unit:
|
||||||
|
# Return a function that does nothing. This is not
|
||||||
|
# in _converter_factory because we do not want to wrap
|
||||||
|
# it with the None check in converter_factory_allow_none.
|
||||||
|
return lambda value: value
|
||||||
|
|
||||||
|
return cls._converter_factory(from_unit, to_unit)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache
|
||||||
|
def converter_factory_allow_none(
|
||||||
|
cls, from_unit: str | None, to_unit: str | None
|
||||||
|
) -> Callable[[float | None], float | None]:
|
||||||
|
"""Return a function to convert a speed from one unit to another which allows None."""
|
||||||
|
if from_unit == to_unit:
|
||||||
|
# Return a function that does nothing. This is not
|
||||||
|
# in _converter_factory because we do not want to wrap
|
||||||
|
# it with the None check in this case.
|
||||||
|
return lambda value: value
|
||||||
|
|
||||||
|
convert = cls._converter_factory(from_unit, to_unit)
|
||||||
|
return lambda value: None if value is None else convert(value)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _converter_factory(
|
||||||
|
cls, from_unit: str | None, to_unit: str | None
|
||||||
|
) -> Callable[[float], float]:
|
||||||
|
"""Convert a speed from one unit to another, eg. 14m/s will return 7Bft."""
|
||||||
|
# We cannot use the implementation from BaseUnitConverter here because the
|
||||||
|
# Beaufort scale is not a constant value to divide or multiply with.
|
||||||
|
if (
|
||||||
|
from_unit not in SpeedConverter.VALID_UNITS
|
||||||
|
or to_unit not in SpeedConverter.VALID_UNITS
|
||||||
|
):
|
||||||
|
raise HomeAssistantError(
|
||||||
|
UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit, cls.UNIT_CLASS)
|
||||||
|
)
|
||||||
|
|
||||||
|
if from_unit == UnitOfSpeed.BEAUFORT:
|
||||||
|
to_ratio = cls._UNIT_CONVERSION[to_unit]
|
||||||
|
return lambda val: cls._beaufort_to_ms(val) * to_ratio
|
||||||
|
if to_unit == UnitOfSpeed.BEAUFORT:
|
||||||
|
from_ratio = cls._UNIT_CONVERSION[from_unit]
|
||||||
|
return lambda val: cls._ms_to_beaufort(val / from_ratio)
|
||||||
|
|
||||||
|
from_ratio, to_ratio = cls._get_from_to_ratio(from_unit, to_unit)
|
||||||
|
return lambda val: (val / from_ratio) * to_ratio
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _ms_to_beaufort(cls, ms: float) -> float:
|
||||||
|
"""Convert a speed in m/s to Beaufort."""
|
||||||
|
return float(round(((ms / 0.836) ** 2) ** (1 / 3)))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _beaufort_to_ms(cls, beaufort: float) -> float:
|
||||||
|
"""Convert a speed in Beaufort to m/s."""
|
||||||
|
return float(0.836 * beaufort ** (3 / 2))
|
||||||
|
|
||||||
|
|
||||||
class TemperatureConverter(BaseUnitConverter):
|
class TemperatureConverter(BaseUnitConverter):
|
||||||
"""Utility to convert temperature values."""
|
"""Utility to convert temperature values."""
|
||||||
|
@ -30,7 +30,18 @@ async def test_device_class_units(
|
|||||||
msg = await client.receive_json()
|
msg = await client.receive_json()
|
||||||
assert msg["success"]
|
assert msg["success"]
|
||||||
assert msg["result"] == {
|
assert msg["result"] == {
|
||||||
"units": ["ft/s", "in/d", "in/h", "km/h", "kn", "m/s", "mm/d", "mm/h", "mph"]
|
"units": [
|
||||||
|
"Beaufort",
|
||||||
|
"ft/s",
|
||||||
|
"in/d",
|
||||||
|
"in/h",
|
||||||
|
"km/h",
|
||||||
|
"kn",
|
||||||
|
"m/s",
|
||||||
|
"mm/d",
|
||||||
|
"mm/h",
|
||||||
|
"mph",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Device class with units which include `None`
|
# Device class with units which include `None`
|
||||||
|
@ -131,7 +131,16 @@ def test_all() -> None:
|
|||||||
],
|
],
|
||||||
"PRECIPITATION_",
|
"PRECIPITATION_",
|
||||||
)
|
)
|
||||||
+ _create_tuples(const.UnitOfSpeed, "SPEED_")
|
+ _create_tuples(
|
||||||
|
[
|
||||||
|
const.UnitOfSpeed.FEET_PER_SECOND,
|
||||||
|
const.UnitOfSpeed.METERS_PER_SECOND,
|
||||||
|
const.UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||||
|
const.UnitOfSpeed.KNOTS,
|
||||||
|
const.UnitOfSpeed.MILES_PER_HOUR,
|
||||||
|
],
|
||||||
|
"SPEED_",
|
||||||
|
)
|
||||||
+ _create_tuples(
|
+ _create_tuples(
|
||||||
[
|
[
|
||||||
const.UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
|
const.UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
|
||||||
|
@ -413,6 +413,8 @@ _CONVERTED_VALUE: dict[
|
|||||||
(5, UnitOfSpeed.KNOTS, 2.57222, UnitOfSpeed.METERS_PER_SECOND),
|
(5, UnitOfSpeed.KNOTS, 2.57222, UnitOfSpeed.METERS_PER_SECOND),
|
||||||
# 5 ft/s * 0.3048 m/ft = 1.524 m/s
|
# 5 ft/s * 0.3048 m/ft = 1.524 m/s
|
||||||
(5, UnitOfSpeed.FEET_PER_SECOND, 1.524, UnitOfSpeed.METERS_PER_SECOND),
|
(5, UnitOfSpeed.FEET_PER_SECOND, 1.524, UnitOfSpeed.METERS_PER_SECOND),
|
||||||
|
# float(round(((20.7 m/s / 0.836) ** 2) ** (1 / 3))) = 8.0Bft
|
||||||
|
(20.7, UnitOfSpeed.METERS_PER_SECOND, 8.0, UnitOfSpeed.BEAUFORT),
|
||||||
],
|
],
|
||||||
TemperatureConverter: [
|
TemperatureConverter: [
|
||||||
(100, UnitOfTemperature.CELSIUS, 212, UnitOfTemperature.FAHRENHEIT),
|
(100, UnitOfTemperature.CELSIUS, 212, UnitOfTemperature.FAHRENHEIT),
|
||||||
|
@ -515,6 +515,7 @@ UNCONVERTED_UNITS_METRIC_SYSTEM = {
|
|||||||
UnitOfPressure.PA,
|
UnitOfPressure.PA,
|
||||||
),
|
),
|
||||||
SensorDeviceClass.SPEED: (
|
SensorDeviceClass.SPEED: (
|
||||||
|
UnitOfSpeed.BEAUFORT,
|
||||||
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||||
UnitOfSpeed.KNOTS,
|
UnitOfSpeed.KNOTS,
|
||||||
UnitOfSpeed.METERS_PER_SECOND,
|
UnitOfSpeed.METERS_PER_SECOND,
|
||||||
@ -723,6 +724,7 @@ UNCONVERTED_UNITS_US_SYSTEM = {
|
|||||||
),
|
),
|
||||||
SensorDeviceClass.PRESSURE: (UnitOfPressure.INHG, UnitOfPressure.PSI),
|
SensorDeviceClass.PRESSURE: (UnitOfPressure.INHG, UnitOfPressure.PSI),
|
||||||
SensorDeviceClass.SPEED: (
|
SensorDeviceClass.SPEED: (
|
||||||
|
UnitOfSpeed.BEAUFORT,
|
||||||
UnitOfSpeed.FEET_PER_SECOND,
|
UnitOfSpeed.FEET_PER_SECOND,
|
||||||
UnitOfSpeed.KNOTS,
|
UnitOfSpeed.KNOTS,
|
||||||
UnitOfSpeed.MILES_PER_HOUR,
|
UnitOfSpeed.MILES_PER_HOUR,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user