From 574667e406dc4a04a44a62e49d049598810a4aef Mon Sep 17 00:00:00 2001 From: Matthew Hadley Date: Mon, 7 Sep 2026 14:17:02 -0500 Subject: [PATCH] Add missing LitterHopper statuses to Whisker hopper status sensor (#181342) --- .../components/litterrobot/icons.json | 6 ++++- .../components/litterrobot/sensor.py | 10 ++++--- .../components/litterrobot/strings.json | 6 ++++- tests/components/litterrobot/conftest.py | 17 ++++++++++++ tests/components/litterrobot/test_sensor.py | 26 ++++++++++++++++--- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/litterrobot/icons.json b/homeassistant/components/litterrobot/icons.json index 231daf2113b3..484568be55ec 100644 --- a/homeassistant/components/litterrobot/icons.json +++ b/homeassistant/components/litterrobot/icons.json @@ -75,9 +75,13 @@ "disabled": "mdi:filter-remove", "empty": "mdi:filter-minus-outline", "enabled": "mdi:filter-check", + "jammed": "mdi:filter-off", + "litter_low": "mdi:filter-minus", "motor_disconnected": "mdi:engine-off", "motor_fault_short": "mdi:flash-off", - "motor_ot_amps": "mdi:flash-alert" + "motor_ot_amps": "mdi:flash-alert", + "offline": "mdi:cloud-off", + "ready": "mdi:filter-check-outline" } }, "next_filter_replacement": { diff --git a/homeassistant/components/litterrobot/sensor.py b/homeassistant/components/litterrobot/sensor.py index ead39b1fd622..a49fc65b1ab8 100644 --- a/homeassistant/components/litterrobot/sensor.py +++ b/homeassistant/components/litterrobot/sensor.py @@ -128,8 +128,8 @@ ROBOT_SENSOR_MAP: dict[ value_fn=lambda robot: robot.cycle_count, ), ], - LitterRobot4: [ - RobotSensorEntityDescription[LitterRobot4]( + (LitterRobot4, LitterRobot5): [ + RobotSensorEntityDescription[LitterRobot4 | LitterRobot5]( key="hopper_status", translation_key="hopper_status", device_class=SensorDeviceClass.ENUM, @@ -140,6 +140,10 @@ ROBOT_SENSOR_MAP: dict[ "motor_ot_amps", "motor_disconnected", "empty", + "litter_low", + "ready", + "jammed", + "offline", ], value_fn=( lambda robot: ( @@ -147,8 +151,6 @@ ROBOT_SENSOR_MAP: dict[ ) ), ), - ], - (LitterRobot4, LitterRobot5): [ RobotSensorEntityDescription[LitterRobot4 | LitterRobot5]( key="litter_level", translation_key="litter_level", diff --git a/homeassistant/components/litterrobot/strings.json b/homeassistant/components/litterrobot/strings.json index 0aaf12435041..9b0774d0afc7 100644 --- a/homeassistant/components/litterrobot/strings.json +++ b/homeassistant/components/litterrobot/strings.json @@ -127,9 +127,13 @@ "disabled": "[%key:common::state::disabled%]", "empty": "[%key:common::state::empty%]", "enabled": "[%key:common::state::enabled%]", + "jammed": "Jammed", + "litter_low": "Litter low", "motor_disconnected": "Motor disconnected", "motor_fault_short": "Motor shorted", - "motor_ot_amps": "Motor overtorqued" + "motor_ot_amps": "Motor overtorqued", + "offline": "[%key:component::litterrobot::entity::sensor::status_code::state::offline%]", + "ready": "[%key:component::litterrobot::entity::sensor::status_code::state::rdy%]" } }, "last_feeding": { diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index ffa80a5a3186..fc75ca374287 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -154,6 +154,23 @@ def mock_account_with_litterhopper() -> MagicMock: ) +@pytest.fixture +def mock_account_with_litterhopper_5() -> MagicMock: + """Mock account with LitterHopper attached to Litter-Robot 5.""" + return create_mock_account( + robot_data={ + "state": { + **ROBOT_5_DATA["state"], + "hopperStatusIndicator": { + "title": "Litter low", + "value": HopperStatus.LITTER_LOW.value, + }, + } + }, + v5=True, + ) + + @pytest.fixture def mock_account_with_feederrobot() -> MagicMock: """Mock account with Feeder-Robot.""" diff --git a/tests/components/litterrobot/test_sensor.py b/tests/components/litterrobot/test_sensor.py index 354431531ccc..f6c7fb18daca 100644 --- a/tests/components/litterrobot/test_sensor.py +++ b/tests/components/litterrobot/test_sensor.py @@ -2,10 +2,12 @@ from unittest.mock import MagicMock +from pylitterbot.robot.litterrobot4 import HopperStatus import pytest from homeassistant.components.litterrobot.sensor import icon_for_gauge_level from homeassistant.components.sensor import ( + ATTR_OPTIONS, DOMAIN as SENSOR_DOMAIN, SensorDeviceClass, SensorStateClass, @@ -147,13 +149,31 @@ async def test_pet_visits_today_sensor( assert sensor.state == "2" +@pytest.mark.parametrize( + ("account_fixture", "expected_state"), + [ + pytest.param("mock_account_with_litterhopper", "enabled", id="litter_robot_4"), + pytest.param( + "mock_account_with_litterhopper_5", "litter_low", id="litter_robot_5" + ), + ], +) async def test_litterhopper_sensor( - hass: HomeAssistant, mock_account_with_litterhopper: MagicMock + hass: HomeAssistant, + request: pytest.FixtureRequest, + account_fixture: str, + expected_state: str, ) -> None: """Tests LitterHopper sensors.""" - await setup_integration(hass, mock_account_with_litterhopper, SENSOR_DOMAIN) + await setup_integration( + hass, request.getfixturevalue(account_fixture), SENSOR_DOMAIN + ) sensor = hass.states.get("sensor.test_hopper_status") - assert sensor.state == "enabled" + assert sensor.state == expected_state + # a status the library can report but the sensor does not declare is invalid + assert {status.name.lower() for status in HopperStatus} <= set( + sensor.attributes[ATTR_OPTIONS] + ) @pytest.mark.usefixtures("entity_registry_enabled_by_default")