Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 13 additions & 1 deletion roborock/devices/traits/b01/q10/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
"""Traits for Q10 B01 devices."""

import asyncio
import logging
Comment thread
allenporter marked this conversation as resolved.
Outdated
from typing import Any
Comment thread
allenporter marked this conversation as resolved.
Outdated

from roborock.devices.rpc.b01_q7_channel import send_decoded_command
from roborock import B01Props
Comment thread
allenporter marked this conversation as resolved.
Outdated
from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP
Comment thread
allenporter marked this conversation as resolved.
Outdated
from roborock.devices.traits import Trait
from roborock.devices.transport.mqtt_channel import MqttChannel

from .command import CommandTrait
from .vacuum import VacuumTrait

_LOGGER = logging.getLogger(__name__)
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The logger '_LOGGER' is defined but never used in this file. This unused variable should be removed.

Copilot uses AI. Check for mistakes.

__all__ = [
"Q10PropertiesApi",
Expand All @@ -19,9 +25,15 @@ class Q10PropertiesApi(Trait):
command: CommandTrait
"""Trait for sending commands to Q10 devices."""

vacuum: VacuumTrait
"""Trait for sending Vacuum related commands to Q10 devices"""
Comment thread
allenporter marked this conversation as resolved.
Outdated

def __init__(self, channel: MqttChannel) -> None:
"""Initialize the B01Props API."""
self.command = CommandTrait(channel)
self.vacuum = VacuumTrait(self.command)
self._channel = channel
self._task: asyncio.Task | None = None
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The instance variables '_channel' and '_task' are assigned but never used. If these are intended for future functionality, consider removing them until they are needed. Otherwise, they should be utilized or removed to avoid confusion.

Copilot uses AI. Check for mistakes.


def create(channel: MqttChannel) -> Q10PropertiesApi:
Expand Down
60 changes: 60 additions & 0 deletions roborock/devices/traits/b01/q10/vacuum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Traits for Q10 B01 devices."""

import logging

from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP

from .command import CommandTrait

_LOGGER = logging.getLogger(__name__)


Comment thread
allenporter marked this conversation as resolved.
Outdated
Comment thread
allenporter marked this conversation as resolved.
Outdated
class VacuumTrait:
"""Trait for sending vacuum commands.

This is wrapper around the CommandTrait for sending vacuum related
Comment thread
Lash-L marked this conversation as resolved.
Outdated
commands to Q10 devices.
"""

def __init__(self, command: CommandTrait) -> None:
"""Initialize the CommandTrait."""
Comment thread
allenporter marked this conversation as resolved.
Outdated
self._command = command

async def start_clean(self) -> None:
"""Start cleaning."""
await self._command.send(
command=B01_Q10_DP.START_CLEAN,
# TODO: figure out other commands
# 1 = start cleaning
# 2 = "electoral" clean, also has "clean_parameters"
Comment thread
allenporter marked this conversation as resolved.
# 4 = fast create map
params={"cmd": 1},
)

async def pause_clean(self) -> None:
"""Pause cleaning."""
await self._command.send(
command=B01_Q10_DP.PAUSE,
params={},
)

async def resume_clean(self) -> None:
"""Resume cleaning."""
await self._command.send(
command=B01_Q10_DP.RESUME,
params={},
)

async def stop_clean(self) -> None:
"""Stop cleaning."""
await self._command.send(
command=B01_Q10_DP.STOP,
params={},
)

async def return_to_dock(self) -> None:
"""Return to dock."""
await self._command.send(
command=B01_Q10_DP.START_DOCK_TASK,
params={},
)
50 changes: 50 additions & 0 deletions tests/devices/traits/b01/q10/test_vacuum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import json
from collections.abc import Awaitable, Callable
from typing import Any

import pytest

from roborock.devices.traits.b01.q10 import Q10PropertiesApi
from roborock.devices.traits.b01.q10.vacuum import VacuumTrait
from tests.fixtures.channel_fixtures import FakeChannel


@pytest.fixture(name="fake_channel")
def fake_channel_fixture() -> FakeChannel:
return FakeChannel()


@pytest.fixture(name="q10_api")
def q10_api_fixture(fake_channel: FakeChannel) -> Q10PropertiesApi:
return Q10PropertiesApi(fake_channel) # type: ignore[arg-type]


@pytest.fixture(name="vacuumm")
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The fixture name has a typo: "vacuumm" should be "vacuum". This typo appears in the fixture name and affects the test function parameter as well.

Copilot uses AI. Check for mistakes.
def vacuumm_fixture(q10_api: Q10PropertiesApi) -> VacuumTrait:
return q10_api.vacuum


@pytest.mark.parametrize(
("command_fn", "expected_payload"),
[
(lambda x: x.start_clean(), {"201": {"cmd": 1}}),
(lambda x: x.pause_clean(), {"204": {}}),
(lambda x: x.resume_clean(), {"205": {}}),
(lambda x: x.stop_clean(), {"206": {}}),
(lambda x: x.return_to_dock(), {"203": {}}),
],
)
async def test_q7_api_set_fan_speed(
vacuumm: VacuumTrait,
fake_channel: FakeChannel,
command_fn: Callable[[VacuumTrait], Awaitable[None]],
expected_payload: dict[str, Any],
) -> None:
"""Test sending a vacuum start command."""
Comment thread
allenporter marked this conversation as resolved.
Outdated
Comment thread
allenporter marked this conversation as resolved.
await command_fn(vacuumm)

assert len(fake_channel.published_messages) == 1
message = fake_channel.published_messages[0]
assert message.payload
payload_data = json.loads(message.payload.decode())
assert payload_data == {"dps": expected_payload}
Loading