From d1bdf73bc56a99a230ecb6ae0b3bf3106f80413a Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 22 May 2024 16:03:48 +0200 Subject: [PATCH] Add clear night to smhi (#115998) --- homeassistant/components/smhi/weather.py | 13 +- tests/components/smhi/conftest.py | 6 + .../components/smhi/fixtures/smhi_night.json | 700 ++++++++++++++++++ .../smhi/snapshots/test_weather.ambr | 81 ++ tests/components/smhi/test_weather.py | 44 +- 5 files changed, 840 insertions(+), 4 deletions(-) create mode 100644 tests/components/smhi/fixtures/smhi_night.json diff --git a/homeassistant/components/smhi/weather.py b/homeassistant/components/smhi/weather.py index bf069f4b26a8c..3d5642a27847a 100644 --- a/homeassistant/components/smhi/weather.py +++ b/homeassistant/components/smhi/weather.py @@ -13,6 +13,7 @@ from smhi.smhi_lib import SmhiForecast, SmhiForecastException from homeassistant.components.weather import ( + ATTR_CONDITION_CLEAR_NIGHT, ATTR_CONDITION_CLOUDY, ATTR_CONDITION_EXCEPTIONAL, ATTR_CONDITION_FOG, @@ -55,11 +56,11 @@ UnitOfTemperature, ) 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.entity_platform import AddEntitiesCallback 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 @@ -189,6 +190,10 @@ async def async_update(self) -> None: self._attr_native_wind_gust_speed = self._forecast_daily[0].wind_gust self._attr_cloud_coverage = self._forecast_daily[0].cloudiness 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")) async def retry_update(self, _: datetime) -> None: @@ -206,6 +211,10 @@ def _get_forecast_data( for forecast in forecast_data[1:]: 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( { diff --git a/tests/components/smhi/conftest.py b/tests/components/smhi/conftest.py index 62da520756527..95fbc15e69d4a 100644 --- a/tests/components/smhi/conftest.py +++ b/tests/components/smhi/conftest.py @@ -13,6 +13,12 @@ def api_response(): 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") def api_response_lack_data(): """Return an API response.""" diff --git a/tests/components/smhi/fixtures/smhi_night.json b/tests/components/smhi/fixtures/smhi_night.json new file mode 100644 index 0000000000000..121544bd2f19a --- /dev/null +++ b/tests/components/smhi/fixtures/smhi_night.json @@ -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] + } + ] + } + ] +} diff --git a/tests/components/smhi/snapshots/test_weather.ambr b/tests/components/smhi/snapshots/test_weather.ambr index 0fef9e19ec3d3..0d2f6b3b3bfb2 100644 --- a/tests/components/smhi/snapshots/test_weather.ambr +++ b/tests/components/smhi/snapshots/test_weather.ambr @@ -1,4 +1,85 @@ # 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': , + 'pressure': 992.0, + 'pressure_unit': , + 'supported_features': , + 'temperature': 18.0, + 'temperature_unit': , + 'thunder_probability': 37, + 'visibility': 0.4, + 'visibility_unit': , + 'wind_bearing': 93, + 'wind_gust_speed': 22.32, + 'wind_speed': 9.0, + 'wind_speed_unit': , + }) +# --- # name: test_forecast_service[get_forecast] dict({ 'forecast': list([ diff --git a/tests/components/smhi/test_weather.py b/tests/components/smhi/test_weather.py index 4d187e7c72819..e5b8155f9caad 100644 --- a/tests/components/smhi/test_weather.py +++ b/tests/components/smhi/test_weather.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from unittest.mock import patch +from freezegun import freeze_time import pytest from smhi.smhi_lib import APIURL_TEMPLATE, SmhiForecast, SmhiForecastException from syrupy.assertion import SnapshotAssertion @@ -10,6 +11,7 @@ from homeassistant.components.smhi.const import ATTR_SMHI_THUNDER_PROBABILITY from homeassistant.components.smhi.weather import CONDITION_CLASSES, RETRY_TIMEOUT from homeassistant.components.weather import ( + ATTR_CONDITION_CLEAR_NIGHT, ATTR_FORECAST_CONDITION, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_PRESSURE, @@ -29,7 +31,7 @@ from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN, UnitOfSpeed from homeassistant.core import HomeAssistant 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 @@ -66,6 +68,44 @@ async def test_setup_hass( 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: """Test properties when no API data available.""" 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.""" entry = MockConfigEntry(domain="smhi", data=TEST_CONFIG, version=2) entry.add_to_hass(hass) - now = utcnow() + now = dt_util.utcnow() with patch( "homeassistant.components.smhi.weather.Smhi.async_get_forecast",