mirror of
https://github.com/home-assistant/core.git
synced 2025-11-14 21:40:16 +00:00
Add globe light settings for Litter-Robot 4 (#152190)
This commit is contained in:
@@ -31,6 +31,21 @@
|
|||||||
"cycle_delay": {
|
"cycle_delay": {
|
||||||
"default": "mdi:timer-outline"
|
"default": "mdi:timer-outline"
|
||||||
},
|
},
|
||||||
|
"globe_brightness": {
|
||||||
|
"default": "mdi:lightbulb-question",
|
||||||
|
"state": {
|
||||||
|
"low": "mdi:lightbulb-on-30",
|
||||||
|
"medium": "mdi:lightbulb-on-50",
|
||||||
|
"high": "mdi:lightbulb-on"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globe_light": {
|
||||||
|
"state": {
|
||||||
|
"off": "mdi:lightbulb-off",
|
||||||
|
"on": "mdi:lightbulb-on",
|
||||||
|
"auto": "mdi:lightbulb-auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
"meal_insert_size": {
|
"meal_insert_size": {
|
||||||
"default": "mdi:scale"
|
"default": "mdi:scale"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from dataclasses import dataclass
|
|||||||
from typing import Any, Generic, TypeVar
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
from pylitterbot import FeederRobot, LitterRobot, LitterRobot4, Robot
|
from pylitterbot import FeederRobot, LitterRobot, LitterRobot4, Robot
|
||||||
from pylitterbot.robot.litterrobot4 import BrightnessLevel
|
from pylitterbot.robot.litterrobot4 import BrightnessLevel, NightLightMode
|
||||||
|
|
||||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||||
from homeassistant.const import EntityCategory, UnitOfTime
|
from homeassistant.const import EntityCategory, UnitOfTime
|
||||||
@@ -32,35 +32,73 @@ class RobotSelectEntityDescription(
|
|||||||
select_fn: Callable[[_WhiskerEntityT, str], Coroutine[Any, Any, bool]]
|
select_fn: Callable[[_WhiskerEntityT, str], Coroutine[Any, Any, bool]]
|
||||||
|
|
||||||
|
|
||||||
ROBOT_SELECT_MAP: dict[type[Robot], RobotSelectEntityDescription] = {
|
ROBOT_SELECT_MAP: dict[type[Robot], tuple[RobotSelectEntityDescription, ...]] = {
|
||||||
LitterRobot: RobotSelectEntityDescription[LitterRobot, int]( # type: ignore[type-abstract] # only used for isinstance check
|
LitterRobot: (
|
||||||
key="cycle_delay",
|
RobotSelectEntityDescription[LitterRobot, int]( # type: ignore[type-abstract] # only used for isinstance check
|
||||||
translation_key="cycle_delay",
|
key="cycle_delay",
|
||||||
unit_of_measurement=UnitOfTime.MINUTES,
|
translation_key="cycle_delay",
|
||||||
current_fn=lambda robot: robot.clean_cycle_wait_time_minutes,
|
unit_of_measurement=UnitOfTime.MINUTES,
|
||||||
options_fn=lambda robot: robot.VALID_WAIT_TIMES,
|
current_fn=lambda robot: robot.clean_cycle_wait_time_minutes,
|
||||||
select_fn=lambda robot, opt: robot.set_wait_time(int(opt)),
|
options_fn=lambda robot: robot.VALID_WAIT_TIMES,
|
||||||
),
|
select_fn=lambda robot, opt: robot.set_wait_time(int(opt)),
|
||||||
LitterRobot4: RobotSelectEntityDescription[LitterRobot4, str](
|
|
||||||
key="panel_brightness",
|
|
||||||
translation_key="brightness_level",
|
|
||||||
current_fn=(
|
|
||||||
lambda robot: bri.name.lower()
|
|
||||||
if (bri := robot.panel_brightness) is not None
|
|
||||||
else None
|
|
||||||
),
|
|
||||||
options_fn=lambda _: [level.name.lower() for level in BrightnessLevel],
|
|
||||||
select_fn=(
|
|
||||||
lambda robot, opt: robot.set_panel_brightness(BrightnessLevel[opt.upper()])
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FeederRobot: RobotSelectEntityDescription[FeederRobot, float](
|
LitterRobot4: (
|
||||||
key="meal_insert_size",
|
RobotSelectEntityDescription[LitterRobot4, str](
|
||||||
translation_key="meal_insert_size",
|
key="globe_brightness",
|
||||||
unit_of_measurement="cups",
|
translation_key="globe_brightness",
|
||||||
current_fn=lambda robot: robot.meal_insert_size,
|
current_fn=(
|
||||||
options_fn=lambda robot: robot.VALID_MEAL_INSERT_SIZES,
|
lambda robot: bri.name.lower()
|
||||||
select_fn=lambda robot, opt: robot.set_meal_insert_size(float(opt)),
|
if (bri := robot.night_light_level) is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
options_fn=lambda _: [level.name.lower() for level in BrightnessLevel],
|
||||||
|
select_fn=(
|
||||||
|
lambda robot, opt: robot.set_night_light_brightness(
|
||||||
|
BrightnessLevel[opt.upper()]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RobotSelectEntityDescription[LitterRobot4, str](
|
||||||
|
key="globe_light",
|
||||||
|
translation_key="globe_light",
|
||||||
|
current_fn=(
|
||||||
|
lambda robot: mode.name.lower()
|
||||||
|
if (mode := robot.night_light_mode) is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
options_fn=lambda _: [mode.name.lower() for mode in NightLightMode],
|
||||||
|
select_fn=(
|
||||||
|
lambda robot, opt: robot.set_night_light_mode(
|
||||||
|
NightLightMode[opt.upper()]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RobotSelectEntityDescription[LitterRobot4, str](
|
||||||
|
key="panel_brightness",
|
||||||
|
translation_key="brightness_level",
|
||||||
|
current_fn=(
|
||||||
|
lambda robot: bri.name.lower()
|
||||||
|
if (bri := robot.panel_brightness) is not None
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
options_fn=lambda _: [level.name.lower() for level in BrightnessLevel],
|
||||||
|
select_fn=(
|
||||||
|
lambda robot, opt: robot.set_panel_brightness(
|
||||||
|
BrightnessLevel[opt.upper()]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FeederRobot: (
|
||||||
|
RobotSelectEntityDescription[FeederRobot, float](
|
||||||
|
key="meal_insert_size",
|
||||||
|
translation_key="meal_insert_size",
|
||||||
|
unit_of_measurement="cups",
|
||||||
|
current_fn=lambda robot: robot.meal_insert_size,
|
||||||
|
options_fn=lambda robot: robot.VALID_MEAL_INSERT_SIZES,
|
||||||
|
select_fn=lambda robot, opt: robot.set_meal_insert_size(float(opt)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +115,9 @@ async def async_setup_entry(
|
|||||||
robot=robot, coordinator=coordinator, description=description
|
robot=robot, coordinator=coordinator, description=description
|
||||||
)
|
)
|
||||||
for robot in coordinator.account.robots
|
for robot in coordinator.account.robots
|
||||||
for robot_type, description in ROBOT_SELECT_MAP.items()
|
for robot_type, descriptions in ROBOT_SELECT_MAP.items()
|
||||||
if isinstance(robot, robot_type)
|
if isinstance(robot, robot_type)
|
||||||
|
for description in descriptions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,22 @@
|
|||||||
"cycle_delay": {
|
"cycle_delay": {
|
||||||
"name": "Clean cycle wait time minutes"
|
"name": "Clean cycle wait time minutes"
|
||||||
},
|
},
|
||||||
|
"globe_brightness": {
|
||||||
|
"name": "Globe brightness",
|
||||||
|
"state": {
|
||||||
|
"low": "[%key:common::state::low%]",
|
||||||
|
"medium": "[%key:common::state::medium%]",
|
||||||
|
"high": "[%key:common::state::high%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"globe_light": {
|
||||||
|
"name": "Globe light",
|
||||||
|
"state": {
|
||||||
|
"auto": "[%key:common::state::auto%]",
|
||||||
|
"off": "[%key:common::state::off%]",
|
||||||
|
"on": "[%key:common::state::on%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
"meal_insert_size": {
|
"meal_insert_size": {
|
||||||
"name": "Meal insert size"
|
"name": "Meal insert size"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ ROBOT_4_DATA = {
|
|||||||
"cleanCycleWaitTime": 15,
|
"cleanCycleWaitTime": 15,
|
||||||
"isKeypadLockout": False,
|
"isKeypadLockout": False,
|
||||||
"nightLightMode": "OFF",
|
"nightLightMode": "OFF",
|
||||||
"nightLightBrightness": 85,
|
"nightLightBrightness": 50,
|
||||||
"isPanelSleepMode": False,
|
"isPanelSleepMode": False,
|
||||||
|
"panelBrightnessHigh": 50,
|
||||||
"panelSleepTime": 0,
|
"panelSleepTime": 0,
|
||||||
"panelWakeTime": 0,
|
"panelWakeTime": 0,
|
||||||
"weekdaySleepModeEnabled": {
|
"weekdaySleepModeEnabled": {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ from homeassistant.helpers import entity_registry as er
|
|||||||
from .conftest import setup_integration
|
from .conftest import setup_integration
|
||||||
|
|
||||||
SELECT_ENTITY_ID = "select.test_clean_cycle_wait_time_minutes"
|
SELECT_ENTITY_ID = "select.test_clean_cycle_wait_time_minutes"
|
||||||
PANEL_BRIGHTNESS_ENTITY_ID = "select.test_panel_brightness"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_wait_time_select(
|
async def test_wait_time_select(
|
||||||
@@ -69,26 +68,38 @@ async def test_invalid_wait_time_select(hass: HomeAssistant, mock_account) -> No
|
|||||||
assert not mock_account.robots[0].set_wait_time.called
|
assert not mock_account.robots[0].set_wait_time.called
|
||||||
|
|
||||||
|
|
||||||
async def test_panel_brightness_select(
|
@pytest.mark.parametrize(
|
||||||
|
("entity_id", "initial_value", "robot_command"),
|
||||||
|
[
|
||||||
|
("select.test_globe_brightness", "medium", "set_night_light_brightness"),
|
||||||
|
("select.test_globe_light", "off", "set_night_light_mode"),
|
||||||
|
("select.test_panel_brightness", "medium", "set_panel_brightness"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_litterrobot_4_select(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_account_with_litterrobot_4: MagicMock,
|
mock_account_with_litterrobot_4: MagicMock,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
|
entity_id: str,
|
||||||
|
initial_value: str,
|
||||||
|
robot_command: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Tests the wait time select entity."""
|
"""Tests a Litter-Robot 4 select entity."""
|
||||||
await setup_integration(hass, mock_account_with_litterrobot_4, SELECT_DOMAIN)
|
await setup_integration(hass, mock_account_with_litterrobot_4, SELECT_DOMAIN)
|
||||||
|
|
||||||
select = hass.states.get(PANEL_BRIGHTNESS_ENTITY_ID)
|
select = hass.states.get(entity_id)
|
||||||
assert select
|
assert select
|
||||||
assert len(select.attributes[ATTR_OPTIONS]) == 3
|
assert len(select.attributes[ATTR_OPTIONS]) == 3
|
||||||
|
assert select.state == initial_value
|
||||||
|
|
||||||
entity_entry = entity_registry.async_get(PANEL_BRIGHTNESS_ENTITY_ID)
|
entity_entry = entity_registry.async_get(entity_id)
|
||||||
assert entity_entry
|
assert entity_entry
|
||||||
assert entity_entry.entity_category is EntityCategory.CONFIG
|
assert entity_entry.entity_category is EntityCategory.CONFIG
|
||||||
|
|
||||||
data = {ATTR_ENTITY_ID: PANEL_BRIGHTNESS_ENTITY_ID}
|
data = {ATTR_ENTITY_ID: entity_id}
|
||||||
|
|
||||||
robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0]
|
robot: LitterRobot4 = mock_account_with_litterrobot_4.robots[0]
|
||||||
robot.set_panel_brightness = AsyncMock(return_value=True)
|
setattr(robot, robot_command, AsyncMock(return_value=True))
|
||||||
|
|
||||||
for count, option in enumerate(select.attributes[ATTR_OPTIONS]):
|
for count, option in enumerate(select.attributes[ATTR_OPTIONS]):
|
||||||
data[ATTR_OPTION] = option
|
data[ATTR_OPTION] = option
|
||||||
@@ -100,4 +111,4 @@ async def test_panel_brightness_select(
|
|||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert robot.set_panel_brightness.call_count == count + 1
|
assert getattr(robot, robot_command).call_count == count + 1
|
||||||
|
|||||||
Reference in New Issue
Block a user