From 1ede67e51f389bbe1442e3e956d1f48ce1a325ac Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Sat, 30 Apr 2022 03:43:13 -0600 Subject: [PATCH] Reflect unavailable state when litter robot hasn't been seen recently (#70810) --- .../components/litterrobot/vacuum.py | 31 +++++++++++-------- tests/components/litterrobot/conftest.py | 10 ++++++ tests/components/litterrobot/test_vacuum.py | 15 ++++++++- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/litterrobot/vacuum.py b/homeassistant/components/litterrobot/vacuum.py index fe20c6bfe50..96908c1fa9d 100644 --- a/homeassistant/components/litterrobot/vacuum.py +++ b/homeassistant/components/litterrobot/vacuum.py @@ -1,6 +1,7 @@ """Support for Litter-Robot "Vacuum".""" from __future__ import annotations +from datetime import datetime, timedelta, timezone import logging from typing import Any @@ -17,7 +18,7 @@ from homeassistant.components.vacuum import ( VacuumEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_OFF +from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -34,6 +35,19 @@ SERVICE_RESET_WASTE_DRAWER = "reset_waste_drawer" SERVICE_SET_SLEEP_MODE = "set_sleep_mode" SERVICE_SET_WAIT_TIME = "set_wait_time" +LITTER_BOX_STATUS_STATE_MAP = { + LitterBoxStatus.CLEAN_CYCLE: STATE_CLEANING, + LitterBoxStatus.EMPTY_CYCLE: STATE_CLEANING, + LitterBoxStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED, + LitterBoxStatus.CAT_SENSOR_TIMING: STATE_DOCKED, + LitterBoxStatus.DRAWER_FULL_1: STATE_DOCKED, + LitterBoxStatus.DRAWER_FULL_2: STATE_DOCKED, + LitterBoxStatus.READY: STATE_DOCKED, + LitterBoxStatus.CAT_SENSOR_INTERRUPTED: STATE_PAUSED, + LitterBoxStatus.OFF: STATE_OFF, +} +UNAVAILABLE_AFTER = timedelta(minutes=30) + async def async_setup_entry( hass: HomeAssistant, @@ -85,19 +99,10 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity): @property def state(self) -> str: """Return the state of the cleaner.""" - switcher = { - LitterBoxStatus.CLEAN_CYCLE: STATE_CLEANING, - LitterBoxStatus.EMPTY_CYCLE: STATE_CLEANING, - LitterBoxStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED, - LitterBoxStatus.CAT_SENSOR_TIMING: STATE_DOCKED, - LitterBoxStatus.DRAWER_FULL_1: STATE_DOCKED, - LitterBoxStatus.DRAWER_FULL_2: STATE_DOCKED, - LitterBoxStatus.READY: STATE_DOCKED, - LitterBoxStatus.CAT_SENSOR_INTERRUPTED: STATE_PAUSED, - LitterBoxStatus.OFF: STATE_OFF, - } + if self.robot.last_seen < datetime.now(timezone.utc) - UNAVAILABLE_AFTER: + return STATE_UNAVAILABLE - return switcher.get(self.robot.status, STATE_ERROR) + return LITTER_BOX_STATUS_STATE_MAP.get(self.robot.status, STATE_ERROR) @property def status(self) -> str: diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index c5355a218af..839ffe2952d 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -1,6 +1,7 @@ """Configure pytest for Litter-Robot tests.""" from __future__ import annotations +from datetime import datetime from typing import Any from unittest.mock import AsyncMock, MagicMock, patch @@ -9,6 +10,7 @@ from pylitterbot.exceptions import InvalidCommandException import pytest from homeassistant.components import litterrobot +from homeassistant.components.litterrobot.vacuum import UNAVAILABLE_AFTER from homeassistant.core import HomeAssistant from .common import CONFIG, ROBOT_DATA @@ -65,6 +67,14 @@ def mock_account_with_sleeping_robot() -> MagicMock: return create_mock_account({"sleepModeActive": "102:00:00"}) +@pytest.fixture +def mock_account_with_robot_not_recently_seen() -> MagicMock: + """Mock a Litter-Robot account with a sleeping robot.""" + return create_mock_account( + {"lastSeen": (datetime.now() - UNAVAILABLE_AFTER).isoformat()} + ) + + @pytest.fixture def mock_account_with_error() -> MagicMock: """Mock a Litter-Robot account with error.""" diff --git a/tests/components/litterrobot/test_vacuum.py b/tests/components/litterrobot/test_vacuum.py index 89f8f077b55..3adf820d6aa 100644 --- a/tests/components/litterrobot/test_vacuum.py +++ b/tests/components/litterrobot/test_vacuum.py @@ -24,7 +24,7 @@ from homeassistant.components.vacuum import ( STATE_DOCKED, STATE_ERROR, ) -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.util.dt import utcnow @@ -62,6 +62,19 @@ async def test_vacuum_status_when_sleeping( assert vacuum.attributes.get(ATTR_STATUS) == "Ready (Sleeping)" +async def test_vacuum_state_when_not_recently_seen( + hass: HomeAssistant, mock_account_with_robot_not_recently_seen: MagicMock +) -> None: + """Tests the vacuum state when not seen recently.""" + await setup_integration( + hass, mock_account_with_robot_not_recently_seen, PLATFORM_DOMAIN + ) + + vacuum = hass.states.get(VACUUM_ENTITY_ID) + assert vacuum + assert vacuum.state == STATE_UNAVAILABLE + + async def test_no_robots( hass: HomeAssistant, mock_account_with_no_robots: MagicMock ) -> None: