Skip to content

Commit 5dcbc1d

Browse files
authored
feat(roborock): Add Q10 empty dustbin button entity (#166149)
1 parent 3068653 commit 5dcbc1d

File tree

4 files changed

+163
-1
lines changed

4 files changed

+163
-1
lines changed

homeassistant/components/roborock/button.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@
2020

2121
from .const import DOMAIN
2222
from .coordinator import (
23+
RoborockB01Q10UpdateCoordinator,
2324
RoborockConfigEntry,
2425
RoborockDataUpdateCoordinator,
2526
RoborockDataUpdateCoordinatorA01,
2627
RoborockWashingMachineUpdateCoordinator,
2728
)
28-
from .entity import RoborockCoordinatedEntityA01, RoborockEntity, RoborockEntityV1
29+
from .entity import (
30+
RoborockCoordinatedEntityA01,
31+
RoborockCoordinatedEntityB01Q10,
32+
RoborockEntity,
33+
RoborockEntityV1,
34+
)
2935

3036
_LOGGER = logging.getLogger(__name__)
3137

@@ -97,6 +103,14 @@ class RoborockButtonDescriptionA01(ButtonEntityDescription):
97103
]
98104

99105

106+
Q10_BUTTON_DESCRIPTIONS = [
107+
ButtonEntityDescription(
108+
key="empty_dustbin",
109+
translation_key="empty_dustbin",
110+
),
111+
]
112+
113+
100114
async def async_setup_entry(
101115
hass: HomeAssistant,
102116
config_entry: RoborockConfigEntry,
@@ -139,6 +153,15 @@ async def async_setup_entry(
139153
if isinstance(coordinator, RoborockWashingMachineUpdateCoordinator)
140154
for description in ZEO_BUTTON_DESCRIPTIONS
141155
),
156+
(
157+
RoborockQ10EmptyDustbinButtonEntity(
158+
coordinator,
159+
description,
160+
)
161+
for coordinator in config_entry.runtime_data.b01_q10
162+
if isinstance(coordinator, RoborockB01Q10UpdateCoordinator)
163+
for description in Q10_BUTTON_DESCRIPTIONS
164+
),
142165
)
143166
)
144167

@@ -233,3 +256,37 @@ async def async_press(self) -> None:
233256
) from err
234257
finally:
235258
await self.coordinator.async_request_refresh()
259+
260+
261+
class RoborockQ10EmptyDustbinButtonEntity(
262+
RoborockCoordinatedEntityB01Q10, ButtonEntity
263+
):
264+
"""A class to define Q10 empty dustbin button entity."""
265+
266+
entity_description: ButtonEntityDescription
267+
coordinator: RoborockB01Q10UpdateCoordinator
268+
269+
def __init__(
270+
self,
271+
coordinator: RoborockB01Q10UpdateCoordinator,
272+
entity_description: ButtonEntityDescription,
273+
) -> None:
274+
"""Create a Q10 empty dustbin button entity."""
275+
self.entity_description = entity_description
276+
super().__init__(
277+
f"{entity_description.key}_{coordinator.duid_slug}",
278+
coordinator,
279+
)
280+
281+
async def async_press(self, **kwargs: Any) -> None:
282+
"""Press the button to empty dustbin."""
283+
try:
284+
await self.coordinator.api.vacuum.empty_dustbin()
285+
except RoborockException as err:
286+
raise HomeAssistantError(
287+
translation_domain=DOMAIN,
288+
translation_key="command_failed",
289+
translation_placeholders={
290+
"command": "empty_dustbin",
291+
},
292+
) from err

homeassistant/components/roborock/strings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
}
8585
},
8686
"button": {
87+
"empty_dustbin": {
88+
"name": "Empty dustbin"
89+
},
8790
"pause": {
8891
"name": "Pause"
8992
},

tests/components/roborock/snapshots/test_button.ambr

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,54 @@
11
# serializer version: 1
2+
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': list([
5+
None,
6+
]),
7+
'area_id': None,
8+
'capabilities': None,
9+
'config_entry_id': <ANY>,
10+
'config_subentry_id': <ANY>,
11+
'device_class': None,
12+
'device_id': <ANY>,
13+
'disabled_by': None,
14+
'domain': 'button',
15+
'entity_category': None,
16+
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
17+
'has_entity_name': True,
18+
'hidden_by': None,
19+
'icon': None,
20+
'id': <ANY>,
21+
'labels': set({
22+
}),
23+
'name': None,
24+
'object_id_base': 'Empty dustbin',
25+
'options': dict({
26+
}),
27+
'original_device_class': None,
28+
'original_icon': None,
29+
'original_name': 'Empty dustbin',
30+
'platform': 'roborock',
31+
'previous_unique_id': None,
32+
'suggested_object_id': None,
33+
'supported_features': 0,
34+
'translation_key': 'empty_dustbin',
35+
'unique_id': 'empty_dustbin_q10_duid',
36+
'unit_of_measurement': None,
37+
})
38+
# ---
39+
# name: test_buttons[button.roborock_q10_s5_empty_dustbin-state]
40+
StateSnapshot({
41+
'attributes': ReadOnlyDict({
42+
'friendly_name': 'Roborock Q10 S5+ Empty dustbin',
43+
}),
44+
'context': <ANY>,
45+
'entity_id': 'button.roborock_q10_s5_empty_dustbin',
46+
'last_changed': <ANY>,
47+
'last_reported': <ANY>,
48+
'last_updated': <ANY>,
49+
'state': 'unknown',
50+
})
51+
# ---
252
# name: test_buttons[button.roborock_s7_2_reset_air_filter_consumable-entry]
353
EntityRegistryEntrySnapshot({
454
'aliases': list([

tests/components/roborock/test_button.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,55 @@ async def test_press_a01_button_failure(
272272

273273
washing_machine.zeo.set_value.assert_called_once()
274274
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
275+
276+
277+
@pytest.mark.freeze_time("2023-10-30 08:50:00")
278+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
279+
async def test_press_q10_empty_dustbin_button_success(
280+
hass: HomeAssistant,
281+
bypass_api_client_fixture: None,
282+
setup_entry: MockConfigEntry,
283+
fake_q10_vacuum: FakeDevice,
284+
) -> None:
285+
"""Test pressing Q10 empty dustbin button entity."""
286+
entity_id = "button.roborock_q10_s5_empty_dustbin"
287+
288+
assert hass.states.get(entity_id) is not None
289+
await hass.services.async_call(
290+
"button",
291+
SERVICE_PRESS,
292+
blocking=True,
293+
target={"entity_id": entity_id},
294+
)
295+
296+
assert fake_q10_vacuum.b01_q10_properties is not None
297+
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
298+
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"
299+
300+
301+
@pytest.mark.freeze_time("2023-10-30 08:50:00")
302+
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
303+
async def test_press_q10_empty_dustbin_button_failure(
304+
hass: HomeAssistant,
305+
bypass_api_client_fixture: None,
306+
setup_entry: MockConfigEntry,
307+
fake_q10_vacuum: FakeDevice,
308+
) -> None:
309+
"""Test failure while pressing Q10 empty dustbin button entity."""
310+
entity_id = "button.roborock_q10_s5_empty_dustbin"
311+
assert fake_q10_vacuum.b01_q10_properties is not None
312+
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.side_effect = (
313+
RoborockException
314+
)
315+
316+
assert hass.states.get(entity_id) is not None
317+
with pytest.raises(HomeAssistantError, match="Error while calling empty_dustbin"):
318+
await hass.services.async_call(
319+
"button",
320+
SERVICE_PRESS,
321+
blocking=True,
322+
target={"entity_id": entity_id},
323+
)
324+
325+
fake_q10_vacuum.b01_q10_properties.vacuum.empty_dustbin.assert_called_once()
326+
assert hass.states.get(entity_id).state == "2023-10-30T08:50:00+00:00"

0 commit comments

Comments
 (0)