Skip to content
Open
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
11 changes: 0 additions & 11 deletions roborock/data/v1/v1_code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,17 +587,6 @@ class RoborockDockDustCollectionModeCode(RoborockEnum):
max = 4


class RoborockDockWashTowelModeCode(RoborockEnum):
"""Describes the wash towel mode of the vacuum cleaner."""

# TODO: Get the correct values for various different docks
unknown = -9999
light = 0
balanced = 1
deep = 2
smart = 10


class RoborockStateCode(RoborockEnum):
unknown = 0
starting = 1
Expand Down
4 changes: 2 additions & 2 deletions roborock/data/v1/v1_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from roborock.exceptions import RoborockException

from ..containers import RoborockBase, RoborockBaseTimer, _attr_repr
from .v1_clean_modes import WashTowelModes
from .v1_code_mappings import (
CleanFluidStatus,
ClearWaterBoxStatus,
Expand All @@ -48,7 +49,6 @@
RoborockDockDustCollectionModeCode,
RoborockDockErrorCode,
RoborockDockTypeCode,
RoborockDockWashTowelModeCode,
RoborockErrorCode,
RoborockFanPowerCode,
RoborockFanSpeedP10,
Expand Down Expand Up @@ -593,7 +593,7 @@ class DustCollectionMode(RoborockBase):

@dataclass
class WashTowelMode(RoborockBase):
wash_mode: RoborockDockWashTowelModeCode | None = None
wash_mode: WashTowelModes | None = None


@dataclass
Expand Down
8 changes: 5 additions & 3 deletions roborock/devices/traits/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,13 @@ def __init__(
self._map_rpc_channel = map_rpc_channel
self._web_api = web_api
self._device_cache = device_cache

self.device_features = DeviceFeaturesTrait(product, self._device_cache)
self.status = StatusTrait(product)
self.consumables = ConsumableTrait()
self.rooms = RoomsTrait(home_data)
self.maps = MapsTrait(self.status)
self.map_content = MapContentTrait(map_parser_config)
self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, self._device_cache)
self.device_features = DeviceFeaturesTrait(product, self._device_cache)
self.network_info = NetworkInfoTrait(device_uid, self._device_cache)
self.routines = RoutinesTrait(device_uid, web_api)

Expand Down Expand Up @@ -246,7 +245,10 @@ async def discover_features(self) -> None:
_LOGGER.debug("Trait '%s' not supported, skipping", item.name)
continue
_LOGGER.debug("Trait '%s' is supported, initializing", item.name)
trait = item_type()
if item_type is WashTowelModeTrait:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about initializing this outside the loop before entering? This would be similar to how it works in the constructor where you first create the types that have special argument, then create the rest that haven't been set. This code will ignore it if its set already.

trait = item_type(self.device_features)
else:
trait = item_type()
setattr(self, item.name, trait)
trait._rpc_channel = self._get_rpc_channel(trait)

Expand Down
32 changes: 31 additions & 1 deletion roborock/devices/traits/v1/wash_towel_mode.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Trait for wash towel mode."""

from roborock.data import WashTowelMode
from functools import cached_property

from roborock.data import WashTowelMode, WashTowelModes, get_wash_towel_modes
from roborock.device_features import is_wash_n_fill_dock
from roborock.devices.traits.v1 import common
from roborock.devices.traits.v1.device_features import DeviceFeaturesTrait
from roborock.roborock_typing import RoborockCommand


Expand All @@ -11,3 +14,30 @@ class WashTowelModeTrait(WashTowelMode, common.V1TraitMixin):

command = RoborockCommand.GET_WASH_TOWEL_MODE
requires_dock_type = is_wash_n_fill_dock

def __init__(
self,
device_feature_trait: DeviceFeaturesTrait | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is this will always be set so it won't ever be None. Is that righT? (Can simplify a check below)

wash_mode: WashTowelModes | None = None,
) -> None:
super().__init__()
self.device_feature_trait = device_feature_trait
self.wash_mode = wash_mode

@cached_property
def wash_towel_mode_options(self) -> list[WashTowelModes]:
if self.device_feature_trait is None:
return []
return get_wash_towel_modes(self.device_feature_trait)

async def set_wash_towel_mode(self, mode: WashTowelModes) -> None:
"""Set the wash towel mode."""
await self.rpc_channel.send_command(RoborockCommand.SET_WASH_TOWEL_MODE, params={"wash_mode": mode.code})

async def start_wash(self) -> None:
"""Start washing the mop."""
await self.rpc_channel.send_command(RoborockCommand.APP_START_WASH)

async def stop_wash(self) -> None:
"""Stop washing the mop."""
await self.rpc_channel.send_command(RoborockCommand.APP_STOP_WASH)
1 change: 1 addition & 0 deletions tests/devices/traits/v1/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,4 @@ async def discover_features_fixture(
await device.connect()
assert device.v1_properties.status.dock_type == dock_type_code
mock_rpc_channel.send_command.reset_mock()
mock_rpc_channel.send_command.side_effect = None
139 changes: 136 additions & 3 deletions tests/devices/traits/v1/test_wash_towel_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

import pytest

from roborock.data import RoborockDockTypeCode, RoborockDockWashTowelModeCode
from roborock.data import (
RoborockDockTypeCode,
WashTowelModes,
)
from roborock.devices.device import RoborockDevice
from roborock.devices.traits.v1.wash_towel_mode import WashTowelModeTrait
from roborock.roborock_typing import RoborockCommand

WASH_TOWEL_MODE_DATA = [{"wash_mode": RoborockDockWashTowelModeCode.smart}]
WASH_TOWEL_MODE_DATA = {"wash_mode": WashTowelModes.SMART.code}


@pytest.fixture(name="wash_towel_mode")
Expand Down Expand Up @@ -50,7 +53,7 @@ async def test_wash_towel_mode_available(
]
)

assert wash_towel_mode.wash_mode == RoborockDockWashTowelModeCode.smart
assert wash_towel_mode.wash_mode == WashTowelModes.SMART


@pytest.mark.parametrize(
Expand All @@ -65,3 +68,133 @@ async def test_unsupported_wash_towel_mode(
) -> None:
"""Test that the trait is not available for unsupported dock types."""
assert wash_towel_mode is None


@pytest.mark.parametrize(
("dock_type_code"),
[(RoborockDockTypeCode.s8_dock)],
)
@pytest.mark.parametrize(
("wash_mode"),
[
(WashTowelModes.SMART),
(WashTowelModes.LIGHT),
],
)
async def test_set_wash_towel_mode(
wash_towel_mode: WashTowelModeTrait | None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this doesn't need to be | None here and below

mock_rpc_channel: AsyncMock,
wash_mode: WashTowelModes,
dock_type_code: RoborockDockTypeCode,
) -> None:
"""Test setting the wash towel mode."""
assert wash_towel_mode is not None

await wash_towel_mode.set_wash_towel_mode(wash_mode)

mock_rpc_channel.send_command.assert_called_with(
RoborockCommand.SET_WASH_TOWEL_MODE, params={"wash_mode": wash_mode.code}
)


@pytest.mark.parametrize(
("dock_type_code"),
[(RoborockDockTypeCode.s8_dock)],
)
async def test_start_wash(
wash_towel_mode: WashTowelModeTrait | None,
mock_rpc_channel: AsyncMock,
dock_type_code: RoborockDockTypeCode,
) -> None:
"""Test starting the wash."""
assert wash_towel_mode is not None

await wash_towel_mode.start_wash()

mock_rpc_channel.send_command.assert_called_with(RoborockCommand.APP_START_WASH)


@pytest.mark.parametrize(
("dock_type_code"),
[(RoborockDockTypeCode.s8_dock)],
)
async def test_stop_wash(
wash_towel_mode: WashTowelModeTrait | None,
mock_rpc_channel: AsyncMock,
dock_type_code: RoborockDockTypeCode,
) -> None:
"""Test stopping the wash."""
assert wash_towel_mode is not None

await wash_towel_mode.stop_wash()

mock_rpc_channel.send_command.assert_called_with(RoborockCommand.APP_STOP_WASH)


@pytest.mark.parametrize(
("dock_type_code"),
[(RoborockDockTypeCode.s8_dock)],
)
@pytest.mark.parametrize(
(
"is_super_deep_wash_supported",
"is_dirty_replenish_clean_supported",
"expected_modes",
),
[
(
False,
False,
[WashTowelModes.LIGHT, WashTowelModes.BALANCED, WashTowelModes.DEEP],
),
(
True,
False,
[
WashTowelModes.LIGHT,
WashTowelModes.BALANCED,
WashTowelModes.DEEP,
WashTowelModes.SUPER_DEEP,
],
),
(
False,
True,
[
WashTowelModes.LIGHT,
WashTowelModes.BALANCED,
WashTowelModes.DEEP,
WashTowelModes.SMART,
],
),
(
True,
True,
[
WashTowelModes.LIGHT,
WashTowelModes.BALANCED,
WashTowelModes.DEEP,
WashTowelModes.SMART,
],
),
],
)
async def test_wash_towel_mode_options(
wash_towel_mode: WashTowelModeTrait | None,
dock_type_code: RoborockDockTypeCode,
is_super_deep_wash_supported: bool,
is_dirty_replenish_clean_supported: bool,
expected_modes: list[WashTowelModes],
) -> None:
"""Test what modes are available based on device features."""
assert wash_towel_mode is not None
# We need to clear the cached property to ensure it re-reads the features
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really required? My impression is we're testing with a new instance of WashTowelModeTrait for every test and there are new arguments to the trait each time. If that is not the case then it would also be a problem in production code?

if "wash_towel_mode_options" in wash_towel_mode.__dict__:
del wash_towel_mode.__dict__["wash_towel_mode_options"]

# Mock the device features
assert wash_towel_mode.device_feature_trait is not None
wash_towel_mode.device_feature_trait.is_super_deep_wash_supported = is_super_deep_wash_supported
wash_towel_mode.device_feature_trait.is_dirty_replenish_clean_supported = is_dirty_replenish_clean_supported

assert wash_towel_mode.wash_towel_mode_options == expected_modes