diff --git a/homeassistant/components/litterrobot/__init__.py b/homeassistant/components/litterrobot/__init__.py index d4a4f3bfe91..cf14239b22d 100644 --- a/homeassistant/components/litterrobot/__init__.py +++ b/homeassistant/components/litterrobot/__init__.py @@ -6,12 +6,15 @@ from pylitterbot import FeederRobot, LitterRobot, LitterRobot3, Robot from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue +from homeassistant.helpers.typing import ConfigType from .const import DOMAIN from .hub import LitterRobotHub PLATFORMS_BY_TYPE = { Robot: ( + Platform.BINARY_SENSOR, Platform.SELECT, Platform.SENSOR, Platform.SWITCH, @@ -33,6 +36,21 @@ def get_platforms_for_robots(robots: list[Robot]) -> set[Platform]: } +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the Litter-Robot integration.""" + async_create_issue( + hass, + DOMAIN, + "migrated_attributes", + breaks_in_ha_version="2022.12.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="migrated_attributes", + ) + + return True + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Litter-Robot from a config entry.""" hass.data.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/litterrobot/binary_sensor.py b/homeassistant/components/litterrobot/binary_sensor.py new file mode 100644 index 00000000000..781cfb73b7f --- /dev/null +++ b/homeassistant/components/litterrobot/binary_sensor.py @@ -0,0 +1,95 @@ +"""Support for Litter-Robot binary sensors.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Generic + +from pylitterbot import LitterRobot, Robot + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import LitterRobotEntity, _RobotT +from .hub import LitterRobotHub + + +@dataclass +class RequiredKeysMixin(Generic[_RobotT]): + """A class that describes robot binary sensor entity required keys.""" + + is_on_fn: Callable[[_RobotT], bool] + + +@dataclass +class RobotBinarySensorEntityDescription( + BinarySensorEntityDescription, RequiredKeysMixin[_RobotT] +): + """A class that describes robot binary sensor entities.""" + + +class LitterRobotBinarySensorEntity(LitterRobotEntity[_RobotT], BinarySensorEntity): + """Litter-Robot binary sensor entity.""" + + entity_description: RobotBinarySensorEntityDescription[_RobotT] + + @property + def is_on(self) -> bool: + """Return the state.""" + return self.entity_description.is_on_fn(self.robot) + + +BINARY_SENSOR_MAP: dict[type[Robot], tuple[RobotBinarySensorEntityDescription, ...]] = { + LitterRobot: ( + RobotBinarySensorEntityDescription[LitterRobot]( + key="sleeping", + name="Sleeping", + icon="mdi:sleep", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + is_on_fn=lambda robot: robot.is_sleeping, + ), + RobotBinarySensorEntityDescription[LitterRobot]( + key="sleep_mode", + name="Sleep mode", + icon="mdi:sleep", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + is_on_fn=lambda robot: robot.sleep_mode_enabled, + ), + ), + Robot: ( + RobotBinarySensorEntityDescription[Robot]( + key="power_status", + name="Power status", + device_class=BinarySensorDeviceClass.PLUG, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + is_on_fn=lambda robot: robot.power_status == "AC", + ), + ), +} + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Litter-Robot binary sensors using config entry.""" + hub: LitterRobotHub = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + LitterRobotBinarySensorEntity(robot=robot, hub=hub, description=description) + for robot in hub.account.robots + for robot_type, entity_descriptions in BINARY_SENSOR_MAP.items() + if isinstance(robot, robot_type) + for description in entity_descriptions + ) diff --git a/homeassistant/components/litterrobot/strings.json b/homeassistant/components/litterrobot/strings.json index 140a0308188..f2256249b8e 100644 --- a/homeassistant/components/litterrobot/strings.json +++ b/homeassistant/components/litterrobot/strings.json @@ -24,5 +24,11 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } + }, + "issues": { + "migrated_attributes": { + "title": "Litter-Robot attributes are now their own sensors", + "description": "The vacuum entity attributes are now available as diagnostic sensors.\n\nPlease adjust any automations or scripts you may have that use these attributes." + } } } diff --git a/homeassistant/components/litterrobot/translations/en.json b/homeassistant/components/litterrobot/translations/en.json index 3d6ab4dfaaa..3d500bb0f10 100644 --- a/homeassistant/components/litterrobot/translations/en.json +++ b/homeassistant/components/litterrobot/translations/en.json @@ -24,5 +24,11 @@ } } } + }, + "issues": { + "migrated_attributes": { + "title": "Litter-Robot attributes are now their own sensors", + "description": "The vacuum entity attributes are now available as diagnostic sensors.\n\nPlease adjust any automations or scripts you may have that use these attributes." + } } -} \ No newline at end of file +} diff --git a/tests/components/litterrobot/test_binary_sensor.py b/tests/components/litterrobot/test_binary_sensor.py new file mode 100644 index 00000000000..cbcdd447760 --- /dev/null +++ b/tests/components/litterrobot/test_binary_sensor.py @@ -0,0 +1,31 @@ +"""Test the Litter-Robot binary sensor entity.""" +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from homeassistant.components.binary_sensor import ( + DOMAIN as PLATFORM_DOMAIN, + BinarySensorDeviceClass, +) +from homeassistant.const import ATTR_DEVICE_CLASS +from homeassistant.core import HomeAssistant + +from .conftest import setup_integration + + +@pytest.mark.freeze_time("2022-09-18 23:00:44+00:00") +async def test_binary_sensors( + hass: HomeAssistant, + mock_account: MagicMock, + entity_registry_enabled_by_default: AsyncMock, +) -> None: + """Tests binary sensors.""" + await setup_integration(hass, mock_account, PLATFORM_DOMAIN) + + state = hass.states.get("binary_sensor.test_sleeping") + assert state.state == "off" + state = hass.states.get("binary_sensor.test_sleep_mode") + assert state.state == "on" + state = hass.states.get("binary_sensor.test_power_status") + assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.PLUG + assert state.state == "on"