mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix wind speed SMHI (#72999)
This commit is contained in:
parent
b2c84a4c4a
commit
2f0fe0df82
@ -2,9 +2,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from collections.abc import Mapping
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Final
|
from typing import Any, Final
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import async_timeout
|
import async_timeout
|
||||||
@ -27,10 +28,14 @@ from homeassistant.components.weather import (
|
|||||||
ATTR_CONDITION_WINDY,
|
ATTR_CONDITION_WINDY,
|
||||||
ATTR_CONDITION_WINDY_VARIANT,
|
ATTR_CONDITION_WINDY_VARIANT,
|
||||||
ATTR_FORECAST_CONDITION,
|
ATTR_FORECAST_CONDITION,
|
||||||
ATTR_FORECAST_PRECIPITATION,
|
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||||
ATTR_FORECAST_TEMP,
|
ATTR_FORECAST_NATIVE_PRESSURE,
|
||||||
ATTR_FORECAST_TEMP_LOW,
|
ATTR_FORECAST_NATIVE_TEMP,
|
||||||
|
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||||
|
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||||
ATTR_FORECAST_TIME,
|
ATTR_FORECAST_TIME,
|
||||||
|
ATTR_FORECAST_WIND_BEARING,
|
||||||
|
ROUNDING_PRECISION,
|
||||||
Forecast,
|
Forecast,
|
||||||
WeatherEntity,
|
WeatherEntity,
|
||||||
)
|
)
|
||||||
@ -41,6 +46,8 @@ from homeassistant.const import (
|
|||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
LENGTH_KILOMETERS,
|
LENGTH_KILOMETERS,
|
||||||
LENGTH_MILLIMETERS,
|
LENGTH_MILLIMETERS,
|
||||||
|
PRESSURE_HPA,
|
||||||
|
SPEED_METERS_PER_SECOND,
|
||||||
TEMP_CELSIUS,
|
TEMP_CELSIUS,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
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 import DeviceInfo
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_call_later
|
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 (
|
from .const import (
|
||||||
ATTR_SMHI_CLOUDINESS,
|
ATTR_SMHI_CLOUDINESS,
|
||||||
@ -112,9 +119,11 @@ class SmhiWeather(WeatherEntity):
|
|||||||
"""Representation of a weather entity."""
|
"""Representation of a weather entity."""
|
||||||
|
|
||||||
_attr_attribution = "Swedish weather institute (SMHI)"
|
_attr_attribution = "Swedish weather institute (SMHI)"
|
||||||
_attr_temperature_unit = TEMP_CELSIUS
|
_attr_native_temperature_unit = TEMP_CELSIUS
|
||||||
_attr_visibility_unit = LENGTH_KILOMETERS
|
_attr_native_visibility_unit = LENGTH_KILOMETERS
|
||||||
_attr_precipitation_unit = LENGTH_MILLIMETERS
|
_attr_native_precipitation_unit = LENGTH_MILLIMETERS
|
||||||
|
_attr_native_wind_speed_unit = SPEED_METERS_PER_SECOND
|
||||||
|
_attr_native_pressure_unit = PRESSURE_HPA
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -139,7 +148,23 @@ class SmhiWeather(WeatherEntity):
|
|||||||
configuration_url="http://opendata.smhi.se/apidocs/metfcst/parameters.html",
|
configuration_url="http://opendata.smhi.se/apidocs/metfcst/parameters.html",
|
||||||
)
|
)
|
||||||
self._attr_condition = None
|
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)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
async def async_update(self) -> None:
|
async def async_update(self) -> None:
|
||||||
@ -156,13 +181,12 @@ class SmhiWeather(WeatherEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self._forecasts:
|
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
|
self._attr_humidity = self._forecasts[0].humidity
|
||||||
# Convert from m/s to km/h
|
self._attr_native_wind_speed = self._forecasts[0].wind_speed
|
||||||
self._attr_wind_speed = round(self._forecasts[0].wind_speed * 18 / 5)
|
|
||||||
self._attr_wind_bearing = self._forecasts[0].wind_direction
|
self._attr_wind_bearing = self._forecasts[0].wind_direction
|
||||||
self._attr_visibility = self._forecasts[0].horizontal_visibility
|
self._attr_native_visibility = self._forecasts[0].horizontal_visibility
|
||||||
self._attr_pressure = self._forecasts[0].pressure
|
self._attr_native_pressure = self._forecasts[0].pressure
|
||||||
self._attr_condition = next(
|
self._attr_condition = next(
|
||||||
(
|
(
|
||||||
k
|
k
|
||||||
@ -171,12 +195,6 @@ class SmhiWeather(WeatherEntity):
|
|||||||
),
|
),
|
||||||
None,
|
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:
|
async def retry_update(self, _: datetime) -> None:
|
||||||
"""Retry refresh weather forecast."""
|
"""Retry refresh weather forecast."""
|
||||||
@ -200,10 +218,13 @@ class SmhiWeather(WeatherEntity):
|
|||||||
data.append(
|
data.append(
|
||||||
{
|
{
|
||||||
ATTR_FORECAST_TIME: forecast.valid_time.isoformat(),
|
ATTR_FORECAST_TIME: forecast.valid_time.isoformat(),
|
||||||
ATTR_FORECAST_TEMP: forecast.temperature_max,
|
ATTR_FORECAST_NATIVE_TEMP: forecast.temperature_max,
|
||||||
ATTR_FORECAST_TEMP_LOW: forecast.temperature_min,
|
ATTR_FORECAST_NATIVE_TEMP_LOW: forecast.temperature_min,
|
||||||
ATTR_FORECAST_PRECIPITATION: round(forecast.total_precipitation, 1),
|
ATTR_FORECAST_NATIVE_PRECIPITATION: forecast.total_precipitation,
|
||||||
ATTR_FORECAST_CONDITION: condition,
|
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,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,18 +16,24 @@ from homeassistant.components.weather import (
|
|||||||
ATTR_FORECAST,
|
ATTR_FORECAST,
|
||||||
ATTR_FORECAST_CONDITION,
|
ATTR_FORECAST_CONDITION,
|
||||||
ATTR_FORECAST_PRECIPITATION,
|
ATTR_FORECAST_PRECIPITATION,
|
||||||
|
ATTR_FORECAST_PRESSURE,
|
||||||
ATTR_FORECAST_TEMP,
|
ATTR_FORECAST_TEMP,
|
||||||
ATTR_FORECAST_TEMP_LOW,
|
ATTR_FORECAST_TEMP_LOW,
|
||||||
ATTR_FORECAST_TIME,
|
ATTR_FORECAST_TIME,
|
||||||
|
ATTR_FORECAST_WIND_BEARING,
|
||||||
|
ATTR_FORECAST_WIND_SPEED,
|
||||||
ATTR_WEATHER_HUMIDITY,
|
ATTR_WEATHER_HUMIDITY,
|
||||||
ATTR_WEATHER_PRESSURE,
|
ATTR_WEATHER_PRESSURE,
|
||||||
ATTR_WEATHER_TEMPERATURE,
|
ATTR_WEATHER_TEMPERATURE,
|
||||||
ATTR_WEATHER_VISIBILITY,
|
ATTR_WEATHER_VISIBILITY,
|
||||||
ATTR_WEATHER_WIND_BEARING,
|
ATTR_WEATHER_WIND_BEARING,
|
||||||
ATTR_WEATHER_WIND_SPEED,
|
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.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
from . import ENTITY_ID, TEST_CONFIG
|
from . import ENTITY_ID, TEST_CONFIG
|
||||||
@ -58,13 +64,13 @@ async def test_setup_hass(
|
|||||||
assert state.state == "sunny"
|
assert state.state == "sunny"
|
||||||
assert state.attributes[ATTR_SMHI_CLOUDINESS] == 50
|
assert state.attributes[ATTR_SMHI_CLOUDINESS] == 50
|
||||||
assert state.attributes[ATTR_SMHI_THUNDER_PROBABILITY] == 33
|
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_ATTRIBUTION].find("SMHI") >= 0
|
||||||
assert state.attributes[ATTR_WEATHER_HUMIDITY] == 55
|
assert state.attributes[ATTR_WEATHER_HUMIDITY] == 55
|
||||||
assert state.attributes[ATTR_WEATHER_PRESSURE] == 1024
|
assert state.attributes[ATTR_WEATHER_PRESSURE] == 1024
|
||||||
assert state.attributes[ATTR_WEATHER_TEMPERATURE] == 17
|
assert state.attributes[ATTR_WEATHER_TEMPERATURE] == 17
|
||||||
assert state.attributes[ATTR_WEATHER_VISIBILITY] == 50
|
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 state.attributes[ATTR_WEATHER_WIND_BEARING] == 134
|
||||||
assert len(state.attributes["forecast"]) == 4
|
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_TEMP_LOW] == 6
|
||||||
assert forecast[ATTR_FORECAST_PRECIPITATION] == 0
|
assert forecast[ATTR_FORECAST_PRECIPITATION] == 0
|
||||||
assert forecast[ATTR_FORECAST_CONDITION] == "partlycloudy"
|
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:
|
async def test_properties_no_data(hass: HomeAssistant) -> None:
|
||||||
@ -305,3 +314,35 @@ def test_condition_class():
|
|||||||
assert get_condition(23) == "snowy-rainy"
|
assert get_condition(23) == "snowy-rainy"
|
||||||
# 24. Heavy sleet
|
# 24. Heavy sleet
|
||||||
assert get_condition(24) == "snowy-rainy"
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user