Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
18 changes: 15 additions & 3 deletions homeassistant/components/climacell/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
LENGTH_MILES,
PRESSURE_HPA,
PRESSURE_INHG,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_FAHRENHEIT,
)
from homeassistant.core import HomeAssistant
Expand All @@ -45,6 +47,7 @@
from homeassistant.util import dt as dt_util
from homeassistant.util.distance import convert as distance_convert
from homeassistant.util.pressure import convert as pressure_convert
from homeassistant.util.speed import convert as speed_convert

from . import ClimaCellDataUpdateCoordinator, ClimaCellEntity
from .const import (
Expand Down Expand Up @@ -166,7 +169,10 @@ def _forecast_dict(
)
if wind_speed:
wind_speed = round(
distance_convert(wind_speed, LENGTH_MILES, LENGTH_KILOMETERS), 4
speed_convert(
wind_speed, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR
),
4,
)

data = {
Expand All @@ -188,7 +194,10 @@ def extra_state_attributes(self) -> Mapping[str, Any] | None:
wind_gust = self.wind_gust
if wind_gust and self.hass.config.units.is_metric:
wind_gust = round(
distance_convert(self.wind_gust, LENGTH_MILES, LENGTH_KILOMETERS), 4
speed_convert(
self.wind_gust, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR
),
4,
)
cloud_cover = self.cloud_cover
return {
Expand Down Expand Up @@ -236,7 +245,10 @@ def wind_speed(self):
"""Return the wind speed."""
if self.hass.config.units.is_metric and self._wind_speed:
return round(
distance_convert(self._wind_speed, LENGTH_MILES, LENGTH_KILOMETERS), 4
speed_convert(
self._wind_speed, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR
),
4,
)
return self._wind_speed

Expand Down
9 changes: 6 additions & 3 deletions homeassistant/components/met/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,18 @@
CONF_LONGITUDE,
CONF_NAME,
LENGTH_INCHES,
LENGTH_KILOMETERS,
LENGTH_MILES,
LENGTH_MILLIMETERS,
PRESSURE_HPA,
PRESSURE_INHG,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.speed import convert as convert_speed

from .const import (
ATTR_FORECAST_PRECIPITATION,
Expand Down Expand Up @@ -198,7 +199,9 @@ def wind_speed(self):
if self._is_metric or speed_km_h is None:
return speed_km_h

speed_mi_h = convert_distance(speed_km_h, LENGTH_KILOMETERS, LENGTH_MILES)
speed_mi_h = convert_speed(
speed_km_h, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR
)
return int(round(speed_mi_h))

@property
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/met_eireann/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
CONF_LONGITUDE,
CONF_NAME,
LENGTH_INCHES,
LENGTH_METERS,
LENGTH_MILES,
LENGTH_MILLIMETERS,
PRESSURE_HPA,
PRESSURE_INHG,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.speed import convert as convert_speed

from .const import ATTRIBUTION, CONDITION_MAP, DEFAULT_NAME, DOMAIN, FORECAST_MAP

Expand Down Expand Up @@ -129,8 +130,9 @@ def wind_speed(self):
if self._is_metric or speed_m_s is None:
return speed_m_s

speed_mi_s = convert_distance(speed_m_s, LENGTH_METERS, LENGTH_MILES)
speed_mi_h = speed_mi_s / 3600.0
speed_mi_h = convert_speed(
speed_m_s, SPEED_METERS_PER_SECOND, SPEED_MILES_PER_HOUR
)
return int(round(speed_mi_h))

@property
Expand Down
7 changes: 5 additions & 2 deletions homeassistant/components/nws/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
ATTR_ATTRIBUTION,
CONF_LATITUDE,
CONF_LONGITUDE,
LENGTH_KILOMETERS,
LENGTH_METERS,
LENGTH_MILES,
PERCENTAGE,
PRESSURE_INHG,
PRESSURE_PA,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
)
Expand All @@ -19,6 +19,7 @@
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.dt import utcnow
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.speed import convert as convert_speed

from . import base_unique_id, device_info
from .const import (
Expand Down Expand Up @@ -86,7 +87,9 @@ def native_value(self):
# Set alias to unit property -> prevent unnecessary hasattr calls
unit_of_measurement = self.native_unit_of_measurement
if unit_of_measurement == SPEED_MILES_PER_HOUR:
return round(convert_distance(value, LENGTH_KILOMETERS, LENGTH_MILES))
return round(
convert_speed(value, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR)
)
if unit_of_measurement == LENGTH_MILES:
return round(convert_distance(value, LENGTH_METERS, LENGTH_MILES))
if unit_of_measurement == PRESSURE_INHG:
Expand Down
11 changes: 9 additions & 2 deletions homeassistant/components/nws/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
PRESSURE_HPA,
PRESSURE_INHG,
PRESSURE_PA,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
Expand All @@ -28,6 +30,7 @@
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.dt import utcnow
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.speed import convert as convert_speed
from homeassistant.util.temperature import convert as convert_temperature

from . import base_unique_id, device_info
Expand Down Expand Up @@ -196,7 +199,9 @@ def wind_speed(self):
if self.is_metric:
wind = wind_km_hr
else:
wind = convert_distance(wind_km_hr, LENGTH_KILOMETERS, LENGTH_MILES)
wind = convert_speed(
wind_km_hr, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR
)
return round(wind)

@property
Expand Down Expand Up @@ -271,7 +276,9 @@ def forecast(self):
if wind_speed is not None:
if self.is_metric:
data[ATTR_FORECAST_WIND_SPEED] = round(
convert_distance(wind_speed, LENGTH_MILES, LENGTH_KILOMETERS)
convert_speed(
wind_speed, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR
)
)
else:
data[ATTR_FORECAST_WIND_SPEED] = round(wind_speed)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@
PRESSURE: Final = "pressure"
VOLUME: Final = "volume"
TEMPERATURE: Final = "temperature"
SPEED_MS: Final = "speed_ms"
SPEED: Final = "speed"
ILLUMINANCE: Final = "illuminance"

WEEKDAYS: Final[list[str]] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
Expand Down
56 changes: 56 additions & 0 deletions homeassistant/util/speed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Distance util functions."""
from __future__ import annotations

from numbers import Number

from homeassistant.const import (
SPEED,
SPEED_INCHES_PER_DAY,
SPEED_INCHES_PER_HOUR,
SPEED_KILOMETERS_PER_HOUR,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
SPEED_MILLIMETERS_PER_DAY,
UNIT_NOT_RECOGNIZED_TEMPLATE,
)

VALID_UNITS: tuple[str, ...] = (
SPEED_METERS_PER_SECOND,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
SPEED_MILLIMETERS_PER_DAY,
SPEED_INCHES_PER_DAY,
SPEED_INCHES_PER_HOUR,
)

HRS_TO_SECS = 60 * 60 # 1 hr = 3600 seconds
KM_TO_M = 1000 # 1 km = 1000 m
KM_TO_MILE = 0.62137119 # 1 km = 0.62137119 mi
KM_TO_IN = 39370.0787 # 1 km = 39370.0787 in

# Units in terms of m/s
UNIT_CONVERSION: dict[str, float] = {
SPEED_METERS_PER_SECOND: 1,
SPEED_KILOMETERS_PER_HOUR: HRS_TO_SECS / KM_TO_M,
SPEED_MILES_PER_HOUR: HRS_TO_SECS * KM_TO_MILE / KM_TO_M,
SPEED_MILLIMETERS_PER_DAY: (24 * HRS_TO_SECS) * 1000,
SPEED_INCHES_PER_DAY: (24 * HRS_TO_SECS) * KM_TO_IN,
SPEED_INCHES_PER_HOUR: HRS_TO_SECS * KM_TO_IN,
}


def convert(value: float, unit_1: str, unit_2: str) -> float:
"""Convert one unit of measurement to another."""
if unit_1 not in VALID_UNITS:
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_1, SPEED))
if unit_2 not in VALID_UNITS:
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_2, SPEED))

if not isinstance(value, Number):
raise TypeError(f"{value} is not of numeric type")

if unit_1 == unit_2:
return value

mph = value / UNIT_CONVERSION[unit_1]
return mph * UNIT_CONVERSION[unit_2]
Comment thread
rianadon marked this conversation as resolved.
Outdated
15 changes: 11 additions & 4 deletions tests/components/nws/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
PRESSURE_HPA,
PRESSURE_INHG,
PRESSURE_PA,
SPEED_KILOMETERS_PER_HOUR,
SPEED_MILES_PER_HOUR,
TEMP_CELSIUS,
TEMP_FAHRENHEIT,
)
from homeassistant.util.distance import convert as convert_distance
from homeassistant.util.pressure import convert as convert_pressure
from homeassistant.util.speed import convert as convert_speed
from homeassistant.util.temperature import convert as convert_temperature

NWS_CONFIG = {
Expand Down Expand Up @@ -80,8 +83,12 @@
"windChill": str(round(convert_temperature(5, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
"heatIndex": str(round(convert_temperature(15, TEMP_CELSIUS, TEMP_FAHRENHEIT))),
"relativeHumidity": "10",
"windSpeed": str(round(convert_distance(10, LENGTH_KILOMETERS, LENGTH_MILES))),
"windGust": str(round(convert_distance(20, LENGTH_KILOMETERS, LENGTH_MILES))),
"windSpeed": str(
round(convert_speed(10, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR))
),
"windGust": str(
round(convert_speed(20, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR))
),
"windDirection": "180",
"barometricPressure": str(
round(convert_pressure(100000, PRESSURE_PA, PRESSURE_INHG), 2)
Expand All @@ -98,7 +105,7 @@
),
ATTR_WEATHER_WIND_BEARING: 180,
ATTR_WEATHER_WIND_SPEED: round(
convert_distance(10, LENGTH_KILOMETERS, LENGTH_MILES)
convert_speed(10, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR)
),
ATTR_WEATHER_PRESSURE: round(
convert_pressure(100000, PRESSURE_PA, PRESSURE_INHG), 2
Expand Down Expand Up @@ -152,7 +159,7 @@
ATTR_FORECAST_TIME: "2019-08-12T20:00:00-04:00",
ATTR_FORECAST_TEMP: round(convert_temperature(10, TEMP_FAHRENHEIT, TEMP_CELSIUS)),
ATTR_FORECAST_WIND_SPEED: round(
convert_distance(10, LENGTH_MILES, LENGTH_KILOMETERS)
convert_speed(10, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR)
),
ATTR_FORECAST_WIND_BEARING: 180,
ATTR_FORECAST_PRECIPITATION_PROBABILITY: 90,
Expand Down
60 changes: 60 additions & 0 deletions tests/util/test_speed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Test Home Assistant speed utility functions."""
import pytest

from homeassistant.const import (
SPEED_INCHES_PER_DAY,
SPEED_INCHES_PER_HOUR,
SPEED_KILOMETERS_PER_HOUR,
SPEED_METERS_PER_SECOND,
SPEED_MILES_PER_HOUR,
SPEED_MILLIMETERS_PER_DAY,
)
import homeassistant.util.speed as speed_util

INVALID_SYMBOL = "bob"
VALID_SYMBOL = SPEED_KILOMETERS_PER_HOUR


def test_convert_same_unit():
"""Test conversion from any unit to same unit."""
assert speed_util.convert(2, SPEED_INCHES_PER_DAY, SPEED_INCHES_PER_DAY) == 2
assert speed_util.convert(3, SPEED_INCHES_PER_HOUR, SPEED_INCHES_PER_HOUR) == 3
assert (
speed_util.convert(4, SPEED_KILOMETERS_PER_HOUR, SPEED_KILOMETERS_PER_HOUR) == 4
)
assert speed_util.convert(5, SPEED_METERS_PER_SECOND, SPEED_METERS_PER_SECOND) == 5
assert speed_util.convert(6, SPEED_MILES_PER_HOUR, SPEED_MILES_PER_HOUR) == 6
assert (
speed_util.convert(7, SPEED_MILLIMETERS_PER_DAY, SPEED_MILLIMETERS_PER_DAY) == 7
)


def test_convert_invalid_unit():
"""Test exception is thrown for invalid units."""
with pytest.raises(ValueError):
speed_util.convert(5, INVALID_SYMBOL, VALID_SYMBOL)

with pytest.raises(ValueError):
speed_util.convert(5, VALID_SYMBOL, INVALID_SYMBOL)


def test_convert_nonnumeric_value():
"""Test exception is thrown for nonnumeric type."""
with pytest.raises(TypeError):
speed_util.convert("a", SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR)


def test_convert_from_kph():
Comment thread
rianadon marked this conversation as resolved.
Outdated
"""Test conversion from kph to other units."""
kph = 5
assert speed_util.convert(
kph, SPEED_KILOMETERS_PER_HOUR, SPEED_MILES_PER_HOUR
) == pytest.approx(3.10686, abs=1e-5)


def test_convert_from_mph():
"""Test conversion from mph to other units."""
mph = 5
assert speed_util.convert(
mph, SPEED_MILES_PER_HOUR, SPEED_KILOMETERS_PER_HOUR
) == pytest.approx(8.04672, abs=1e-5)