Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions roborock/data/v1/v1_clean_modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ class WaterModes(RoborockModeEnum):
SMART_MODE = ("smart_mode", 209)
PURE_WATER_FLOW_START = ("slight", 221)
PURE_WATER_FLOW_SMALL = ("low", 225)
PURE_WATER_FLOW_GENTLE = ("gentle", 230)
PURE_WATER_FLOW_MIDDLE = ("medium", 235)
PURE_WATER_FLOW_LARGE = ("moderate", 245)
PURE_WATER_SUPER_BEGIN = ("high", 248)
PURE_WATER_FLOW_LARGE = ("moderate", 240)
PURE_WATER_SUPER_BEGIN = ("high", 245)
PURE_WATER_FLOW_END = ("extreme", 250)


Expand Down Expand Up @@ -91,12 +92,25 @@ class CleaningMode(StrEnum):
200: WaterModes.OFF,
221: WaterModes.PURE_WATER_FLOW_START,
225: WaterModes.PURE_WATER_FLOW_SMALL,
230: WaterModes.PURE_WATER_FLOW_GENTLE,
235: WaterModes.PURE_WATER_FLOW_MIDDLE,
245: WaterModes.PURE_WATER_FLOW_LARGE,
248: WaterModes.PURE_WATER_SUPER_BEGIN,
240: WaterModes.PURE_WATER_FLOW_LARGE,
245: WaterModes.PURE_WATER_SUPER_BEGIN,
250: WaterModes.PURE_WATER_FLOW_END,
}

# Water slide devices report the raw slider position (1-30) as 220 + position.
# The app groups positions into named ranges, keyed here by their last position.
_WATER_SLIDE_RANGES: tuple[tuple[int, WaterModes], ...] = (
(3, WaterModes.PURE_WATER_FLOW_START),
(7, WaterModes.PURE_WATER_FLOW_SMALL),
(12, WaterModes.PURE_WATER_FLOW_GENTLE),
(17, WaterModes.PURE_WATER_FLOW_MIDDLE),
(22, WaterModes.PURE_WATER_FLOW_LARGE),
(27, WaterModes.PURE_WATER_SUPER_BEGIN),
(30, WaterModes.PURE_WATER_FLOW_END),
)

ModeEnumT = TypeVar("ModeEnumT", bound=RoborockModeEnum)


Expand Down Expand Up @@ -183,6 +197,19 @@ def get_water_modes(features: DeviceFeatures) -> list[WaterModes]:
return supported_modes


def get_water_slide_mode_name(code: int) -> str | None:
"""Get the app's range name for a water slide code, or None if out of range."""
if code == 200:
return WaterModes.OFF.value
position = code - 220
if position < 1:
return None
for last_position, mode in _WATER_SLIDE_RANGES:
if position <= last_position:
return mode.value
return None


def get_water_mode_mapping(features: DeviceFeatures) -> dict[int, str]:
"""Get water mode mapping by supported feature set.

Expand Down
3 changes: 3 additions & 0 deletions roborock/devices/traits/v1/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
get_current_cleaning_mode,
get_water_mode_mapping,
get_water_modes,
get_water_slide_mode_name,
resolve_cleaning_mode,
)
from roborock.devices.traits.common import DpsDataConverter, TraitUpdateListener
Expand Down Expand Up @@ -103,6 +104,8 @@ def fan_speed_name(self) -> str | None:
def water_mode_name(self) -> str | None:
if self.water_box_mode is None:
return None
if self._device_features_trait.is_water_slide_mode_supported:
return get_water_slide_mode_name(self.water_box_mode)
return self.water_mode_mapping.get(self.water_box_mode)

@property
Expand Down
38 changes: 36 additions & 2 deletions tests/devices/traits/v1/test_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,15 +586,17 @@ def test_water_slide_mode_mapping() -> None:
200: "off",
221: "slight",
225: "low",
230: "gentle",
235: "medium",
245: "moderate",
248: "high",
240: "moderate",
245: "high",
250: "extreme",
}
assert [mode.value for mode in status_trait.water_mode_options] == [
"off",
"slight",
"low",
"gentle",
"medium",
"moderate",
"high",
Expand All @@ -607,6 +609,38 @@ def test_water_slide_mode_mapping() -> None:
assert status_trait.water_mode_name == "off"


@pytest.mark.parametrize(
("water_box_mode", "expected_name"),
[
(200, "off"),
(221, "slight"),
(223, "slight"),
(224, "low"),
(227, "low"),
(228, "gentle"),
(232, "gentle"),
(233, "medium"),
(236, "medium"),
(237, "medium"),
(238, "moderate"),
(242, "moderate"),
(243, "high"),
(247, "high"),
(248, "extreme"),
(250, "extreme"),
(220, None),
(251, None),
(999, None),
],
)
def test_water_slide_mode_name_covers_slider_positions(water_box_mode: int, expected_name: str | None) -> None:
"""Every slider position resolves to the label of its range in the Roborock app."""
status_trait = _create_cleaning_mode_status_trait(is_water_slide_mode_supported=True)
status_trait.water_box_mode = water_box_mode

assert status_trait.water_mode_name == expected_name


def test_update_from_dps(status_trait: StatusTrait) -> None:
"""Test updating status from data protocol push message."""
assert status_trait.battery is None
Expand Down
Loading