mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Modernize SMHI weather (#97275)
* SMHI forecast service * Mod weather * reset weather * Fix tests * coverage * add test
This commit is contained in:
parent
65365d1db5
commit
15eed166ec
@ -40,6 +40,7 @@ from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
Forecast,
|
||||
WeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
@ -128,6 +129,9 @@ class SmhiWeather(WeatherEntity):
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_name = None
|
||||
_attr_supported_features = (
|
||||
WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -138,7 +142,8 @@ class SmhiWeather(WeatherEntity):
|
||||
) -> None:
|
||||
"""Initialize the SMHI weather entity."""
|
||||
self._attr_unique_id = f"{latitude}, {longitude}"
|
||||
self._forecasts: list[SmhiForecast] | None = None
|
||||
self._forecast_daily: list[SmhiForecast] | None = None
|
||||
self._forecast_hourly: list[SmhiForecast] | None = None
|
||||
self._fail_count = 0
|
||||
self._smhi_api = Smhi(longitude, latitude, session=session)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
@ -155,9 +160,9 @@ class SmhiWeather(WeatherEntity):
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return additional attributes."""
|
||||
if self._forecasts:
|
||||
if self._forecast_daily:
|
||||
return {
|
||||
ATTR_SMHI_THUNDER_PROBABILITY: self._forecasts[0].thunder,
|
||||
ATTR_SMHI_THUNDER_PROBABILITY: self._forecast_daily[0].thunder,
|
||||
}
|
||||
return None
|
||||
|
||||
@ -166,7 +171,8 @@ class SmhiWeather(WeatherEntity):
|
||||
"""Refresh the forecast data from SMHI weather API."""
|
||||
try:
|
||||
async with async_timeout.timeout(TIMEOUT):
|
||||
self._forecasts = await self._smhi_api.async_get_forecast()
|
||||
self._forecast_daily = await self._smhi_api.async_get_forecast()
|
||||
self._forecast_hourly = await self._smhi_api.async_get_forecast_hour()
|
||||
self._fail_count = 0
|
||||
except (asyncio.TimeoutError, SmhiForecastException):
|
||||
_LOGGER.error("Failed to connect to SMHI API, retry in 5 minutes")
|
||||
@ -175,23 +181,24 @@ class SmhiWeather(WeatherEntity):
|
||||
async_call_later(self.hass, RETRY_TIMEOUT, self.retry_update)
|
||||
return
|
||||
|
||||
if self._forecasts:
|
||||
self._attr_native_temperature = self._forecasts[0].temperature
|
||||
self._attr_humidity = self._forecasts[0].humidity
|
||||
self._attr_native_wind_speed = self._forecasts[0].wind_speed
|
||||
self._attr_wind_bearing = self._forecasts[0].wind_direction
|
||||
self._attr_native_visibility = self._forecasts[0].horizontal_visibility
|
||||
self._attr_native_pressure = self._forecasts[0].pressure
|
||||
self._attr_native_wind_gust_speed = self._forecasts[0].wind_gust
|
||||
self._attr_cloud_coverage = self._forecasts[0].cloudiness
|
||||
if self._forecast_daily:
|
||||
self._attr_native_temperature = self._forecast_daily[0].temperature
|
||||
self._attr_humidity = self._forecast_daily[0].humidity
|
||||
self._attr_native_wind_speed = self._forecast_daily[0].wind_speed
|
||||
self._attr_wind_bearing = self._forecast_daily[0].wind_direction
|
||||
self._attr_native_visibility = self._forecast_daily[0].horizontal_visibility
|
||||
self._attr_native_pressure = self._forecast_daily[0].pressure
|
||||
self._attr_native_wind_gust_speed = self._forecast_daily[0].wind_gust
|
||||
self._attr_cloud_coverage = self._forecast_daily[0].cloudiness
|
||||
self._attr_condition = next(
|
||||
(
|
||||
k
|
||||
for k, v in CONDITION_CLASSES.items()
|
||||
if self._forecasts[0].symbol in v
|
||||
if self._forecast_daily[0].symbol in v
|
||||
),
|
||||
None,
|
||||
)
|
||||
await self.async_update_listeners(("daily", "hourly"))
|
||||
|
||||
async def retry_update(self, _: datetime) -> None:
|
||||
"""Retry refresh weather forecast."""
|
||||
@ -202,12 +209,12 @@ class SmhiWeather(WeatherEntity):
|
||||
@property
|
||||
def forecast(self) -> list[Forecast] | None:
|
||||
"""Return the forecast."""
|
||||
if self._forecasts is None or len(self._forecasts) < 2:
|
||||
if self._forecast_daily is None or len(self._forecast_daily) < 2:
|
||||
return None
|
||||
|
||||
data: list[Forecast] = []
|
||||
|
||||
for forecast in self._forecasts[1:]:
|
||||
for forecast in self._forecast_daily[1:]:
|
||||
condition = next(
|
||||
(k for k, v in CONDITION_CLASSES.items() if forecast.symbol in v), None
|
||||
)
|
||||
@ -229,3 +236,43 @@ class SmhiWeather(WeatherEntity):
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def _get_forecast_data(
|
||||
self, forecast_data: list[SmhiForecast] | None
|
||||
) -> list[Forecast] | None:
|
||||
"""Get forecast data."""
|
||||
if forecast_data is None or len(forecast_data) < 3:
|
||||
return None
|
||||
|
||||
data: list[Forecast] = []
|
||||
|
||||
for forecast in forecast_data[1:]:
|
||||
condition = next(
|
||||
(k for k, v in CONDITION_CLASSES.items() if forecast.symbol in v), None
|
||||
)
|
||||
|
||||
data.append(
|
||||
{
|
||||
ATTR_FORECAST_TIME: forecast.valid_time.isoformat(),
|
||||
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,
|
||||
ATTR_FORECAST_HUMIDITY: forecast.humidity,
|
||||
ATTR_FORECAST_NATIVE_WIND_GUST_SPEED: forecast.wind_gust,
|
||||
ATTR_FORECAST_CLOUD_COVERAGE: forecast.cloudiness,
|
||||
}
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
async def async_forecast_daily(self) -> list[Forecast] | None:
|
||||
"""Service to retrieve the daily forecast."""
|
||||
return self._get_forecast_data(self._forecast_daily)
|
||||
|
||||
async def async_forecast_hourly(self) -> list[Forecast] | None:
|
||||
"""Service to retrieve the hourly forecast."""
|
||||
return self._get_forecast_data(self._forecast_hourly)
|
||||
|
@ -1,10 +1,18 @@
|
||||
"""Provide common smhi fixtures."""
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.smhi.const import DOMAIN
|
||||
|
||||
from tests.common import load_fixture
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def api_response():
|
||||
"""Return an API response."""
|
||||
return load_fixture("smhi.json")
|
||||
return load_fixture("smhi.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def api_response_lack_data():
|
||||
"""Return an API response."""
|
||||
return load_fixture("smhi_short.json", DOMAIN)
|
||||
|
10084
tests/components/smhi/fixtures/smhi.json
Normal file
10084
tests/components/smhi/fixtures/smhi.json
Normal file
File diff suppressed because it is too large
Load Diff
148
tests/components/smhi/fixtures/smhi_short.json
Normal file
148
tests/components/smhi/fixtures/smhi_short.json
Normal file
@ -0,0 +1,148 @@
|
||||
{
|
||||
"approvedTime": "2023-08-07T07:07:34Z",
|
||||
"referenceTime": "2023-08-07T07:00:00Z",
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [[15.990068, 57.997072]]
|
||||
},
|
||||
"timeSeries": [
|
||||
{
|
||||
"validTime": "2023-08-07T08:00:00Z",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "spp",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "percent",
|
||||
"values": [-9]
|
||||
},
|
||||
{
|
||||
"name": "pcat",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "category",
|
||||
"values": [0]
|
||||
},
|
||||
{
|
||||
"name": "pmin",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "kg/m2/h",
|
||||
"values": [0.0]
|
||||
},
|
||||
{
|
||||
"name": "pmean",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "kg/m2/h",
|
||||
"values": [0.0]
|
||||
},
|
||||
{
|
||||
"name": "pmax",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "kg/m2/h",
|
||||
"values": [0.0]
|
||||
},
|
||||
{
|
||||
"name": "pmedian",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "kg/m2/h",
|
||||
"values": [0.0]
|
||||
},
|
||||
{
|
||||
"name": "tcc_mean",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "octas",
|
||||
"values": [8]
|
||||
},
|
||||
{
|
||||
"name": "lcc_mean",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "octas",
|
||||
"values": [8]
|
||||
},
|
||||
{
|
||||
"name": "mcc_mean",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "octas",
|
||||
"values": [7]
|
||||
},
|
||||
{
|
||||
"name": "hcc_mean",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "octas",
|
||||
"values": [7]
|
||||
},
|
||||
{
|
||||
"name": "t",
|
||||
"levelType": "hl",
|
||||
"level": 2,
|
||||
"unit": "Cel",
|
||||
"values": [18.4]
|
||||
},
|
||||
{
|
||||
"name": "msl",
|
||||
"levelType": "hmsl",
|
||||
"level": 0,
|
||||
"unit": "hPa",
|
||||
"values": [992.4]
|
||||
},
|
||||
{
|
||||
"name": "vis",
|
||||
"levelType": "hl",
|
||||
"level": 2,
|
||||
"unit": "km",
|
||||
"values": [0.4]
|
||||
},
|
||||
{
|
||||
"name": "wd",
|
||||
"levelType": "hl",
|
||||
"level": 10,
|
||||
"unit": "degree",
|
||||
"values": [93]
|
||||
},
|
||||
{
|
||||
"name": "ws",
|
||||
"levelType": "hl",
|
||||
"level": 10,
|
||||
"unit": "m/s",
|
||||
"values": [2.5]
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"levelType": "hl",
|
||||
"level": 2,
|
||||
"unit": "percent",
|
||||
"values": [100]
|
||||
},
|
||||
{
|
||||
"name": "tstm",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "percent",
|
||||
"values": [37]
|
||||
},
|
||||
{
|
||||
"name": "gust",
|
||||
"levelType": "hl",
|
||||
"level": 10,
|
||||
"unit": "m/s",
|
||||
"values": [6.2]
|
||||
},
|
||||
{
|
||||
"name": "Wsymb2",
|
||||
"levelType": "hl",
|
||||
"level": 0,
|
||||
"unit": "category",
|
||||
"values": [7]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
426
tests/components/smhi/snapshots/test_weather.ambr
Normal file
426
tests/components/smhi/snapshots/test_weather.ambr
Normal file
@ -0,0 +1,426 @@
|
||||
# serializer version: 1
|
||||
# name: test_forecast_daily
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T12:00:00',
|
||||
'humidity': 96,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 15.0,
|
||||
'wind_bearing': 114,
|
||||
'wind_gust_speed': 32.76,
|
||||
'wind_speed': 10.08,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_daily.1
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-13T12:00:00',
|
||||
'humidity': 59,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1013.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 234,
|
||||
'wind_gust_speed': 35.64,
|
||||
'wind_speed': 14.76,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_daily.2
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'fog',
|
||||
'datetime': '2023-08-07T09:00:00',
|
||||
'humidity': 100,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 992.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 18.0,
|
||||
'wind_bearing': 103,
|
||||
'wind_gust_speed': 23.76,
|
||||
'wind_speed': 9.72,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_daily.3
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T15:00:00',
|
||||
'humidity': 89,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 16.0,
|
||||
'templow': 16.0,
|
||||
'wind_bearing': 108,
|
||||
'wind_gust_speed': 31.68,
|
||||
'wind_speed': 12.24,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_service
|
||||
dict({
|
||||
'forecast': list([
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T12:00:00',
|
||||
'humidity': 96,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 15.0,
|
||||
'wind_bearing': 114,
|
||||
'wind_gust_speed': 32.76,
|
||||
'wind_speed': 10.08,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'rainy',
|
||||
'datetime': '2023-08-08T12:00:00',
|
||||
'humidity': 97,
|
||||
'precipitation': 10.6,
|
||||
'pressure': 984.0,
|
||||
'temperature': 15.0,
|
||||
'templow': 11.0,
|
||||
'wind_bearing': 183,
|
||||
'wind_gust_speed': 27.36,
|
||||
'wind_speed': 11.16,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'rainy',
|
||||
'datetime': '2023-08-09T12:00:00',
|
||||
'humidity': 95,
|
||||
'precipitation': 6.3,
|
||||
'pressure': 1001.0,
|
||||
'temperature': 12.0,
|
||||
'templow': 11.0,
|
||||
'wind_bearing': 166,
|
||||
'wind_gust_speed': 48.24,
|
||||
'wind_speed': 18.0,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-10T12:00:00',
|
||||
'humidity': 75,
|
||||
'precipitation': 4.8,
|
||||
'pressure': 1011.0,
|
||||
'temperature': 14.0,
|
||||
'templow': 10.0,
|
||||
'wind_bearing': 174,
|
||||
'wind_gust_speed': 29.16,
|
||||
'wind_speed': 11.16,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-11T12:00:00',
|
||||
'humidity': 69,
|
||||
'precipitation': 0.6,
|
||||
'pressure': 1015.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 12.0,
|
||||
'wind_bearing': 197,
|
||||
'wind_gust_speed': 27.36,
|
||||
'wind_speed': 10.08,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-12T12:00:00',
|
||||
'humidity': 82,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 17.0,
|
||||
'templow': 12.0,
|
||||
'wind_bearing': 225,
|
||||
'wind_gust_speed': 28.08,
|
||||
'wind_speed': 8.64,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-13T12:00:00',
|
||||
'humidity': 59,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1013.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 234,
|
||||
'wind_gust_speed': 35.64,
|
||||
'wind_speed': 14.76,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-14T12:00:00',
|
||||
'humidity': 56,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1015.0,
|
||||
'temperature': 21.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 216,
|
||||
'wind_gust_speed': 33.12,
|
||||
'wind_speed': 13.68,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 88,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-15T12:00:00',
|
||||
'humidity': 64,
|
||||
'precipitation': 3.6,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 226,
|
||||
'wind_gust_speed': 33.12,
|
||||
'wind_speed': 13.68,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-16T12:00:00',
|
||||
'humidity': 61,
|
||||
'precipitation': 2.4,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 233,
|
||||
'wind_gust_speed': 33.48,
|
||||
'wind_speed': 14.04,
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_services
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T12:00:00',
|
||||
'humidity': 96,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 15.0,
|
||||
'wind_bearing': 114,
|
||||
'wind_gust_speed': 32.76,
|
||||
'wind_speed': 10.08,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_services.1
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-13T12:00:00',
|
||||
'humidity': 59,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1013.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 234,
|
||||
'wind_gust_speed': 35.64,
|
||||
'wind_speed': 14.76,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_services.2
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'fog',
|
||||
'datetime': '2023-08-07T09:00:00',
|
||||
'humidity': 100,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 992.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 18.0,
|
||||
'wind_bearing': 103,
|
||||
'wind_gust_speed': 23.76,
|
||||
'wind_speed': 9.72,
|
||||
})
|
||||
# ---
|
||||
# name: test_forecast_services.3
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T15:00:00',
|
||||
'humidity': 89,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 16.0,
|
||||
'templow': 16.0,
|
||||
'wind_bearing': 108,
|
||||
'wind_gust_speed': 31.68,
|
||||
'wind_speed': 12.24,
|
||||
})
|
||||
# ---
|
||||
# name: test_setup_hass
|
||||
ReadOnlyDict({
|
||||
'apparent_temperature': 18.0,
|
||||
'attribution': 'Swedish weather institute (SMHI)',
|
||||
'cloud_coverage': 100,
|
||||
'forecast': list([
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-07T12:00:00',
|
||||
'humidity': 96,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 991.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 15.0,
|
||||
'wind_bearing': 114,
|
||||
'wind_gust_speed': 32.76,
|
||||
'wind_speed': 10.08,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'rainy',
|
||||
'datetime': '2023-08-08T12:00:00',
|
||||
'humidity': 97,
|
||||
'precipitation': 10.6,
|
||||
'pressure': 984.0,
|
||||
'temperature': 15.0,
|
||||
'templow': 11.0,
|
||||
'wind_bearing': 183,
|
||||
'wind_gust_speed': 27.36,
|
||||
'wind_speed': 11.16,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'rainy',
|
||||
'datetime': '2023-08-09T12:00:00',
|
||||
'humidity': 95,
|
||||
'precipitation': 6.3,
|
||||
'pressure': 1001.0,
|
||||
'temperature': 12.0,
|
||||
'templow': 11.0,
|
||||
'wind_bearing': 166,
|
||||
'wind_gust_speed': 48.24,
|
||||
'wind_speed': 18.0,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-10T12:00:00',
|
||||
'humidity': 75,
|
||||
'precipitation': 4.8,
|
||||
'pressure': 1011.0,
|
||||
'temperature': 14.0,
|
||||
'templow': 10.0,
|
||||
'wind_bearing': 174,
|
||||
'wind_gust_speed': 29.16,
|
||||
'wind_speed': 11.16,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-11T12:00:00',
|
||||
'humidity': 69,
|
||||
'precipitation': 0.6,
|
||||
'pressure': 1015.0,
|
||||
'temperature': 18.0,
|
||||
'templow': 12.0,
|
||||
'wind_bearing': 197,
|
||||
'wind_gust_speed': 27.36,
|
||||
'wind_speed': 10.08,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'cloudy',
|
||||
'datetime': '2023-08-12T12:00:00',
|
||||
'humidity': 82,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 17.0,
|
||||
'templow': 12.0,
|
||||
'wind_bearing': 225,
|
||||
'wind_gust_speed': 28.08,
|
||||
'wind_speed': 8.64,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-13T12:00:00',
|
||||
'humidity': 59,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1013.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 234,
|
||||
'wind_gust_speed': 35.64,
|
||||
'wind_speed': 14.76,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-14T12:00:00',
|
||||
'humidity': 56,
|
||||
'precipitation': 0.0,
|
||||
'pressure': 1015.0,
|
||||
'temperature': 21.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 216,
|
||||
'wind_gust_speed': 33.12,
|
||||
'wind_speed': 13.68,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 88,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-15T12:00:00',
|
||||
'humidity': 64,
|
||||
'precipitation': 3.6,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 226,
|
||||
'wind_gust_speed': 33.12,
|
||||
'wind_speed': 13.68,
|
||||
}),
|
||||
dict({
|
||||
'cloud_coverage': 75,
|
||||
'condition': 'partlycloudy',
|
||||
'datetime': '2023-08-16T12:00:00',
|
||||
'humidity': 61,
|
||||
'precipitation': 2.4,
|
||||
'pressure': 1014.0,
|
||||
'temperature': 20.0,
|
||||
'templow': 14.0,
|
||||
'wind_bearing': 233,
|
||||
'wind_gust_speed': 33.48,
|
||||
'wind_speed': 14.04,
|
||||
}),
|
||||
]),
|
||||
'friendly_name': 'test',
|
||||
'humidity': 100,
|
||||
'precipitation_unit': <UnitOfPrecipitationDepth.MILLIMETERS: 'mm'>,
|
||||
'pressure': 992.0,
|
||||
'pressure_unit': <UnitOfPressure.HPA: 'hPa'>,
|
||||
'supported_features': <WeatherEntityFeature: 3>,
|
||||
'temperature': 18.0,
|
||||
'temperature_unit': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||
'thunder_probability': 37,
|
||||
'visibility': 0.4,
|
||||
'visibility_unit': <UnitOfLength.KILOMETERS: 'km'>,
|
||||
'wind_bearing': 93,
|
||||
'wind_gust_speed': 22.32,
|
||||
'wind_speed': 9.0,
|
||||
'wind_speed_unit': <UnitOfSpeed.KILOMETERS_PER_HOUR: 'km/h'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_setup_hass.1
|
||||
dict({
|
||||
'cloud_coverage': 100,
|
||||
'condition': 'rainy',
|
||||
'datetime': '2023-08-08T12:00:00',
|
||||
'humidity': 97,
|
||||
'precipitation': 10.6,
|
||||
'pressure': 984.0,
|
||||
'temperature': 15.0,
|
||||
'templow': 11.0,
|
||||
'wind_bearing': 183,
|
||||
'wind_gust_speed': 27.36,
|
||||
'wind_speed': 11.16,
|
||||
})
|
||||
# ---
|
@ -5,21 +5,16 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from smhi.smhi_lib import APIURL_TEMPLATE, SmhiForecast, SmhiForecastException
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.smhi.const import ATTR_SMHI_THUNDER_PROBABILITY
|
||||
from homeassistant.components.smhi.weather import CONDITION_CLASSES, RETRY_TIMEOUT
|
||||
from homeassistant.components.smhi.weather import (
|
||||
CONDITION_CLASSES,
|
||||
RETRY_TIMEOUT,
|
||||
)
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST,
|
||||
ATTR_FORECAST_CLOUD_COVERAGE,
|
||||
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_GUST_SPEED,
|
||||
ATTR_FORECAST_WIND_SPEED,
|
||||
ATTR_WEATHER_HUMIDITY,
|
||||
ATTR_WEATHER_PRESSURE,
|
||||
ATTR_WEATHER_TEMPERATURE,
|
||||
@ -28,6 +23,7 @@ from homeassistant.components.weather import (
|
||||
ATTR_WEATHER_WIND_SPEED,
|
||||
ATTR_WEATHER_WIND_SPEED_UNIT,
|
||||
DOMAIN as WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECAST,
|
||||
)
|
||||
from homeassistant.components.weather.const import (
|
||||
ATTR_WEATHER_CLOUD_COVERAGE,
|
||||
@ -42,10 +38,14 @@ from . import ENTITY_ID, TEST_CONFIG
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
async def test_setup_hass(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, api_response: str
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
api_response: str,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test for successfully setting up the smhi integration."""
|
||||
uri = APIURL_TEMPLATE.format(
|
||||
@ -58,37 +58,19 @@ async def test_setup_hass(
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert aioclient_mock.call_count == 1
|
||||
assert aioclient_mock.call_count == 2
|
||||
|
||||
# Testing the actual entity state for
|
||||
# deeper testing than normal unity test
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
|
||||
assert state
|
||||
assert state.state == "sunny"
|
||||
assert state.attributes[ATTR_WEATHER_CLOUD_COVERAGE] == 50
|
||||
assert state.attributes[ATTR_SMHI_THUNDER_PROBABILITY] == 33
|
||||
assert state.attributes[ATTR_WEATHER_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] == 6.84
|
||||
assert state.attributes[ATTR_WEATHER_WIND_BEARING] == 134
|
||||
assert len(state.attributes["forecast"]) == 4
|
||||
assert state.state == "fog"
|
||||
assert state.attributes == snapshot
|
||||
assert len(state.attributes["forecast"]) == 10
|
||||
|
||||
forecast = state.attributes["forecast"][1]
|
||||
assert forecast[ATTR_FORECAST_TIME] == "2018-09-02T12:00:00"
|
||||
assert forecast[ATTR_FORECAST_TEMP] == 21
|
||||
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
|
||||
assert forecast[ATTR_FORECAST_WIND_GUST_SPEED] == 18.36
|
||||
assert forecast[ATTR_FORECAST_CLOUD_COVERAGE] == 100
|
||||
assert forecast == snapshot
|
||||
|
||||
|
||||
async def test_properties_no_data(hass: HomeAssistant) -> None:
|
||||
@ -188,6 +170,9 @@ async def test_properties_unknown_symbol(hass: HomeAssistant) -> None:
|
||||
with patch(
|
||||
"homeassistant.components.smhi.weather.Smhi.async_get_forecast",
|
||||
return_value=testdata,
|
||||
), patch(
|
||||
"homeassistant.components.smhi.weather.Smhi.async_get_forecast_hour",
|
||||
return_value=None,
|
||||
):
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
@ -341,7 +326,7 @@ async def test_custom_speed_unit(
|
||||
|
||||
assert state
|
||||
assert state.name == "test"
|
||||
assert state.attributes[ATTR_WEATHER_WIND_GUST_SPEED] == 16.92
|
||||
assert state.attributes[ATTR_WEATHER_WIND_GUST_SPEED] == 22.32
|
||||
|
||||
entity_reg = er.async_get(hass)
|
||||
entity_reg.async_update_entity_options(
|
||||
@ -353,4 +338,137 @@ async def test_custom_speed_unit(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state.attributes[ATTR_WEATHER_WIND_GUST_SPEED] == 4.7
|
||||
assert state.attributes[ATTR_WEATHER_WIND_GUST_SPEED] == 6.2
|
||||
|
||||
|
||||
async def test_forecast_services(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
api_response: str,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test multiple forecast."""
|
||||
uri = APIURL_TEMPLATE.format(
|
||||
TEST_CONFIG["location"]["longitude"], TEST_CONFIG["location"]["latitude"]
|
||||
)
|
||||
aioclient_mock.get(uri, text=api_response)
|
||||
|
||||
entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "weather/subscribe_forecast",
|
||||
"forecast_type": "daily",
|
||||
"entity_id": ENTITY_ID,
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] is None
|
||||
subscription_id = msg["id"]
|
||||
|
||||
msg = await client.receive_json()
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
forecast1 = msg["event"]["forecast"]
|
||||
|
||||
assert len(forecast1) == 10
|
||||
assert forecast1[0] == snapshot
|
||||
assert forecast1[6] == snapshot
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "weather/subscribe_forecast",
|
||||
"forecast_type": "hourly",
|
||||
"entity_id": ENTITY_ID,
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] is None
|
||||
subscription_id = msg["id"]
|
||||
|
||||
msg = await client.receive_json()
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
forecast1 = msg["event"]["forecast"]
|
||||
|
||||
assert len(forecast1) == 72
|
||||
assert forecast1[0] == snapshot
|
||||
assert forecast1[6] == snapshot
|
||||
|
||||
|
||||
async def test_forecast_services_lack_of_data(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
api_response_lack_data: str,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test forecast lacking data."""
|
||||
uri = APIURL_TEMPLATE.format(
|
||||
TEST_CONFIG["location"]["longitude"], TEST_CONFIG["location"]["latitude"]
|
||||
)
|
||||
aioclient_mock.get(uri, text=api_response_lack_data)
|
||||
|
||||
entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "weather/subscribe_forecast",
|
||||
"forecast_type": "daily",
|
||||
"entity_id": ENTITY_ID,
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] is None
|
||||
subscription_id = msg["id"]
|
||||
|
||||
msg = await client.receive_json()
|
||||
assert msg["id"] == subscription_id
|
||||
assert msg["type"] == "event"
|
||||
forecast1 = msg["event"]["forecast"]
|
||||
|
||||
assert forecast1 is None
|
||||
|
||||
|
||||
async def test_forecast_service(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
api_response: str,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test forecast service."""
|
||||
uri = APIURL_TEMPLATE.format(
|
||||
TEST_CONFIG["location"]["longitude"], TEST_CONFIG["location"]["latitude"]
|
||||
)
|
||||
aioclient_mock.get(uri, text=api_response)
|
||||
|
||||
entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
response = await hass.services.async_call(
|
||||
WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECAST,
|
||||
{"entity_id": ENTITY_ID, "type": "daily"},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response == snapshot
|
||||
|
1252
tests/fixtures/smhi.json
vendored
1252
tests/fixtures/smhi.json
vendored
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user