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
42 changes: 26 additions & 16 deletions docs/DEVICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,30 +564,40 @@ The new design:
```
roborock/
├── devices/ # Device management and channels
│ ├── device_manager.py # High-level device lifecycle
│ ├── channel.py # Base Channel interface
│ ├── mqtt_channel.py # MQTT channel implementation
│ ├── local_channel.py # Local TCP channel implementation
│ ├── v1_channel.py # V1 protocol channel with RPC strategies
│ ├── a01_channel.py # A01 protocol helpers
│ ├── b01_q7_channel.py # B01 Q7 protocol helpers
│ └── traits/ # Device-specific command traits
│ └── v1/ # V1 device traits
│ ├── __init__.py # Trait initialization
│ ├── clean.py # Cleaning commands
│ ├── map.py # Map management
│ ├── device_manager.py # High-level device lifecycle
│ ├── transport/ # Module for network connections to devices
│ | ├── channel.py # Base Channel interface
│ | ├── mqtt_channel.py # MQTT channel implementation
│ | ├── local_channel.py # Local TCP channel implementation
│ | └── ...
│ ├── rpc/ # Application-level protocol/device-specific glue
│ | ├── v1_channel.py # V1 protocol channel with RPC strategies
│ | ├── a01_channel.py # A01 protocol helpers
│ | ├── b01_q7_channel.py # B01 Q7 protocol helpers
│ | ├── b01_q10_channel.py # B01 Q10 protocol helpers
│ | └── ...
│ └── traits/ # High-level device-specific command traits
│ └── v1/ # V1 device traits
│ ├── __init__.py # Trait initialization
│ ├── clean.py # Cleaning commands
│ ├── map.py # Map management
│ └── ...
├── mqtt/ # MQTT session management
├── mqtt/ # MQTT session management
│ ├── session.py # Base session interface
│ └── roborock_session.py # MQTT session with idle timeout
├── protocols/ # Protocol encoders/decoders
├── protocols/ # Low level protocol encoders/decoders
│ ├── v1_protocol.py # V1 JSON RPC protocol
│ ├── a01_protocol.py # A01 protocol
│ ├── b01_q7_protocol.py # B01 Q7 protocol
│ └── ...
└── data/ # Data containers and mappings
└── data/ # Data containers and mappings
├── containers.py # Status, HomeData, etc.
└── v1/ # V1-specific data structures
├── v1/ # V1-specific data structures
├── dyad/ # Dyad-specific data structures
├── zeo/ # Zeo-specific data structures
├── b01_q7/ # B01 Q7-specific data structures
├── b01_q10/ # B01 Q10-specific data structures
└── ...
```

### Threading Model
Expand Down
2 changes: 1 addition & 1 deletion roborock/devices/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
from roborock.roborock_message import RoborockMessage
from roborock.util import RoborockLoggerAdapter

from .channel import Channel
from .traits import Trait
from .traits.traits_mixin import TraitsMixin
from .transport.channel import Channel

_LOGGER = logging.getLogger(__name__)

Expand Down
6 changes: 3 additions & 3 deletions roborock/devices/device_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
from roborock.web_api import RoborockApiClient, UserWebApiClient

from .cache import Cache, DeviceCache, NoCache
from .channel import Channel
from .mqtt_channel import create_mqtt_channel
from .rpc.v1_channel import create_v1_channel
from .traits import Trait, a01, b01, v1
from .v1_channel import create_v1_channel
from .transport.channel import Channel
from .transport.mqtt_channel import create_mqtt_channel

_LOGGER = logging.getLogger(__name__)

Expand Down
14 changes: 14 additions & 0 deletions roborock/devices/rpc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Module for sending device specific commands to Roborock devices.

This module provides a application-level interface for sending commands to Roborock
devices. These modules can be used by traits (higher level APIs) to send commands.

Each module may contain details that are common across all traits, and may depend
on the transport level modules (e.g. MQTT, Local device) for issuing the
commands.

The lowest level protocol encoding is handled in `roborock.protocols` which
have no dependencies on the transport level modules.
"""

__all__: list[str] = []
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from collections.abc import Callable
from typing import Any, overload

from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.exceptions import RoborockException
from roborock.protocols.a01_protocol import (
decode_rpc_response,
Expand All @@ -16,8 +17,6 @@
RoborockZeoProtocol,
)

from .mqtt_channel import MqttChannel

_LOGGER = logging.getLogger(__name__)
_TIMEOUT = 10.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import logging

from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.exceptions import RoborockException
from roborock.protocols.b01_q10_protocol import (
ParamsType,
encode_mqtt_payload,
)

from .mqtt_channel import MqttChannel

_LOGGER = logging.getLogger(__name__)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging
from typing import Any

from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.exceptions import RoborockException
from roborock.protocols.b01_q7_protocol import (
Q7RequestMessage,
Expand All @@ -15,8 +16,6 @@
)
from roborock.roborock_message import RoborockMessage

from .mqtt_channel import MqttChannel

_LOGGER = logging.getLogger(__name__)
_TIMEOUT = 10.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
from typing import Any, TypeVar

from roborock.data import HomeDataDevice, NetworkInfo, RoborockBase, UserData
from roborock.devices.cache import DeviceCache
from roborock.devices.transport.channel import Channel
from roborock.devices.transport.local_channel import LocalChannel, LocalSession, create_local_session
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.exceptions import RoborockException
from roborock.mqtt.health_manager import HealthManager
from roborock.mqtt.session import MqttParams, MqttSession
Expand All @@ -32,11 +36,6 @@
from roborock.roborock_typing import RoborockCommand
from roborock.util import RoborockLoggerAdapter

from .cache import DeviceCache
from .channel import Channel
from .local_channel import LocalChannel, LocalSession, create_local_session
from .mqtt_channel import MqttChannel

_LOGGER = logging.getLogger(__name__)

__all__ = [
Expand Down
4 changes: 2 additions & 2 deletions roborock/devices/traits/a01/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@
ZeoState,
ZeoTemperature,
)
from roborock.devices.a01_channel import send_decoded_command
from roborock.devices.mqtt_channel import MqttChannel
from roborock.devices.rpc.a01_channel import send_decoded_command
from roborock.devices.traits import Trait
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.roborock_message import RoborockDyadDataProtocol, RoborockZeoProtocol

__init__ = [
Expand Down
4 changes: 2 additions & 2 deletions roborock/devices/traits/b01/q10/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import Any

from roborock.devices.b01_q7_channel import send_decoded_command
from roborock.devices.mqtt_channel import MqttChannel
from roborock.devices.rpc.b01_q7_channel import send_decoded_command
from roborock.devices.traits import Trait
from roborock.devices.transport.mqtt_channel import MqttChannel

from .command import CommandTrait

Expand Down
4 changes: 2 additions & 2 deletions roborock/devices/traits/b01/q10/command.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Any

from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP
from roborock.devices.b01_q10_channel import send_command
from roborock.devices.mqtt_channel import MqttChannel
from roborock.devices.rpc.b01_q10_channel import send_command
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.protocols.b01_q10_protocol import ParamsType


Expand Down
4 changes: 2 additions & 2 deletions roborock/devices/traits/b01/q7/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
SCWindMapping,
WaterLevelMapping,
)
from roborock.devices.b01_q7_channel import send_decoded_command
from roborock.devices.mqtt_channel import MqttChannel
from roborock.devices.rpc.b01_q7_channel import send_decoded_command
from roborock.devices.traits import Trait
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.protocols.b01_q7_protocol import CommandType, ParamsType, Q7RequestMessage
from roborock.roborock_message import RoborockB01Props
from roborock.roborock_typing import RoborockB01Q7Methods
Expand Down
8 changes: 8 additions & 0 deletions roborock/devices/transport/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Module for handling network connections to Roborock devices.

This is used internally by the device manager for creating connections to
Roborock devices. These modules contain common code, not specific to a
particular device or application level protocol.
"""

__all__: list[str] = []
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
from roborock.callbacks import CallbackList, decoder_callback
from roborock.exceptions import RoborockConnectionException, RoborockException
from roborock.protocol import create_local_decoder, create_local_encoder
from roborock.protocols.v1_protocol import LocalProtocolVersion
from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol
from roborock.util import RoborockLoggerAdapter, get_next_int

from ..protocols.v1_protocol import LocalProtocolVersion
from ..util import RoborockLoggerAdapter, get_next_int
from .channel import Channel

_LOGGER = logging.getLogger(__name__)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from roborock.devices.a01_channel import send_decoded_command
from roborock.devices.rpc.a01_channel import send_decoded_command
from roborock.protocols.a01_protocol import encode_mqtt_payload
from roborock.roborock_message import (
RoborockDyadDataProtocol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

from roborock.data import NetworkInfo, RoborockStateCode, S5MaxStatus, UserData
from roborock.devices.cache import DeviceCache, DeviceCacheData, InMemoryCache
from roborock.devices.local_channel import LocalSession
from roborock.devices.v1_channel import V1Channel
from roborock.devices.rpc.v1_channel import V1Channel
from roborock.devices.transport.local_channel import LocalSession
from roborock.exceptions import RoborockException
from roborock.protocol import (
create_local_decoder,
Expand Down Expand Up @@ -107,7 +107,7 @@ def fake_next_int(*args) -> int:
@pytest.fixture(name="mock_create_map_response_decoder")
def setup_mock_map_decoder() -> Iterator[Mock]:
"""Mock the map response decoder to control its behavior in tests."""
with patch("roborock.devices.v1_channel.create_map_response_decoder") as mock_create_decoder:
with patch("roborock.devices.rpc.v1_channel.create_map_response_decoder") as mock_create_decoder:
yield mock_create_decoder


Expand Down
2 changes: 0 additions & 2 deletions tests/devices/test_a01_traits.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
def mock_channel():
channel = Mock()
channel.send_command = AsyncMock()
# Mocking send_decoded_command if it was a method on channel, but it's a standalone function imported in traits.
# However, in traits/__init__.py it is imported as: from roborock.devices.a01_channel import send_decoded_command
return channel


Expand Down
2 changes: 1 addition & 1 deletion tests/devices/traits/b01/q7/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
WaterLevelMapping,
WorkStatusMapping,
)
from roborock.devices.b01_q7_channel import send_decoded_command
from roborock.devices.rpc.b01_q7_channel import send_decoded_command
from roborock.devices.traits.b01.q7 import Q7PropertiesApi
from roborock.exceptions import RoborockException
from roborock.protocols.b01_q7_protocol import B01_VERSION, Q7RequestMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pytest

from roborock.devices.local_channel import LocalChannel, LocalChannelParams
from roborock.devices.transport.local_channel import LocalChannel, LocalChannelParams
from roborock.exceptions import RoborockConnectionException, RoborockException
from roborock.protocol import create_local_decoder, create_local_encoder
from roborock.protocols.v1_protocol import LocalProtocolVersion
Expand Down Expand Up @@ -52,7 +52,7 @@ def setup_mock_loop(mock_transport: Mock) -> Generator[Mock, None, None]:
loop = Mock()
loop.create_connection = AsyncMock(return_value=(mock_transport, Mock()))

with patch("roborock.devices.local_channel.get_running_loop", return_value=loop):
with patch("roborock.devices.transport.local_channel.get_running_loop", return_value=loop):
yield loop


Expand Down Expand Up @@ -427,7 +427,7 @@ async def test_keep_alive_ping_exceptions_handled_gracefully(
local_channel: LocalChannel, mock_loop: Mock, caplog: pytest.LogCaptureFixture
) -> None:
"""Test that exceptions in the ping loop are handled gracefully without stopping the loop."""
from roborock.devices.local_channel import _PING_INTERVAL
from roborock.devices.transport.local_channel import _PING_INTERVAL

# Set log level to capture DEBUG messages
caplog.set_level("DEBUG")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pytest

from roborock.data import HomeData, UserData
from roborock.devices.mqtt_channel import MqttChannel
from roborock.devices.transport.mqtt_channel import MqttChannel
from roborock.mqtt.session import MqttParams
from roborock.protocol import create_mqtt_decoder, create_mqtt_encoder
from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_local_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest
import syrupy

from roborock.devices.local_channel import LocalChannel
from roborock.devices.transport.local_channel import LocalChannel
from roborock.protocol import MessageParser, create_local_decoder
from roborock.protocols.v1_protocol import LocalProtocolVersion
from roborock.roborock_message import RoborockMessage, RoborockMessageProtocol
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/local_async_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def start_handle_write(data: bytes) -> None:

return (mock_transport, protocol)

with patch("roborock.devices.local_channel.get_running_loop") as mock_loop:
with patch("roborock.devices.transport.local_channel.get_running_loop") as mock_loop:
mock_loop.return_value.create_connection.side_effect = create_connection
yield

Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/logging_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def get_token_bytes(n: int) -> bytes:
return result

with (
patch("roborock.devices.local_channel.get_next_int", side_effect=get_next_int),
patch("roborock.devices.transport.local_channel.get_next_int", side_effect=get_next_int),
patch("roborock.protocols.v1_protocol.get_next_int", side_effect=get_next_int),
patch("roborock.protocols.v1_protocol.get_timestamp", side_effect=get_timestamp),
patch("roborock.protocols.v1_protocol.secrets.token_bytes", side_effect=get_token_bytes),
Expand Down