Request Roborock App Binding

Thanks for that.. That looks promising, I will give it a try

Hey, did you manage do use the copystring 0.6.17 or even the dev brunch?

I installed various dependencies during troubleshooting, including the version of copystring/ioBroker.roborock pulled by default. I’m not 100% sure if this was required. I hoped not.

Let me know if anyone needs clarification!

I have updated the project so that the local script runs correctly. Thanks for the hint.
There is now also a develop branch, just adjust the file “prep-local-dev.sh”, that is commented out.

If some is interested to run the bridge as systemd service…

I installed the script in /opt/rr2mqtt so you might want to change that..

create a system user with:

 sudo adduser --system rr2mqtt

change the folder persmision to allow the new user access to the bridge:

chown -R rr2mqtt /opt/rr2mqtt/

and put a ne file named rr2mqtt.service in /etc/systemd/system

[Unit]
Description=Roborock2mqtt 
After=network.target

[Service]
WorkingDirectory=/opt/rr2mqtt/ioBroker.roborock/
Type=simple
Restart=always
User=rr2mqtt
ExecStart = node  --env-file=config.env  rr2mqtt-main.js 

[Install]
WantedBy=multi-user.target

After saving that file, reload the deamons, and enable autostart:

systemctl daemon-reload && systemctl enable rr2mqtt.service

I’ve tried both projects from konqi and csowada with my Q7+ Max.
I liked the look of the first, the demo in docker is nice but I could run it for real and make it talk to my robot.
csowada’s is running in a docker as are the reset of my services so that fits in well with my infrastructure, thanks for your effort.
I need to spend some more time with mqtt-explorer, I’ve not figured out yet what commands I can send. Ideally I’d like to:

  • trigger certain routines, like clean when we’ve gone out.
  • get notified when its stuck again - maybe showing where in its map it was last seen.

Also appreciate the guides on mitm snooping, will try this out on some other devices.

I’m probably going to take a look at this. My previous vacuums used the Mi Home app and work fine in OpenHAB, but I just recently bought a Roborock Qrevo S which only works with the Roborock app.

This API is reasonably well documented at Usage — Python Roborock 0.1 documentation

For me the main key is being able to automate starting a cycle via OpenHAB. The ability to run specific routines may be more problematic, but will see…

Stay tunes, hopefully over the weekend I can at least get a bridge logging into the API and autodiscovering devices. Then I can start to add some simple channels.

If/when I get something working, I’ll start a new post…

5 Likes

Progress: I have a framework committed. It doesn’t do anything yet :slight_smile:

I’m working on deciphering the login using:
Groovy code: Hubitat/roborockRobotVacuum/roborockRobotVacuum.groovy at main · bloodtick/Hubitat · GitHub
Python code: python-roborock/roborock/web_api.py at 148a6faa5c37ce619e5749da78be507e50e2b890 · Python-roborock/python-roborock · GitHub

I’m trying to login using curl - and once I figure the syntax out, I can code this in java.

Using:

curl -d '{ "email":"paul@domain.tld","password":"notmyrealpassword", "needtwostepauth":"false"}' -X POST https://usiot.roborock.com/api/v1/login

Right now, this is returning:
{“msg”:“missing parameters”,“data”:null,“code”:1001}

I know I need to create a header based on https://github.com/Python-roborock/python-roborock/blob/148a6faa5c37ce619e5749da78be507e50e2b890/roborock/web_api.py#L87

That will be a tomorrow problem :slight_smile:

Edit: OK I’m an idiot, the correct syntax for curl is:

curl -X POST "https://euiot.roborock.com/api/v1/login?username=paul@domain.tld&password=notmyrealpassword&needtwostepauth=false"

now to update my OH code

OK so I can now login and get a token, and use this token to get a list of homes.

Next steps are to code the equivalent of Hubitat/roborockRobotVacuum/roborockRobotVacuum.groovy at main · bloodtick/Hubitat · GitHub in Java so that I should be able to retrieve a list of devices.

Update:
I’m failing with getting the house details with:

java.util.concurrent.ExecutionException: org.eclipse.jetty.client.HttpResponseException: HTTP protocol violation: Authentication challenge without WWW-Authenticate header

This is from openhab-addons/bundles/org.openhab.binding.roborock/src/main/java/org/openhab/binding/roborock/internal/RoborockWebTargets.java at roborock · psmedley/openhab-addons · GitHub

I’m not sure if there’s a problem with openhab-addons/bundles/org.openhab.binding.roborock/src/main/java/org/openhab/binding/roborock/internal/RoborockWebTargets.java at roborock · psmedley/openhab-addons · GitHub or something else :frowning:

Edit2: OK fixed it! After going out for lunch, and having a few red wines, found a couple of typos. Now to clean things up and get device discovery working.

Edit3: Discovery now working, and I have a completely useless (for now) thing called “Roborock Qrevo S”

Edit4: Last update for today - I have some basic read-only channels like battery status propogating to the Vacuum. To get any further, I need to do some homework on mqtt and work out how that works. Meanwhile, I’ve been ratelimited for hitting the API too much :frowning:

FYI I got a robot rock s6 maxv that as soon as you get that first channel “start vacuuming” up I’ll do a full reset and start testing your binding :wink:
Regarding local communication, do you figure you’ll be able to get something, or will you always be going through the internet? I’d really like it to know if I’ll have issues when the internet goes down.

It’s likely to take a bit of time to get that first channel. It seems pretty much all data and commands are done via mqtt - and I’ve never created a binding that does that. I have some example code in [ecoflow] Initial contribution by maniac103 · Pull Request #18525 · openhab/openhab-addons · GitHub but I need to try and understand it, and adapt the bits from the python or groovy implementations.

I also need to try and ‘cache’ some of the metadata like the python binding does to reduce unnecessary API calls.

Thanks for the interest :slight_smile:

Edit: I’m now caching the JSON from the login call, which helped to ‘un’ rate limit myself as my token was still valid. At some point, I’ll need to add a check for ‘Unauthorised’ and refresh the token, but that can wait.

Also forgot to mention, but current code is at GitHub - psmedley/openhab-addons at roborock

1 Like

Just FYI I’m adding Robotic Vacuum Cleaner support soon into the Matter binding (Matter is in 5.0 builds) , this is specifically to support the Roborock series of Matter compatible cleaners (its the only one i have access to data on ). I have the code mostly done, just finishing some unit tests for it. This would allow monitoring its operating state, sending commands (go home, clean, etc..) and selecting the areas (rooms) for cleaning. I don’t personally own one, and I’m not sure which models support matter, so take that for what it’s worth.

2 Likes

Appears my Qrevo S doesn’t have Matter compatible firmware.

Here is a list of models with Matter support.
https://www.roborock.com.my/blogs/news/roborock-issues-update-for-matter-support-in-ios-18-4

Roborock remains committed to expanding Matter support across its product range. In the coming weeks and months, the company will extend compatibility to the Roborock S8 Max Ultra and Qrevo Slim, with further models to follow.

FWIW - I’m hoping I can “recycle” some of the mqtt code from the ecovacs binding by @maniac103:

&

I hope to have some time to look at this over the weekend.

Friday update: With some pointers from @maniac103 and borrowing from the code from the ecovacs binding, I’m now subscribing to mqtt and getting messages!

Got MQTT message on topic

1.0\00\00B\00\00\00yhT�n\00f\000�q��V�6;� Y�*N�٤��’6��x���>Qj#"���~{��c�HHt�8F

Now to implement decoding the message using (I guess) Hubitat/roborockRobotVacuum/roborockRobotVacuum.groovy at main · bloodtick/Hubitat · GitHub

Current code at GitHub - psmedley/openhab-addons at roborock

OK, enough for one day - still working on parsing the encrypted mqtt message.

Right now decryption is failing at:

Excerpt of log is:

2025-06-20 19:09:41.434 [TRACE] [orock.internal.RoborockVacuumHandler] - 5VjNggUEIQUQcTbhSbEGb2: Got MQTT message on topic rr/m/o/66YKtrtqkaoVXmgKvf7ds4/7d3ff95f/5VjNggUEIQUQcTbhSbEGb2: 1.0\00\00�\00\00\00�hU,[\00f\000��F��,�򦷌���2����|j}�J+����ucD?ɱ�vj�$vj4���
2025-06-20 19:09:41.435 [DEBUG] [orock.internal.RoborockVacuumHandler] - parsed message version: 1.0, sequence: 2299, random: 199, timestamp: 1750412379, protocol: 102, payloadLen: 48
2025-06-20 19:09:41.435 [DEBUG] [orock.internal.RoborockVacuumHandler] - payload = [-118, -115, 70, -53, -51, 20, 44, -51, 2, -14, -90, -73, -116, -77, -94, -83, 50, -67, -12, -83, 5, -117, 124, 11, 106, 20, 125, -30, 74, 43, -81, -120, -92, -53, 117, 99, 68, 63, -55, -79, -99, 118, 106, -21, -102, 36, 118]
2025-06-20 19:09:41.436 [DEBUG] [orock.internal.RoborockVacuumHandler] - encodedtimestamp = c55b8562
2025-06-20 19:09:41.436 [DEBUG] [orock.internal.RoborockVacuumHandler] -  key = c55b8562R9ssyr1K6U4oP8wxTXdfu$jyZ#TZHsg4, localKey = R9ssyr1K6U4oP8wx, salt = TXdfu$jyZ#TZHsg4
2025-06-20 19:09:41.437 [INFO ] [orock.internal.RoborockVacuumHandler] - decrypt4
2025-06-20 19:09:41.437 [DEBUG] [orock.internal.RoborockVacuumHandler] - Exception decrypting payload

Edit: I added an e.getMessage() to the error message. doFinal is failing due to:

Exception decrypting payload, Input length must be multiple of 16 when decrypting with padded cipher

Roborock don’t make things easy… then to send commands it will be necessary to reverse the decryption code to encrypt things

Modelling code from the groovy implementation… Hubitat/roborockRobotVacuum/roborockRobotVacuum.groovy at main · bloodtick/Hubitat · GitHub

OK found the problem, now able to decrypt mqtt messages - now to work out what to do with them :stuck_out_tongue:

Some examples:

2025-06-20 20:11:44.409 [DEBUG] [orock.internal.RoborockVacuumHandler] - stringResult = {"t":1750416103,"dps":{"102":"{\"id\":1837,\"result\":[{\"msg_ver\":2,\"msg_seq\":357,\"state\":8,\"battery\":100,\"clean_time\":0,\"clean_area\":0,\"error_code\":0,\"map_present\":1,\"in_cleaning\":0,\"in_returning\":0,\"in_fresh_state\":1,\"lab_status\":1,\"water_box_status\":1,\"fan_power\":104,\"dnd_enabled\":0,\"map_status\":3,\"is_locating\":0,\"lock_status\":0,\"water_box_mode\":203,\"distance_off\":160,\"water_box_carriage_status\":1,\"mop_forbidden_enable\":1,\"camera_status\":1,\"is_exploring\":0,\"adbumper_status\":[0,0,0],\"water_shortage_status\":0,\"dock_type\":15,\"dust_collection_status\":0,\"auto_dust_collection\":1,\"avoid_count\":46,\"mop_mode\":300,\"in_warmup\":0,\"back_type\":-1,\"wash_phase\":0,\"wash_ready\":1,\"wash_status\":768,\"debug_mode\":0,\"collision_avoid_status\":1,\"switch_map_mode\":0,\"dock_error_status\":0,\"charge_status\":1,\"unsave_map_reason\":4,\"unsave_map_flag\":0,\"dry_status\":0,\"rdt\":0,\"clean_percent\":0,\"rss\":2,\"dss\":169,\"common_status\":2,\"corner_clean_mode\":0,\"repeat\":1,\"kct\":0,\"events\":[],\"switch_status\":0,\"replenish_mode\":0,\"subdivision_sets\":0}]}"}}
2025-06-20 20:12:46.344 [DEBUG] [orock.internal.RoborockVacuumHandler] - stringResult = {"t":1750416164,"dps":{"102":"{\"id\":1891,\"result\":{\"file\":\"\",\"result\":\"ok\",\"nonce\":1750416163,\"snapshot\":{\"36\":{\"nonce\":1750391670,\"limit_len\":5},\"41\":{\"nonce\":1750391670,\"limit_len\":16},\"3\":{\"nonce\":1750412380,\"limit_len\":500},\"25\":{\"nonce\":1750391670,\"limit_len\":100},\"15\":{\"nonce\":1750407859,\"limit_len\":80},\"11\":{\"nonce\":1750391670,\"limit_len\":50},\"16\":{\"nonce\":1750391670,\"limit_len\":80},\"21\":{\"nonce\":1750391670,\"limit_len\":50},\"6\":{\"nonce\":1750391670,\"limit_len\":50},\"29\":{\"nonce\":1750391670,\"limit_len\":5},\"24\":{\"nonce\":1750391670,\"limit_len\":32},\"30\":{\"nonce\":1750391670,\"limit_len\":5},\"31\":{\"nonce\":1750391670,\"limit_len\":5}}}}"}}
2025-06-20 20:12:46.345 [DEBUG] [orock.internal.RoborockVacuumHandler] - stringResult = {"t":1750416165,"dps":{"102":"{\"id\":1891,\"result\":[\"ok\"]}"}}
2 Likes

Saturday evening update. I’m still not sure how to handle the received mqtt messages. There’s no apparent way to know what the messages relate to and hence how to decode them. Still thinking about this, and will try have a look at the python home assistant code to see how they handle it.

Starting looking at implementing commands… I think I’ve built the right message format, but when I send it I get:

MQTT disconnected (source SERVER): Server closed connection without DISCONNECT.

So I guess the syntax isn’t quite right :frowning:

Oops - just noticed that when subscribing, the topic syntax is:
“rr/m/o/${rriot.u}/${mqttUser}/${deviceId}”

when publishing, it should be:
“rr/m/i/${rriot.u}/${mqttUser}/${deviceId}”

That’s what I get for copy/pasting…

The good news with the updated topic is that the server doesn’t disconnect after receiving the message any more, the bad new is the command doesn’t do anything.

1 Like

Still following with great interest :smiley: I wonder if you could find some hints on either the home assistant robotrock implementation or even the miio OpenHAB binding - since we can send commands directly fine..?

The HA roborock binding talks to the python roborock library. miio binding uses http commands, not commands sent via mqtt.

I’ve raised Trying to create OpenHAB binding - problem sending commands · Issue #393 · Python-roborock/python-roborock · GitHub to see if they have any clues for me, and meanwhile, I’ll keep trying things.

OK, so I added a printf to the python roborock code to allow me to look at the payload they are generating.

Turns out I had another typo. I didn’t notice that for sending a command, the protocol is 101, not 102.

Now I can start/stop the vacuum via OpenHAB :slight_smile:

A VERY skeletal form of the binding is at https://smedley.id.au/tmp/org.openhab.binding.roborock-5.0.0-SNAPSHOT.jar compiled with Java17 and OH 4.3.0 support.

The channel list is 100% based on the MIIO binding - right now, only 8 or so channels are actually populated.

Now that i have commands working, I think I can see how I can populate more channels. When you send a command, you send it with a specific ID, and the response comes back with the same ID. So I think I should be able to send a “get_status” command with a known ID, then potentially use the publish handler to parse the response.

Configuration right now requires a username and password - down the track I’ll look at allowing use of a verification code, but that’s an extra complexity right now.

As time permits, I’ll also look at further populating the channels.

NB: as this is VERY early stages of development, it’s likely that channels will be added/removed which will require removing things and re-adding them.

The code is also pretty messy right now and will need considerable refactoring before being included in the OH codebase.

Edit: Not seeing a way with the mqtt3 client to wait for a response after publishing - might have to try mqtt5 like https://github.com/psmedley/openhab-addons/blob/698624ed13ec4f6a006a1e61a9db4812d594596e/bundles/org.openhab.binding.meross/src/main/java/org/openhab/binding/meross/internal/api/MerossMqttConnector.java#L84

It’s either that, or somehow log the ‘id’ value of the submitted requests, then when the message comes in, compare the id to the list of sent requests to see what type of request it was…

1 Like

OK I found a hacky solution to knowing which mqtt response relates to the get_status and get_consumable command.

Updated the link - right now it’s only updating one variable from each command, I’ll start to work through working out which json responses related to which channels…

Edit: Updated a bunch more channels, thankfully the json entries were quite similar to the miio binding which saved a bunch of work.

Link updated.

A few notes whilst they’re fresh.

  • all the mqtt connections are handled from the ‘vacuum’ thing. I’m not sure yet what will happen if you have more than one vacuum connected, and whether the roborock mqtt server will allow multiple connections.
  • if the roborock server won’t allow multiple connections, there will be significant refactoring to move the mqtt code into the Account handler, and subscribe to ALL channels.
  • I’ve only tested with a Roborock Qrevo S - so I’m not sure if the json provided by other vacuums is the same or not, there’s a chance this could break things.
  • at some point, I’ll try and connect one of my older Roborock’s to the roborock app instead of Mi Home and see if this works.
1 Like

https://smedley.id.au/tmp/org.openhab.binding.roborock-5.0.0-SNAPSHOT.jar is updated. Whilst this mention 5.0.0 in the filename, it is built with OH 4.3 and java 17 compatability.

I’ve also adapted the miio code that determines vacuum capabilitity so it doesn’t try and update channels that aren’t supported by the vac.

I need to work on adding support for more commands, so far we just have vacuum on/off and setting the clean mode to Vacuum / Spot/ Pause / Dock.

I see how to start a routine in the Python code - I just to find a way to populate a list based on querying the API to show the supported Routines. Same with Room cleaning I guess.