diff --git a/custom_components/nordpool/__init__.py b/custom_components/nordpool/__init__.py index 7043ddf..cec4f61 100644 --- a/custom_components/nordpool/__init__.py +++ b/custom_components/nordpool/__init__.py @@ -1,9 +1,9 @@ import logging from collections import defaultdict -from datetime import datetime, timedelta -from functools import partial +from datetime import timedelta from random import randint +import backoff import voluptuous as vol from homeassistant.config_entries import ConfigEntry from homeassistant.core import Config, HomeAssistant @@ -13,7 +13,7 @@ from homeassistant.util import dt as dt_utils from pytz import timezone -from .aio_price import AioPrices +from .aio_price import AioPrices, InvalidValueException from .events import async_track_time_change_in_tz DOMAIN = "nordpool" @@ -74,12 +74,12 @@ async def _update(self, type_="today", dt=None): if data: self._data[currency][type_] = data["areas"] - async def update_today(self, _: datetime): + async def update_today(self): """Update today's prices""" _LOGGER.debug("Updating today's prices.") await self._update("today") - async def update_tomorrow(self, _: datetime): + async def update_tomorrow(self): """Update tomorrows prices.""" _LOGGER.debug("Updating tomorrows prices.") await self._update(type_="tomorrow", dt=dt_utils.now() + timedelta(hours=24)) @@ -96,8 +96,11 @@ async def _someday(self, area: str, currency: str, day: str): # set in the sensor. if currency not in self.currency: self.currency.append(currency) - await self.update_today(None) - await self.update_tomorrow(None) + await self.update_today() + try: + await self.update_tomorrow() + except InvalidValueException: + _LOGGER.debug("No data available for tomorrow, retrying later") # Send a new data request after new data is updated for this first run # This way if the user has multiple sensors they will all update @@ -128,7 +131,7 @@ async def new_day_cb(_): for curr in api.currency: if not api._data.get(curr, {}).get("tomorrow"): - api._data[curr]["today"] = await api.update_today(None) + api._data[curr]["today"] = await api.update_today() else: api._data[curr]["today"] = api._data[curr]["tomorrow"] api._data[curr]["tomorrow"] = {} @@ -140,12 +143,16 @@ async def new_hr(_): _LOGGER.debug("Called new_hr callback") async_dispatcher_send(hass, EVENT_NEW_HOUR) - async def new_data_cb(tdo): + @backoff.on_exception( + backoff.constant, + (InvalidValueException), + logger=_LOGGER, interval=600, max_time=7200, jitter=None) + async def new_data_cb(_): """Callback to fetch new data for tomorrows prices at 1300ish CET and notify any sensors, about the new data """ # _LOGGER.debug("Called new_data_cb") - await api.update_tomorrow(tdo) + await api.update_tomorrow() async_dispatcher_send(hass, EVENT_NEW_PRICE) # Handles futures updates diff --git a/custom_components/nordpool/aio_price.py b/custom_components/nordpool/aio_price.py index 5a947b0..7314ac6 100644 --- a/custom_components/nordpool/aio_price.py +++ b/custom_components/nordpool/aio_price.py @@ -90,6 +90,12 @@ "PL ": "PL", } +INVALID_VALUES = frozenset((None, float("inf"))) + + +class InvalidValueException(ValueError): + pass + def join_result_for_correct_time(results, dt): """Parse a list of responses from the api @@ -135,6 +141,8 @@ def join_result_for_correct_time(results, dt): local = val["start"].astimezone(zone) local_end = val["end"].astimezone(zone) if start_of_day <= local and local <= end_of_day: + if val['value'] in INVALID_VALUES: + raise InvalidValueException() if local == local_end: _LOGGER.info( "Hour has the same start and end, most likly due to dst change %s exluded this hour", @@ -179,7 +187,10 @@ async def _fetch_json(self, data_type, end_date=None): # Add more exceptions as we find them. KeyError is raised when the api return # junk due to currency not being available in the data. - @backoff.on_exception(backoff.expo, (aiohttp.ClientError, KeyError), logger=_LOGGER) + @backoff.on_exception( + backoff.expo, + (aiohttp.ClientError, KeyError), + logger=_LOGGER, max_value=20, max_time=60) async def fetch(self, data_type, end_date=None, areas=None): """ Fetch data from API.