mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 08:26:41 +01:00
Add entity-unavailable and log-when-unavailable (#165486)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,11 +46,18 @@ class LitterRobotDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
"""Update all device states from the Litter-Robot API."""
|
||||
await self.account.refresh_robots()
|
||||
await self.account.load_pets()
|
||||
for pet in self.account.pets:
|
||||
# Need to fetch weight history for `get_visits_since`
|
||||
await pet.fetch_weight_history()
|
||||
try:
|
||||
await self.account.refresh_robots()
|
||||
await self.account.load_pets()
|
||||
for pet in self.account.pets:
|
||||
# Need to fetch weight history for `get_visits_since`
|
||||
await pet.fetch_weight_history()
|
||||
except LitterRobotLoginException as ex:
|
||||
raise ConfigEntryAuthFailed("Invalid credentials") from ex
|
||||
except LitterRobotException as ex:
|
||||
raise UpdateFailed(
|
||||
f"Unable to fetch data from the Whisker API: {ex}"
|
||||
) from ex
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Set up the coordinator."""
|
||||
|
||||
@@ -29,9 +29,9 @@ rules:
|
||||
status: done
|
||||
comment: No options to configure
|
||||
docs-installation-parameters: done
|
||||
entity-unavailable: todo
|
||||
entity-unavailable: done
|
||||
integration-owner: done
|
||||
log-when-unavailable: todo
|
||||
log-when-unavailable: done
|
||||
parallel-updates: done
|
||||
reauthentication-flow: done
|
||||
test-coverage:
|
||||
|
||||
81
tests/components/litterrobot/test_coordinator.py
Normal file
81
tests/components/litterrobot/test_coordinator.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""Tests for the Litter-Robot coordinator."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pylitterbot.exceptions import LitterRobotException, LitterRobotLoginException
|
||||
|
||||
from homeassistant.components.litterrobot.const import DOMAIN
|
||||
from homeassistant.components.litterrobot.coordinator import UPDATE_INTERVAL
|
||||
from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import VACUUM_ENTITY_ID
|
||||
from .conftest import setup_integration
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def test_coordinator_update_error(
|
||||
hass: HomeAssistant,
|
||||
mock_account: MagicMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test entities become unavailable when coordinator update fails."""
|
||||
await setup_integration(hass, mock_account, VACUUM_DOMAIN)
|
||||
|
||||
assert (state := hass.states.get(VACUUM_ENTITY_ID))
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Simulate an API error during update
|
||||
mock_account.refresh_robots.side_effect = LitterRobotException("Unable to connect")
|
||||
freezer.tick(UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (state := hass.states.get(VACUUM_ENTITY_ID))
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Recover
|
||||
mock_account.refresh_robots.side_effect = None
|
||||
freezer.tick(UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (state := hass.states.get(VACUUM_ENTITY_ID))
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_coordinator_update_auth_error(
|
||||
hass: HomeAssistant,
|
||||
mock_account: MagicMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test reauthentication flow is triggered on login error during update."""
|
||||
entry = await setup_integration(hass, mock_account, VACUUM_DOMAIN)
|
||||
|
||||
assert (state := hass.states.get(VACUUM_ENTITY_ID))
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Simulate an authentication error during update
|
||||
mock_account.refresh_robots.side_effect = LitterRobotLoginException(
|
||||
"Invalid credentials"
|
||||
)
|
||||
freezer.tick(UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (state := hass.states.get(VACUUM_ENTITY_ID))
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Ensure a reauthentication flow was triggered
|
||||
flows = hass.config_entries.flow.async_progress()
|
||||
assert len(flows) == 1
|
||||
|
||||
flow = flows[0]
|
||||
assert flow["step_id"] == "reauth_confirm"
|
||||
assert flow["handler"] == DOMAIN
|
||||
assert flow["context"].get("source") == SOURCE_REAUTH
|
||||
assert flow["context"].get("entry_id") == entry.entry_id
|
||||
Reference in New Issue
Block a user