Skip to content

Add Roborock dock control buttons and mop wash mode select#163373

Closed
iainswarts wants to merge 8 commits into
home-assistant:devfrom
iainswarts:add-roborock-dock-controls
Closed

Add Roborock dock control buttons and mop wash mode select#163373
iainswarts wants to merge 8 commits into
home-assistant:devfrom
iainswarts:add-roborock-dock-controls

Conversation

@iainswarts

Copy link
Copy Markdown

Proposed change

Exposes Roborock dock controls as Home Assistant entities, matching the three actions visible in the Roborock app's "Dock Control" section, plus a mop wash mode select.

New button entities (under the dock sub-device):

  • button.*_dock_empty — triggers auto-empty of the dustbin (APP_START_COLLECT_DUST)
  • button.*_dock_wash_mop — triggers mop washing (APP_START_WASH)
  • button.*_dock_stop_drying — stops the air-drying cycle (APP_STOP_WASH)

New select entity (under the dock sub-device):

  • select.*_mop_wash_mode — sets the wash towel mode (light / balanced / deep / smart)

All entities are only created when the device hardware supports the relevant trait (dust_collection_mode or wash_towel_mode).

Type of change

  • Dependency bump
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (thank you!)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • Commands (APP_START_COLLECT_DUST, APP_START_WASH, APP_STOP_WASH, SET_WASH_TOWEL_MODE) are already present in python-roborock v4.14.0.
  • smart_wash_params (wash interval + smart wash toggle) is intentionally left out — setting it requires sending both fields together, which does not fit the existing number/switch patterns cleanly. Can be a follow-up PR.

Checklist

  • The code change is tested and works locally
  • Local tests pass (pytest tests/components/roborock/)
  • There is no commented out code in this PR
  • I have followed the development checklist
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @iainswarts

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

@iainswarts
iainswarts marked this pull request as ready for review February 18, 2026 15:07
Copilot AI review requested due to automatic review settings February 18, 2026 15:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds dock control entities to the Roborock integration (3 dock action buttons + a mop wash mode select), gated by supported device traits.

Changes:

  • Add dock command button entities (empty, wash mop, stop drying) under the dock sub-device.
  • Add a wash towel / mop wash mode select entity under the dock sub-device.
  • Extend tests and test fixtures to cover new entities and default wash mode.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/components/roborock/test_button.py Adds tests for new dock command buttons (success/failure + trait gating).
tests/components/roborock/conftest.py Updates fake v1 properties with a default wash towel mode.
homeassistant/components/roborock/strings.json Adds translations for new dock buttons and mop wash mode select states.
homeassistant/components/roborock/select.py Introduces select description for wash towel (mop wash) mode.
homeassistant/components/roborock/button.py Adds dock command button descriptions/entities and wires them into setup.

Comment thread homeassistant/components/roborock/button.py
Comment on lines +224 to +226
async def async_press(self) -> None:
"""Send the dock command."""
await self.send(self.entity_description.api_command)

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

ButtonEntity.async_press is commonly implemented with an **kwargs: Any signature (as you already do for RoborockRoutineButtonEntity). Keeping the same signature here avoids potential TypeError if the button service ever passes keyword args through to async_press. Suggest changing this to async def async_press(self, **kwargs: Any) -> None:.

Copilot uses AI. Check for mistakes.
Comment thread homeassistant/components/roborock/select.py Outdated
Comment on lines +150 to +153
"light": "[%key:component::roborock::entity::select::dust_collection_mode::state::light%]",
"balanced": "[%key:component::roborock::entity::vacuum::roborock::state_attributes::fan_speed::state::balanced%]",
"deep": "[%key:component::roborock::entity::select::mop_mode::state::deep%]",
"smart": "[%key:component::roborock::entity::select::dust_collection_mode::state::smart%]"

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

These state labels are pulled from several unrelated translation paths (dust collection mode, vacuum fan speed, mop mode). While it deduplicates strings, it also tightly couples this select’s UX text to other entities’ translation structures and semantics. Consider defining the four labels directly under mop_wash_mode.state (or referencing a more clearly “shared/common” key if available) to avoid future breakage when other entity translation keys change.

Suggested change
"light": "[%key:component::roborock::entity::select::dust_collection_mode::state::light%]",
"balanced": "[%key:component::roborock::entity::vacuum::roborock::state_attributes::fan_speed::state::balanced%]",
"deep": "[%key:component::roborock::entity::select::mop_mode::state::deep%]",
"smart": "[%key:component::roborock::entity::select::dust_collection_mode::state::smart%]"
"light": "Light",
"balanced": "Balanced",
"deep": "Deep",
"smart": "Smart"

Copilot uses AI. Check for mistakes.
Comment thread homeassistant/components/roborock/button.py
@it-rec

it-rec commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

No entity_registry_enabled_default = False consideration
Three new button entities will appear for every supported dock by default. For users who don't use dock controls, this could be noisy. The HA convention for "less common but valid" controls is often to set entity_registry_enabled_default = False and let users opt in. This is worth discussing, especially for stop_drying which is quite niche

@it-rec

it-rec commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

wash_towel_mode as availability gate for both wash and stop
Both dock_wash_mop and dock_stop_drying use api.wash_towel_mode is not None as their availability check. This is fine logically, but the comment/intent could be clearer — these buttons should arguably only show up when the dock actually has a mop washing station, and wash_towel_mode being non-None is presumably the right proxy for that. If there's a more explicit "has mop wash" capability flag in the API, that would be more robust.

@it-rec

it-rec commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

The PR description mentions tests for buttons but doesn't explicitly call out select tests. The conftest update adds a default wash_towel_mode, but it's worth confirming there are tests for:
a) the select showing the correct current mode
b) changing the mode calls the right command
c) the select is absent when wash_towel_mode is None

@joostlek joostlek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The pre commit checks are failing, can you take a look?

@home-assistant
home-assistant Bot marked this pull request as draft February 18, 2026 23:42
@home-assistant

Copy link
Copy Markdown
Contributor

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@iainswarts
iainswarts marked this pull request as ready for review February 18, 2026 23:49
Copilot AI review requested due to automatic review settings February 18, 2026 23:49
@home-assistant
home-assistant Bot requested a review from joostlek February 18, 2026 23:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Comment thread homeassistant/components/roborock/button.py
Comment on lines +206 to +212
async def test_press_dock_command_button_success(
hass: HomeAssistant,
setup_entry: MockConfigEntry,
entity_id: str,
expected_command: RoborockCommand,
fake_vacuum: FakeDevice,
) -> None:

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

For consistency with other tests in this file that use the setup_entry fixture, these tests should include the bypass_api_client_fixture parameter. This fixture patches the API client during integration setup to avoid making real API calls. While the tests may work without it due to mocking, including it ensures consistency with the existing test patterns in test_button.py (see test_update_success, test_update_failure, and test_press_routine_button_success).

Copilot uses AI. Check for mistakes.
Comment thread tests/components/roborock/test_button.py
if api.wash_towel_mode is not None
else None
),
parameter_lambda=lambda key, _: [RoborockDockWashTowelModeCode.as_dict()[key]],

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

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

The parameter_lambda should use .get(key) instead of [key] for consistency with the dust_collection_mode select entity on line 133 and for defensive programming. While Home Assistant's select entity framework should prevent invalid keys from being passed, using dictionary .get() provides better error handling and consistency across the codebase.

Suggested change
parameter_lambda=lambda key, _: [RoborockDockWashTowelModeCode.as_dict()[key]],
parameter_lambda=lambda key, _: [
RoborockDockWashTowelModeCode.as_dict().get(key)
],

Copilot uses AI. Check for mistakes.
@iainswarts
iainswarts marked this pull request as draft February 28, 2026 17:38
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines +171 to +190
RoborockSelectDescription(
key="wash_towel_mode",
translation_key="mop_wash_mode",
api_command=RoborockCommand.SET_WASH_TOWEL_MODE,
value_fn=lambda api: (
mode.name
if api.wash_towel_mode is not None
and (mode := api.wash_towel_mode.wash_mode) is not None
else None
),
entity_category=EntityCategory.CONFIG,
options_lambda=lambda api: (
RoborockDockWashTowelModeCode.keys()
if api.wash_towel_mode is not None
else None
),
parameter_lambda=lambda key, _: [
RoborockDockWashTowelModeCode.as_dict().get(key)
],
is_dock_entity=True,

Copilot AI Feb 28, 2026

Copy link

Choose a reason for hiding this comment

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

parameter_lambda uses RoborockDockWashTowelModeCode.as_dict().get(key), which can return None if a service call provides an unsupported option. Since RoborockSelectEntity.async_select_option currently forwards whatever it receives without validating against self.options, this can result in sending [None] to the API. Consider validating option against the available options (and raising ServiceValidationError, similar to the A01 select entity) and/or making the lookup strict so unsupported options cannot be sent.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This follows the same pattern as dust_collection_mode directly above (line 167), which also uses .get(key). Changing one without the other would introduce inconsistency. Happy to update both if a maintainer prefers strict lookup, but that feels like a separate cleanup.

@iainswarts
iainswarts marked this pull request as ready for review February 28, 2026 20:11

@allenporter allenporter left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I saw another test problem?

@home-assistant
home-assistant Bot marked this pull request as draft March 1, 2026 04:25
Delete 6 unused snapshot entries (button.zeo_one, _2, _3) that were
generated during an earlier snapshot update when entity naming was
broken. The correctly-named snapshots (_pause, _shutdown, _start)
remain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@iainswarts
iainswarts marked this pull request as ready for review March 9, 2026 00:31
@home-assistant
home-assistant Bot requested a review from allenporter March 9, 2026 00:31
@iainswarts

Copy link
Copy Markdown
Author

Hopefully its all working now, I tried running all the tests I could locally but couldn't perfectly replicate the CI system set up in GitHub (this is also my first OSS contribution, so excuse my clumsiness)

@Lash-L

Lash-L commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

Hey @iainswarts - congrats on your first contribution and thank you!

I think if possible, let's hold off on the mop wash select entity. That doesn't actually cover all of the modes as they are dynamic per device.

I am working on this on the library side here: Python-roborock/python-roborock#765 but i don't have it done yet. Would you mind removing the select entity and I can ping you when I get this finished on the library side? Alternatively, you can just wait until I get it done on the library side and keep it in this PR. What are your thoughts?

Edit: will likely get it done early this week - a few extra things to still figure out

Lash-L noted the wash towel modes are dynamic per device, not
a fixed enum. Removing select entity until python-roborock#765
lands with proper per-device mode support. Dock buttons retained.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@iainswarts

Copy link
Copy Markdown
Author

Happy to follow up with re-adding it after your changes have landed :)

},
"button": {
"dock_empty": {
"name": "Empty"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does empty mean? Dock Empty but what does it empty? Maybe this is obvious as I don't have the device, but now it sounds very ambiguous

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it means to start emptying the robot vaccum's dust bin into the dock's dust bin. So "Empty" as a verb meaning empty the robots dust bin. The other name is to start dust connection.

Looking at some videos online, the app has a button that says "Empty" and the device speaks "Empty Dust Bin" so maybe that would be a better name?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah, I have a roborock with dock and when it is docked, I can push the button in the roborock app on my phone and it empties the dustbin of the robot into the dock (which is much much bigger). So it would Empty the robot into the dock

},
"button": {
"dock_empty": {
"name": "Empty"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it means to start emptying the robot vaccum's dust bin into the dock's dust bin. So "Empty" as a verb meaning empty the robots dust bin. The other name is to start dust connection.

Looking at some videos online, the app has a button that says "Empty" and the device speaks "Empty Dust Bin" so maybe that would be a better name?

@home-assistant
home-assistant Bot marked this pull request as draft March 21, 2026 05:59
@Lash-L

Lash-L commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

Hey @iainswarts - The cutoff to be included in 2026.4/the beta is this Wednesday the 25th. Looks like there are a few outstanding comments as well as merge conflicts. I know you said this was your first contribution and I'm sure you're looking for the feature in your HA instance, so I figured I would let you know as if it is not done by (typically early) Wednesday, the earliest you will see it in HA is 2026.5

@it-rec

it-rec commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Review: Add Roborock dock control buttons and mop wash mode select

Nice feature addition — the dock control buttons are a welcome complement to the existing Roborock integration. A few things I noticed:

🔴 Blocker: Unrelated Zeo snapshot regressions

The PR includes significant snapshot changes for Zeo entities that are unrelated to the dock control feature:

  • binary_sensor.zeo_one_detergentzeo_one_problem (name/ID lost)
  • binary_sensor.zeo_one_softenerzeo_one_problem_2
  • button.zeo_one_start/pause/shutdownzeo_one/zeo_one_2/zeo_one_3 (translations lost, generic names)
  • switch.zeo_one_sound_settingswitch.zeo_one
  • sensor.zeo_one_times_after_cleansensor.zeo_one

These are breaking changes for existing Zeo users — entity IDs change, friendly names become generic, automations break. This looks like a translation/naming regression, likely from a rebase or dependency bump. Please revert the unrelated snapshot changes and address them in a separate PR if needed.

🟡 Missing error handling in async_press

RoborockDockCommandButtonEntity.async_press() calls await self.send(...) without try/except. Compare with the existing RoborockRoutineButtonEntity.async_press() which wraps RoborockException into HomeAssistantError and calls async_request_refresh() in a finally block. The new entity should follow the same pattern to avoid unhandled exceptions surfacing to the user.

🟡 Select entity missing?

The PR description mentions a select.*_mop_wash_mode entity, but I don't see any select platform code in the changed files. The conftest.py change (wash_towel_mode.wash_mode = RoborockDockWashTowelModeCode.light) looks like preparation for it. Either the select implementation is missing or the description should be updated.

🟢 Minor: Consider negative test cases

There are no tests verifying that dock buttons are not created when the relevant trait is None (e.g. a device without dust_collection_mode should not get dock_empty). The availability_fn gating in async_setup_entry looks correct, but an explicit test would add confidence.

What looks good

  • Clean RoborockDockCommandButtonDescription dataclass with api_command + availability_fn — declarative and extensible
  • entity_registry_enabled_default=False on all three buttons — right call for niche controls
  • Correct **kwargs: Any signature on async_press
  • Entities properly assigned to the dock sub-device via coordinator.dock_device_info
  • Trait-gating with double check (coordinator type + availability_fn) prevents entities on incompatible devices

@github-actions

Copy link
Copy Markdown

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions Bot added the stale label May 25, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants