Skip to content
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

[miio] Add support for viomi S9 vacuum (v18) #9878

Merged
merged 3 commits into from
Feb 4, 2021

Conversation

marcelrv
Copy link
Contributor

Signed-off-by: Marcel Verpaalen marcel@verpaalen.com

@jonasbark would you mind testing the json file with OH3

@marcelrv marcelrv added enhancement An enhancement or new feature for an existing add-on work in progress A PR that is not yet ready to be merged labels Jan 19, 2021
@jonasbark
Copy link

@marcelrv I tried and yes, it works very well on the Xiaomi Cloud - thanks a bunch!
I have not, however, been successful with direct connection (time out).

Could you tell me how you've been able to find out about the changes required regarding the API? I tried my luck with

  • Wireshark (haven't been able to detect UDP packages except a single handshake)
  • Charles Proxy for Xiaomi Cloud (failed at the SSL root cert step)

Also I'm willing to investigate further regarding the timeouts - I just need a few more pointers I guess.

@marcelrv
Copy link
Contributor Author

for cloud, I'm using android device that runs through mitmproxy. You indeed need to import the certificates.
But from there onwards it is pretty straight forward to see the messages as it is no further encrypted.

For the development way back on the UDP protocol, indeed wiresharking and there was the initial version of a javascript library that helped comparing my calculated encryption against their implementation.

@marcelrv
Copy link
Contributor Author

Are you sure the mihome app communicates direct with the device (outside maybe the first handshake/miio ping)
In the app source it looks like the majority of the calls use callMethodFromCloud which typically means a cloud only device.

@jonasbark
Copy link

I rechecked - after I set the timeout quite low (5) the whole thing works fairly well locally. Lots of timeouts or empty responses but in general it's usable.

@jonasbark
Copy link

jonasbark commented Jan 20, 2021

The responses assignment to the calling requests seem to mix up themselves:

2021-01-20 16:04:27.257  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:battery-level: -3600 % - miio:basic:13A28C08
2021-01-20 16:04:27.257  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:battery-level: -3600 - miio:basic:13A28C08

or

2021-01-20 16:04:57.189  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:status: 0 - miio:basic:13A28C08
2021-01-20 16:05:27.260  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:status: 2 - miio:basic:13A28C08
2021-01-20 16:02:57.210  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:status: -3600 - miio:basic:13A28C08
2021-01-20 16:03:57.239  INFO 62780 --- [ageSenderThread] f.f.server.openhab.ThingHandlerCallback  : miio:basic:13A28C08:mode: -3600 - miio:basic:13A28C08

Breakpoint at MiioAsyncCommunication.java#254:
image
Note the difference between did in the command and response.

To "fix" this I changed updatePropsFromJsonArray from MiioBasicHandler to use 'res' instead of 'para' as source of truth:
https://github.com/feilfeilundfeil/openhab-addons/commit/bbd3aee2b1d5f62925a3233335c2f4758e2c9f33

@marcelrv
Copy link
Contributor Author

marcelrv commented Jan 20, 2021

Indeed that is something funcky that needs looking after. I think the MiioAsyncCommunication expects the device to respond to the request just issued... but long time I haven't looked at that code so I'll need to check.

Can you share a debug log of a full refresh, so it is clear which channels (siid/piids) respond and which not.

Identified manual actions for execution
action{"did":"vacuum-start-sweep","siid":2,"aiid":1,"in":[]}
action{"did":"vacuum-stop-sweeping","siid":2,"aiid":2,"in":[]}
action{"did":"vacuum-pause","siid":2,"aiid":3,"in":[]}
action{"did":"vacuum-start-charge","siid":2,"aiid":4,"in":[]}
action{"did":"vacuum-stop-massage","siid":2,"aiid":5,"in":[]}
action{"did":"vacuum-start-mop","siid":2,"aiid":6,"in":[]}
action{"did":"vacuum-start-only-sweep","siid":2,"aiid":7,"in":[]}
action{"did":"vacuum-start-sweep-mop","siid":2,"aiid":8,"in":[]}
action{"did":"viomi-vacuum-reset-map","siid":4,"aiid":7,"in":[]}
action{"did":"viomi-vacuum-set-calibration","siid":4,"aiid":10,"in":[]}
action{"did":"viomi-vacuum-reset-consumable","siid":4,"aiid":11,"in":[35.0]}
action{"did":"viomi-vacuum-set-room-clean","siid":4,"aiid":13,"in":[36.0, 37.0, 38.0]}
action{"did":"order-del","siid":5,"aiid":2,"in":[1.0]}
action{"did":"order-get","siid":5,"aiid":3,"in":[]}
action{"did":"point-zone-start-point-clean","siid":6,"aiid":1,"in":[]}
action{"did":"point-zone-pause-point-clean","siid":6,"aiid":2,"in":[]}
action{"did":"point-zone-start-zone-clean","siid":6,"aiid":5,"in":[]}
action{"did":"point-zone-pause-zone-clean","siid":6,"aiid":6,"in":[]}
action{"did":"map-upload-by-maptype","siid":7,"aiid":1,"in":[]}
action{"did":"map-upload-by-mapid","siid":7,"aiid":2,"in":[]}
action{"did":"map-set-cur-map","siid":7,"aiid":3,"in":[2.0, 15.0]}
action{"did":"map-del-map","siid":7,"aiid":5,"in":[2.0]}
action{"did":"map-rename-map","siid":7,"aiid":7,"in":[2.0, 4.0]}
action{"did":"map-arrange-room","siid":7,"aiid":8,"in":[2.0, 5.0, 6.0, 14.0]}
action{"did":"map-split-room","siid":7,"aiid":9,"in":[2.0, 5.0, 7.0, 8.0, 14.0]}
action{"did":"map-rename-room","siid":7,"aiid":10,"in":[2.0, 7.0, 9.0, 14.0]}
action{"did":"map-get-map-list","siid":7,"aiid":11,"in":[]}
action{"did":"map-get-cleaning-path","siid":7,"aiid":12,"in":[12.0]}
action{"did":"map-set-new-map","siid":7,"aiid":13,"in":[]}
action{"did":"map-deal-new-map","siid":7,"aiid":14,"in":[16.0]}
action{"did":"voice-find-device","siid":8,"aiid":2,"in":[]}
action{"did":"voice-download-voice","siid":8,"aiid":3,"in":[3.0, 7.0, 8.0]}
action{"did":"voice-get-downloadstatus","siid":8,"aiid":4,"in":[]}

Esp the starting/stopping ones would be good to have confirmed working as well

@jonasbark
Copy link

For the refresh:
https://gist.github.com/jonasbark/3c378506386f2dc61d4afedf480c1fc4
You can see it struggling with unexpected responses, e.g. when it receives a channel response instead of the miio.info one.

Regarding actions: the vacuum-start-sweep command worked, although the logs stated no response. Same applies to vacuum-stop-sweeping. When I find the time I will try out other actions.

@wborn wborn added rebuild Triggers Jenkins PR build and removed rebuild Triggers Jenkins PR build labels Jan 21, 2021
@marcelrv
Copy link
Contributor Author

I think your timeout indeed is rather low, esp as the viomies have poor performance wrt response times.
Anyways, the out of sync should not be causing any misaligned updates. #9966 is there to address the out of sync issue.

Will go over your gist in more detail and update the json.

@jonasbark
Copy link

jonasbark commented Jan 26, 2021

Hi Marcel!
I finally managed to run mitmproxy successfully - I noticed the latest version of the app sends most of their requests to app/v2/*. E.g. app/v2/home/device_list_page

Accessing those endpoints with the same method results in
{"code":-1,"message":"no permit","result":null}

So I'm wondering, does it use a different authentication method?

EDIT: Found it myself - using e.g. 5.5.66 lets me view what I need :) in the aforementioned request the parameter
{"getVirtualModel":true,"getHuamiDevices":1}
was missing

@marcelrv
Copy link
Contributor Author

yes... those seem to be the magic parameters :)
Yes, I found the same, old app using v1 methods, new app the v2.

Actually the binding works without any issue with the v2 api as well (meaning if all requests are done through the v2 api rather than without v2)
I think just some of the endpoints are not available in the v1, or maybe have one or more data elements added

@marcelrv marcelrv removed the work in progress A PR that is not yet ready to be merged label Feb 2, 2021
@marcelrv marcelrv requested a review from cpmeister February 2, 2021 22:01
Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
@marcelrv
Copy link
Contributor Author

marcelrv commented Feb 3, 2021

@Hilbrand included wider columns in this readme update
Most channel names & descriptions and longest common type (Number:Dimensionless) are now fitting

Co-authored-by: Connor Petty <mistercpp2000@gmail.com>
Signed-off-by: Marcel Verpaalen marcel@verpaalen.com
@cpmeister cpmeister merged commit 40dbd50 into openhab:main Feb 4, 2021
@cpmeister cpmeister added this to the 3.1 milestone Feb 4, 2021
@jonasbark
Copy link

Hi @marcelrv - sorry for pinging you again in this already merged PRs but I'm not sure where else to ask:

I'm trying to find out the commands that are sent when adding entries to the "cleaning plan". This used to be easy to find out with an older version (e.g. 5.5.66) of the Xiaomi app due to unencrypted messages but now it complains that it needs a newer version when opening the Viomi robot.

Is there an alternative way to read or decrypt the plain text commands?

@marcelrv marcelrv deleted the miio-viomi18 branch February 23, 2021 08:04
@marcelrv
Copy link
Contributor Author

yes, that's nowadays a bit tricky.

The way I log traffic is by using the cloud communication rather than the local communication.
Than I use a docker with mitmproxy to log the traffic, with college proxy app to route the trafic to it.
You see the commands in the POST to /home/rpc/[yourdeviceId]

I think the other way is to wireshark the communication between device and phone, and decode it using the token... there are some python/node scripts that help with it. But I found that always a even more tricky setup.

@marcelrv
Copy link
Contributor Author

If you manage to set it up, please share a log of the regular communicaton.
Likewise, what is the communication for selecting and pulling the map would be interesting + one or 2 maps files.

If it is anything like the rockrobo's, during the communication you see the URL location of the map file, you can download these map files by copying the url to the browser.

If you can't make the logging with mitmproxy to work, you can share your device (in the mihome app) for a few days and I can log it for you.

@jonasbark
Copy link

I'm also logging the traffic successfully with this combination (for future reference):

  • Charles Proxy for Mac
  • Custom root certificate
  • Installed on a rooted Android device with a Magisk module

College Proxy sounds easier, though.

The issue I'm having is that even the POST requests to /home/rpc/ are encrypted as well so I can't read what's going on. I did not find a python or node script to decrypt the communication apart from this but it relies on UDP traffic.

Can you point me to the scripts you mentioned?

I do plan to take a look at the Map situation as well and contribute accordingly! I'm just not sure yet when :)

@marcelrv
Copy link
Contributor Author

ah, okay, did not get that... so the communication to the cloud is also encrypted that is a new 'feature'
It is odd, as it also accepts the non-encrypted commands (as that is what the binding is sending).
Is the encrypting something specific to the cleaning plan commands?

I think something like this can be used, but it may be similar to the link you gave.
https://github.com/OpenMiHome/mihome-binary-protocol/blob/master/pcap-decrypt.py
But than you need to hope that the same communication to the device is non encrypted on command level for the same command.

The fallback can be to analyse the code of the plugin for your device.

@jonasbark
Copy link

Yes regarding the encryption - I tried lots of old Xiaomi Home app versions and it seems encryption was enabled around the time 5.5.66 was released (so September 2019).

Encryption is active on every part of the API, so rpc + everything else (except some specific ones, such as /app/service/getappconfig or /version/online_version). I guess unencrypted communication is still possible for backward compatibility.
Are you using a different Xiaomi Home app version where it still sends unencrypted messages?

I tried the python script:

import miio

data = 'lWZ7SRZRHpjWUY4Hdh+DYNMlDp4aG0mvrklbqk65r/UsH8gjwu/SNpRD2vxHqin2e76gnfFiKiVTKIZOs+htpRPc2EwKj/fcK8Q1uzI8ba6H6K2tq4mypNf1Nxll9jYlSwImVCFxChgS0O7xLN1YaOZ+j+vUjClZTH64Dpu6JMOHFlTMnAnA5vQLdBaoFYdGyzStynSeVTkXGg=='
array = data.encode('utf-8')

decrypted = miio.decrypt('***'.encode('utf-8'), array)
if decrypted:
    print(decrypted.decode('UTF-8'))

but it fails with an error "Invalid padding bytes."

@marcelrv
Copy link
Contributor Author

marcelrv commented Feb 23, 2021

The python tool is for decrypting the traffic between device and phone.
In order to use this, you need to wireshark the traffic betwen device & phone

Seems the cloud command encryption is different. Do you see this for all devices/commands or just for the viomi vacuum? (in other words, is it something specific to the device or general change)

I indeed still have bit of older mihome version installed for logging. I havn;'t seen this encrypted traffic.
Another thought to work around the issue would be to install the last version without encryption and trick the mihome app in using the latest version of the viomi apk (e.g. by changing the communication on the fly with charles proxy)

@jonasbark
Copy link

Can you tell me which version you're using of the mihome app? Maybe yours is newer than what I found to be the last unencrypted version.

The traffic is also encrypted for a Xiaomi Mi Robot so I guess the change is in general.

I'll see what I can find out regarding the newer APK with the old version.

@jonasbark
Copy link

jonasbark commented Feb 23, 2021

For reference:
https://cdn.alsgp0.fds.api.mi-img.com/rn-plugins/2020-12-16/signed_10048_1004059_16_ANDROID_bundle_745ae3c6b358fac1c3d12dedc9d7dcd8.zip that's the file that contains the latest Viomi software. The main.bundle in it is just a bundled Javascript and quite easy to read from e.g. IntelliJ

@marcelrv
Copy link
Contributor Author

marcelrv commented Feb 23, 2021

yes, that was what I meant with

The fallback can be to analyse the code of the plugin for your device.

I'll confirm the mihome version I have tonight, but I recall it is 5.5.23
If all devices are encrypted, it is the mihome app to be analysed. You can open it with jdax

@marcelrv
Copy link
Contributor Author

I checked, my version is 5.6.74 (android) I can see the traffic unencrypted

@jonasbark
Copy link

That version also works with the latest Viomi module - thanks for the hint!

themillhousegroup pushed a commit to themillhousegroup/openhab2-addons that referenced this pull request May 10, 2021
* [miio] Add support for viomi S9 vacuum (v18)
* [miio] improved translations & wider columns readme
* [miio] Apply review suggestions

Signed-off-by: Marcel Verpaalen marcel@verpaalen.com
Signed-off-by: John Marshall <john.marshall.au@gmail.com>
@jens-schiffke
Copy link

jens-schiffke commented Sep 13, 2021

Indeed that is something funcky that needs looking after. I think the MiioAsyncCommunication expects the device to respond to the request just issued... but long time I haven't looked at that code so I'll need to check.

Can you share a debug log of a full refresh, so it is clear which channels (siid/piids) respond and which not.

Identified manual actions for execution
action{"did":"viomi-vacuum-set-room-clean","siid":4,"aiid":13,"in":[36.0, 37.0, 38.0]}

Esp the starting/stopping ones would be good to have confirmed working as well

Can you attach please an example of how to start a room cleaning?
action{"did":"viomi-vacuum-set-room-clean","siid":4,"aiid":13,"in":[0, 1, "1,2"]} leads to no result, there is no answer from the cloud.

p.s. I found the solution:
action{"did":"viomi-vacuum-set-room-clean","siid":4,"aiid":13,"in":[{"piid": 36, "value": 0}, {"piid": 37, "value": 1}, {"piid": 38, "value": "1,2"}]}

thinkingstone pushed a commit to thinkingstone/openhab-addons that referenced this pull request Nov 7, 2021
* [miio] Add support for viomi S9 vacuum (v18)
* [miio] improved translations & wider columns readme
* [miio] Apply review suggestions

Signed-off-by: Marcel Verpaalen marcel@verpaalen.com
marcfischerboschio pushed a commit to bosch-io/openhab-addons that referenced this pull request May 5, 2022
* [miio] Add support for viomi S9 vacuum (v18)
* [miio] improved translations & wider columns readme
* [miio] Apply review suggestions

Signed-off-by: Marcel Verpaalen marcel@verpaalen.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature for an existing add-on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants