Add clear night to smhi (#115998)

This commit is contained in:
G Johansson 2024-05-22 16:03:48 +02:00 committed by GitHub
parent 5c9c71ba2c
commit d1bdf73bc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 840 additions and 4 deletions

View file

@ -13,6 +13,7 @@ from smhi import Smhi
from smhi.smhi_lib import SmhiForecast, SmhiForecastException from smhi.smhi_lib import SmhiForecast, SmhiForecastException
from homeassistant.components.weather import ( from homeassistant.components.weather import (
ATTR_CONDITION_CLEAR_NIGHT,
ATTR_CONDITION_CLOUDY, ATTR_CONDITION_CLOUDY,
ATTR_CONDITION_EXCEPTIONAL, ATTR_CONDITION_EXCEPTIONAL,
ATTR_CONDITION_FOG, ATTR_CONDITION_FOG,
@ -55,11 +56,11 @@ from homeassistant.const import (
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client, sun
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, 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, dt as dt_util, slugify
from .const import ATTR_SMHI_THUNDER_PROBABILITY, DOMAIN, ENTITY_ID_SENSOR_FORMAT from .const import ATTR_SMHI_THUNDER_PROBABILITY, DOMAIN, ENTITY_ID_SENSOR_FORMAT
@ -189,6 +190,10 @@ class SmhiWeather(WeatherEntity):
self._attr_native_wind_gust_speed = self._forecast_daily[0].wind_gust self._attr_native_wind_gust_speed = self._forecast_daily[0].wind_gust
self._attr_cloud_coverage = self._forecast_daily[0].cloudiness self._attr_cloud_coverage = self._forecast_daily[0].cloudiness
self._attr_condition = CONDITION_MAP.get(self._forecast_daily[0].symbol) self._attr_condition = CONDITION_MAP.get(self._forecast_daily[0].symbol)
if self._attr_condition == ATTR_CONDITION_SUNNY and not sun.is_up(
self.hass
):
self._attr_condition = ATTR_CONDITION_CLEAR_NIGHT
await self.async_update_listeners(("daily", "hourly")) await self.async_update_listeners(("daily", "hourly"))
async def retry_update(self, _: datetime) -> None: async def retry_update(self, _: datetime) -> None:
@ -206,6 +211,10 @@ class SmhiWeather(WeatherEntity):
for forecast in forecast_data[1:]: for forecast in forecast_data[1:]:
condition = CONDITION_MAP.get(forecast.symbol) condition = CONDITION_MAP.get(forecast.symbol)
if condition == ATTR_CONDITION_SUNNY and not sun.is_up(
self.hass, forecast.valid_time.replace(tzinfo=dt_util.UTC)
):
condition = ATTR_CONDITION_CLEAR_NIGHT
data.append( data.append(
{ {

View file

@ -13,6 +13,12 @@ def api_response():
return load_fixture("smhi.json", DOMAIN) return load_fixture("smhi.json", DOMAIN)
@pytest.fixture(scope="package")
def api_response_night():
"""Return an API response for night only."""
return load_fixture("smhi_night.json", DOMAIN)
@pytest.fixture(scope="package") @pytest.fixture(scope="package")
def api_response_lack_data(): def api_response_lack_data():
"""Return an API response.""" """Return an API response."""

View file

@ -0,0 +1,700 @@
{
"approvedTime": "2023-08-07T07:07:34Z",
"referenceTime": "2023-08-07T07:00:00Z",
"geometry": {
"type": "Point",
"coordinates": [[15.990068, 57.997072]]
},
"timeSeries": [
{
"validTime": "2023-08-07T23: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": [1]
}
]
},
{
"validTime": "2023-08-08T00:00:00Z",
"parameters": [
{
"name": "spp",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [0]
},
{
"name": "pcat",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [3]
},
{
"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.1]
},
{
"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": [8]
},
{
"name": "hcc_mean",
"levelType": "hl",
"level": 0,
"unit": "octas",
"values": [6]
},
{
"name": "t",
"levelType": "hl",
"level": 2,
"unit": "Cel",
"values": [18.2]
},
{
"name": "msl",
"levelType": "hmsl",
"level": 0,
"unit": "hPa",
"values": [992.4]
},
{
"name": "vis",
"levelType": "hl",
"level": 2,
"unit": "km",
"values": [0.1]
},
{
"name": "wd",
"levelType": "hl",
"level": 10,
"unit": "degree",
"values": [103]
},
{
"name": "ws",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [2.7]
},
{
"name": "r",
"levelType": "hl",
"level": 2,
"unit": "percent",
"values": [100]
},
{
"name": "tstm",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [27]
},
{
"name": "gust",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [6.6]
},
{
"name": "Wsymb2",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [1]
}
]
},
{
"validTime": "2023-08-08T01:00:00Z",
"parameters": [
{
"name": "spp",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [0]
},
{
"name": "pcat",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [3]
},
{
"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.1]
},
{
"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": [5]
},
{
"name": "hcc_mean",
"levelType": "hl",
"level": 0,
"unit": "octas",
"values": [6]
},
{
"name": "t",
"levelType": "hl",
"level": 2,
"unit": "Cel",
"values": [17.5]
},
{
"name": "msl",
"levelType": "hmsl",
"level": 0,
"unit": "hPa",
"values": [992.4]
},
{
"name": "vis",
"levelType": "hl",
"level": 2,
"unit": "km",
"values": [1.6]
},
{
"name": "wd",
"levelType": "hl",
"level": 10,
"unit": "degree",
"values": [104]
},
{
"name": "ws",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [2.7]
},
{
"name": "r",
"levelType": "hl",
"level": 2,
"unit": "percent",
"values": [100]
},
{
"name": "tstm",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [27]
},
{
"name": "gust",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [7.6]
},
{
"name": "Wsymb2",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [1]
}
]
},
{
"validTime": "2023-08-08T02:00:00Z",
"parameters": [
{
"name": "spp",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [0]
},
{
"name": "pcat",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [3]
},
{
"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.1]
},
{
"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": [3]
},
{
"name": "hcc_mean",
"levelType": "hl",
"level": 0,
"unit": "octas",
"values": [6]
},
{
"name": "t",
"levelType": "hl",
"level": 2,
"unit": "Cel",
"values": [17.6]
},
{
"name": "msl",
"levelType": "hmsl",
"level": 0,
"unit": "hPa",
"values": [992.2]
},
{
"name": "vis",
"levelType": "hl",
"level": 2,
"unit": "km",
"values": [3.0]
},
{
"name": "wd",
"levelType": "hl",
"level": 10,
"unit": "degree",
"values": [109]
},
{
"name": "ws",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [3.6]
},
{
"name": "r",
"levelType": "hl",
"level": 2,
"unit": "percent",
"values": [97]
},
{
"name": "tstm",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [0]
},
{
"name": "gust",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [9.0]
},
{
"name": "Wsymb2",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [1]
}
]
},
{
"validTime": "2023-08-08T03: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": [1]
},
{
"name": "hcc_mean",
"levelType": "hl",
"level": 0,
"unit": "octas",
"values": [5]
},
{
"name": "t",
"levelType": "hl",
"level": 2,
"unit": "Cel",
"values": [17.1]
},
{
"name": "msl",
"levelType": "hmsl",
"level": 0,
"unit": "hPa",
"values": [991.7]
},
{
"name": "vis",
"levelType": "hl",
"level": 2,
"unit": "km",
"values": [3.2]
},
{
"name": "wd",
"levelType": "hl",
"level": 10,
"unit": "degree",
"values": [114]
},
{
"name": "ws",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [2.8]
},
{
"name": "r",
"levelType": "hl",
"level": 2,
"unit": "percent",
"values": [96]
},
{
"name": "tstm",
"levelType": "hl",
"level": 0,
"unit": "percent",
"values": [0]
},
{
"name": "gust",
"levelType": "hl",
"level": 10,
"unit": "m/s",
"values": [9.1]
},
{
"name": "Wsymb2",
"levelType": "hl",
"level": 0,
"unit": "category",
"values": [1]
}
]
}
]
}

View file

@ -1,4 +1,85 @@
# serializer version: 1 # serializer version: 1
# name: test_clear_night[clear-night_forecast]
dict({
'weather.smhi_test': dict({
'forecast': list([
dict({
'cloud_coverage': 100,
'condition': 'clear-night',
'datetime': '2023-08-08T00: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,
}),
dict({
'cloud_coverage': 100,
'condition': 'clear-night',
'datetime': '2023-08-08T01:00:00',
'humidity': 100,
'precipitation': 0.0,
'pressure': 992.0,
'temperature': 18.0,
'templow': 18.0,
'wind_bearing': 104,
'wind_gust_speed': 27.36,
'wind_speed': 9.72,
}),
dict({
'cloud_coverage': 100,
'condition': 'clear-night',
'datetime': '2023-08-08T02:00:00',
'humidity': 97,
'precipitation': 0.0,
'pressure': 992.0,
'temperature': 18.0,
'templow': 18.0,
'wind_bearing': 109,
'wind_gust_speed': 32.4,
'wind_speed': 12.96,
}),
dict({
'cloud_coverage': 100,
'condition': 'sunny',
'datetime': '2023-08-08T03:00:00',
'humidity': 96,
'precipitation': 0.0,
'pressure': 991.0,
'temperature': 17.0,
'templow': 17.0,
'wind_bearing': 114,
'wind_gust_speed': 32.76,
'wind_speed': 10.08,
}),
]),
}),
})
# ---
# name: test_clear_night[clear_night]
ReadOnlyDict({
'attribution': 'Swedish weather institute (SMHI)',
'cloud_coverage': 100,
'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_forecast_service[get_forecast] # name: test_forecast_service[get_forecast]
dict({ dict({
'forecast': list([ 'forecast': list([

View file

@ -3,6 +3,7 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from unittest.mock import patch from unittest.mock import patch
from freezegun import freeze_time
import pytest import pytest
from smhi.smhi_lib import APIURL_TEMPLATE, SmhiForecast, SmhiForecastException from smhi.smhi_lib import APIURL_TEMPLATE, SmhiForecast, SmhiForecastException
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
@ -10,6 +11,7 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components.smhi.const import ATTR_SMHI_THUNDER_PROBABILITY 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 ( from homeassistant.components.weather import (
ATTR_CONDITION_CLEAR_NIGHT,
ATTR_FORECAST_CONDITION, ATTR_FORECAST_CONDITION,
ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_HUMIDITY,
ATTR_WEATHER_PRESSURE, ATTR_WEATHER_PRESSURE,
@ -29,7 +31,7 @@ from homeassistant.components.weather.const import (
from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN, UnitOfSpeed from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN, UnitOfSpeed
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.util.dt import utcnow from homeassistant.util import dt as dt_util
from . import ENTITY_ID, TEST_CONFIG from . import ENTITY_ID, TEST_CONFIG
@ -66,6 +68,44 @@ async def test_setup_hass(
assert state.attributes == snapshot assert state.attributes == snapshot
@freeze_time(datetime(2023, 8, 7, 1, tzinfo=dt_util.UTC))
async def test_clear_night(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
api_response_night: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test for successfully setting up the smhi integration."""
hass.config.latitude = "59.32624"
hass.config.longitude = "17.84197"
uri = APIURL_TEMPLATE.format(
TEST_CONFIG["location"]["longitude"], TEST_CONFIG["location"]["latitude"]
)
aioclient_mock.get(uri, text=api_response_night)
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()
assert aioclient_mock.call_count == 2
state = hass.states.get(ENTITY_ID)
assert state
assert state.state == ATTR_CONDITION_CLEAR_NIGHT
assert state.attributes == snapshot(name="clear_night")
response = await hass.services.async_call(
WEATHER_DOMAIN,
SERVICE_GET_FORECASTS,
{"entity_id": ENTITY_ID, "type": "hourly"},
blocking=True,
return_response=True,
)
assert response == snapshot(name="clear-night_forecast")
async def test_properties_no_data(hass: HomeAssistant) -> None: async def test_properties_no_data(hass: HomeAssistant) -> None:
"""Test properties when no API data available.""" """Test properties when no API data available."""
entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2) entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2)
@ -197,7 +237,7 @@ async def test_refresh_weather_forecast_retry(
"""Test the refresh weather forecast function.""" """Test the refresh weather forecast function."""
entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2) entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2)
entry.add_to_hass(hass) entry.add_to_hass(hass)
now = utcnow() now = dt_util.utcnow()
with patch( with patch(
"homeassistant.components.smhi.weather.Smhi.async_get_forecast", "homeassistant.components.smhi.weather.Smhi.async_get_forecast",