Skip to content

Commit 2829cc1

Browse files
authored
Add visits today sensor for pets (home-assistant#147459)
1 parent 8881919 commit 2829cc1

File tree

7 files changed

+59
-2
lines changed

7 files changed

+59
-2
lines changed

homeassistant/components/litterrobot/coordinator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ async def _async_update_data(self) -> None:
4848
"""Update all device states from the Litter-Robot API."""
4949
await self.account.refresh_robots()
5050
await self.account.load_pets()
51+
for pet in self.account.pets:
52+
# Need to fetch weight history for `get_visits_since`
53+
await pet.fetch_weight_history()
5154

5255
async def _async_setup(self) -> None:
5356
"""Set up the coordinator."""

homeassistant/components/litterrobot/icons.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
},
5050
"total_cycles": {
5151
"default": "mdi:counter"
52+
},
53+
"visits_today": {
54+
"default": "mdi:counter"
5255
}
5356
},
5457
"switch": {

homeassistant/components/litterrobot/sensor.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfMass
1919
from homeassistant.core import HomeAssistant
2020
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
21+
from homeassistant.util import dt as dt_util
2122

2223
from .coordinator import LitterRobotConfigEntry
2324
from .entity import LitterRobotEntity, _WhiskerEntityT
@@ -39,6 +40,7 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti
3940
"""A class that describes robot sensor entities."""
4041

4142
icon_fn: Callable[[Any], str | None] = lambda _: None
43+
last_reset_fn: Callable[[], datetime | None] = lambda: None
4244
value_fn: Callable[[_WhiskerEntityT], float | datetime | str | None]
4345

4446

@@ -179,7 +181,14 @@ class RobotSensorEntityDescription(SensorEntityDescription, Generic[_WhiskerEnti
179181
native_unit_of_measurement=UnitOfMass.POUNDS,
180182
state_class=SensorStateClass.MEASUREMENT,
181183
value_fn=lambda pet: pet.weight,
182-
)
184+
),
185+
RobotSensorEntityDescription[Pet](
186+
key="visits_today",
187+
translation_key="visits_today",
188+
state_class=SensorStateClass.TOTAL,
189+
last_reset_fn=dt_util.start_of_local_day,
190+
value_fn=lambda pet: pet.get_visits_since(dt_util.start_of_local_day()),
191+
),
183192
]
184193

185194

@@ -225,3 +234,8 @@ def icon(self) -> str | None:
225234
if (icon := self.entity_description.icon_fn(self.state)) is not None:
226235
return icon
227236
return super().icon
237+
238+
@property
239+
def last_reset(self) -> datetime | None:
240+
"""Return the time when the sensor was last reset, if any."""
241+
return self.entity_description.last_reset_fn() or super().last_reset

homeassistant/components/litterrobot/strings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@
122122
"name": "Total cycles",
123123
"unit_of_measurement": "cycles"
124124
},
125+
"visits_today": {
126+
"name": "Visits today",
127+
"unit_of_measurement": "visits"
128+
},
125129
"waste_drawer": {
126130
"name": "Waste drawer"
127131
}

tests/components/litterrobot/common.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@
159159
"gender": "FEMALE",
160160
"lastWeightReading": 9.1,
161161
"breeds": ["sphynx"],
162+
"weightHistory": [
163+
{"weight": 6.48, "timestamp": "2025-06-13T16:12:36"},
164+
{"weight": 6.6, "timestamp": "2025-06-14T03:52:00"},
165+
{"weight": 6.59, "timestamp": "2025-06-14T17:20:32"},
166+
{"weight": 6.5, "timestamp": "2025-06-14T19:22:48"},
167+
{"weight": 6.35, "timestamp": "2025-06-15T03:12:15"},
168+
{"weight": 6.45, "timestamp": "2025-06-15T15:27:21"},
169+
{"weight": 6.25, "timestamp": "2025-06-15T15:29:26"},
170+
],
162171
}
163172

164173
VACUUM_ENTITY_ID = "vacuum.test_litter_box"

tests/components/litterrobot/conftest.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ def create_mock_robot(
5252
return robot
5353

5454

55+
def create_mock_pet(
56+
pet_data: dict | None,
57+
account: Account,
58+
side_effect: Any | None = None,
59+
) -> Pet:
60+
"""Create a mock Pet."""
61+
if not pet_data:
62+
pet_data = {}
63+
64+
pet = Pet(data={**PET_DATA, **pet_data}, session=account.session)
65+
pet.fetch_weight_history = AsyncMock(side_effect=side_effect)
66+
return pet
67+
68+
5569
def create_mock_account(
5670
robot_data: dict | None = None,
5771
side_effect: Any | None = None,
@@ -69,7 +83,7 @@ def create_mock_account(
6983
if skip_robots
7084
else [create_mock_robot(robot_data, account, v4, feeder, side_effect)]
7185
)
72-
account.pets = [Pet(PET_DATA, account.session)] if pet else []
86+
account.pets = [create_mock_pet(PET_DATA, account, side_effect)] if pet else []
7387
return account
7488

7589

tests/components/litterrobot/test_sensor.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ async def test_pet_weight_sensor(
124124
assert sensor.attributes["unit_of_measurement"] == UnitOfMass.POUNDS
125125

126126

127+
@pytest.mark.freeze_time("2025-06-15 12:00:00+00:00")
128+
async def test_pet_visits_today_sensor(
129+
hass: HomeAssistant, mock_account_with_pet: MagicMock
130+
) -> None:
131+
"""Tests pet visits today sensors."""
132+
await setup_integration(hass, mock_account_with_pet, PLATFORM_DOMAIN)
133+
sensor = hass.states.get("sensor.kitty_visits_today")
134+
assert sensor.state == "2"
135+
136+
127137
async def test_litterhopper_sensor(
128138
hass: HomeAssistant, mock_account_with_litterhopper: MagicMock
129139
) -> None:

0 commit comments

Comments
 (0)