mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Expose LitterHopper status for LR4 (#143684)
* Expose LitterHopper status for LR4 * Proper naming and icons * Add simple tests * fix test: lowercase enabled * over-torque, not OT * Don't use icon_fn for simple state map * short not Short * Better state names
This commit is contained in:
parent
30656a4e72
commit
e05f7a9633
@ -6,7 +6,7 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Generic
|
from typing import Generic
|
||||||
|
|
||||||
from pylitterbot import LitterRobot, Robot
|
from pylitterbot import LitterRobot, LitterRobot4, Robot
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorDeviceClass,
|
BinarySensorDeviceClass,
|
||||||
@ -47,6 +47,15 @@ BINARY_SENSOR_MAP: dict[type[Robot], tuple[RobotBinarySensorEntityDescription, .
|
|||||||
is_on_fn=lambda robot: robot.sleep_mode_enabled,
|
is_on_fn=lambda robot: robot.sleep_mode_enabled,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
LitterRobot4: (
|
||||||
|
RobotBinarySensorEntityDescription[LitterRobot4](
|
||||||
|
key="hopper_connected",
|
||||||
|
translation_key="hopper_connected",
|
||||||
|
device_class=BinarySensorDeviceClass.CONNECTIVITY,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
is_on_fn=lambda robot: not robot.is_hopper_removed,
|
||||||
|
),
|
||||||
|
),
|
||||||
Robot: ( # type: ignore[type-abstract] # only used for isinstance check
|
Robot: ( # type: ignore[type-abstract] # only used for isinstance check
|
||||||
RobotBinarySensorEntityDescription[Robot](
|
RobotBinarySensorEntityDescription[Robot](
|
||||||
key="power_status",
|
key="power_status",
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
},
|
},
|
||||||
"sleep_mode": {
|
"sleep_mode": {
|
||||||
"default": "mdi:sleep"
|
"default": "mdi:sleep"
|
||||||
|
},
|
||||||
|
"hopper_connected": {
|
||||||
|
"default": "mdi:filter-check"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
@ -32,6 +35,19 @@
|
|||||||
"default": "mdi:scale"
|
"default": "mdi:scale"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sensor": {
|
||||||
|
"hopper_status": {
|
||||||
|
"default": "mdi:filter",
|
||||||
|
"state": {
|
||||||
|
"disabled": "mdi:filter-remove",
|
||||||
|
"empty": "mdi:filter-minus-outline",
|
||||||
|
"enabled": "mdi:filter-check",
|
||||||
|
"motor_disconnected": "mdi:engine-off",
|
||||||
|
"motor_fault_short": "mdi:flash-off",
|
||||||
|
"motor_ot_amps": "mdi:flash-alert"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
"night_light_mode": {
|
"night_light_mode": {
|
||||||
"default": "mdi:lightbulb-off",
|
"default": "mdi:lightbulb-off",
|
||||||
|
@ -57,9 +57,9 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = {
|
|||||||
translation_key="sleep_mode_start_time",
|
translation_key="sleep_mode_start_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
value_fn=(
|
value_fn=(
|
||||||
lambda robot: robot.sleep_mode_start_time
|
lambda robot: (
|
||||||
if robot.sleep_mode_enabled
|
robot.sleep_mode_start_time if robot.sleep_mode_enabled else None
|
||||||
else None
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
RobotSensorEntityDescription[LitterRobot](
|
RobotSensorEntityDescription[LitterRobot](
|
||||||
@ -67,9 +67,9 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = {
|
|||||||
translation_key="sleep_mode_end_time",
|
translation_key="sleep_mode_end_time",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
value_fn=(
|
value_fn=(
|
||||||
lambda robot: robot.sleep_mode_end_time
|
lambda robot: (
|
||||||
if robot.sleep_mode_enabled
|
robot.sleep_mode_end_time if robot.sleep_mode_enabled else None
|
||||||
else None
|
)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
RobotSensorEntityDescription[LitterRobot](
|
RobotSensorEntityDescription[LitterRobot](
|
||||||
@ -117,6 +117,24 @@ ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
LitterRobot4: [
|
LitterRobot4: [
|
||||||
|
RobotSensorEntityDescription[LitterRobot4](
|
||||||
|
key="hopper_status",
|
||||||
|
translation_key="hopper_status",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
options=[
|
||||||
|
"enabled",
|
||||||
|
"disabled",
|
||||||
|
"motor_fault_short",
|
||||||
|
"motor_ot_amps",
|
||||||
|
"motor_disconnected",
|
||||||
|
"empty",
|
||||||
|
],
|
||||||
|
value_fn=(
|
||||||
|
lambda robot: (
|
||||||
|
status.name.lower() if (status := robot.hopper_status) else None
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
RobotSensorEntityDescription[LitterRobot4](
|
RobotSensorEntityDescription[LitterRobot4](
|
||||||
key="litter_level",
|
key="litter_level",
|
||||||
translation_key="litter_level",
|
translation_key="litter_level",
|
||||||
|
@ -34,6 +34,9 @@
|
|||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"binary_sensor": {
|
"binary_sensor": {
|
||||||
|
"hopper_connected": {
|
||||||
|
"name": "Hopper connected"
|
||||||
|
},
|
||||||
"sleeping": {
|
"sleeping": {
|
||||||
"name": "Sleeping"
|
"name": "Sleeping"
|
||||||
},
|
},
|
||||||
@ -59,6 +62,17 @@
|
|||||||
"food_level": {
|
"food_level": {
|
||||||
"name": "Food level"
|
"name": "Food level"
|
||||||
},
|
},
|
||||||
|
"hopper_status": {
|
||||||
|
"name": "Hopper status",
|
||||||
|
"state": {
|
||||||
|
"enabled": "[%key:common::state::enabled%]",
|
||||||
|
"disabled": "[%key:common::state::disabled%]",
|
||||||
|
"motor_fault_short": "Motor shorted",
|
||||||
|
"motor_ot_amps": "Motor overtorqued",
|
||||||
|
"motor_disconnected": "Motor disconnected",
|
||||||
|
"empty": "Empty"
|
||||||
|
}
|
||||||
|
},
|
||||||
"last_seen": {
|
"last_seen": {
|
||||||
"name": "Last seen"
|
"name": "Last seen"
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||||||
|
|
||||||
from pylitterbot import Account, FeederRobot, LitterRobot3, LitterRobot4, Pet, Robot
|
from pylitterbot import Account, FeederRobot, LitterRobot3, LitterRobot4, Pet, Robot
|
||||||
from pylitterbot.exceptions import InvalidCommandException
|
from pylitterbot.exceptions import InvalidCommandException
|
||||||
|
from pylitterbot.robot.litterrobot4 import HopperStatus
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -84,6 +85,15 @@ def mock_account_with_litterrobot_4() -> MagicMock:
|
|||||||
return create_mock_account(v4=True)
|
return create_mock_account(v4=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_account_with_litterhopper() -> MagicMock:
|
||||||
|
"""Mock account with LitterHopper attached to Litter-Robot 4."""
|
||||||
|
return create_mock_account(
|
||||||
|
robot_data={"hopperStatus": HopperStatus.ENABLED, "isHopperRemoved": False},
|
||||||
|
v4=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_account_with_feederrobot() -> MagicMock:
|
def mock_account_with_feederrobot() -> MagicMock:
|
||||||
"""Mock account with Feeder-Robot."""
|
"""Mock account with Feeder-Robot."""
|
||||||
|
@ -30,3 +30,18 @@ async def test_binary_sensors(
|
|||||||
state = hass.states.get("binary_sensor.test_power_status")
|
state = hass.states.get("binary_sensor.test_power_status")
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.PLUG
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.PLUG
|
||||||
assert state.state == "on"
|
assert state.state == "on"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
async def test_litterhopper_binary_sensors(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_account_with_litterhopper: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Tests LitterHopper-specific binary sensors."""
|
||||||
|
await setup_integration(hass, mock_account_with_litterhopper, BINARY_SENSOR_DOMAIN)
|
||||||
|
|
||||||
|
state = hass.states.get("binary_sensor.test_hopper_connected")
|
||||||
|
assert state.state == "on"
|
||||||
|
assert (
|
||||||
|
state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.CONNECTIVITY
|
||||||
|
)
|
||||||
|
@ -114,3 +114,12 @@ async def test_pet_weight_sensor(
|
|||||||
sensor = hass.states.get("sensor.kitty_weight")
|
sensor = hass.states.get("sensor.kitty_weight")
|
||||||
assert sensor.state == "9.1"
|
assert sensor.state == "9.1"
|
||||||
assert sensor.attributes["unit_of_measurement"] == UnitOfMass.POUNDS
|
assert sensor.attributes["unit_of_measurement"] == UnitOfMass.POUNDS
|
||||||
|
|
||||||
|
|
||||||
|
async def test_litterhopper_sensor(
|
||||||
|
hass: HomeAssistant, mock_account_with_litterhopper: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Tests LitterHopper sensors."""
|
||||||
|
await setup_integration(hass, mock_account_with_litterhopper, PLATFORM_DOMAIN)
|
||||||
|
sensor = hass.states.get("sensor.test_hopper_status")
|
||||||
|
assert sensor.state == "enabled"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user