feat: add Q10 zone, position, and goto support - #908
Conversation
09d3295 to
0ada4c3
Compare
allenporter
left a comment
There was a problem hiding this comment.
Thank you for this great contribution, see my comments below.
|
|
||
| @property | ||
| def roborock_position(self) -> Q10Point | None: | ||
| """Current position in the common Roborock millimetre coordinate space.""" |
There was a problem hiding this comment.
You have information written in the PR, but it should also be here (e.g. Whats the difference between roborock_positiion and roborock_position from an end user perspective? how should I be using either of these?)
But really, I would advocate for taking a more opinionated stance: Should we make a breaking change here and always use the common coordinate system? We can consider the Q10 specific thing an implementation.
|
|
||
| async def clean_zone( | ||
| self, | ||
| x1: int, |
There was a problem hiding this comment.
I think we should:
- Define a new variation of Q10Point that is in roborock coordinate space (and we say Q10 is in trace space) e.g. Q10RoborockPoint (or some better name). This can be used for input here and output (e.g. exposed current position)
- Accept Q10RoborockPoint (or new name here).
- Q10Point and Q10RoborockPoint can have functions for converting between each other (e.g. handling the roborock to vector coordinate functions)
- Then for our internal start command params add a
CleanParamsdataclass object that holds the roborock points or q10 points, and handles sorting them. - Move the struct packing in roborock/protocols/b01_q10_protocol.py e.g.
def encode_clean_params(params: CleanParams) -> str
|
|
||
| async def goto_position(self, x: int, y: int) -> None: | ||
| """Move to a coordinate using an owned 40 cm zone-clean task.""" | ||
| if (position := self._map.roborock_position) is not None and hypot( |
There was a problem hiding this comment.
Will _async_monitor_goto_target effectively do the same thing?
There was a problem hiding this comment.
Not entirely, as I understand it. The monitor pauses the goto it already owns when the robot reaches that goto’s original target. This early check also avoids starting a new zone when the robot is already at the requested position. It additionally handles a new goto request for the robot’s current position while another owned goto is still active, even if the previous target was different.
I think the comment below addresses this as well, right?
| self._goto_monitor_task = None | ||
| self._goto_trace_sequence = None | ||
|
|
||
| async def _async_monitor_goto_target( |
There was a problem hiding this comment.
A lot of the logic in this method is related to understanding if the current task is allowed to change state -- and it's intermixed with the other business logic for trying to coordinate the vacuum move. I think we can simplify this by having a separate standalone object that manages a single goto lifecycle (e.g. GotoAction. The action can handle the logic for the next step to take given the information it has (e.g. checking current status of the vaccum, and deciding when to stop) but keep the actions handled by vacuum.py which manages the lifecycle of GotoAction -- it would register listeners/callbacks on the GotoAction then do the work to call the "pause" or "stop" command.
| self._goto_monitor_task.cancel() | ||
| self._goto_monitor_task = None | ||
| self._goto_trace_sequence = None | ||
|
|
There was a problem hiding this comment.
Overall I like this approach of listening for map and status events. Are you finding that results are streamed back reliably such that it works without additional polling?
There was a problem hiding this comment.
Yes, at least on my ss07 running firmware 03.11.24. During the live goto tests, the trace and status updates arrived reliably enough to detect arrival and pause the robot. We did not use periodic polling or call refresh() from the goto monitor.
|
@allenporter Thanks for the detailed review. I used Codex to help build this, so I want to be transparent about that. I do understand the functional side of the implementation (it actually developed this based on my questions regarding robot positioning and if we could use the zoned cleaning as alternative for the Codex:
Regarding the push updates: on my ss07 running firmware 03.11.24, the map/trace and status updates were streamed reliably during the live goto tests. The robot reached the target and was paused based solely on those updates; we did not use any additional polling or periodic refresh() calls. I was actually quite happy we got this approach to work 😃 There is also a downstream Home Assistant PR using this API: home-assistant/core#177780. You may want to review that as well, particularly to make sure the public API introduced here fits the way Home Assistant consumes it. If the above matches what you have in mind, I’ll have Codex work through the individual comments and update the PR accordingly. Also, if you prefer to take over and implement it yourself, I'd be ok with that as well. As said, I am not a dev, but a Q10 (ss07) owner happy to have my tokens do some useful work so don't hold back on commenting. I'm actually having quite some fun guiding Codex doing this. |
Summary
VacuumTrait.clean_zone()supportVacuumTrait.goto_position()as one managed 40 x 40 cm mini-zone operationWhy
Q10/B01 devices use two dock-relative coordinate encodings: trace points use
2.5 mm units and zone vectors use 5 mm units. Home Assistant's existing
Roborock actions use the common millimetre coordinate space with the dock at
(25500, 25500).Q10 firmware does not expose the V1
app_goto_targetcommand. Goto is thereforeemulated with the official app's minimum 40 x 40 cm zone. The lifecycle belongs
in python-roborock so callers do not need to encode device protocol details or
implement their own polling state machine. The trace session counter identifies
the task started by goto; a newer session is treated as a replacement and is
never paused or stopped by the old monitor.
Hardware validation
Validated on a Roborock Q10 (
roborock.vacuum.ss07) running firmware 03.11.24.(29900, 28650)paused at(30020, 28705), 13.2 cm awayTests
roborockandteststrees