mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 18:27:51 +00:00
Add button to litterrobot (#59734)
This commit is contained in:
parent
ce9385d442
commit
eaaa53d8d5
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
from pylitterbot.exceptions import LitterRobotException, LitterRobotLoginException
|
from pylitterbot.exceptions import LitterRobotException, LitterRobotLoginException
|
||||||
|
|
||||||
|
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
|
||||||
|
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
||||||
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
|
from homeassistant.components.vacuum import DOMAIN as VACUUM_DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
@ -9,7 +14,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .hub import LitterRobotHub
|
from .hub import LitterRobotHub
|
||||||
|
|
||||||
PLATFORMS = ["select", "sensor", "switch", "vacuum"]
|
PLATFORMS = [BUTTON_DOMAIN, SELECT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN, VACUUM_DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
43
homeassistant/components/litterrobot/button.py
Normal file
43
homeassistant/components/litterrobot/button.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
"""Support for Litter-Robot button."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.components.button import ButtonEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import ENTITY_CATEGORY_CONFIG
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .entity import LitterRobotEntity
|
||||||
|
from .hub import LitterRobotHub
|
||||||
|
|
||||||
|
TYPE_RESET_WASTE_DRAWER = "Reset Waste Drawer"
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up Litter-Robot cleaner using config entry."""
|
||||||
|
hub: LitterRobotHub = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_entities(
|
||||||
|
[
|
||||||
|
LitterRobotResetWasteDrawerButton(
|
||||||
|
robot=robot, entity_type=TYPE_RESET_WASTE_DRAWER, hub=hub
|
||||||
|
)
|
||||||
|
for robot in hub.account.robots
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LitterRobotResetWasteDrawerButton(LitterRobotEntity, ButtonEntity):
|
||||||
|
"""Litter-Robot reset waste drawer button."""
|
||||||
|
|
||||||
|
_attr_icon = "mdi:delete-variant"
|
||||||
|
_attr_entity_category = ENTITY_CATEGORY_CONFIG
|
||||||
|
|
||||||
|
async def async_press(self) -> None:
|
||||||
|
"""Press the button."""
|
||||||
|
await self.robot.reset_waste_drawer()
|
||||||
|
self.coordinator.async_set_updated_data(True)
|
@ -1,6 +1,7 @@
|
|||||||
"""Support for Litter-Robot "Vacuum"."""
|
"""Support for Litter-Robot "Vacuum"."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pylitterbot.enums import LitterBoxStatus
|
from pylitterbot.enums import LitterBoxStatus
|
||||||
@ -29,6 +30,8 @@ from .const import DOMAIN
|
|||||||
from .entity import LitterRobotControlEntity
|
from .entity import LitterRobotControlEntity
|
||||||
from .hub import LitterRobotHub
|
from .hub import LitterRobotHub
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORT_LITTERROBOT = (
|
SUPPORT_LITTERROBOT = (
|
||||||
SUPPORT_START | SUPPORT_STATE | SUPPORT_STATUS | SUPPORT_TURN_OFF | SUPPORT_TURN_ON
|
SUPPORT_START | SUPPORT_STATE | SUPPORT_STATUS | SUPPORT_TURN_OFF | SUPPORT_TURN_ON
|
||||||
)
|
)
|
||||||
@ -121,6 +124,13 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity):
|
|||||||
|
|
||||||
async def async_reset_waste_drawer(self) -> None:
|
async def async_reset_waste_drawer(self) -> None:
|
||||||
"""Reset the waste drawer level."""
|
"""Reset the waste drawer level."""
|
||||||
|
# The Litter-Robot reset waste drawer service has been replaced by a
|
||||||
|
# dedicated button entity and marked as deprecated
|
||||||
|
_LOGGER.warning(
|
||||||
|
"The 'litterrobot.reset_waste_drawer' service is deprecated and "
|
||||||
|
"replaced by a dedicated reset waste drawer button entity; Please "
|
||||||
|
"use that entity to reset the waste drawer instead"
|
||||||
|
)
|
||||||
await self.robot.reset_waste_drawer()
|
await self.robot.reset_waste_drawer()
|
||||||
self.coordinator.async_set_updated_data(True)
|
self.coordinator.async_set_updated_data(True)
|
||||||
|
|
||||||
@ -136,6 +146,13 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity):
|
|||||||
|
|
||||||
async def async_set_wait_time(self, minutes: int) -> None:
|
async def async_set_wait_time(self, minutes: int) -> None:
|
||||||
"""Set the wait time."""
|
"""Set the wait time."""
|
||||||
|
# The Litter-Robot set wait time service has been replaced by a
|
||||||
|
# dedicated select entity and marked as deprecated
|
||||||
|
_LOGGER.warning(
|
||||||
|
"The 'litterrobot.set_wait_time' service is deprecated and "
|
||||||
|
"replaced by a dedicated set wait time select entity; Please "
|
||||||
|
"use that entity to set the wait time instead"
|
||||||
|
)
|
||||||
await self.perform_action_and_refresh(self.robot.set_wait_time, minutes)
|
await self.perform_action_and_refresh(self.robot.set_wait_time, minutes)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
48
tests/components/litterrobot/test_button.py
Normal file
48
tests/components/litterrobot/test_button.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
"""Test the Litter-Robot button entity."""
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_ICON,
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from .conftest import setup_integration
|
||||||
|
|
||||||
|
BUTTON_ENTITY = "button.test_reset_waste_drawer"
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time("2021-11-15 17:37:00", tz_offset=-7)
|
||||||
|
async def test_button(hass: HomeAssistant, mock_account: MagicMock) -> None:
|
||||||
|
"""Test the creation and values of the Litter-Robot button."""
|
||||||
|
await setup_integration(hass, mock_account, BUTTON_DOMAIN)
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
|
state = hass.states.get(BUTTON_ENTITY)
|
||||||
|
assert state
|
||||||
|
assert state.attributes.get(ATTR_ICON) == "mdi:delete-variant"
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
entry = entity_registry.async_get(BUTTON_ENTITY)
|
||||||
|
assert entry
|
||||||
|
assert entry.entity_category == ENTITY_CATEGORY_CONFIG
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
BUTTON_DOMAIN,
|
||||||
|
SERVICE_PRESS,
|
||||||
|
{ATTR_ENTITY_ID: BUTTON_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert mock_account.robots[0].reset_waste_drawer.call_count == 1
|
||||||
|
mock_account.robots[0].reset_waste_drawer.assert_called_with()
|
||||||
|
|
||||||
|
state = hass.states.get(BUTTON_ENTITY)
|
||||||
|
assert state
|
||||||
|
assert state.state == "2021-11-15T10:37:00+00:00"
|
@ -1,5 +1,9 @@
|
|||||||
"""Test the Litter-Robot vacuum entity."""
|
"""Test the Litter-Robot vacuum entity."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from voluptuous.error import MultipleInvalid
|
from voluptuous.error import MultipleInvalid
|
||||||
@ -36,7 +40,7 @@ COMPONENT_SERVICE_DOMAIN = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_vacuum(hass: HomeAssistant, mock_account):
|
async def test_vacuum(hass: HomeAssistant, mock_account: MagicMock) -> None:
|
||||||
"""Tests the vacuum entity was set up."""
|
"""Tests the vacuum entity was set up."""
|
||||||
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
||||||
assert hass.services.has_service(DOMAIN, SERVICE_RESET_WASTE_DRAWER)
|
assert hass.services.has_service(DOMAIN, SERVICE_RESET_WASTE_DRAWER)
|
||||||
@ -48,8 +52,8 @@ async def test_vacuum(hass: HomeAssistant, mock_account):
|
|||||||
|
|
||||||
|
|
||||||
async def test_vacuum_status_when_sleeping(
|
async def test_vacuum_status_when_sleeping(
|
||||||
hass: HomeAssistant, mock_account_with_sleeping_robot
|
hass: HomeAssistant, mock_account_with_sleeping_robot: MagicMock
|
||||||
):
|
) -> None:
|
||||||
"""Tests the vacuum status when sleeping."""
|
"""Tests the vacuum status when sleeping."""
|
||||||
await setup_integration(hass, mock_account_with_sleeping_robot, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account_with_sleeping_robot, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
@ -58,14 +62,18 @@ async def test_vacuum_status_when_sleeping(
|
|||||||
assert vacuum.attributes.get(ATTR_STATUS) == "Ready (Sleeping)"
|
assert vacuum.attributes.get(ATTR_STATUS) == "Ready (Sleeping)"
|
||||||
|
|
||||||
|
|
||||||
async def test_no_robots(hass: HomeAssistant, mock_account_with_no_robots):
|
async def test_no_robots(
|
||||||
|
hass: HomeAssistant, mock_account_with_no_robots: MagicMock
|
||||||
|
) -> None:
|
||||||
"""Tests the vacuum entity was set up."""
|
"""Tests the vacuum entity was set up."""
|
||||||
await setup_integration(hass, mock_account_with_no_robots, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account_with_no_robots, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
assert not hass.services.has_service(DOMAIN, SERVICE_RESET_WASTE_DRAWER)
|
assert not hass.services.has_service(DOMAIN, SERVICE_RESET_WASTE_DRAWER)
|
||||||
|
|
||||||
|
|
||||||
async def test_vacuum_with_error(hass: HomeAssistant, mock_account_with_error):
|
async def test_vacuum_with_error(
|
||||||
|
hass: HomeAssistant, mock_account_with_error: MagicMock
|
||||||
|
) -> None:
|
||||||
"""Tests a vacuum entity with an error."""
|
"""Tests a vacuum entity with an error."""
|
||||||
await setup_integration(hass, mock_account_with_error, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account_with_error, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
@ -80,39 +88,34 @@ async def test_vacuum_with_error(hass: HomeAssistant, mock_account_with_error):
|
|||||||
(SERVICE_START, "start_cleaning", None),
|
(SERVICE_START, "start_cleaning", None),
|
||||||
(SERVICE_TURN_OFF, "set_power_status", None),
|
(SERVICE_TURN_OFF, "set_power_status", None),
|
||||||
(SERVICE_TURN_ON, "set_power_status", None),
|
(SERVICE_TURN_ON, "set_power_status", None),
|
||||||
(
|
(SERVICE_RESET_WASTE_DRAWER, "reset_waste_drawer", {"deprecated": True}),
|
||||||
SERVICE_RESET_WASTE_DRAWER,
|
|
||||||
"reset_waste_drawer",
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
SERVICE_SET_SLEEP_MODE,
|
SERVICE_SET_SLEEP_MODE,
|
||||||
"set_sleep_mode",
|
"set_sleep_mode",
|
||||||
{"enabled": True, "start_time": "22:30"},
|
{"data": {"enabled": True, "start_time": "22:30"}},
|
||||||
),
|
),
|
||||||
|
(SERVICE_SET_SLEEP_MODE, "set_sleep_mode", {"data": {"enabled": True}}),
|
||||||
|
(SERVICE_SET_SLEEP_MODE, "set_sleep_mode", {"data": {"enabled": False}}),
|
||||||
(
|
(
|
||||||
SERVICE_SET_SLEEP_MODE,
|
SERVICE_SET_WAIT_TIME,
|
||||||
"set_sleep_mode",
|
"set_wait_time",
|
||||||
{"enabled": True},
|
{"data": {"minutes": 3}, "deprecated": True},
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_SET_SLEEP_MODE,
|
|
||||||
"set_sleep_mode",
|
|
||||||
{"enabled": False},
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SERVICE_SET_WAIT_TIME,
|
SERVICE_SET_WAIT_TIME,
|
||||||
"set_wait_time",
|
"set_wait_time",
|
||||||
{"minutes": 3},
|
{"data": {"minutes": "15"}, "deprecated": True},
|
||||||
),
|
|
||||||
(
|
|
||||||
SERVICE_SET_WAIT_TIME,
|
|
||||||
"set_wait_time",
|
|
||||||
{"minutes": "15"},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_commands(hass: HomeAssistant, mock_account, service, command, extra):
|
async def test_commands(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_account: MagicMock,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
service: str,
|
||||||
|
command: str,
|
||||||
|
extra: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test sending commands to the vacuum."""
|
"""Test sending commands to the vacuum."""
|
||||||
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
@ -120,9 +123,9 @@ async def test_commands(hass: HomeAssistant, mock_account, service, command, ext
|
|||||||
assert vacuum
|
assert vacuum
|
||||||
assert vacuum.state == STATE_DOCKED
|
assert vacuum.state == STATE_DOCKED
|
||||||
|
|
||||||
data = {ATTR_ENTITY_ID: VACUUM_ENTITY_ID}
|
extra = extra or {}
|
||||||
if extra:
|
data = {ATTR_ENTITY_ID: VACUUM_ENTITY_ID, **extra.get("data", {})}
|
||||||
data.update(extra)
|
deprecated = extra.get("deprecated", False)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
COMPONENT_SERVICE_DOMAIN.get(service, PLATFORM_DOMAIN),
|
COMPONENT_SERVICE_DOMAIN.get(service, PLATFORM_DOMAIN),
|
||||||
@ -133,9 +136,10 @@ async def test_commands(hass: HomeAssistant, mock_account, service, command, ext
|
|||||||
future = utcnow() + timedelta(seconds=REFRESH_WAIT_TIME_SECONDS)
|
future = utcnow() + timedelta(seconds=REFRESH_WAIT_TIME_SECONDS)
|
||||||
async_fire_time_changed(hass, future)
|
async_fire_time_changed(hass, future)
|
||||||
getattr(mock_account.robots[0], command).assert_called_once()
|
getattr(mock_account.robots[0], command).assert_called_once()
|
||||||
|
assert (f"'{DOMAIN}.{service}' service is deprecated" in caplog.text) is deprecated
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_wait_time(hass: HomeAssistant, mock_account):
|
async def test_invalid_wait_time(hass: HomeAssistant, mock_account: MagicMock) -> None:
|
||||||
"""Test an attempt to send an invalid wait time to the vacuum."""
|
"""Test an attempt to send an invalid wait time to the vacuum."""
|
||||||
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
await setup_integration(hass, mock_account, PLATFORM_DOMAIN)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user