diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index b63d32dc538..cbe2ad37fe9 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -2,9 +2,10 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping from datetime import datetime, timedelta import logging -from typing import Final +from typing import Any, Final import aiohttp import async_timeout @@ -27,10 +28,14 @@ from homeassistant.components.weather import ( ATTR_CONDITION_WINDY, ATTR_CONDITION_WINDY_VARIANT, ATTR_FORECAST_CONDITION, - ATTR_FORECAST_PRECIPITATION, - ATTR_FORECAST_TEMP, - ATTR_FORECAST_TEMP_LOW, + ATTR_FORECAST_NATIVE_PRECIPITATION, + ATTR_FORECAST_NATIVE_PRESSURE, + ATTR_FORECAST_NATIVE_TEMP, + ATTR_FORECAST_NATIVE_TEMP_LOW, + ATTR_FORECAST_NATIVE_WIND_SPEED, ATTR_FORECAST_TIME, + ATTR_FORECAST_WIND_BEARING, + ROUNDING_PRECISION, Forecast, WeatherEntity, ) @@ -41,6 +46,8 @@ from homeassistant.const import ( CONF_NAME, LENGTH_KILOMETERS, LENGTH_MILLIMETERS, + PRESSURE_HPA, + SPEED_METERS_PER_SECOND, TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant @@ -49,7 +56,7 @@ from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later -from homeassistant.util import Throttle, slugify +from homeassistant.util import Throttle, slugify, speed as speed_util from .const import ( ATTR_SMHI_CLOUDINESS, @@ -112,9 +119,11 @@ class SmhiWeather(WeatherEntity): """Representation of a weather entity.""" _attr_attribution = "Swedish weather institute (SMHI)" - _attr_temperature_unit = TEMP_CELSIUS - _attr_visibility_unit = LENGTH_KILOMETERS - _attr_precipitation_unit = LENGTH_MILLIMETERS + _attr_native_temperature_unit = TEMP_CELSIUS + _attr_native_visibility_unit = LENGTH_KILOMETERS + _attr_native_precipitation_unit = LENGTH_MILLIMETERS + _attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND + _attr_native_pressure_unit = PRESSURE_HPA def __init__( self, @@ -139,7 +148,23 @@ class SmhiWeather(WeatherEntity): configuration_url="http://opendata.smhi.se/apidocs/metfcst/parameters.html", ) self._attr_condition = None - self._attr_temperature = None + self._attr_native_temperature = None + + @property + def extra_state_attributes(self) -> Mapping[str, Any] | None: + """Return additional attributes.""" + if self._forecasts: + wind_gust = speed_util.convert( + self._forecasts[0].wind_gust, + SPEED_METERS_PER_SECOND, + self._wind_speed_unit, + ) + return { + ATTR_SMHI_CLOUDINESS: self._forecasts[0].cloudiness, + ATTR_SMHI_WIND_GUST_SPEED: round(wind_gust, ROUNDING_PRECISION), + ATTR_SMHI_THUNDER_PROBABILITY: self._forecasts[0].thunder, + } + return None @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self) -> None: @@ -156,13 +181,12 @@ class SmhiWeather(WeatherEntity): return if self._forecasts: - self._attr_temperature = self._forecasts[0].temperature + self._attr_native_temperature = self._forecasts[0].temperature self._attr_humidity = self._forecasts[0].humidity - # Convert from m/s to km/h - self._attr_wind_speed = round(self._forecasts[0].wind_speed * 18 / 5) + self._attr_native_wind_speed = self._forecasts[0].wind_speed self._attr_wind_bearing = self._forecasts[0].wind_direction - self._attr_visibility = self._forecasts[0].horizontal_visibility - self._attr_pressure = self._forecasts[0].pressure + self._attr_native_visibility = self._forecasts[0].horizontal_visibility + self._attr_native_pressure = self._forecasts[0].pressure self._attr_condition = next( ( k @@ -171,12 +195,6 @@ class SmhiWeather(WeatherEntity): ), None, ) - self._attr_extra_state_attributes = { - ATTR_SMHI_CLOUDINESS: self._forecasts[0].cloudiness, - # Convert from m/s to km/h - ATTR_SMHI_WIND_GUST_SPEED: round(self._forecasts[0].wind_gust * 18 / 5), - ATTR_SMHI_THUNDER_PROBABILITY: self._forecasts[0].thunder, - } async def retry_update(self, _: datetime) -> None: """Retry refresh weather forecast.""" @@ -200,10 +218,13 @@ class SmhiWeather(WeatherEntity): data.append( { ATTR_FORECAST_TIME: forecast.valid_time.isoformat(), - ATTR_FORECAST_TEMP: forecast.temperature_max, - ATTR_FORECAST_TEMP_LOW: forecast.temperature_min, - ATTR_FORECAST_PRECIPITATION: round(forecast.total_precipitation, 1), + ATTR_FORECAST_NATIVE_TEMP: forecast.temperature_max, + ATTR_FORECAST_NATIVE_TEMP_LOW: forecast.temperature_min, + ATTR_FORECAST_NATIVE_PRECIPITATION: forecast.total_precipitation, ATTR_FORECAST_CONDITION: condition, + ATTR_FORECAST_NATIVE_PRESSURE: forecast.pressure, + ATTR_FORECAST_WIND_BEARING: forecast.wind_direction, + ATTR_FORECAST_NATIVE_WIND_SPEED: forecast.wind_speed, } ) diff --git a/tests/components/smhi/test_weather.py b/tests/components/smhi/test_weather.py index c890ad62216..0097a7a5c5a 100644 --- a/tests/components/smhi/test_weather.py +++ b/tests/components/smhi/test_weather.py @@ -16,18 +16,24 @@ from homeassistant.components.weather import ( ATTR_FORECAST, ATTR_FORECAST_CONDITION, ATTR_FORECAST_PRECIPITATION, + ATTR_FORECAST_PRESSURE, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, + ATTR_FORECAST_WIND_BEARING, + ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_VISIBILITY, ATTR_WEATHER_WIND_BEARING, ATTR_WEATHER_WIND_SPEED, + ATTR_WEATHER_WIND_SPEED_UNIT, + DOMAIN as WEATHER_DOMAIN, ) -from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN +from homeassistant.const import ATTR_ATTRIBUTION, SPEED_METERS_PER_SECOND, STATE_UNKNOWN from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.util.dt import utcnow from . import ENTITY_ID, TEST_CONFIG @@ -58,13 +64,13 @@ async def test_setup_hass( assert state.state == "sunny" assert state.attributes[ATTR_SMHI_CLOUDINESS] == 50 assert state.attributes[ATTR_SMHI_THUNDER_PROBABILITY] == 33 - assert state.attributes[ATTR_SMHI_WIND_GUST_SPEED] == 17 + assert state.attributes[ATTR_SMHI_WIND_GUST_SPEED] == 16.92 assert state.attributes[ATTR_ATTRIBUTION].find("SMHI") >= 0 assert state.attributes[ATTR_WEATHER_HUMIDITY] == 55 assert state.attributes[ATTR_WEATHER_PRESSURE] == 1024 assert state.attributes[ATTR_WEATHER_TEMPERATURE] == 17 assert state.attributes[ATTR_WEATHER_VISIBILITY] == 50 - assert state.attributes[ATTR_WEATHER_WIND_SPEED] == 7 + assert state.attributes[ATTR_WEATHER_WIND_SPEED] == 6.84 assert state.attributes[ATTR_WEATHER_WIND_BEARING] == 134 assert len(state.attributes["forecast"]) == 4 @@ -74,6 +80,9 @@ async def test_setup_hass( assert forecast[ATTR_FORECAST_TEMP_LOW] == 6 assert forecast[ATTR_FORECAST_PRECIPITATION] == 0 assert forecast[ATTR_FORECAST_CONDITION] == "partlycloudy" + assert forecast[ATTR_FORECAST_PRESSURE] == 1026 + assert forecast[ATTR_FORECAST_WIND_BEARING] == 203 + assert forecast[ATTR_FORECAST_WIND_SPEED] == 6.12 async def test_properties_no_data(hass: HomeAssistant) -> None: @@ -305,3 +314,35 @@ def test_condition_class(): assert get_condition(23) == "snowy-rainy" # 24. Heavy sleet assert get_condition(24) == "snowy-rainy" + + +async def test_custom_speed_unit( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, api_response: str +) -> None: + """Test Wind Gust speed with custom unit.""" + uri = APIURL_TEMPLATE.format(TEST_CONFIG["longitude"], TEST_CONFIG["latitude"]) + aioclient_mock.get(uri, text=api_response) + + entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + + assert state + assert state.name == "test" + assert state.attributes[ATTR_SMHI_WIND_GUST_SPEED] == 16.92 + + entity_reg = er.async_get(hass) + entity_reg.async_update_entity_options( + state.entity_id, + WEATHER_DOMAIN, + {ATTR_WEATHER_WIND_SPEED_UNIT: SPEED_METERS_PER_SECOND}, + ) + + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.attributes[ATTR_SMHI_WIND_GUST_SPEED] == 4.7