Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions roborock/devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def device_creator(home_data: HomeData, device: HomeDataDevice, product: HomeDat
channel.rpc_channel,
channel.mqtt_rpc_channel,
channel.map_rpc_channel,
web_api,
cache,
map_parser_config=map_parser_config,
)
Expand Down
9 changes: 9 additions & 0 deletions roborock/devices/traits/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from roborock.devices.traits import Trait
from roborock.devices.v1_rpc_channel import V1RpcChannel
from roborock.map.map_parser import MapParserConfig
from roborock.web_api import UserWebApiClient

from .child_lock import ChildLockTrait
from .clean_summary import CleanSummaryTrait
Expand All @@ -56,6 +57,7 @@
from .maps import MapsTrait
from .network_info import NetworkInfoTrait
from .rooms import RoomsTrait
from .routines import RoutinesTrait
from .smart_wash_params import SmartWashParamsTrait
from .status import StatusTrait
from .valley_electricity_timer import ValleyElectricityTimerTrait
Expand Down Expand Up @@ -85,6 +87,7 @@
"WashTowelModeTrait",
"SmartWashParamsTrait",
"NetworkInfoTrait",
"RoutinesTrait",
]


Expand All @@ -108,6 +111,7 @@ class PropertiesApi(Trait):
home: HomeTrait
device_features: DeviceFeaturesTrait
network_info: NetworkInfoTrait
routines: RoutinesTrait

# Optional features that may not be supported on all devices
child_lock: ChildLockTrait | None = None
Expand All @@ -126,6 +130,7 @@ def __init__(
rpc_channel: V1RpcChannel,
mqtt_rpc_channel: V1RpcChannel,
map_rpc_channel: V1RpcChannel,
web_api: UserWebApiClient,
cache: Cache,
map_parser_config: MapParserConfig | None = None,
) -> None:
Expand All @@ -134,6 +139,7 @@ def __init__(
self._rpc_channel = rpc_channel
self._mqtt_rpc_channel = mqtt_rpc_channel
self._map_rpc_channel = map_rpc_channel
self._web_api = web_api
self._cache = cache

self.status = StatusTrait(product)
Expand All @@ -144,6 +150,7 @@ def __init__(
self.home = HomeTrait(self.status, self.maps, self.map_content, self.rooms, cache)
self.device_features = DeviceFeaturesTrait(product.product_nickname, cache)
self.network_info = NetworkInfoTrait(device_uid, cache)
self.routines = RoutinesTrait(device_uid, web_api)

# Dynamically create any traits that need to be populated
for item in fields(self):
Expand Down Expand Up @@ -267,6 +274,7 @@ def create(
rpc_channel: V1RpcChannel,
mqtt_rpc_channel: V1RpcChannel,
map_rpc_channel: V1RpcChannel,
web_api: UserWebApiClient,
cache: Cache,
map_parser_config: MapParserConfig | None = None,
) -> PropertiesApi:
Expand All @@ -278,6 +286,7 @@ def create(
rpc_channel,
mqtt_rpc_channel,
map_rpc_channel,
web_api,
cache,
map_parser_config,
)
26 changes: 26 additions & 0 deletions roborock/devices/traits/v1/routines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Routines trait for V1 devices."""

from roborock.data.containers import HomeDataScene
from roborock.web_api import UserWebApiClient


class RoutinesTrait:
"""Trait for interacting with routines."""

def __init__(self, device_id: str, web_api: UserWebApiClient) -> None:
"""Initialize the routines trait."""
self._device_id = device_id
self._web_api = web_api

async def get_routines(self) -> list[HomeDataScene]:
"""Get available routines."""
return await self._web_api.get_routines(self._device_id)

async def execute_routine(self, routine_id: int) -> None:
"""Execute a routine by its ID.

Technically, routines are per-device, but the API does not
require the device ID to execute them. This can execute a
routine for any device but it is exposed here for convenience.
"""
await self._web_api.execute_routine(routine_id)
8 changes: 8 additions & 0 deletions roborock/web_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,11 @@ def __init__(self, web_api: RoborockApiClient, user_data: UserData) -> None:
async def get_home_data(self) -> HomeData:
"""Fetch home data using the API client."""
return await self._web_api.get_home_data_v3(self._user_data)

async def get_routines(self, device_id: str) -> list[HomeDataScene]:
"""Fetch routines (scenes) for a specific device."""
return await self._web_api.get_scenes(self._user_data, device_id)

async def execute_routine(self, scene_id: int) -> None:
"""Execute a specific routine (scene) by its ID."""
await self._web_api.execute_scene(self._user_data, scene_id)
1 change: 1 addition & 0 deletions tests/devices/test_v1_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def device_fixture(channel: AsyncMock, rpc_channel: AsyncMock, mqtt_rpc_channel:
rpc_channel,
mqtt_rpc_channel,
AsyncMock(),
AsyncMock(),
NoCache(),
),
)
Expand Down
8 changes: 8 additions & 0 deletions tests/devices/traits/v1/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ def map_rpc_channel_fixture() -> AsyncMock:
return AsyncMock()


@pytest.fixture(autouse=True, name="web_api_client")
def web_api_client_fixture() -> AsyncMock:
"""Fixture to set up the channel for tests."""
Comment thread
allenporter marked this conversation as resolved.
Outdated
return AsyncMock()


@pytest.fixture(autouse=True, name="roborock_cache")
def roborock_cache_fixture() -> Cache:
"""Fixture to provide a NoCache instance for tests."""
Expand All @@ -52,6 +58,7 @@ def device_fixture(
mock_rpc_channel: AsyncMock,
mock_mqtt_rpc_channel: AsyncMock,
mock_map_rpc_channel: AsyncMock,
web_api_client: AsyncMock,
roborock_cache: Cache,
) -> RoborockDevice:
"""Fixture to set up the device for tests."""
Expand All @@ -66,6 +73,7 @@ def device_fixture(
mock_rpc_channel,
mock_mqtt_rpc_channel,
mock_map_rpc_channel,
web_api_client,
roborock_cache,
),
)
Expand Down
31 changes: 31 additions & 0 deletions tests/devices/traits/v1/test_routines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Tests for the RoutinesTrait."""

from unittest.mock import AsyncMock

import pytest

from roborock.data.containers import HomeDataScene
from roborock.devices.device import RoborockDevice
from roborock.devices.traits.v1.routines import RoutinesTrait


@pytest.fixture(name="routines_trait")
def routines_trait_fixture(device: RoborockDevice) -> RoutinesTrait:
"""Fixture for the routines trait."""
assert device.v1_properties
return device.v1_properties.routines


async def test_get_routines(routines_trait: RoutinesTrait, web_api_client: AsyncMock) -> None:
"""Test getting routines."""
web_api_client.get_routines.return_value = [HomeDataScene(id=1, name="test_scene")]
routines = await routines_trait.get_routines()
assert len(routines) == 1
assert routines[0].name == "test_scene"
web_api_client.get_routines.assert_called_once()


async def test_execute_routine(routines_trait: RoutinesTrait, web_api_client: AsyncMock) -> None:
"""Test executing a routine."""
await routines_trait.execute_routine(1)
web_api_client.execute_routine.assert_called_once_with(1)
Loading