Provide request retry option to overcome intermittant enphase_envoy failures (#168222)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Arie Catsman
2026-05-18 20:40:14 +02:00
committed by GitHub
co-authored by Copilot
parent c468ae77f3
commit f66652c729
3 changed files with 57 additions and 2 deletions
@@ -16,6 +16,9 @@ PLATFORMS = [
INVALID_AUTH_ERRORS = (EnvoyAuthenticationError, EnvoyAuthenticationRequired)
SETUP_RETRY_TIMEOUT = 50
OPERATIONAL_RETRY_TIMEOUT = 200
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES = "diagnostics_include_fixtures"
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE = False
@@ -18,7 +18,12 @@ from homeassistant.helpers.event import async_call_later, async_track_time_inter
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from .const import DOMAIN, INVALID_AUTH_ERRORS
from .const import (
DOMAIN,
INVALID_AUTH_ERRORS,
OPERATIONAL_RETRY_TIMEOUT,
SETUP_RETRY_TIMEOUT,
)
SCAN_INTERVAL = timedelta(seconds=60)
@@ -50,6 +55,7 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.username = entry_data[CONF_USERNAME]
self.password = entry_data[CONF_PASSWORD]
self._setup_complete = False
self._operational_timeout = False
self.envoy_firmware = ""
self.interface = None
self._cancel_token_refresh: CALLBACK_TYPE | None = None
@@ -265,10 +271,15 @@ class EnphaseUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
try:
if not self._setup_complete:
_LOGGER.debug("update on try %s, setup not complete", tries)
self.envoy.set_retry_policy(max_delay=SETUP_RETRY_TIMEOUT)
self._operational_timeout = False
await self._async_setup_and_authenticate()
self._async_mark_setup_complete()
# dump all received data in debug mode to assist troubleshooting
envoy_data = await envoy.update()
if not self._operational_timeout:
self.envoy.set_retry_policy(max_delay=OPERATIONAL_RETRY_TIMEOUT)
self._operational_timeout = True
except INVALID_AUTH_ERRORS as err:
_LOGGER.debug("update on try %s, INVALID_AUTH_ERRORS %s", tries, err)
if self._setup_complete and tries == 0:
+42 -1
View File
@@ -2,7 +2,7 @@
from datetime import timedelta
import logging
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock, MagicMock, call, patch
from freezegun.api import FrozenDateTimeFactory
from jwt import encode
@@ -13,8 +13,10 @@ import respx
from homeassistant.components.enphase_envoy import DOMAIN
from homeassistant.components.enphase_envoy.const import (
OPERATIONAL_RETRY_TIMEOUT,
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
OPTION_DISABLE_KEEP_ALIVE,
SETUP_RETRY_TIMEOUT,
Platform,
)
from homeassistant.components.enphase_envoy.coordinator import (
@@ -627,3 +629,42 @@ async def test_coordinator_interface_information_mac_also_in_other_device(
"00:11:22:33:44:55",
)
}
@pytest.mark.freeze_time("2024-07-23 00:00:00+00:00")
async def test_retry_timeout_settings(
hass: HomeAssistant,
mock_envoy: AsyncMock,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test coordinator with token provided from config."""
token = encode(
payload={"name": "envoy", "exp": 1907837780},
key="secret",
algorithm="HS256",
)
entry = MockConfigEntry(
domain=DOMAIN,
entry_id="45a36e55aaddb2007c5f6602e0c38e72",
title="Envoy 1234",
unique_id="1234",
data={
CONF_HOST: "1.1.1.1",
CONF_NAME: "Envoy 1234",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_TOKEN: token,
},
)
mock_envoy.auth = EnvoyTokenAuth("127.0.0.1", token=token, envoy_serial="1234")
await setup_integration(hass, entry)
assert (entity_state := hass.states.get("sensor.inverter_1"))
assert entity_state.state == "116"
assert mock_envoy.mock_calls[0] == call.set_retry_policy(
max_delay=SETUP_RETRY_TIMEOUT
)
assert mock_envoy.mock_calls[-1] == call.set_retry_policy(
max_delay=OPERATIONAL_RETRY_TIMEOUT
)