Skip to content

Commit 258e918

Browse files
Lash-LCopilot
andauthored
feat: add stronger mop washing support (#765)
* feat: add stronger mop washing support * chore: call super init Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * chore: simplify device_feature_trait usage * chore: address test comments * chore: move outside of loop * chore: clean up and better match status trait --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 9d88efa commit 258e918

File tree

6 files changed

+179
-18
lines changed

6 files changed

+179
-18
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 NamedRoomMapping, 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,
@@ -750,7 +750,7 @@ class DustCollectionMode(RoborockBase):
750750

751751
@dataclass
752752
class WashTowelMode(RoborockBase):
753-
wash_mode: RoborockDockWashTowelModeCode | None = None
753+
wash_mode: WashTowelModes | None = None
754754

755755

756756
@dataclass

roborock/devices/traits/v1/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ async def discover_features(self) -> None:
234234
# Dock type also acts like a device feature for some traits.
235235
dock_type = await self._dock_type()
236236

237+
# Initialize traits with special arguments before the generic loop
238+
if self.wash_towel_mode is None and self._is_supported(WashTowelModeTrait, "wash_towel_mode", dock_type):
239+
wash_towel_mode = WashTowelModeTrait(self.device_features)
240+
wash_towel_mode._rpc_channel = self._get_rpc_channel(wash_towel_mode) # type: ignore[assignment]
241+
self.wash_towel_mode = wash_towel_mode
242+
237243
# Dynamically create any traits that need to be populated
238244
for item in fields(self):
239245
if (trait := getattr(self, item.name, None)) is not None:
Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
"""Trait for wash towel mode."""
22

3-
from roborock.data import WashTowelMode
3+
from functools import cached_property
4+
from typing import Self
5+
6+
from roborock.data import WashTowelMode, WashTowelModes, get_wash_towel_modes
47
from roborock.device_features import is_wash_n_fill_dock
58
from roborock.devices.traits.v1 import common
9+
from roborock.devices.traits.v1.device_features import DeviceFeaturesTrait
610
from roborock.roborock_typing import RoborockCommand
711

812

@@ -11,3 +15,34 @@ class WashTowelModeTrait(WashTowelMode, common.V1TraitMixin):
1115

1216
command = RoborockCommand.GET_WASH_TOWEL_MODE
1317
requires_dock_type = is_wash_n_fill_dock
18+
19+
def __init__(
20+
self,
21+
device_feature_trait: DeviceFeaturesTrait,
22+
) -> None:
23+
super().__init__()
24+
self.device_feature_trait = device_feature_trait
25+
26+
def _parse_response(self, response: common.V1ResponseData) -> Self:
27+
"""Parse the response from the device into a WashTowelMode object."""
28+
if isinstance(response, list):
29+
response = response[0]
30+
if isinstance(response, dict):
31+
return WashTowelMode.from_dict(response)
32+
raise ValueError(f"Unexpected wash towel mode format: {response!r}")
33+
34+
@cached_property
35+
def wash_towel_mode_options(self) -> list[WashTowelModes]:
36+
return get_wash_towel_modes(self.device_feature_trait)
37+
38+
async def set_wash_towel_mode(self, mode: WashTowelModes) -> None:
39+
"""Set the wash towel mode."""
40+
await self.rpc_channel.send_command(RoborockCommand.SET_WASH_TOWEL_MODE, params={"wash_mode": mode.code})
41+
42+
async def start_wash(self) -> None:
43+
"""Start washing the mop."""
44+
await self.rpc_channel.send_command(RoborockCommand.APP_START_WASH)
45+
46+
async def stop_wash(self) -> None:
47+
"""Stop washing the mop."""
48+
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
@@ -126,3 +126,4 @@ async def discover_features_fixture(
126126
await device.connect()
127127
assert device.v1_properties.status.dock_type == dock_type_code
128128
mock_rpc_channel.send_command.reset_mock()
129+
mock_rpc_channel.send_command.side_effect = None

tests/devices/traits/v1/test_wash_towel_mode.py

Lines changed: 134 additions & 4 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")
@@ -31,7 +34,7 @@ def wash_towel_mode_trait(
3134
],
3235
)
3336
async def test_wash_towel_mode_available(
34-
wash_towel_mode: WashTowelModeTrait | None,
37+
wash_towel_mode: WashTowelModeTrait,
3538
mock_rpc_channel: AsyncMock,
3639
dock_type_code: RoborockDockTypeCode,
3740
) -> None:
@@ -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,130 @@ 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,
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,
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,
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,
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+
192+
# Mock the device features
193+
assert wash_towel_mode.device_feature_trait is not None
194+
wash_towel_mode.device_feature_trait.is_super_deep_wash_supported = is_super_deep_wash_supported
195+
wash_towel_mode.device_feature_trait.is_dirty_replenish_clean_supported = is_dirty_replenish_clean_supported
196+
197+
assert wash_towel_mode.wash_towel_mode_options == expected_modes

0 commit comments

Comments
 (0)