mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 10:47:10 +00:00
Add WS command weather/convertible_units (#85681)
This commit is contained in:
parent
4e66554298
commit
b6f66b3568
@ -1,7 +1,6 @@
|
|||||||
"""Weather component that handles meteorological data for your location."""
|
"""Weather component that handles meteorological data for your location."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@ -16,8 +15,6 @@ from homeassistant.const import (
|
|||||||
PRECISION_HALVES,
|
PRECISION_HALVES,
|
||||||
PRECISION_TENTHS,
|
PRECISION_TENTHS,
|
||||||
PRECISION_WHOLE,
|
PRECISION_WHOLE,
|
||||||
UnitOfLength,
|
|
||||||
UnitOfPrecipitationDepth,
|
|
||||||
UnitOfPressure,
|
UnitOfPressure,
|
||||||
UnitOfSpeed,
|
UnitOfSpeed,
|
||||||
UnitOfTemperature,
|
UnitOfTemperature,
|
||||||
@ -30,14 +27,27 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
|
|||||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util.unit_conversion import (
|
|
||||||
DistanceConverter,
|
|
||||||
PressureConverter,
|
|
||||||
SpeedConverter,
|
|
||||||
TemperatureConverter,
|
|
||||||
)
|
|
||||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
ATTR_WEATHER_HUMIDITY,
|
||||||
|
ATTR_WEATHER_OZONE,
|
||||||
|
ATTR_WEATHER_PRECIPITATION_UNIT,
|
||||||
|
ATTR_WEATHER_PRESSURE,
|
||||||
|
ATTR_WEATHER_PRESSURE_UNIT,
|
||||||
|
ATTR_WEATHER_TEMPERATURE,
|
||||||
|
ATTR_WEATHER_TEMPERATURE_UNIT,
|
||||||
|
ATTR_WEATHER_VISIBILITY,
|
||||||
|
ATTR_WEATHER_VISIBILITY_UNIT,
|
||||||
|
ATTR_WEATHER_WIND_BEARING,
|
||||||
|
ATTR_WEATHER_WIND_SPEED,
|
||||||
|
ATTR_WEATHER_WIND_SPEED_UNIT,
|
||||||
|
DOMAIN,
|
||||||
|
UNIT_CONVERSIONS,
|
||||||
|
VALID_UNITS,
|
||||||
|
)
|
||||||
|
from .websocket_api import async_setup as async_setup_ws_api
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ATTR_CONDITION_CLASS = "condition_class"
|
ATTR_CONDITION_CLASS = "condition_class"
|
||||||
@ -71,20 +81,6 @@ ATTR_FORECAST_TIME: Final = "datetime"
|
|||||||
ATTR_FORECAST_WIND_BEARING: Final = "wind_bearing"
|
ATTR_FORECAST_WIND_BEARING: Final = "wind_bearing"
|
||||||
ATTR_FORECAST_NATIVE_WIND_SPEED: Final = "native_wind_speed"
|
ATTR_FORECAST_NATIVE_WIND_SPEED: Final = "native_wind_speed"
|
||||||
ATTR_FORECAST_WIND_SPEED: Final = "wind_speed"
|
ATTR_FORECAST_WIND_SPEED: Final = "wind_speed"
|
||||||
ATTR_WEATHER_HUMIDITY = "humidity"
|
|
||||||
ATTR_WEATHER_OZONE = "ozone"
|
|
||||||
ATTR_WEATHER_PRESSURE = "pressure"
|
|
||||||
ATTR_WEATHER_PRESSURE_UNIT = "pressure_unit"
|
|
||||||
ATTR_WEATHER_TEMPERATURE = "temperature"
|
|
||||||
ATTR_WEATHER_TEMPERATURE_UNIT = "temperature_unit"
|
|
||||||
ATTR_WEATHER_VISIBILITY = "visibility"
|
|
||||||
ATTR_WEATHER_VISIBILITY_UNIT = "visibility_unit"
|
|
||||||
ATTR_WEATHER_WIND_BEARING = "wind_bearing"
|
|
||||||
ATTR_WEATHER_WIND_SPEED = "wind_speed"
|
|
||||||
ATTR_WEATHER_WIND_SPEED_UNIT = "wind_speed_unit"
|
|
||||||
ATTR_WEATHER_PRECIPITATION_UNIT = "precipitation_unit"
|
|
||||||
|
|
||||||
DOMAIN = "weather"
|
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
@ -92,48 +88,6 @@ SCAN_INTERVAL = timedelta(seconds=30)
|
|||||||
|
|
||||||
ROUNDING_PRECISION = 2
|
ROUNDING_PRECISION = 2
|
||||||
|
|
||||||
VALID_UNITS_PRESSURE: set[str] = {
|
|
||||||
UnitOfPressure.HPA,
|
|
||||||
UnitOfPressure.MBAR,
|
|
||||||
UnitOfPressure.INHG,
|
|
||||||
UnitOfPressure.MMHG,
|
|
||||||
}
|
|
||||||
VALID_UNITS_TEMPERATURE: set[str] = {
|
|
||||||
UnitOfTemperature.CELSIUS,
|
|
||||||
UnitOfTemperature.FAHRENHEIT,
|
|
||||||
}
|
|
||||||
VALID_UNITS_PRECIPITATION: set[str] = {
|
|
||||||
UnitOfPrecipitationDepth.MILLIMETERS,
|
|
||||||
UnitOfPrecipitationDepth.INCHES,
|
|
||||||
}
|
|
||||||
VALID_UNITS_VISIBILITY: set[str] = {
|
|
||||||
UnitOfLength.KILOMETERS,
|
|
||||||
UnitOfLength.MILES,
|
|
||||||
}
|
|
||||||
VALID_UNITS_WIND_SPEED: set[str] = {
|
|
||||||
UnitOfSpeed.FEET_PER_SECOND,
|
|
||||||
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
|
||||||
UnitOfSpeed.KNOTS,
|
|
||||||
UnitOfSpeed.METERS_PER_SECOND,
|
|
||||||
UnitOfSpeed.MILES_PER_HOUR,
|
|
||||||
}
|
|
||||||
|
|
||||||
UNIT_CONVERSIONS: dict[str, Callable[[float, str, str], float]] = {
|
|
||||||
ATTR_WEATHER_PRESSURE_UNIT: PressureConverter.convert,
|
|
||||||
ATTR_WEATHER_TEMPERATURE_UNIT: TemperatureConverter.convert,
|
|
||||||
ATTR_WEATHER_VISIBILITY_UNIT: DistanceConverter.convert,
|
|
||||||
ATTR_WEATHER_PRECIPITATION_UNIT: DistanceConverter.convert,
|
|
||||||
ATTR_WEATHER_WIND_SPEED_UNIT: SpeedConverter.convert,
|
|
||||||
}
|
|
||||||
|
|
||||||
VALID_UNITS: dict[str, set[str]] = {
|
|
||||||
ATTR_WEATHER_PRESSURE_UNIT: VALID_UNITS_PRESSURE,
|
|
||||||
ATTR_WEATHER_TEMPERATURE_UNIT: VALID_UNITS_TEMPERATURE,
|
|
||||||
ATTR_WEATHER_VISIBILITY_UNIT: VALID_UNITS_VISIBILITY,
|
|
||||||
ATTR_WEATHER_PRECIPITATION_UNIT: VALID_UNITS_PRECIPITATION,
|
|
||||||
ATTR_WEATHER_WIND_SPEED_UNIT: VALID_UNITS_WIND_SPEED,
|
|
||||||
}
|
|
||||||
|
|
||||||
# mypy: disallow-any-generics
|
# mypy: disallow-any-generics
|
||||||
|
|
||||||
|
|
||||||
@ -182,6 +136,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
component = hass.data[DOMAIN] = EntityComponent[WeatherEntity](
|
component = hass.data[DOMAIN] = EntityComponent[WeatherEntity](
|
||||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL
|
||||||
)
|
)
|
||||||
|
async_setup_ws_api(hass)
|
||||||
await component.async_setup(config)
|
await component.async_setup(config)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
76
homeassistant/components/weather/const.py
Normal file
76
homeassistant/components/weather/const.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""Constants for weather."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
UnitOfLength,
|
||||||
|
UnitOfPrecipitationDepth,
|
||||||
|
UnitOfPressure,
|
||||||
|
UnitOfSpeed,
|
||||||
|
UnitOfTemperature,
|
||||||
|
)
|
||||||
|
from homeassistant.util.unit_conversion import (
|
||||||
|
DistanceConverter,
|
||||||
|
PressureConverter,
|
||||||
|
SpeedConverter,
|
||||||
|
TemperatureConverter,
|
||||||
|
)
|
||||||
|
|
||||||
|
ATTR_WEATHER_HUMIDITY = "humidity"
|
||||||
|
ATTR_WEATHER_OZONE = "ozone"
|
||||||
|
ATTR_WEATHER_PRESSURE = "pressure"
|
||||||
|
ATTR_WEATHER_PRESSURE_UNIT = "pressure_unit"
|
||||||
|
ATTR_WEATHER_TEMPERATURE = "temperature"
|
||||||
|
ATTR_WEATHER_TEMPERATURE_UNIT = "temperature_unit"
|
||||||
|
ATTR_WEATHER_VISIBILITY = "visibility"
|
||||||
|
ATTR_WEATHER_VISIBILITY_UNIT = "visibility_unit"
|
||||||
|
ATTR_WEATHER_WIND_BEARING = "wind_bearing"
|
||||||
|
ATTR_WEATHER_WIND_SPEED = "wind_speed"
|
||||||
|
ATTR_WEATHER_WIND_SPEED_UNIT = "wind_speed_unit"
|
||||||
|
ATTR_WEATHER_PRECIPITATION_UNIT = "precipitation_unit"
|
||||||
|
|
||||||
|
DOMAIN: Final = "weather"
|
||||||
|
|
||||||
|
VALID_UNITS_PRESSURE: set[str] = {
|
||||||
|
UnitOfPressure.HPA,
|
||||||
|
UnitOfPressure.MBAR,
|
||||||
|
UnitOfPressure.INHG,
|
||||||
|
UnitOfPressure.MMHG,
|
||||||
|
}
|
||||||
|
VALID_UNITS_TEMPERATURE: set[str] = {
|
||||||
|
UnitOfTemperature.CELSIUS,
|
||||||
|
UnitOfTemperature.FAHRENHEIT,
|
||||||
|
}
|
||||||
|
VALID_UNITS_PRECIPITATION: set[str] = {
|
||||||
|
UnitOfPrecipitationDepth.MILLIMETERS,
|
||||||
|
UnitOfPrecipitationDepth.INCHES,
|
||||||
|
}
|
||||||
|
VALID_UNITS_VISIBILITY: set[str] = {
|
||||||
|
UnitOfLength.KILOMETERS,
|
||||||
|
UnitOfLength.MILES,
|
||||||
|
}
|
||||||
|
VALID_UNITS_WIND_SPEED: set[str] = {
|
||||||
|
UnitOfSpeed.FEET_PER_SECOND,
|
||||||
|
UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||||
|
UnitOfSpeed.KNOTS,
|
||||||
|
UnitOfSpeed.METERS_PER_SECOND,
|
||||||
|
UnitOfSpeed.MILES_PER_HOUR,
|
||||||
|
}
|
||||||
|
|
||||||
|
UNIT_CONVERSIONS: dict[str, Callable[[float, str, str], float]] = {
|
||||||
|
ATTR_WEATHER_PRESSURE_UNIT: PressureConverter.convert,
|
||||||
|
ATTR_WEATHER_TEMPERATURE_UNIT: TemperatureConverter.convert,
|
||||||
|
ATTR_WEATHER_VISIBILITY_UNIT: DistanceConverter.convert,
|
||||||
|
ATTR_WEATHER_PRECIPITATION_UNIT: DistanceConverter.convert,
|
||||||
|
ATTR_WEATHER_WIND_SPEED_UNIT: SpeedConverter.convert,
|
||||||
|
}
|
||||||
|
|
||||||
|
VALID_UNITS: dict[str, set[str]] = {
|
||||||
|
ATTR_WEATHER_PRESSURE_UNIT: VALID_UNITS_PRESSURE,
|
||||||
|
ATTR_WEATHER_TEMPERATURE_UNIT: VALID_UNITS_TEMPERATURE,
|
||||||
|
ATTR_WEATHER_VISIBILITY_UNIT: VALID_UNITS_VISIBILITY,
|
||||||
|
ATTR_WEATHER_PRECIPITATION_UNIT: VALID_UNITS_PRECIPITATION,
|
||||||
|
ATTR_WEATHER_WIND_SPEED_UNIT: VALID_UNITS_WIND_SPEED,
|
||||||
|
}
|
30
homeassistant/components/weather/websocket_api.py
Normal file
30
homeassistant/components/weather/websocket_api.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""The weather websocket API."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components import websocket_api
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
|
||||||
|
from .const import VALID_UNITS
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_setup(hass: HomeAssistant) -> None:
|
||||||
|
"""Set up the weather websocket API."""
|
||||||
|
websocket_api.async_register_command(hass, ws_convertible_units)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@websocket_api.websocket_command(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "weather/convertible_units",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def ws_convertible_units(
|
||||||
|
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Return supported units for a device class."""
|
||||||
|
connection.send_result(msg["id"], {"units": VALID_UNITS})
|
31
tests/components/weather/test_websocket_api.py
Normal file
31
tests/components/weather/test_websocket_api.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""Test the weather websocket API."""
|
||||||
|
from pytest_unordered import unordered
|
||||||
|
|
||||||
|
from homeassistant.components.weather.const import DOMAIN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_class_units(hass: HomeAssistant, hass_ws_client) -> None:
|
||||||
|
"""Test we can get supported units."""
|
||||||
|
assert await async_setup_component(hass, DOMAIN, {})
|
||||||
|
|
||||||
|
client = await hass_ws_client(hass)
|
||||||
|
|
||||||
|
await client.send_json(
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "weather/convertible_units",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
msg = await client.receive_json()
|
||||||
|
assert msg["success"]
|
||||||
|
assert msg["result"] == {
|
||||||
|
"units": {
|
||||||
|
"precipitation_unit": unordered(["mm", "in"]),
|
||||||
|
"pressure_unit": unordered(["mbar", "mmHg", "inHg", "hPa"]),
|
||||||
|
"temperature_unit": unordered(["°F", "°C"]),
|
||||||
|
"visibility_unit": unordered(["km", "mi"]),
|
||||||
|
"wind_speed_unit": unordered(["mph", "km/h", "kn", "m/s", "ft/s"]),
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user