Repository navigation
feat(dyad): add F25 Ultra drying mode, schedule and reset support #1019
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,12 +31,13 @@ | |
| from datetime import UTC, datetime, time | ||
| from typing import Any, Generic, TypeVar | ||
|
|
||
| from roborock.data import DyadProductInfo, DyadSndState, HomeDataProduct, RoborockCategory | ||
| from roborock.data import DyadProductInfo, DyadScheduleTask, DyadSndState, HomeDataProduct, RoborockCategory | ||
| from roborock.data.dyad.dyad_code_mappings import ( | ||
| DyadBrushSpeed, | ||
| DyadCleanAssistPower, | ||
| DyadCleanMode, | ||
| DyadCleanserAmount, | ||
| DyadDryingMode, | ||
| DyadError, | ||
| DyadPowerwheelDirection, | ||
| DyadSelfCleanIntensity, | ||
|
|
@@ -88,6 +89,13 @@ | |
| ] | ||
|
|
||
|
|
||
| def _decode_dyad_schedule(value: str) -> list[DyadScheduleTask]: | ||
| """Decode the schedule data point, which is a JSON list or an empty string.""" | ||
| if not value: | ||
| return [] | ||
| return [DyadScheduleTask.from_dict(task) for task in json.loads(value)] | ||
|
|
||
|
|
||
| DYAD_PROTOCOL_ENTRIES: dict[RoborockDyadDataProtocol, Callable] = { | ||
| RoborockDyadDataProtocol.STATUS: lambda val: RoborockDyadStateCode(val).name, | ||
| RoborockDyadDataProtocol.SELF_CLEAN_MODE: lambda val: DyadSelfCleanMode(val).name, | ||
|
|
@@ -107,7 +115,7 @@ | |
| RoborockDyadDataProtocol.STAND_LOCK_AUTO_RUN: lambda val: bool(val), | ||
| RoborockDyadDataProtocol.AUTO_SELF_CLEAN_SET_MODE: lambda val: DyadSelfCleanLevel(val).name, | ||
| RoborockDyadDataProtocol.AUTO_DRY_MODE: lambda val: bool(val), | ||
| RoborockDyadDataProtocol.SILENT_DRY_DURATION: lambda val: int(val), # in minutes | ||
| RoborockDyadDataProtocol.SILENT_DRY_DURATION: lambda val: int(val), # minutes, or DyadDryingMode on F25 | ||
| RoborockDyadDataProtocol.SILENT_MODE: lambda val: bool(val), | ||
| RoborockDyadDataProtocol.SILENT_MODE_START_TIME: lambda val: time( | ||
| hour=int(val / 60), minute=val % 60 | ||
|
|
@@ -134,7 +142,8 @@ | |
| RoborockDyadDataProtocol.POWERWHEEL_SPEED: lambda val: int(val), | ||
| RoborockDyadDataProtocol.SELF_CLEAN_CUR_MODE: lambda val: DyadSelfCleanIntensity(val).name, | ||
| RoborockDyadDataProtocol.SELF_CLEAN_CUR_TEMP: lambda val: DyadSelfCleanWaterTemp(val).name, | ||
| RoborockDyadDataProtocol.DRY_CUR_MODE: lambda val: int(val), # in minutes | ||
| RoborockDyadDataProtocol.DRY_CUR_MODE: lambda val: DyadDryingMode(val).name, | ||
| RoborockDyadDataProtocol.SCHEDULE_TASK: _decode_dyad_schedule, | ||
| RoborockDyadDataProtocol.SND_STATE: lambda val: DyadSndState.from_dict(val), | ||
| RoborockDyadDataProtocol.PRODUCT_INFO: lambda val: DyadProductInfo.from_dict(val), | ||
| } | ||
|
|
@@ -315,6 +324,28 @@ async def set_value(self, protocol: RoborockDyadDataProtocol, value: Any) -> dic | |
| params = {protocol: value} | ||
| return await send_decoded_command(self._channel, params) | ||
|
|
||
| async def start_drying(self) -> None: | ||
| """Start drying with the drying mode currently set on the device. | ||
|
|
||
| The app sends the same command for fast and silent drying on the F25 Ultra. | ||
| """ | ||
| await send_decoded_command( | ||
| self._channel, | ||
| {RoborockDyadDataProtocol.START: 1, RoborockDyadDataProtocol.WARM_LEVEL: 1}, | ||
| ) | ||
|
|
||
| async def stop_drying(self) -> None: | ||
| """Stop a running drying cycle.""" | ||
| await send_decoded_command(self._channel, {RoborockDyadDataProtocol.START: 0}) | ||
|
Comment on lines
+327
to
+339
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking suggestion (either here or in a follow-up): Since this PR introduces Right now, callers wanting to change the drying mode have to drop down to async def set_drying_mode(self, mode: DyadDryingMode) -> None:
"""Set the drying mode (e.g. fast or silent on the F25 Ultra)."""
if mode == DyadDryingMode.unknown:
raise ValueError("Cannot set drying mode to unknown")
await send_decoded_command(
self._channel,
{RoborockDyadDataProtocol.SILENT_DRY_DURATION: int(mode)},
) |
||
|
|
||
| async def reset_filter(self) -> None: | ||
| """Reset the filter usage counter.""" | ||
| await send_decoded_command(self._channel, {RoborockDyadDataProtocol.MESH_RESET: 1}) | ||
|
|
||
| async def reset_brush(self) -> None: | ||
| """Reset the brush roller usage counter.""" | ||
| await send_decoded_command(self._channel, {RoborockDyadDataProtocol.BRUSH_RESET: 1}) | ||
|
|
||
| async def add_listener(self, callback: Callable[[dict[RoborockDyadDataProtocol, Any]], None]) -> Callable[[], None]: | ||
| """Listen for state the device pushes on its own. | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.