Add retry to api calls in Nord Pool (#132768)

pull/132869/head
G Johansson 2024-12-10 18:26:49 +01:00 committed by GitHub
parent dba405dd88
commit f99239538c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 31 additions and 15 deletions

View File

@ -2,6 +2,7 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
@ -69,23 +70,30 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
self.unsub = async_track_point_in_utc_time(
self.hass, self.fetch_data, self.get_next_interval(dt_util.utcnow())
)
data = await self.api_call()
if data:
self.async_set_updated_data(data)
async def api_call(self, retry: int = 3) -> DeliveryPeriodData | None:
"""Make api call to retrieve data with retry if failure."""
data = None
try:
data = await self.client.async_get_delivery_period(
dt_util.now(),
Currency(self.config_entry.data[CONF_CURRENCY]),
self.config_entry.data[CONF_AREAS],
)
except NordPoolEmptyResponseError as error:
LOGGER.debug("Empty response error: %s", error)
self.async_set_update_error(error)
return
except NordPoolResponseError as error:
LOGGER.debug("Response error: %s", error)
self.async_set_update_error(error)
return
except NordPoolError as error:
except (
NordPoolEmptyResponseError,
NordPoolResponseError,
NordPoolError,
) as error:
LOGGER.debug("Connection error: %s", error)
if retry > 0:
next_run = (4 - retry) * 15
LOGGER.debug("Wait %d seconds for next try", next_run)
await asyncio.sleep(next_run)
return await self.api_call(retry - 1)
self.async_set_update_error(error)
return
self.async_set_updated_data(data)
return data

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import AsyncGenerator
from datetime import datetime
import json
from typing import Any
@ -23,6 +24,13 @@ from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(autouse=True)
async def no_sleep() -> AsyncGenerator[None]:
"""No sleeping."""
with patch("homeassistant.components.nordpool.coordinator.asyncio.sleep"):
yield
@pytest.fixture
async def load_int(
hass: HomeAssistant, get_data: DeliveryPeriodData

View File

@ -58,7 +58,7 @@ async def test_coordinator(
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
mock_data.assert_called_once()
assert mock_data.call_count == 4
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
mock_data.reset_mock()
@ -68,7 +68,7 @@ async def test_coordinator(
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
mock_data.assert_called_once()
assert mock_data.call_count == 4
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
assert "Authentication error" in caplog.text
@ -79,7 +79,7 @@ async def test_coordinator(
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
mock_data.assert_called_once()
assert mock_data.call_count == 4
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
assert "Empty response" in caplog.text
@ -90,7 +90,7 @@ async def test_coordinator(
freezer.tick(timedelta(hours=1))
async_fire_time_changed(hass)
await hass.async_block_till_done(wait_background_tasks=True)
mock_data.assert_called_once()
assert mock_data.call_count == 4
state = hass.states.get("sensor.nord_pool_se3_current_price")
assert state.state == STATE_UNAVAILABLE
assert "Response error" in caplog.text