Skip to content

Saros 10R: set_clean_motor_mode generates invalid Mop payload (-10005 Param error) #930

Open

Description

@BlackRockCity

Description

python-roborock appears to generate an incorrect set_clean_motor_mode payload when selecting Mop mode on a Roborock Saros 10R.

The generated command is rejected by the robot with:

code: -10005
message: Param error

I captured the command generated through the Home Assistant Roborock integration, compared it with the command sent by the official Roborock iOS app, and then independently tested the official-app parameters through vacuum.send_command.

The official-app parameters work successfully.

Environment

  • Robot: Roborock Saros 10R
  • Model: roborock.vacuum.a144
  • Firmware: 02.50.56
  • Home Assistant: 2026.8.3
  • Integration: Home Assistant Roborock integration / python-roborock

Reproduction

Using the Home Assistant Roborock integration, the cleaning mode entity exposes:

vacuum
vac_and_mop
mop

Selecting mop results in SET_CLEAN_MOTOR_MODE being sent with:

[
    {
        "fan_power": 109,
        "water_box_mode": 202,
        "mop_mode": 300
    }
]

The Saros 10R rejects this payload:

{'code': -10005, 'message': 'Param error'}

This occurs both through the local connection and MQTT.

Official Roborock app behavior

I captured the RPC sent by the official Roborock iOS app when selecting Mop on the same robot.

The official app sends:

{
  "method": "set_clean_motor_mode",
  "params": [
    {
      "fan_power": 105,
      "mop_mode": 300,
      "water_box_mode": 225
    }
  ]
}

The robot accepts this command.

Independent verification

I then sent the official-app parameters directly through Home Assistant:

action: vacuum.send_command
target:
  entity_id: vacuum.saros_10r
data:
  command: set_clean_motor_mode
  params:
    - fan_power: 105
      mop_mode: 300
      water_box_mode: 225

This succeeds.

The robot changes to Mop mode, and the official Roborock iOS app subsequently reflects Mop as the active cleaning mode.

This appears to rule out the robot, MQTT/local transport, and Home Assistant service call itself as the cause. The Saros 10R accepts set_clean_motor_mode; the issue appears to be the model-specific values being generated for that command.

Additional mode captures

For comparison, I captured the following commands from the official Roborock app on this Saros 10R:

Mode fan_power mop_mode water_box_mode
Vacuum 102 300 200
Vac + Mop 102 300 225
Mop 105 300 225

Therefore the official app uses:

Vacuum

{
  "fan_power": 102,
  "mop_mode": 300,
  "water_box_mode": 200
}

Vacuum + Mop

{
  "fan_power": 102,
  "mop_mode": 300,
  "water_box_mode": 225
}

Mop

{
  "fan_power": 105,
  "mop_mode": 300,
  "water_box_mode": 225
}

All three were accepted when tested against the robot.

Cleaning mode state also becomes unknown

There appears to be a related state-decoding issue.

After sending the valid Mop command above, the robot is demonstrably in Mop mode and the official Roborock app displays Mop, but Home Assistant's:

select.saros_10r_cleaning_mode

reports:

unknown

So there may be two Saros 10R-specific problems:

  1. The library generates incorrect set_clean_motor_mode parameters for Mop.
  2. The resulting/actual Saros 10R cleaning-mode state is not correctly decoded back into the cleaning mode enum.

Related fourth cleaning mode

The Saros 10R also supports "Vacuum followed by Mop" / "Clean then mop."

That is being discussed separately in #914.

I captured that mode using a different RPC:

app_set_clean_sequence_type

and I added the protocol details to #914 rather than duplicate them here.

Expected behavior

Selecting Mop should cause the Saros 10R to receive the correct model-specific set_clean_motor_mode parameters and the cleaning-mode entity should subsequently report mop.

Actual behavior

python-roborock/Home Assistant sends:

{
  "fan_power": 109,
  "water_box_mode": 202,
  "mop_mode": 300
}

and the Saros 10R responds:

-10005 Param error

The official app instead sends:

{
  "fan_power": 105,
  "mop_mode": 300,
  "water_box_mode": 225
}

which the same robot accepts.

I can provide the full debug/local-server traces or perform additional command/state tests if useful.

Activity

  1. liborplisek commented on Aug 28, 2026

    @liborplisek

    The water_box_mode: 202 part has a root cause, filed as #931.

    Four WaterModes members share a value string with an earlier member, and StrEnum de-duplicates by value, so they silently become aliases carrying the earlier member's code:

    PURE_WATER_FLOW_SMALL  = ("low", 225)     -> alias of LOW,     .code 201
    PURE_WATER_FLOW_MIDDLE = ("medium", 235)  -> alias of MEDIUM,  .code 202
    PURE_WATER_SUPER_BEGIN = ("high", 248)    -> alias of HIGH,    .code 203
    PURE_WATER_FLOW_END    = ("extreme", 250) -> alias of EXTREME, .code 208
    

    _get_default_mopping_water_mode() returns PURE_WATER_FLOW_MIDDLE specifically to avoid 202 (its comment says water-slide devices don't accept it), but the alias resolves to MEDIUM, whose code is 202.

    Two notes on your report, checked against a clean install of 7.1.1:

    The fan_power: 109 looks already fixed. On 7.1.1 get_mop_only_vacuum_mode() returns VacuumModes.OFF (105), matching your app capture, so the payload is now {'fan_power': 105, 'water_box_mode': 202, 'mop_mode': 300}. The 109 in your report suggests an older version. The water code is still wrong.

    The unknown cleaning-mode state has the same root cause. get_current_cleaning_mode() resolves the reported water code through WaterModes.from_code_optional(), which returns None for 225, 235, 248 and 250 — exactly the four collapsed codes — and the function then returns None:

    >>> get_current_cleaning_mode(clean_mode=105, water_mode=225, mop_mode=300, features=features)
    None

    So after your successful manual command with water_box_mode: 225, the state genuinely cannot be classified. That is not a separate Saros 10R decoding problem, it is the same aliasing.

    #931 has a reproduction and a patch that keeps the user-facing option strings unchanged.

  2. BlackRockCity commented on Aug 28, 2026

    @BlackRockCity
    Author

    Thanks, this explains the behavior very clearly.

    One clarification on my original report: I am currently on Home Assistant 2026.8.3. Since Home Assistant pins python-roborock==7.1.1, the water_box_mode behavior I am seeing is consistent with the aliasing bug described in #931.

    The earlier fan_power: 109 capture appears to be a separate historical difference. As you noted, a clean 7.1.1 install now produces fan_power: 105, which matches the official Roborock app capture on my Saros 10R.

    For the Saros 10R, the official app sends Mop as:

    {
      "fan_power": 105,
      "mop_mode": 300,
      "water_box_mode": 225
    }

    and I independently verified that this payload succeeds.

    The #931 explanation also accounts for the unknown cleaning-mode state I observed after manually sending that valid command, because WaterModes.from_code_optional(225) cannot currently resolve the collapsed slide-water code.

    So I agree that the bad outgoing water mode and the unknown decoded state are manifestations of the same WaterModes aliasing issue rather than two separate Saros 10R bugs.

    I also noted the Home Assistant follow-up in #931: once the enum members use unique internal values plus display_name, HA's water select will need to use mode.display_name rather than mode.value.

    Happy to retest the Saros 10R against the fix once it is available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions