Add button platform to Roborock (#103010)

* add button platform to roborock

* Update tests/components/roborock/test_button.py

Co-authored-by: Duco Sebel <74970928+DCSBL@users.noreply.github.com>

* Remove device class

* improve tests

* sort platforms

---------

Co-authored-by: Duco Sebel <74970928+DCSBL@users.noreply.github.com>
This commit is contained in:
Luke Lashley 2023-11-01 16:34:04 -04:00 committed by GitHub
parent f05d2eb261
commit 47d6d6c344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 172 additions and 3 deletions

View File

@ -0,0 +1,112 @@
"""Support for Roborock button."""
from __future__ import annotations
from dataclasses import dataclass
from roborock.roborock_typing import RoborockCommand
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import slugify
from .const import DOMAIN
from .coordinator import RoborockDataUpdateCoordinator
from .device import RoborockEntity
@dataclass
class RoborockButtonDescriptionMixin:
"""Define an entity description mixin for button entities."""
command: RoborockCommand
param: list | dict | None
@dataclass
class RoborockButtonDescription(
ButtonEntityDescription, RoborockButtonDescriptionMixin
):
"""Describes a Roborock button entity."""
CONSUMABLE_BUTTON_DESCRIPTIONS = [
RoborockButtonDescription(
key="reset_sensor_consumable",
icon="mdi:eye-outline",
translation_key="reset_sensor_consumable",
command=RoborockCommand.RESET_CONSUMABLE,
param=["sensor_dirty_time"],
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
RoborockButtonDescription(
key="reset_air_filter_consumable",
icon="mdi:air-filter",
translation_key="reset_air_filter_consumable",
command=RoborockCommand.RESET_CONSUMABLE,
param=["filter_work_time"],
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
RoborockButtonDescription(
key="reset_side_brush_consumable",
icon="mdi:brush",
translation_key="reset_side_brush_consumable",
command=RoborockCommand.RESET_CONSUMABLE,
param=["side_brush_work_time"],
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
RoborockButtonDescription(
key="reset_main_brush_consumable",
icon="mdi:brush",
translation_key="reset_main_brush_consumable",
command=RoborockCommand.RESET_CONSUMABLE,
param=["main_brush_work_time"],
entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Roborock button platform."""
coordinators: dict[str, RoborockDataUpdateCoordinator] = hass.data[DOMAIN][
config_entry.entry_id
]
async_add_entities(
RoborockButtonEntity(
f"{description.key}_{slugify(device_id)}",
coordinator,
description,
)
for device_id, coordinator in coordinators.items()
for description in CONSUMABLE_BUTTON_DESCRIPTIONS
)
class RoborockButtonEntity(RoborockEntity, ButtonEntity):
"""A class to define Roborock button entities."""
entity_description: RoborockButtonDescription
def __init__(
self,
unique_id: str,
coordinator: RoborockDataUpdateCoordinator,
entity_description: RoborockButtonDescription,
) -> None:
"""Create a button entity."""
super().__init__(unique_id, coordinator.device_info, coordinator.api)
self.entity_description = entity_description
async def async_press(self) -> None:
"""Press the button."""
await self.send(self.entity_description.command, self.entity_description.param)

View File

@ -7,11 +7,12 @@ CONF_BASE_URL = "base_url"
CONF_USER_DATA = "user_data"
PLATFORMS = [
Platform.VACUUM,
Platform.BUTTON,
Platform.BINARY_SENSOR,
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.TIME,
Platform.NUMBER,
Platform.BINARY_SENSOR,
Platform.VACUUM,
]

View File

@ -44,6 +44,20 @@
"name": "Water shortage"
}
},
"button": {
"reset_sensor_consumable": {
"name": "Reset sensor consumable"
},
"reset_air_filter_consumable": {
"name": "Reset air filter consumable"
},
"reset_side_brush_consumable": {
"name": "Reset side brush consumable"
},
"reset_main_brush_consumable": {
"name": "Reset main brush consumable"
}
},
"number": {
"volume": {
"name": "Volume"

View File

@ -0,0 +1,42 @@
"""Test Roborock Button platform."""
from unittest.mock import patch
import pytest
from homeassistant.components.button import SERVICE_PRESS
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("entity_id"),
[
("button.roborock_s7_maxv_reset_sensor_consumable"),
("button.roborock_s7_maxv_reset_air_filter_consumable"),
("button.roborock_s7_maxv_reset_side_brush_consumable"),
"button.roborock_s7_maxv_reset_main_brush_consumable",
],
)
@pytest.mark.freeze_time("2023-10-30 08:50:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_update_success(
hass: HomeAssistant,
bypass_api_fixture,
setup_entry: MockConfigEntry,
entity_id: str,
) -> None:
"""Test pressing the button entities."""
# Ensure that the entity exist, as these test can pass even if there is no entity.
assert hass.states.get(entity_id).state == "unknown"
with patch(
"homeassistant.components.roborock.coordinator.RoborockLocalClient.send_message"
) as mock_send_message:
await hass.services.async_call(
"button",
SERVICE_PRESS,
blocking=True,
target={"entity_id": entity_id},
)
assert mock_send_message.assert_called_once
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"