Update buienweather data before adding entities (#98455)

* Update buienweather data before adding entities

* Fix tests
This commit is contained in:
Erik Montnemery 2023-08-15 18:15:23 +02:00 committed by GitHub
parent ffe3d7c255
commit 90413daa8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 42 deletions

View file

@ -14,10 +14,10 @@ CONF_TIMEFRAME = "timeframe"
SUPPORTED_COUNTRY_CODES = ["NL", "BE"]
DEFAULT_COUNTRY = "NL"
"""Schedule next call after (minutes)."""
SCHEDULE_OK = 10
"""When an error occurred, new call after (minutes)."""
"""Schedule next call after (minutes)."""
SCHEDULE_NOK = 2
"""When an error occurred, new call after (minutes)."""
STATE_CONDITIONS = ["clear", "cloudy", "fog", "rainy", "snowy", "lightning"]

View file

@ -714,17 +714,18 @@ async def async_setup_entry(
timeframe,
)
# create weather entities:
entities = [
BrSensor(config.get(CONF_NAME, "Buienradar"), coordinates, description)
for description in SENSOR_TYPES
]
async_add_entities(entities)
# create weather data:
data = BrData(hass, coordinates, timeframe, entities)
# schedule the first update in 1 minute from now:
await data.schedule_update(1)
hass.data[DOMAIN][entry.entry_id][Platform.SENSOR] = data
await data.async_update()
async_add_entities(entities)
class BrSensor(SensorEntity):
@ -755,7 +756,7 @@ class BrSensor(SensorEntity):
@callback
def data_updated(self, data: BrData):
"""Update data."""
if self.hass and self._load_data(data.data):
if self._load_data(data.data) and self.hass:
self.async_write_ha_state()
@callback

View file

@ -27,7 +27,7 @@ from buienradar.constants import (
from buienradar.urls import JSON_FEED_URL, json_precipitation_forecast_url
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import CALLBACK_TYPE
from homeassistant.core import CALLBACK_TYPE, callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.util import dt as dt_util
@ -77,7 +77,8 @@ class BrData:
for dev in self.devices:
dev.data_updated(self)
async def schedule_update(self, minute=1):
@callback
def async_schedule_update(self, minute=1):
"""Schedule an update after minute minutes."""
_LOGGER.debug("Scheduling next update in %s minutes", minute)
nxt = dt_util.utcnow() + timedelta(minutes=minute)
@ -110,7 +111,7 @@ class BrData:
if resp is not None:
await resp.release()
async def async_update(self, *_):
async def _async_update(self):
"""Update the data from buienradar."""
content = await self.get_data(JSON_FEED_URL)
@ -123,9 +124,7 @@ class BrData:
content.get(MESSAGE),
content.get(STATUS_CODE),
)
# schedule new call
await self.schedule_update(SCHEDULE_NOK)
return
return None
self.load_error_count = 0
# rounding coordinates prevents unnecessary redirects/calls
@ -143,9 +142,7 @@ class BrData:
raincontent.get(MESSAGE),
raincontent.get(STATUS_CODE),
)
# schedule new call
await self.schedule_update(SCHEDULE_NOK)
return
return None
self.rain_error_count = 0
result = parse_data(
@ -164,12 +161,21 @@ class BrData:
"Unable to parse data from Buienradar. (Msg: %s)",
result.get(MESSAGE),
)
await self.schedule_update(SCHEDULE_NOK)
return None
return result[DATA]
async def async_update(self, *_):
"""Update the data from buienradar and schedule the next update."""
data = await self._async_update()
if data is None:
self.async_schedule_update(SCHEDULE_NOK)
return
self.data = result.get(DATA)
self.data = data
await self.update_devices()
await self.schedule_update(SCHEDULE_OK)
self.async_schedule_update(SCHEDULE_OK)
@property
def attribution(self):

View file

@ -82,6 +82,11 @@ CONDITION_CLASSES = {
ATTR_CONDITION_WINDY_VARIANT: (),
ATTR_CONDITION_EXCEPTIONAL: (),
}
CONDITION_MAP = {
cond_code: cond_ha
for cond_ha, cond_codes in CONDITION_CLASSES.items()
for cond_code in cond_codes
}
async def async_setup_entry(
@ -106,20 +111,10 @@ async def async_setup_entry(
# create weather data:
data = BrData(hass, coordinates, DEFAULT_TIMEFRAME, entities)
hass.data[DOMAIN][entry.entry_id][Platform.WEATHER] = data
# create condition helper
if DATA_CONDITION not in hass.data[DOMAIN]:
cond_keys = [str(chr(x)) for x in range(97, 123)]
hass.data[DOMAIN][DATA_CONDITION] = dict.fromkeys(cond_keys)
for cond, condlst in CONDITION_CLASSES.items():
for condi in condlst:
hass.data[DOMAIN][DATA_CONDITION][condi] = cond
await data.async_update()
async_add_entities(entities)
# schedule the first update in 1 minute from now:
await data.schedule_update(1)
class BrWeather(WeatherEntity):
"""Representation of a weather condition."""
@ -143,9 +138,6 @@ class BrWeather(WeatherEntity):
@callback
def data_updated(self, data: BrData) -> None:
"""Update data."""
if not self.hass:
return
self._attr_attribution = data.attribution
self._attr_condition = self._calc_condition(data)
self._attr_forecast = self._calc_forecast(data)
@ -158,22 +150,20 @@ class BrWeather(WeatherEntity):
self._attr_native_visibility = data.visibility
self._attr_native_wind_speed = data.wind_speed
self._attr_wind_bearing = data.wind_bearing
if not self.hass:
return
self.async_write_ha_state()
def _calc_condition(self, data: BrData):
"""Return the current condition."""
if (
data.condition
and (ccode := data.condition.get(CONDCODE))
and (conditions := self.hass.data[DOMAIN].get(DATA_CONDITION))
):
return conditions.get(ccode)
if data.condition and (ccode := data.condition.get(CONDCODE)):
return CONDITION_MAP.get(ccode)
return None
def _calc_forecast(self, data: BrData):
"""Return the forecast array."""
fcdata_out = []
cond = self.hass.data[DOMAIN][DATA_CONDITION]
if not data.forecast:
return None
@ -181,10 +171,10 @@ class BrWeather(WeatherEntity):
for data_in in data.forecast:
# remap keys from external library to
# keys understood by the weather component:
condcode = data_in.get(CONDITION, []).get(CONDCODE)
condcode = data_in.get(CONDITION, {}).get(CONDCODE)
data_out = {
ATTR_FORECAST_TIME: data_in.get(DATETIME).isoformat(),
ATTR_FORECAST_CONDITION: cond[condcode],
ATTR_FORECAST_CONDITION: CONDITION_MAP.get(condcode),
ATTR_FORECAST_NATIVE_TEMP_LOW: data_in.get(MIN_TEMP),
ATTR_FORECAST_NATIVE_TEMP: data_in.get(MAX_TEMP),
ATTR_FORECAST_NATIVE_PRECIPITATION: data_in.get(RAIN),

View file

@ -1,4 +1,6 @@
"""The tests for the Buienradar sensor platform."""
from http import HTTPStatus
from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
@ -18,6 +20,9 @@ async def test_smoke_test_setup_component(
aioclient_mock: AiohttpClientMocker, hass: HomeAssistant
) -> None:
"""Smoke test for successfully set-up with default config."""
aioclient_mock.get(
"https://data.buienradar.nl/2.0/feed/json", status=HTTPStatus.NOT_FOUND
)
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
mock_entry.add_to_hass(hass)

View file

@ -1,4 +1,6 @@
"""The tests for the buienradar weather component."""
from http import HTTPStatus
from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
@ -13,6 +15,9 @@ async def test_smoke_test_setup_component(
aioclient_mock: AiohttpClientMocker, hass: HomeAssistant
) -> None:
"""Smoke test for successfully set-up with default config."""
aioclient_mock.get(
"https://data.buienradar.nl/2.0/feed/json", status=HTTPStatus.NOT_FOUND
)
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
mock_entry.add_to_hass(hass)