From 557db04c24e01ab2f7c253ff2e51cc26d2d79115 Mon Sep 17 00:00:00 2001 From: dennismdejong Date: Mon, 27 Jul 2026 21:44:00 +0200 Subject: [PATCH] fix: keep last energy/device data during transient 5xx/429 errors (Closes #371, Closes #403) --- .../panasonic_cc/error_handler.py | 6 ++++ .../panasonic_cc/panasonic/coordinator.py | 30 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/custom_components/panasonic_cc/error_handler.py b/custom_components/panasonic_cc/error_handler.py index 47fcef5..48fe19e 100644 --- a/custom_components/panasonic_cc/error_handler.py +++ b/custom_components/panasonic_cc/error_handler.py @@ -121,6 +121,12 @@ def is_persistent(self) -> bool: message="The Panasonic server timed out waiting for a response from an upstream service.", suggestion="This is a temporary issue. It should resolve automatically.", ), + 5300: FriendlyError( + category=ErrorCategory.SERVER_ERROR, + title="Service Under Maintenance", + message="The Panasonic service is temporarily unavailable due to scheduled maintenance.", + suggestion="This should resolve automatically once maintenance is complete.", + ), } # Regex patterns for error message matching (fallback when no code is found) diff --git a/custom_components/panasonic_cc/panasonic/coordinator.py b/custom_components/panasonic_cc/panasonic/coordinator.py index cde2d6c..0a59e48 100644 --- a/custom_components/panasonic_cc/panasonic/coordinator.py +++ b/custom_components/panasonic_cc/panasonic/coordinator.py @@ -218,8 +218,21 @@ async def _async_update_data(self) -> int: ) _create_auth_expired_notification(self.hass) raise UpdateFailed("Authentication failed — coordinator disabled") from err - self._handle_failure(err) friendly = classify_error(err) + if ( + friendly.category in (ErrorCategory.RATE_LIMIT, ErrorCategory.SERVER_ERROR) + and self._device is not None + ): + self._handle_failure(err) + _LOGGER.warning( + "%s Transient error %s: %s — keeping last device data and retrying after backoff.", + self._device_info.name, + friendly.title, + friendly.message, + ) + self.async_update_listeners() + return self._update_id + self._handle_failure(err) raise UpdateFailed(f"{friendly.title}: {friendly.message}") from err return self._update_id @@ -359,8 +372,21 @@ async def _async_update_data(self) -> int: ) _create_auth_expired_notification(self.hass) raise UpdateFailed("Authentication failed — coordinator disabled") from err - self._handle_failure(err) friendly = classify_error(err) + if ( + friendly.category in (ErrorCategory.RATE_LIMIT, ErrorCategory.SERVER_ERROR) + and self._energy is not None + ): + self._handle_failure(err) + _LOGGER.warning( + "%s Transient error %s: %s — keeping last energy data and retrying after backoff.", + self._device_info.name, + friendly.title, + friendly.message, + ) + self.async_update_listeners() + return self._update_id + self._handle_failure(err) raise UpdateFailed(f"{friendly.title}: {friendly.message}") from err return self._update_id