Add tests for LitterRobot sensors (#78638)
This commit is contained in:
parent
9655f30146
commit
aa0cbf0afe
3 changed files with 191 additions and 9 deletions
|
@ -1,6 +1,4 @@
|
||||||
"""Common utils for Litter-Robot tests."""
|
"""Common utils for Litter-Robot tests."""
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from homeassistant.components.litterrobot import DOMAIN
|
from homeassistant.components.litterrobot import DOMAIN
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||||
|
|
||||||
|
@ -11,7 +9,7 @@ ROBOT_NAME = "Test"
|
||||||
ROBOT_SERIAL = "LR3C012345"
|
ROBOT_SERIAL = "LR3C012345"
|
||||||
ROBOT_DATA = {
|
ROBOT_DATA = {
|
||||||
"powerStatus": "AC",
|
"powerStatus": "AC",
|
||||||
"lastSeen": datetime.now().isoformat(),
|
"lastSeen": "2022-09-17T13:06:37.884Z",
|
||||||
"cleanCycleWaitTimeMinutes": "7",
|
"cleanCycleWaitTimeMinutes": "7",
|
||||||
"unitStatus": "RDY",
|
"unitStatus": "RDY",
|
||||||
"litterRobotNickname": ROBOT_NAME,
|
"litterRobotNickname": ROBOT_NAME,
|
||||||
|
@ -24,5 +22,123 @@ ROBOT_DATA = {
|
||||||
"nightLightActive": "1",
|
"nightLightActive": "1",
|
||||||
"sleepModeActive": "112:50:19",
|
"sleepModeActive": "112:50:19",
|
||||||
}
|
}
|
||||||
|
ROBOT_4_DATA = {
|
||||||
|
"name": ROBOT_NAME,
|
||||||
|
"serial": "LR4C010001",
|
||||||
|
"userId": "1234567",
|
||||||
|
"espFirmware": "1.1.50",
|
||||||
|
"picFirmwareVersion": "10512.2560.2.53",
|
||||||
|
"laserBoardFirmwareVersion": "4.0.65.4",
|
||||||
|
"wifiRssi": -53.0,
|
||||||
|
"unitPowerType": "AC",
|
||||||
|
"catWeight": 12.0,
|
||||||
|
"unitTimezone": "America/New_York",
|
||||||
|
"unitTime": None,
|
||||||
|
"cleanCycleWaitTime": 15,
|
||||||
|
"isKeypadLockout": False,
|
||||||
|
"nightLightMode": "OFF",
|
||||||
|
"nightLightBrightness": 85,
|
||||||
|
"isPanelSleepMode": False,
|
||||||
|
"panelSleepTime": 0,
|
||||||
|
"panelWakeTime": 0,
|
||||||
|
"weekdaySleepModeEnabled": {
|
||||||
|
"Sunday": {"sleepTime": 0, "wakeTime": 0, "isEnabled": False},
|
||||||
|
"Monday": {"sleepTime": 0, "wakeTime": 180, "isEnabled": True},
|
||||||
|
"Tuesday": {"sleepTime": 0, "wakeTime": 180, "isEnabled": True},
|
||||||
|
"Wednesday": {"sleepTime": 0, "wakeTime": 180, "isEnabled": True},
|
||||||
|
"Thursday": {"sleepTime": 0, "wakeTime": 180, "isEnabled": True},
|
||||||
|
"Friday": {"sleepTime": 0, "wakeTime": 180, "isEnabled": True},
|
||||||
|
"Saturday": {"sleepTime": 0, "wakeTime": 0, "isEnabled": False},
|
||||||
|
},
|
||||||
|
"unitPowerStatus": "ON",
|
||||||
|
"sleepStatus": "WAKE",
|
||||||
|
"robotStatus": "ROBOT_IDLE",
|
||||||
|
"globeMotorFaultStatus": "FAULT_CLEAR",
|
||||||
|
"pinchStatus": "CLEAR",
|
||||||
|
"catDetect": "CAT_DETECT_CLEAR",
|
||||||
|
"isBonnetRemoved": False,
|
||||||
|
"isNightLightLEDOn": False,
|
||||||
|
"odometerPowerCycles": 8,
|
||||||
|
"odometerCleanCycles": 158,
|
||||||
|
"odometerEmptyCycles": 1,
|
||||||
|
"odometerFilterCycles": 0,
|
||||||
|
"isDFIResetPending": False,
|
||||||
|
"DFINumberOfCycles": 104,
|
||||||
|
"DFILevelPercent": 76,
|
||||||
|
"isDFIFull": True,
|
||||||
|
"DFIFullCounter": 3,
|
||||||
|
"DFITriggerCount": 42,
|
||||||
|
"litterLevel": 460,
|
||||||
|
"DFILevelMM": 115,
|
||||||
|
"isCatDetectPending": False,
|
||||||
|
"globeMotorRetractFaultStatus": "FAULT_CLEAR",
|
||||||
|
"robotCycleStatus": "CYCLE_IDLE",
|
||||||
|
"robotCycleState": "CYCLE_STATE_WAIT_ON",
|
||||||
|
"weightSensor": -3.0,
|
||||||
|
"isOnline": True,
|
||||||
|
"isOnboarded": True,
|
||||||
|
"isProvisioned": True,
|
||||||
|
"isDebugModeActive": False,
|
||||||
|
"lastSeen": "2022-09-17T12:06:37.884Z",
|
||||||
|
"sessionId": "abcdef12-e358-4b6c-9022-012345678912",
|
||||||
|
"setupDateTime": "2022-08-28T17:01:12.644Z",
|
||||||
|
"isFirmwareUpdateTriggered": False,
|
||||||
|
"firmwareUpdateStatus": "NONE",
|
||||||
|
"wifiModeStatus": "ROUTER_CONNECTED",
|
||||||
|
"isUSBPowerOn": True,
|
||||||
|
"USBFaultStatus": "CLEAR",
|
||||||
|
"isDFIPartialFull": True,
|
||||||
|
}
|
||||||
|
FEEDER_ROBOT_DATA = {
|
||||||
|
"id": 1,
|
||||||
|
"name": ROBOT_NAME,
|
||||||
|
"serial": "RF1C000001",
|
||||||
|
"timezone": "America/Denver",
|
||||||
|
"isEighthCupEnabled": False,
|
||||||
|
"created_at": "2021-12-15T06:45:00.000000+00:00",
|
||||||
|
"household_id": 1,
|
||||||
|
"state": {
|
||||||
|
"id": 1,
|
||||||
|
"info": {
|
||||||
|
"level": 2,
|
||||||
|
"power": True,
|
||||||
|
"online": True,
|
||||||
|
"acPower": True,
|
||||||
|
"dcPower": False,
|
||||||
|
"gravity": False,
|
||||||
|
"chuteFull": False,
|
||||||
|
"fwVersion": "1.0.0",
|
||||||
|
"onBoarded": True,
|
||||||
|
"unitMeals": 0,
|
||||||
|
"motorJammed": False,
|
||||||
|
"chuteFullExt": False,
|
||||||
|
"panelLockout": False,
|
||||||
|
"unitPortions": 0,
|
||||||
|
"autoNightMode": True,
|
||||||
|
"mealInsertSize": 1,
|
||||||
|
},
|
||||||
|
"updated_at": "2022-09-08T15:07:00.000000+00:00",
|
||||||
|
},
|
||||||
|
"feeding_snack": [
|
||||||
|
{"timestamp": "2022-09-04T03:03:00.000000+00:00", "amount": 0.125},
|
||||||
|
{"timestamp": "2022-08-30T16:34:00.000000+00:00", "amount": 0.25},
|
||||||
|
],
|
||||||
|
"feeding_meal": [
|
||||||
|
{
|
||||||
|
"timestamp": "2022-09-08T18:00:00.000000+00:00",
|
||||||
|
"amount": 0.125,
|
||||||
|
"meal_name": "Lunch",
|
||||||
|
"meal_number": 2,
|
||||||
|
"meal_total_portions": 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-09-08T12:00:00.000000+00:00",
|
||||||
|
"amount": 0.125,
|
||||||
|
"meal_name": "Breakfast",
|
||||||
|
"meal_number": 1,
|
||||||
|
"meal_total_portions": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
VACUUM_ENTITY_ID = "vacuum.test_litter_box"
|
VACUUM_ENTITY_ID = "vacuum.test_litter_box"
|
||||||
|
|
|
@ -4,26 +4,35 @@ from __future__ import annotations
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
from pylitterbot import Account, LitterRobot3, Robot
|
from pylitterbot import Account, FeederRobot, LitterRobot3, LitterRobot4, Robot
|
||||||
from pylitterbot.exceptions import InvalidCommandException
|
from pylitterbot.exceptions import InvalidCommandException
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import litterrobot
|
from homeassistant.components import litterrobot
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .common import CONFIG, ROBOT_DATA
|
from .common import CONFIG, FEEDER_ROBOT_DATA, ROBOT_4_DATA, ROBOT_DATA
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
def create_mock_robot(
|
def create_mock_robot(
|
||||||
robot_data: dict | None, account: Account, side_effect: Any | None = None
|
robot_data: dict | None,
|
||||||
|
account: Account,
|
||||||
|
v4: bool,
|
||||||
|
feeder: bool,
|
||||||
|
side_effect: Any | None = None,
|
||||||
) -> Robot:
|
) -> Robot:
|
||||||
"""Create a mock Litter-Robot device."""
|
"""Create a mock Litter-Robot device."""
|
||||||
if not robot_data:
|
if not robot_data:
|
||||||
robot_data = {}
|
robot_data = {}
|
||||||
|
|
||||||
robot = LitterRobot3(data={**ROBOT_DATA, **robot_data}, account=account)
|
if v4:
|
||||||
|
robot = LitterRobot4(data={**ROBOT_4_DATA, **robot_data}, account=account)
|
||||||
|
elif feeder:
|
||||||
|
robot = FeederRobot(data={**FEEDER_ROBOT_DATA, **robot_data}, account=account)
|
||||||
|
else:
|
||||||
|
robot = LitterRobot3(data={**ROBOT_DATA, **robot_data}, account=account)
|
||||||
robot.start_cleaning = AsyncMock(side_effect=side_effect)
|
robot.start_cleaning = AsyncMock(side_effect=side_effect)
|
||||||
robot.set_power_status = AsyncMock(side_effect=side_effect)
|
robot.set_power_status = AsyncMock(side_effect=side_effect)
|
||||||
robot.reset_waste_drawer = AsyncMock(side_effect=side_effect)
|
robot.reset_waste_drawer = AsyncMock(side_effect=side_effect)
|
||||||
|
@ -39,13 +48,17 @@ def create_mock_account(
|
||||||
robot_data: dict | None = None,
|
robot_data: dict | None = None,
|
||||||
side_effect: Any | None = None,
|
side_effect: Any | None = None,
|
||||||
skip_robots: bool = False,
|
skip_robots: bool = False,
|
||||||
|
v4: bool = False,
|
||||||
|
feeder: bool = False,
|
||||||
) -> MagicMock:
|
) -> MagicMock:
|
||||||
"""Create a mock Litter-Robot account."""
|
"""Create a mock Litter-Robot account."""
|
||||||
account = MagicMock(spec=Account)
|
account = MagicMock(spec=Account)
|
||||||
account.connect = AsyncMock()
|
account.connect = AsyncMock()
|
||||||
account.refresh_robots = AsyncMock()
|
account.refresh_robots = AsyncMock()
|
||||||
account.robots = (
|
account.robots = (
|
||||||
[] if skip_robots else [create_mock_robot(robot_data, account, side_effect)]
|
[]
|
||||||
|
if skip_robots
|
||||||
|
else [create_mock_robot(robot_data, account, v4, feeder, side_effect)]
|
||||||
)
|
)
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
@ -56,6 +69,18 @@ def mock_account() -> MagicMock:
|
||||||
return create_mock_account()
|
return create_mock_account()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_account_with_litterrobot_4() -> MagicMock:
|
||||||
|
"""Mock account with Litter-Robot 4."""
|
||||||
|
return create_mock_account(v4=True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_account_with_feederrobot() -> MagicMock:
|
||||||
|
"""Mock account with Feeder-Robot."""
|
||||||
|
return create_mock_account(feeder=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_account_with_no_robots() -> MagicMock:
|
def mock_account_with_no_robots() -> MagicMock:
|
||||||
"""Mock a Litter-Robot account."""
|
"""Mock a Litter-Robot account."""
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from homeassistant.components.sensor import DOMAIN as PLATFORM_DOMAIN, SensorDeviceClass
|
from homeassistant.components.sensor import DOMAIN as PLATFORM_DOMAIN, SensorDeviceClass
|
||||||
from homeassistant.const import PERCENTAGE, STATE_UNKNOWN
|
from homeassistant.const import MASS_POUNDS, PERCENTAGE, STATE_UNKNOWN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .conftest import setup_integration
|
from .conftest import setup_integration
|
||||||
|
|
||||||
WASTE_DRAWER_ENTITY_ID = "sensor.test_waste_drawer"
|
WASTE_DRAWER_ENTITY_ID = "sensor.test_waste_drawer"
|
||||||
|
SLEEP_END_TIME_ENTITY_ID = "sensor.test_sleep_mode_end_time"
|
||||||
SLEEP_START_TIME_ENTITY_ID = "sensor.test_sleep_mode_start_time"
|
SLEEP_START_TIME_ENTITY_ID = "sensor.test_sleep_mode_start_time"
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,6 +37,10 @@ async def test_sleep_time_sensor_with_sleep_disabled(
|
||||||
assert sensor.state == STATE_UNKNOWN
|
assert sensor.state == STATE_UNKNOWN
|
||||||
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
||||||
|
|
||||||
|
sensor = hass.states.get(SLEEP_END_TIME_ENTITY_ID)
|
||||||
|
assert sensor.state == STATE_UNKNOWN
|
||||||
|
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
||||||
|
|
||||||
|
|
||||||
async def test_gauge_icon() -> None:
|
async def test_gauge_icon() -> None:
|
||||||
"""Test icon generator for gauge sensor."""
|
"""Test icon generator for gauge sensor."""
|
||||||
|
@ -59,3 +64,39 @@ async def test_gauge_icon() -> None:
|
||||||
assert icon_for_gauge_level(40, 10) == GAUGE_LOW
|
assert icon_for_gauge_level(40, 10) == GAUGE_LOW
|
||||||
assert icon_for_gauge_level(80, 10) == GAUGE
|
assert icon_for_gauge_level(80, 10) == GAUGE
|
||||||
assert icon_for_gauge_level(100, 10) == GAUGE_FULL
|
assert icon_for_gauge_level(100, 10) == GAUGE_FULL
|
||||||
|
|
||||||
|
|
||||||
|
async def test_litter_robot_sensor(
|
||||||
|
hass: HomeAssistant, mock_account_with_litterrobot_4: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Tests Litter-Robot sensors."""
|
||||||
|
await setup_integration(hass, mock_account_with_litterrobot_4, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
|
sensor = hass.states.get(SLEEP_START_TIME_ENTITY_ID)
|
||||||
|
assert sensor.state == "2022-09-19T04:00:00+00:00"
|
||||||
|
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
||||||
|
sensor = hass.states.get(SLEEP_END_TIME_ENTITY_ID)
|
||||||
|
assert sensor.state == "2022-09-16T07:00:00+00:00"
|
||||||
|
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
||||||
|
sensor = hass.states.get("sensor.test_last_seen")
|
||||||
|
assert sensor.state == "2022-09-17T12:06:37+00:00"
|
||||||
|
assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP
|
||||||
|
sensor = hass.states.get("sensor.test_status_code")
|
||||||
|
assert sensor.state == "dfs"
|
||||||
|
assert sensor.attributes["device_class"] == "litterrobot__status_code"
|
||||||
|
sensor = hass.states.get("sensor.test_litter_level")
|
||||||
|
assert sensor.state == "70.0"
|
||||||
|
assert sensor.attributes["unit_of_measurement"] == PERCENTAGE
|
||||||
|
sensor = hass.states.get("sensor.test_pet_weight")
|
||||||
|
assert sensor.state == "12.0"
|
||||||
|
assert sensor.attributes["unit_of_measurement"] == MASS_POUNDS
|
||||||
|
|
||||||
|
|
||||||
|
async def test_feeder_robot_sensor(
|
||||||
|
hass: HomeAssistant, mock_account_with_feederrobot: MagicMock
|
||||||
|
) -> None:
|
||||||
|
"""Tests Feeder-Robot sensors."""
|
||||||
|
await setup_integration(hass, mock_account_with_feederrobot, PLATFORM_DOMAIN)
|
||||||
|
sensor = hass.states.get("sensor.test_food_level")
|
||||||
|
assert sensor.state == "20"
|
||||||
|
assert sensor.attributes["unit_of_measurement"] == PERCENTAGE
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue