Skip to content

Commit 70c1c48

Browse files
committed
feat: add stronger mop washing support
1 parent dfa2fb1 commit 70c1c48

File tree

6 files changed

+174
-20
lines changed

6 files changed

+174
-20
lines changed

roborock/data/v1/v1_code_mappings.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -587,17 +587,6 @@ class RoborockDockDustCollectionModeCode(RoborockEnum):
587587
max = 4
588588

589589

590-
class RoborockDockWashTowelModeCode(RoborockEnum):
591-
"""Describes the wash towel mode of the vacuum cleaner."""
592-
593-
# TODO: Get the correct values for various different docks
594-
unknown = -9999
595-
light = 0
596-
balanced = 1
597-
deep = 2
598-
smart = 10
599-
600-
601590
class RoborockStateCode(RoborockEnum):
602591
unknown = 0
603592
starting = 1

roborock/data/v1/v1_containers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from roborock.exceptions import RoborockException
4040

4141
from ..containers import RoborockBase, RoborockBaseTimer, _attr_repr
42+
from .v1_clean_modes import WashTowelModes
4243
from .v1_code_mappings import (
4344
CleanFluidStatus,
4445
ClearWaterBoxStatus,
@@ -48,7 +49,6 @@
4849
RoborockDockDustCollectionModeCode,
4950
RoborockDockErrorCode,
5051
RoborockDockTypeCode,
51-
RoborockDockWashTowelModeCode,
5252
RoborockErrorCode,
5353
RoborockFanPowerCode,
5454
RoborockFanSpeedP10,
@@ -593,7 +593,7 @@ class DustCollectionMode(RoborockBase):
593593

594594
@dataclass
595595
class WashTowelMode(RoborockBase):
596-
wash_mode: RoborockDockWashTowelModeCode | None = None
596+
wash_mode: WashTowelModes | None = None
597597

598598

599599
@dataclass

roborock/devices/traits/v1/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,13 @@ def __init__(
188188
self._map_rpc_channel = map_rpc_channel
189189
self._web_api = web_api
190190
self._device_cache = device_cache
191-
191+
self.device_features = DeviceFeaturesTrait(product, self._device_cache)
192192
self.status = StatusTrait(product)
193193
self.consumables = ConsumableTrait()
194194
self.rooms = RoomsTrait(home_data)
195195
self.maps = MapsTrait(self.status)
196196
self.map_content = MapContentTrait(map_parser_config)
197197
self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, self._device_cache)
198-
self.device_features = DeviceFeaturesTrait(product, self._device_cache)
199198
self.network_info = NetworkInfoTrait(device_uid, self._device_cache)
200199
self.routines = RoutinesTrait(device_uid, web_api)
201200

@@ -246,7 +245,10 @@ async def discover_features(self) -> None:
246245
_LOGGER.debug("Trait '%s' not supported, skipping", item.name)
247246
continue
248247
_LOGGER.debug("Trait '%s' is supported, initializing", item.name)
249-
trait = item_type()
248+
if item_type is WashTowelModeTrait:
249+
trait = item_type(self.device_features)
250+
else:
251+
trait = item_type()
250252
setattr(self, item.name, trait)
251253
trait._rpc_channel = self._get_rpc_channel(trait)
252254

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"""Trait for wash towel mode."""
22

3-
from roborock.data import WashTowelMode
3+
from functools import cached_property
4+
5+
from roborock.data import WashTowelMode, WashTowelModes, get_wash_towel_modes
46
from roborock.device_features import is_wash_n_fill_dock
57
from roborock.devices.traits.v1 import common
8+
from roborock.devices.traits.v1.device_features import DeviceFeaturesTrait
69
from roborock.roborock_typing import RoborockCommand
710

811

@@ -11,3 +14,29 @@ class WashTowelModeTrait(WashTowelMode, common.V1TraitMixin):
1114

1215
command = RoborockCommand.GET_WASH_TOWEL_MODE
1316
requires_dock_type = is_wash_n_fill_dock
17+
18+
def __init__(
19+
self,
20+
device_feature_trait: DeviceFeaturesTrait | None = None,
21+
wash_mode: WashTowelModes | None = None,
22+
) -> None:
23+
self.device_feature_trait = device_feature_trait
24+
self.wash_mode = wash_mode
25+
26+
@cached_property
27+
def wash_towel_mode_options(self) -> list[WashTowelModes]:
28+
if self.device_feature_trait is None:
29+
return []
30+
return get_wash_towel_modes(self.device_feature_trait)
31+
32+
async def set_wash_towel_mode(self, mode: WashTowelModes) -> None:
33+
"""Set the wash towel mode."""
34+
await self.rpc_channel.send_command(RoborockCommand.SET_WASH_TOWEL_MODE, params={"wash_mode": mode.code})
35+
36+
async def start_wash(self) -> None:
37+
"""Start washing the mop."""
38+
await self.rpc_channel.send_command(RoborockCommand.APP_START_WASH)
39+
40+
async def stop_wash(self) -> None:
41+
"""Stop washing the mop."""
42+
await self.rpc_channel.send_command(RoborockCommand.APP_STOP_WASH)

tests/devices/traits/v1/fixtures.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,4 @@ async def discover_features_fixture(
124124
await device.connect()
125125
assert device.v1_properties.status.dock_type == dock_type_code
126126
mock_rpc_channel.send_command.reset_mock()
127+
mock_rpc_channel.send_command.side_effect = None

tests/devices/traits/v1/test_wash_towel_mode.py

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
import pytest
66

7-
from roborock.data import RoborockDockTypeCode, RoborockDockWashTowelModeCode
7+
from roborock.data import (
8+
RoborockDockTypeCode,
9+
WashTowelModes,
10+
)
811
from roborock.devices.device import RoborockDevice
912
from roborock.devices.traits.v1.wash_towel_mode import WashTowelModeTrait
1013
from roborock.roborock_typing import RoborockCommand
1114

12-
WASH_TOWEL_MODE_DATA = [{"wash_mode": RoborockDockWashTowelModeCode.smart}]
15+
WASH_TOWEL_MODE_DATA = {"wash_mode": WashTowelModes.SMART.code}
1316

1417

1518
@pytest.fixture(name="wash_towel_mode")
@@ -50,7 +53,7 @@ async def test_wash_towel_mode_available(
5053
]
5154
)
5255

53-
assert wash_towel_mode.wash_mode == RoborockDockWashTowelModeCode.smart
56+
assert wash_towel_mode.wash_mode == WashTowelModes.SMART
5457

5558

5659
@pytest.mark.parametrize(
@@ -65,3 +68,133 @@ async def test_unsupported_wash_towel_mode(
6568
) -> None:
6669
"""Test that the trait is not available for unsupported dock types."""
6770
assert wash_towel_mode is None
71+
72+
73+
@pytest.mark.parametrize(
74+
("dock_type_code"),
75+
[(RoborockDockTypeCode.s8_dock)],
76+
)
77+
@pytest.mark.parametrize(
78+
("wash_mode"),
79+
[
80+
(WashTowelModes.SMART),
81+
(WashTowelModes.LIGHT),
82+
],
83+
)
84+
async def test_set_wash_towel_mode(
85+
wash_towel_mode: WashTowelModeTrait | None,
86+
mock_rpc_channel: AsyncMock,
87+
wash_mode: WashTowelModes,
88+
dock_type_code: RoborockDockTypeCode,
89+
) -> None:
90+
"""Test setting the wash towel mode."""
91+
assert wash_towel_mode is not None
92+
93+
await wash_towel_mode.set_wash_towel_mode(wash_mode)
94+
95+
mock_rpc_channel.send_command.assert_called_with(
96+
RoborockCommand.SET_WASH_TOWEL_MODE, params={"wash_mode": wash_mode.code}
97+
)
98+
99+
100+
@pytest.mark.parametrize(
101+
("dock_type_code"),
102+
[(RoborockDockTypeCode.s8_dock)],
103+
)
104+
async def test_start_wash(
105+
wash_towel_mode: WashTowelModeTrait | None,
106+
mock_rpc_channel: AsyncMock,
107+
dock_type_code: RoborockDockTypeCode,
108+
) -> None:
109+
"""Test starting the wash."""
110+
assert wash_towel_mode is not None
111+
112+
await wash_towel_mode.start_wash()
113+
114+
mock_rpc_channel.send_command.assert_called_with(RoborockCommand.APP_START_WASH)
115+
116+
117+
@pytest.mark.parametrize(
118+
("dock_type_code"),
119+
[(RoborockDockTypeCode.s8_dock)],
120+
)
121+
async def test_stop_wash(
122+
wash_towel_mode: WashTowelModeTrait | None,
123+
mock_rpc_channel: AsyncMock,
124+
dock_type_code: RoborockDockTypeCode,
125+
) -> None:
126+
"""Test stopping the wash."""
127+
assert wash_towel_mode is not None
128+
129+
await wash_towel_mode.stop_wash()
130+
131+
mock_rpc_channel.send_command.assert_called_with(RoborockCommand.APP_STOP_WASH)
132+
133+
134+
@pytest.mark.parametrize(
135+
("dock_type_code"),
136+
[(RoborockDockTypeCode.s8_dock)],
137+
)
138+
@pytest.mark.parametrize(
139+
(
140+
"is_super_deep_wash_supported",
141+
"is_dirty_replenish_clean_supported",
142+
"expected_modes",
143+
),
144+
[
145+
(
146+
False,
147+
False,
148+
[WashTowelModes.LIGHT, WashTowelModes.BALANCED, WashTowelModes.DEEP],
149+
),
150+
(
151+
True,
152+
False,
153+
[
154+
WashTowelModes.LIGHT,
155+
WashTowelModes.BALANCED,
156+
WashTowelModes.DEEP,
157+
WashTowelModes.SUPER_DEEP,
158+
],
159+
),
160+
(
161+
False,
162+
True,
163+
[
164+
WashTowelModes.LIGHT,
165+
WashTowelModes.BALANCED,
166+
WashTowelModes.DEEP,
167+
WashTowelModes.SMART,
168+
],
169+
),
170+
(
171+
True,
172+
True,
173+
[
174+
WashTowelModes.LIGHT,
175+
WashTowelModes.BALANCED,
176+
WashTowelModes.DEEP,
177+
WashTowelModes.SMART,
178+
],
179+
),
180+
],
181+
)
182+
async def test_wash_towel_mode_options(
183+
wash_towel_mode: WashTowelModeTrait | None,
184+
dock_type_code: RoborockDockTypeCode,
185+
is_super_deep_wash_supported: bool,
186+
is_dirty_replenish_clean_supported: bool,
187+
expected_modes: list[WashTowelModes],
188+
) -> None:
189+
"""Test what modes are available based on device features."""
190+
assert wash_towel_mode is not None
191+
# We need to clear the cached property to ensure it re-reads the features
192+
if "wash_towel_mode_options" in wash_towel_mode.__dict__:
193+
del wash_towel_mode.__dict__["wash_towel_mode_options"]
194+
195+
# Mock the device features
196+
assert wash_towel_mode.device_feature_trait is not None
197+
wash_towel_mode.device_feature_trait.is_super_deep_wash_supported = is_super_deep_wash_supported
198+
wash_towel_mode.device_feature_trait.is_dirty_replenish_clean_supported = is_dirty_replenish_clean_supported
199+
200+
assert wash_towel_mode.wash_towel_mode_options == expected_modes

0 commit comments

Comments
 (0)