Update buienweather data before adding entities (#98455)
* Update buienweather data before adding entities * Fix tests
This commit is contained in:
parent
ffe3d7c255
commit
90413daa8a
6 changed files with 49 additions and 42 deletions
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue