Compare commits

..

2 Commits

Author SHA1 Message Date
Ludovic BOUÉ
5dcbc1d5d9 feat(roborock): Add Q10 empty dustbin button entity (#166149) 2026-03-22 00:36:43 +01:00
Ludovic BOUÉ
3068653cc7 Update python-roborock to 4.26.2 (#166152) 2026-03-21 23:44:02 +01:00
4 changed files with 163 additions and 1 deletions

View File

@@ -20,12 +20,18 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .coordinator import (
RoborockB01Q10UpdateCoordinator,
RoborockConfigEntry,
RoborockDataUpdateCoordinator,
RoborockDataUpdateCoordinatorA01,
RoborockWashingMachineUpdateCoordinator,
)
from .entity import RoborockCoordinatedEntityA01, RoborockEntity, RoborockEntityV1
from .entity import (
RoborockCoordinatedEntityA01,
RoborockCoordinatedEntityB01Q10,
RoborockEntity,
RoborockEntityV1,
)
_LOGGER = logging.getLogger(__name__)
@@ -97,6 +103,14 @@ ZEO_BUTTON_DESCRIPTIONS = [
]
Q10_BUTTON_DESCRIPTIONS = [
ButtonEntityDescription(
key="empty_dustbin",
translation_key="empty_dustbin",
),
]
async def async_setup_entry(
hass: HomeAssistant,
config_entry: RoborockConfigEntry,
@@ -139,6 +153,15 @@ async def async_setup_entry(
if isinstance(coordinator, RoborockWashingMachineUpdateCoordinator)
for description in ZEO_BUTTON_DESCRIPTIONS
),
(
RoborockQ10EmptyDustbinButtonEntity(
coordinator,
description,
)
for coordinator in config_entry.runtime_data.b01_q10
if isinstance(coordinator, RoborockB01Q10UpdateCoordinator)
for description in Q10_BUTTON_DESCRIPTIONS
),
)
)
@@ -233,3 +256,37 @@ class RoborockButtonEntityA01(RoborockCoordinatedEntityA01, ButtonEntity):
) from err
finally:
await self.coordinator.async_request_refresh()
class RoborockQ10EmptyDustbinButtonEntity(
RoborockCoordinatedEntityB01Q10, ButtonEntity
):
"""A class to define Q10 empty dustbin button entity."""
entity_description: ButtonEntityDescription
coordinator: RoborockB01Q10UpdateCoordinator
def __init__(
self,
coordinator: RoborockB01Q10UpdateCoordinator,
entity_description: ButtonEntityDescription,
) -> None:
"""Create a Q10 empty dustbin button entity."""
self.entity_description = entity_description
super().__init__(
f"{entity_description.key}_{coordinator.duid_slug}",
coordinator,
)
async def async_press(self, **kwargs: Any) -> None:
"""Press the button to empty dustbin."""
try:
await self.coordinator.api.vacuum.empty_dustbin()
except RoborockException as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="command_failed",
translation_placeholders={
"command": "empty_dustbin",
},
) from err

View File

@@ -84,6 +84,9 @@
}
},
"button": {
"empty_dustbin": {
"name": "Empty dustbin"
},
"pause": {
"name": "Pause"
},

View File

@@ -1,4 +1,54 @@
# serializer version: 1
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Empty dustbin',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Empty dustbin',
'platform': 'roborock',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'empty_dustbin',
'unique_id': 'empty_dustbin_q10_duid',
'unit_of_measurement': None,
})
# ---
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Roborock Q10 S5+ Empty dustbin',
}),
'context': <ANY>,
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_buttons[button.roborock_s7_2_reset_air_filter_consumable-entry]
EntityRegistryEntrySnapshot({
'aliases': list([

View File

@@ -272,3 +272,55 @@ async def test_press_a01_button_failure(
washing_machine.zeo.set_value.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
@pytest.mark.freeze_time("2023-10-30 08:50:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_press_q10_empty_dustbin_button_success(
hass: HomeAssistant,
bypass_api_client_fixture: None,
setup_entry: MockConfigEntry,
fake_q10_vacuum: FakeDevice,
) -> None:
"""Test pressing Q10 empty dustbin button entity."""
entity_id = "button.roborock_q10_s5_empty_dustbin"
assert hass.states.get(entity_id) is not None
await hass.services.async_call(
"button",
SERVICE_PRESS,
blocking=True,
target={"entity_id": entity_id},
)
assert fake_q10_vacuum.b01_q10_properties is not None
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
@pytest.mark.freeze_time("2023-10-30 08:50:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_press_q10_empty_dustbin_button_failure(
hass: HomeAssistant,
bypass_api_client_fixture: None,
setup_entry: MockConfigEntry,
fake_q10_vacuum: FakeDevice,
) -> None:
"""Test failure while pressing Q10 empty dustbin button entity."""
entity_id = "button.roborock_q10_s5_empty_dustbin"
assert fake_q10_vacuum.b01_q10_properties is not None
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.side_effect = (
RoborockException
)
assert hass.states.get(entity_id) is not None
with pytest.raises(HomeAssistantError, match="Error while calling empty_dustbin"):
await hass.services.async_call(
"button",
SERVICE_PRESS,
blocking=True,
target={"entity_id": entity_id},
)
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"