Summary
There's currently no way to start or stop a mop drying cycle through the trait API. dry_status is readable, it was annotated with dps metadata in #740 and is exposed to Home Assistant via is_field_supported(StatusV2, StatusField.DRY_STATUS). But the write side has no trait behind it, so consumers have to fall back to RoborockCommand.APP_SET_DRYER_STATUS.
I want to contribute a function to Home Assistant to switch the dryer on and off , but this is currently only possible via this workaround that I do not think fits into the design. Other switches in the Home Assistant integration are now entirely trait-driven (child_lock, flow_led_status, dnd, valley_electricity_timer).
Prior Art
#262 asked for exactly this in Dec 2024:
correctly sets the mop dry time, however does not turn on the drying. Is this intended, as there seems to be no way to programmatically start the drying process?
It was closed by the reporter with:
Hi, solved with app_set_dryer_status with param status:1
So the command is confirmed working by another user, but the finding only ever lived in a closed issue and it was never carried into the library. APP_SET_DRYER_SETTING configures the dry duration; APP_SET_DRYER_STATUS starts and stops the cycle. I've since confirmed the latter against my own dock.
That issue predates the trait system entirely, so as far as I can tell this was never considered and rejected.
Proposed fix
A switch trait in the shape of the existing ones (ChildLockTrait, ValleyElectricityTimerTrait), gated on the dock being able to dry:
class MopDryerTrait(common.V1TraitMixin, common.RoborockSwitchBase):
"""Trait for controlling the dock mop dryer."""
requires_dock_features = lambda f: f.is_dryable
def __init__(self, status_trait: StatusTrait) -> None:
super().__init__()
self._status_trait = status_trait
async def refresh(self) -> None:
await self._status_trait.refresh()
@property
def is_on(self) -> bool:
return bool(self._status_trait.dry_status)
async def enable(self) -> None:
await self.rpc_channel.send_command(
RoborockCommand.APP_SET_DRYER_STATUS, params={"status": 1}
)
self._status_trait.dry_status = 1 # optimistic, as in the other switch traits
async def disable(self) -> None:
await self.rpc_channel.send_command(
RoborockCommand.APP_SET_DRYER_STATUS, params={"status": 0}
)
self._status_trait.dry_status = 0
Because it takes a constructor argument it would be created in the special-case block in discover_features(), alongside WashTowelModeTrait and ObstaclePhotoTrait.
This follows the same pattern as the action methods being added to V1 traits elsewhere (#856, and the q7 series in #841–#844).
TBC before opening a PR
1. Where should the state come from?
Unlike every other switch trait, this one has no query command of its own. If a cycle is running is reported as dry_status on StatusV2. The sketch above therefore holds the status trait and refreshes through it, which keeps V1TraitMixin's command/converter unused.
The alternative is a self-contained trait backed by APP_GET_DRYER_SETTING with a new DryerSetting dataclass. I didn't go that way because I have no sample of that response and didn't want to guess at the format, But I'm happy to capture it from my dock if you'd prefer that shape, it would also make the dry-duration setting available.
2. Which capability should be used to check?
The code uses dock_features.is_dryable. The alternatives are is_supported_drying (device feature bit) or is_field_supported(StatusV2, StatusField.DRY_STATUS), which is what the binary sensor on the HA side already uses.
Summary
There's currently no way to start or stop a mop drying cycle through the trait API.
dry_statusis readable, it was annotated with dps metadata in #740 and is exposed to Home Assistant viais_field_supported(StatusV2, StatusField.DRY_STATUS). But the write side has no trait behind it, so consumers have to fall back toRoborockCommand.APP_SET_DRYER_STATUS.I want to contribute a function to Home Assistant to switch the dryer on and off , but this is currently only possible via this workaround that I do not think fits into the design. Other switches in the Home Assistant integration are now entirely trait-driven (
child_lock,flow_led_status,dnd,valley_electricity_timer).Prior Art
#262 asked for exactly this in Dec 2024:
It was closed by the reporter with:
So the command is confirmed working by another user, but the finding only ever lived in a closed issue and it was never carried into the library.
APP_SET_DRYER_SETTINGconfigures the dry duration;APP_SET_DRYER_STATUSstarts and stops the cycle. I've since confirmed the latter against my own dock.That issue predates the trait system entirely, so as far as I can tell this was never considered and rejected.
Proposed fix
A switch trait in the shape of the existing ones (
ChildLockTrait,ValleyElectricityTimerTrait), gated on the dock being able to dry:Because it takes a constructor argument it would be created in the special-case block in
discover_features(), alongsideWashTowelModeTraitandObstaclePhotoTrait.This follows the same pattern as the action methods being added to V1 traits elsewhere (#856, and the q7 series in #841–#844).
TBC before opening a PR
1. Where should the state come from?
Unlike every other switch trait, this one has no query command of its own. If a cycle is running is reported as
dry_statusonStatusV2. The sketch above therefore holds the status trait and refreshes through it, which keepsV1TraitMixin'scommand/converterunused.The alternative is a self-contained trait backed by
APP_GET_DRYER_SETTINGwith a newDryerSettingdataclass. I didn't go that way because I have no sample of that response and didn't want to guess at the format, But I'm happy to capture it from my dock if you'd prefer that shape, it would also make the dry-duration setting available.2. Which capability should be used to check?
The code uses
dock_features.is_dryable. The alternatives areis_supported_drying(device feature bit) oris_field_supported(StatusV2, StatusField.DRY_STATUS), which is what the binary sensor on the HA side already uses.