Perform sync when device list changes in Alexa Devices (#183478)

This commit is contained in:
jameson_uk
2026-09-28 15:43:01 +02:00
committed by GitHub
parent d9009c2b5b
commit dc417e75da
4 changed files with 86 additions and 7 deletions
@@ -61,7 +61,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
for sync_call in (
coordinator.sync_todo_list_items,
coordinator.sync_history_state,
coordinator.sync_media_state,
):
await _async_initial_sync(sync_call)
@@ -216,10 +216,16 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
) from err
else:
current_devices = set(data.keys())
device_list_changed = current_devices != self.previous_devices
if stale_devices := self.previous_devices - current_devices:
await self._async_remove_device_stale(stale_devices)
self.previous_devices = current_devices
# sync data on first refresh and after the device list changes
# self.data is None only on the first refresh
if self.data is None or device_list_changed:
await self._async_sync_on_device_list_change()
current_routines = {
f"{slugify(self.config_entry.unique_id)}-{slugify(routine)}"
for routine in self.api.routines
@@ -238,6 +244,18 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
return data
async def _async_sync_on_device_list_change(self) -> None:
"""Sync per-device state on first refresh and after the device list changes."""
for sync_call in (self.sync_media_state,):
try:
await sync_call()
except ConfigEntryNotReady as err:
LOGGER.warning(
"Sync failed for %s: %s. Data may be missing or incomplete until updates are pushed by Amazon",
sync_call.__name__,
err,
)
async def _async_remove_device_stale(
self,
stale_devices: set[str],
@@ -213,3 +213,71 @@ async def test_sync_media_state_auth_failed(
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is expected_state
@pytest.mark.parametrize(
"error",
[
pytest.param(
CannotConnect("429 - Too Many Requests"),
id="http_429_too_many_requests",
),
pytest.param(
CannotRetrieveData("503 - Service Unavailable"),
id="http_503_service_unavailable",
),
],
)
async def test_media_state_sync_failure_logged_on_first_refresh(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
mock_amazon_devices_client: AsyncMock,
mock_config_entry: MockConfigEntry,
error: Exception,
) -> None:
"""Test a failing media sync on first refresh is logged but does not block setup."""
mock_amazon_devices_client.sync_media_state.side_effect = error
await setup_integration(hass, mock_config_entry)
assert "Sync failed for sync_media_state:" in caplog.text
assert str(error) in caplog.text
assert (
"Data may be missing or incomplete until updates are pushed by Amazon"
in caplog.text
)
assert mock_config_entry.state is ConfigEntryState.LOADED
async def test_media_state_sync_on_device_list_change(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_amazon_devices_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test media state is resynced when a device is added."""
mock_amazon_devices_client.get_devices_data.return_value = {
TEST_DEVICE_1_SN: TEST_DEVICE_1,
}
await setup_integration(hass, mock_config_entry)
mock_amazon_devices_client.sync_media_state.assert_awaited_once()
mock_amazon_devices_client.get_devices_data.return_value = {
TEST_DEVICE_1_SN: TEST_DEVICE_1,
TEST_DEVICE_2_SN: TEST_DEVICE_2,
}
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert mock_amazon_devices_client.sync_media_state.call_count == 2
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Device list unchanged: no additional resync
assert mock_amazon_devices_client.sync_media_state.call_count == 2
@@ -43,11 +43,6 @@ def _fail_history_state(client: AsyncMock, error: Exception) -> None:
client.sync_history_state.side_effect = error
def _fail_media_state(client: AsyncMock, error: Exception) -> None:
"""Make the media state sync fail."""
client.sync_media_state.side_effect = error
async def test_device_info(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
@@ -252,7 +247,6 @@ async def test_http2_stop_processing_called_on_shutdown(
[
(_fail_todo_list_items, "sync_todo_list_items"),
(_fail_history_state, "sync_history_state"),
(_fail_media_state, "sync_media_state"),
],
)
@pytest.mark.parametrize(