Skip to content

feat(dyad): decode F25 Ultra data points - #1017

Merged
allenporter merged 1 commit into
Python-roborock:mainfrom
infy1995:feat/dyad-f25-unknown-dps
Nov 8, 2026
Merged

allenporter merged 1 commit into
Python-roborock:mainfrom
infy1995:feat/dyad-f25-unknown-dps

Conversation

@infy1995

@infy1995 infy1995 commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Summary

The Roborock F25 Ultra (roborock.wdv.a194, firmware 01.03.38) pushes several A01 data points that are not in RoborockDyadDataProtocol, so DyadApi drops them. This PR adds them, plus converters for two data points that were already defined but never decoded.

Names come from the product schema returned by the cloud (product.schema in the Home Assistant diagnostics), for example 238 set_cleanser_amount, 244 water_mode_suction, 246 charge_error_status. Value mappings were verified on the device.

Changes

New RoborockDyadDataProtocol members and converters:

DP Name Decoded as
238 SET_CLEANSER_AMOUNT DyadCleanserAmount: off 0, low 1, standard 2, high 3
239 AUTO_SMART_SELF_CLEAN_SET bool (smart self-clean intensity)
240 LIGHT_SET bool (brush head light)
241 CLEAN_ASSIST_POWER_SET DyadCleanAssistPower: gentle 1, moderate 2, strong 3
242 SELF_CLEAN_WATER_TEMP_LEVEL_SET DyadSelfCleanWaterTemp: high 3, steam 4
243 DOCK_TYPE int (unverified, see below)
244 WATER_MODE_SUCTION DyadSuction (suction in sponge mode)
245 WATER_MODE_BRUSH_SPEED DyadBrushSpeed (brush speed in sponge mode)
246 CHARGE_ERROR_STATUS bool
247 DRYING_RATE int, percent
248 FLAT_STATUS bool
249 POWERWHEEL_DIRECTION DyadPowerwheelDirection: stop 0, forward 1, backward 2, left 5, right 6, exit 9
250 POWERWHEEL_SPEED int
270 SELF_CLEAN_CUR_MODE DyadSelfCleanIntensity: smart 0, standard 1, deep 2
271 SELF_CLEAN_CUR_TEMP DyadSelfCleanWaterTemp
272 DRY_CUR_MODE int, minutes

Converters added for existing members:

  • AUTO_SELF_CLEAN_SET (212): bool, 1 when self-cleaning starts after docking.
  • AUTO_SELF_CLEAN_SET_MODE (223): DyadSelfCleanLevel. It always matched SELF_CLEAN_LEVEL (203) in testing.

All new enums define unknown = -1.

Verification on a real device

I ran a small script against this branch. It connected to the F25 Ultra through DeviceManager, queried the new data points and printed every decoded push. I then changed one setting at a time in the Roborock app and compared the output with the app. The data points were also replayed from earlier Home Assistant debug logs.

Selected pushes:

{"238":0} {"238":1} {"238":2} {"238":3}   # detergent: off, low, standard, high
{"240":0} {"240":1}                       # brush light off, on
{"241":1} {"241":2} {"241":3}             # wheel assist: gentle, moderate, strong
{"244":2,"245":1} {"244":3,"245":2} {"244":4,"245":2}   # sponge mode suction (brush speed follows, like 206/208)
{"242":4} {"271":4}                       # self-clean mode: steam
{"242":3} {"271":3}                       # self-clean mode: high
{"203":1,"223":1,"239":0} {"270":1}       # self-clean intensity: standard
{"203":1,"223":1,"239":1} {"270":0}       # self-clean intensity: smart
{"203":2,"223":2,"239":0} {"270":2}       # self-clean intensity: deep
{"212":1}                                 # auto self-clean: run after docking
{"212":0} + 10002 schedule "on":true      # auto self-clean: scheduled
{"212":0} + 10002 schedule "on":false     # auto self-clean: off
{"249":1,"250":15} {"249":2,"250":15} {"249":6,"250":15} {"249":5,"250":15} {"249":0,"250":0}   # remote control: forward, backward, right, left, stop
{"200":0,"249":9}                         # remote control closed
{"225":22} {"272":22} / {"225":5} {"272":5}   # drying mode: silent / fast

Notes on the read-only data points:

  • 246 is named charge_error_status in the schema, but on this device it is 1 whenever the vacuum is not charging: off the dock, during self-clean and during drying. It goes back to 0 a few seconds after the status changes to charging.
  • 247 resets to 0 when drying starts and counts up to 100.
  • 248 is 1 while the vacuum lies flat (needed for remote control) and is pushed every second in that state.
  • 270/271 report the intensity and water temperature of the current self-clean run. While idle they follow the settings (239/203 and 242). While a run is in progress they keep the values the run started with.
  • 272 always matched 225 (SILENT_DRY_DURATION). Fast drying (225 = 5) took exactly 5 minutes. Silent drying (225 = 22) progressed about 4 to 5 times slower. Both values look like the drying time in minutes.

Not verified

  • 243 (DOCK_TYPE) was always 2. With only one dock I could not see other values, so it is exposed as a raw int.
  • 257–268 (lifetest* in the schema) were never reported and are not added.

Other observations (no code change)

  • During drying the device reports status 10 (ventilating). The app shows this as "Drying".
  • On this device WARM_LEVEL (204) follows the drying mode: 2 for fast drying, 1 for silent drying. DyadWarmLevel calls these deep / normal. I did not rename them so existing users are not affected.
  • SCHEDULE_TASK (10002) has no converter yet. It carries a JSON list such as [{"id":1794081537371,"on":true,"cron":"0 22 * * 0,1,2,3,4,5,6","dpAction":{"200":1,"204":1}}]. This could be a follow-up.
  • Clean mode codes 5 and 6 (hot water, steam) are handled in fix(dyad): add steam and hot water clean modes #1015.

Tests

  • pytest tests/devices/traits/a01 tests/conformance tests/devices/test_a01_traits.py tests/testing/test_a01_simulator.py tests/protocols/test_a01_protocol.py tests/devices/rpc/test_a01_channel.py tests/test_roborock_message.py tests/data: 789 passed, 53 xfailed
  • ruff check / ruff format --check: clean
  • mypy on the changed modules: clean

Add the A01 data points that the F25 Ultra (roborock.wdv.a194) reports,
named after the product schema from the cloud. Value mappings were
verified on the device by changing each setting in the Roborock app and
watching the pushes.

New data points:
- 238 detergent dosing (off/low/standard/high)
- 239 smart self-clean intensity switch
- 240 brush light switch
- 241 wheel assist power (gentle/moderate/strong)
- 242 self-clean water temperature setting (high/steam)
- 243 dock type (raw value)
- 244/245 suction and brush speed of the sponge mode
- 246 1 while not charging (off the dock, self-clean, drying)
- 247 drying progress in percent
- 248 lying flat
- 249/250 remote control drive direction and speed
- 270/271 intensity and water temperature of the current self-clean run
- 272 drying time in minutes, follows 225

Also decode AUTO_SELF_CLEAN_SET (212) and AUTO_SELF_CLEAN_SET_MODE (223),
which were defined without a converter, so their values were dropped.
@allenporter

Copy link
Copy Markdown
Contributor

Thank you @infy1995 for the fantastic contribution!

@allenporter
allenporter merged commit 2052b09 into Python-roborock:main Nov 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants