add last_update_success_time and a failed update interval
This commit is contained in:
parent
9adc48c0b0
commit
09428c9686
2 changed files with 81 additions and 28 deletions
|
@ -33,6 +33,7 @@ class DataUpdateCoordinator:
|
|||
update_interval: timedelta,
|
||||
update_method: Optional[Callable[[], Awaitable]] = None,
|
||||
request_refresh_debouncer: Optional[Debouncer] = None,
|
||||
failed_update_interval: Optional[timedelta] = None,
|
||||
):
|
||||
"""Initialize global data updater."""
|
||||
self.hass = hass
|
||||
|
@ -40,13 +41,17 @@ class DataUpdateCoordinator:
|
|||
self.name = name
|
||||
self.update_method = update_method
|
||||
self.update_interval = update_interval
|
||||
|
||||
if failed_update_interval:
|
||||
self.failed_update_interval = failed_update_interval
|
||||
else:
|
||||
self.failed_update_interval = update_interval
|
||||
self.data: Optional[Any] = None
|
||||
|
||||
self._listeners: List[CALLBACK_TYPE] = []
|
||||
self._unsub_refresh: Optional[CALLBACK_TYPE] = None
|
||||
self._request_refresh_task: Optional[asyncio.TimerHandle] = None
|
||||
self.last_update_success = True
|
||||
self.last_update_success_time = None # type: Optional[datetime]
|
||||
|
||||
if request_refresh_debouncer is None:
|
||||
request_refresh_debouncer = Debouncer(
|
||||
|
@ -99,10 +104,14 @@ class DataUpdateCoordinator:
|
|||
# minimizing the time between the point and the real activation.
|
||||
# That way we obtain a constant update frequency,
|
||||
# as long as the update process takes less than a second
|
||||
if self.last_update_success:
|
||||
update_interval = self.update_interval
|
||||
else:
|
||||
update_interval = self.failed_update_interval
|
||||
self._unsub_refresh = async_track_point_in_utc_time(
|
||||
self.hass,
|
||||
self._handle_refresh_interval,
|
||||
utcnow().replace(microsecond=0) + self.update_interval,
|
||||
utcnow().replace(microsecond=0) + update_interval,
|
||||
)
|
||||
|
||||
async def _handle_refresh_interval(self, _now: datetime) -> None:
|
||||
|
@ -163,6 +172,7 @@ class DataUpdateCoordinator:
|
|||
if not self.last_update_success:
|
||||
self.last_update_success = True
|
||||
self.logger.info("Fetching %s data recovered", self.name)
|
||||
self.last_update_success_time = utcnow()
|
||||
|
||||
finally:
|
||||
self.logger.debug(
|
||||
|
|
|
@ -9,7 +9,7 @@ import pytest
|
|||
from homeassistant.helpers import update_coordinator
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.async_mock import AsyncMock, Mock
|
||||
from tests.async_mock import AsyncMock, Mock, patch
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
@ -37,12 +37,16 @@ def crd(hass):
|
|||
|
||||
async def test_async_refresh(crd):
|
||||
"""Test async_refresh for update coordinator."""
|
||||
assert crd.data is None
|
||||
await crd.async_refresh()
|
||||
assert crd.data == 1
|
||||
assert crd.last_update_success is True
|
||||
# Make sure we didn't schedule a refresh because we have 0 listeners
|
||||
assert crd._unsub_refresh is None
|
||||
utc_time = utcnow()
|
||||
with patch("homeassistant.helpers.update_coordinator.utcnow") as mock_utc:
|
||||
mock_utc.return_value = utc_time
|
||||
assert crd.data is None
|
||||
await crd.async_refresh()
|
||||
assert crd.data == 1
|
||||
assert crd.last_update_success is True
|
||||
assert crd.last_update_success_time == utc_time
|
||||
# Make sure we didn't schedule a refresh because we have 0 listeners
|
||||
assert crd._unsub_refresh is None
|
||||
|
||||
updates = []
|
||||
|
||||
|
@ -124,31 +128,70 @@ async def test_refresh_no_update_method(crd):
|
|||
async def test_update_interval(hass, crd):
|
||||
"""Test update interval works."""
|
||||
# Test we don't update without subscriber
|
||||
async_fire_time_changed(hass, utcnow() + crd.update_interval)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data is None
|
||||
utc_time = utcnow()
|
||||
with patch("homeassistant.helpers.update_coordinator.utcnow") as mock_utc:
|
||||
mock_utc.return_value = utc_time + crd.update_interval
|
||||
async_fire_time_changed(hass, mock_utc.return_value)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data is None
|
||||
assert crd.last_update_success_time is None
|
||||
|
||||
# Add subscriber
|
||||
update_callback = Mock()
|
||||
crd.async_add_listener(update_callback)
|
||||
# Add subscriber
|
||||
update_callback = Mock()
|
||||
crd.async_add_listener(update_callback)
|
||||
|
||||
# Test twice we update with subscriber
|
||||
async_fire_time_changed(hass, utcnow() + crd.update_interval)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data == 1
|
||||
# Test twice we update with subscriber
|
||||
mock_utc.return_value += crd.update_interval
|
||||
async_fire_time_changed(hass, mock_utc.return_value)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data == 1
|
||||
assert crd.last_update_success_time == mock_utc.return_value
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + crd.update_interval)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data == 2
|
||||
mock_utc.return_value += crd.update_interval
|
||||
async_fire_time_changed(hass, mock_utc.return_value)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data == 2
|
||||
assert crd.last_update_success_time == mock_utc.return_value
|
||||
last_success_time = mock_utc.return_value
|
||||
|
||||
# Test removing listener
|
||||
crd.async_remove_listener(update_callback)
|
||||
# Test removing listener
|
||||
crd.async_remove_listener(update_callback)
|
||||
|
||||
async_fire_time_changed(hass, utcnow() + crd.update_interval)
|
||||
await hass.async_block_till_done()
|
||||
mock_utc.return_value += crd.update_interval
|
||||
async_fire_time_changed(hass, mock_utc.return_value)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Test we stop updating after we lose last subscriber
|
||||
assert crd.data == 2
|
||||
# Test we stop updating after we lose last subscriber
|
||||
assert crd.data == 2
|
||||
assert crd.last_update_success_time == last_success_time
|
||||
|
||||
|
||||
async def test_failed_update_interval(crd, hass):
|
||||
"""Test failed update interval."""
|
||||
utc_time = utcnow()
|
||||
with patch("homeassistant.core.dt_util.utcnow") as mock_utcnow:
|
||||
mock_utcnow.return_value = utc_time
|
||||
old_update_method = crd.update_method
|
||||
crd.update_method = AsyncMock(side_effect=asyncio.TimeoutError)
|
||||
crd.failed_update_interval = timedelta(seconds=5)
|
||||
|
||||
def update_callback():
|
||||
pass
|
||||
|
||||
crd.async_add_listener(update_callback)
|
||||
|
||||
await crd.async_refresh()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert crd.data is None
|
||||
assert crd.last_update_success is False
|
||||
|
||||
crd.update_method = old_update_method
|
||||
mock_utcnow.return_value += timedelta(seconds=5)
|
||||
async_fire_time_changed(hass, mock_utcnow.return_value)
|
||||
await hass.async_block_till_done()
|
||||
assert crd.data == 1
|
||||
assert crd.last_update_success is True
|
||||
|
||||
|
||||
async def test_refresh_recover(crd, caplog):
|
||||
|
|
Loading…
Add table
Reference in a new issue