mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Silently retry Fronius inverter endpoint 2 times (#61826)
This commit is contained in:
parent
38cb477e7b
commit
37bed64607
@ -5,7 +5,7 @@ from abc import ABC, abstractmethod
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import TYPE_CHECKING, Any, Dict, TypeVar
|
from typing import TYPE_CHECKING, Any, Dict, TypeVar
|
||||||
|
|
||||||
from pyfronius import FroniusError
|
from pyfronius import BadStatusError, FroniusError
|
||||||
|
|
||||||
from homeassistant.components.sensor import SensorEntityDescription
|
from homeassistant.components.sensor import SensorEntityDescription
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
@ -43,6 +43,8 @@ class FroniusCoordinatorBase(
|
|||||||
error_interval: timedelta
|
error_interval: timedelta
|
||||||
valid_descriptions: list[SensorEntityDescription]
|
valid_descriptions: list[SensorEntityDescription]
|
||||||
|
|
||||||
|
MAX_FAILED_UPDATES = 3
|
||||||
|
|
||||||
def __init__(self, *args: Any, solar_net: FroniusSolarNet, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, solar_net: FroniusSolarNet, **kwargs: Any) -> None:
|
||||||
"""Set up the FroniusCoordinatorBase class."""
|
"""Set up the FroniusCoordinatorBase class."""
|
||||||
self._failed_update_count = 0
|
self._failed_update_count = 0
|
||||||
@ -62,7 +64,7 @@ class FroniusCoordinatorBase(
|
|||||||
data = await self._update_method()
|
data = await self._update_method()
|
||||||
except FroniusError as err:
|
except FroniusError as err:
|
||||||
self._failed_update_count += 1
|
self._failed_update_count += 1
|
||||||
if self._failed_update_count == 3:
|
if self._failed_update_count == self.MAX_FAILED_UPDATES:
|
||||||
self.update_interval = self.error_interval
|
self.update_interval = self.error_interval
|
||||||
raise UpdateFailed(err) from err
|
raise UpdateFailed(err) from err
|
||||||
|
|
||||||
@ -116,6 +118,8 @@ class FroniusInverterUpdateCoordinator(FroniusCoordinatorBase):
|
|||||||
error_interval = timedelta(minutes=10)
|
error_interval = timedelta(minutes=10)
|
||||||
valid_descriptions = INVERTER_ENTITY_DESCRIPTIONS
|
valid_descriptions = INVERTER_ENTITY_DESCRIPTIONS
|
||||||
|
|
||||||
|
SILENT_RETRIES = 3
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, *args: Any, inverter_info: FroniusDeviceInfo, **kwargs: Any
|
self, *args: Any, inverter_info: FroniusDeviceInfo, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -125,9 +129,19 @@ class FroniusInverterUpdateCoordinator(FroniusCoordinatorBase):
|
|||||||
|
|
||||||
async def _update_method(self) -> dict[SolarNetId, Any]:
|
async def _update_method(self) -> dict[SolarNetId, Any]:
|
||||||
"""Return data per solar net id from pyfronius."""
|
"""Return data per solar net id from pyfronius."""
|
||||||
|
# almost 1% of `current_inverter_data` requests on Symo devices result in
|
||||||
|
# `BadStatusError Code: 8 - LNRequestTimeout` due to flaky internal
|
||||||
|
# communication between the logger and the inverter.
|
||||||
|
for silent_retry in range(self.SILENT_RETRIES):
|
||||||
|
try:
|
||||||
data = await self.solar_net.fronius.current_inverter_data(
|
data = await self.solar_net.fronius.current_inverter_data(
|
||||||
self.inverter_info.solar_net_id
|
self.inverter_info.solar_net_id
|
||||||
)
|
)
|
||||||
|
except BadStatusError as err:
|
||||||
|
if silent_retry == (self.SILENT_RETRIES - 1):
|
||||||
|
raise err
|
||||||
|
continue
|
||||||
|
break
|
||||||
# wrap a single devices data in a dict with solar_net_id key for
|
# wrap a single devices data in a dict with solar_net_id key for
|
||||||
# FroniusCoordinatorBase _async_update_data and add_entities_for_seen_keys
|
# FroniusCoordinatorBase _async_update_data and add_entities_for_seen_keys
|
||||||
return {self.inverter_info.solar_net_id: data}
|
return {self.inverter_info.solar_net_id: data}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Test the Fronius update coordinators."""
|
"""Test the Fronius update coordinators."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pyfronius import FroniusError
|
from pyfronius import BadStatusError, FroniusError
|
||||||
|
|
||||||
from homeassistant.components.fronius.coordinator import (
|
from homeassistant.components.fronius.coordinator import (
|
||||||
FroniusInverterUpdateCoordinator,
|
FroniusInverterUpdateCoordinator,
|
||||||
@ -18,27 +18,32 @@ async def test_adaptive_update_interval(hass, aioclient_mock):
|
|||||||
with patch("pyfronius.Fronius.current_inverter_data") as mock_inverter_data:
|
with patch("pyfronius.Fronius.current_inverter_data") as mock_inverter_data:
|
||||||
mock_responses(aioclient_mock)
|
mock_responses(aioclient_mock)
|
||||||
await setup_fronius_integration(hass)
|
await setup_fronius_integration(hass)
|
||||||
assert mock_inverter_data.call_count == 1
|
mock_inverter_data.assert_called_once()
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_inverter_data.call_count == 2
|
mock_inverter_data.assert_called_once()
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
mock_inverter_data.side_effect = FroniusError
|
mock_inverter_data.side_effect = FroniusError()
|
||||||
# first 3 requests at default interval - 4th has different interval
|
# first 3 bad requests at default interval - 4th has different interval
|
||||||
for _ in range(4):
|
for _ in range(3):
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_inverter_data.call_count == 5
|
assert mock_inverter_data.call_count == 3
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_inverter_data.call_count == 6
|
assert mock_inverter_data.call_count == 1
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
mock_inverter_data.side_effect = None
|
mock_inverter_data.side_effect = None
|
||||||
# next successful request resets to default interval
|
# next successful request resets to default interval
|
||||||
@ -46,10 +51,23 @@ async def test_adaptive_update_interval(hass, aioclient_mock):
|
|||||||
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.error_interval
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_inverter_data.call_count == 7
|
mock_inverter_data.assert_called_once()
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_inverter_data.call_count == 8
|
mock_inverter_data.assert_called_once()
|
||||||
|
mock_inverter_data.reset_mock()
|
||||||
|
|
||||||
|
# BadStatusError on inverter endpoints have special handling
|
||||||
|
mock_inverter_data.side_effect = BadStatusError("mock_endpoint", 8)
|
||||||
|
# first 3 requests at default interval - 4th has different interval
|
||||||
|
for _ in range(3):
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass, dt.utcnow() + FroniusInverterUpdateCoordinator.default_interval
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
# BadStatusError does 3 silent retries for inverter endpoint * 3 request intervals = 9
|
||||||
|
assert mock_inverter_data.call_count == 9
|
||||||
|
Loading…
x
Reference in New Issue
Block a user