Skip to content

Commit

Permalink
Fix an issue on stale start with auto-update
Browse files Browse the repository at this point in the history
  • Loading branch information
autoSteve committed Dec 30, 2024
1 parent 44cd6b0 commit e3e0764
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@ v4.2.8
* Fix an issue when half-hourly breakdown is disabled but hourly is enabled by @autoSteve
* Fix an issue with transitioning from granular to legacy dampening by @autoSteve
* Fix an issue with using multiple hard limits by @autoSteve
* Fix an issue with stale start when auto-update is enabled by @autoSteve
* Add integration and unit tests by @autoSteve

Full Changelog: https://github.com/BJReplay/ha-solcast-solar/compare/v4.2.7...v4.2.8
Expand Down
25 changes: 14 additions & 11 deletions custom_components/solcast_solar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,17 +214,15 @@ async def __get_granular_dampening(hass: HomeAssistant, entry: ConfigEntry, solc
hass.data[DOMAIN]["entry_options"] = entry.options


async def __check_stale_start(coordinator: SolcastUpdateCoordinator):
async def __check_stale_start(coordinator: SolcastUpdateCoordinator) -> bool:
"""Check whether the integration has been failed for some time and then is restarted, and if so update forecast."""
_LOGGER.debug("Checking for stale start")
stale = False
if coordinator.solcast.is_stale_data():
try:
if coordinator.solcast.options.auto_update == 0:
_LOGGER.warning("The update automation has not been running, updating forecast")
await coordinator.service_event_update(completion="Completed task stale_update")
else:
_LOGGER.warning("Many auto updates have been missed, updating forecast")
await coordinator.service_event_update(ignore_auto_enabled=True, completion="Completed task stale_update")
_LOGGER.warning("The update automation has not been running, updating forecast")
await coordinator.service_event_update(completion="Completed task stale_update")
stale = True
except Exception as e: # noqa: BLE001 # pragma: no cover, catch unexpected exceptions
_LOGGER.error(
"Exception fetching data on stale start: %s: %s",
Expand All @@ -234,10 +232,12 @@ async def __check_stale_start(coordinator: SolcastUpdateCoordinator):
_LOGGER.warning("Continuing... ")
else:
_LOGGER.debug("Start is not stale")
return stale


async def __check_auto_update_missed(coordinator: SolcastUpdateCoordinator):
async def __check_auto_update_missed(coordinator: SolcastUpdateCoordinator) -> bool:
"""Check whether an auto-update has been missed, and if so update forecast."""
stale = False
if coordinator.solcast.options.auto_update > 0:
if coordinator.solcast.get_data()["auto_updated"]:
_LOGGER.debug("Checking whether auto update forecast is stale")
Expand All @@ -253,6 +253,7 @@ async def __check_auto_update_missed(coordinator: SolcastUpdateCoordinator):
coordinator.interval_just_passed.astimezone(coordinator.solcast.options.tz).strftime(DATE_FORMAT),
)
await coordinator.service_event_update(ignore_auto_enabled=True, completion="Completed task update_missed")
stale = True
else: # pragma: no cover, time-of-day dependent
_LOGGER.debug("Auto update forecast is fresh")
except TypeError: # pragma: no cover, catch unexpected exceptions
Expand All @@ -263,6 +264,7 @@ async def __check_auto_update_missed(coordinator: SolcastUpdateCoordinator):
e,
traceback.format_exc(),
)
return stale


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # noqa: C901
Expand Down Expand Up @@ -322,8 +324,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: #
entry.async_on_unload(entry.add_update_listener(async_update_options))

__log_hard_limit_set(solcast)
await __check_stale_start(coordinator)
await __check_auto_update_missed(coordinator)
if not await __check_auto_update_missed(coordinator):
await __check_stale_start(coordinator)

hass.data[DOMAIN]["has_loaded"] = True

async def action_call_update_forecast(call: ServiceCall):
Expand Down Expand Up @@ -622,7 +625,7 @@ async def tasks_cancel(hass: HomeAssistant, entry: ConfigEntry) -> bool:
try:
coordinator = hass.data[DOMAIN][entry.entry_id]
# Terminate solcastapi tasks in progress
for task, cancel in coordinator.solcast.tasks.items():
for task, cancel in coordinator.solcast.tasks.items(): # pragma: no cover, unlikely under test conditions
_LOGGER.debug("Cancelling solcastapi task %s", task)
cancel.cancel()
# Terminate coordinator tasks in progress
Expand Down
4 changes: 2 additions & 2 deletions custom_components/solcast_solar/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ async def __update_utc_midnight_usage_sensor_data(self, *args): # pragma: no co
except: # noqa: E722
_LOGGER.error("Exception in __update_utc_midnight_usage_sensor_data(): %s", traceback.format_exc())

async def __update_midnight_spline_recalculate(self): # pragma: no cover
async def __update_midnight_spline_recalculate(self): # pragma: no cover, testing only possible when running
"""Re-calculates splines at midnight local time."""
try:
await self.solcast.check_data_records()
Expand Down Expand Up @@ -328,7 +328,7 @@ def format_intervals(intervals):
intervals_tomorrow = get_intervals(self._sunrise_tomorrow, self._sunset_tomorrow, log=False)
self._intervals = intervals_today + intervals_tomorrow

if len(intervals_today) > 0:
if len(intervals_today) > 0: # pragma: no cover, testing only reliable when running
_LOGGER.info(
"Auto forecast update%s for today at %s",
"s" if len(intervals_today) > 1 else "",
Expand Down
10 changes: 5 additions & 5 deletions custom_components/solcast_solar/solcastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2380,7 +2380,7 @@ def next_update(): # pragma: no cover, testing only possible when running
do_past=do_past,
force=force,
)
if result == DataCallStatus.FAIL: # pragma: no cover, will not occur under test conditions
if result == DataCallStatus.FAIL:
failure = True
if self.hass.data[DOMAIN].get(self.entry.entry_id) is not None:
if len(self.sites) > 1:
Expand All @@ -2391,21 +2391,21 @@ def next_update(): # pragma: no cover, testing only possible when running
" - API use count may be odd" if len(self.sites) > 2 and sites_succeeded and not force else "",
next_update(),
)
else:
else: # pragma: no cover, will not occur under test conditions
_LOGGER.warning(
"Forecast update for the last site queued failed (%s)%s%s",
site["resource_id"],
" - API use count may be odd" if sites_succeeded and not force else "",
next_update(),
)
status = "At least one site forecast get failed"
else:
else: # pragma: no cover, will not occur under test conditions
_LOGGER.warning("Forecast update failed%s", next_update())
status = "Forecast get failed"
else:
status = "KILLED"
status = "KILLED" # pragma: no cover, will not occur under test conditions
break
if result == DataCallStatus.ABORT:
if result == DataCallStatus.ABORT: # pragma: no cover, will not occur under test conditions
return ""
if result == DataCallStatus.SUCCESS:
sites_succeeded += 1
Expand Down

0 comments on commit e3e0764

Please sign in to comment.