rytilahti / python-miio

Python library & console tool for controlling Xiaomi smart appliances
https://python-miio.readthedocs.io
GNU General Public License v3.0
3.73k stars 558 forks source link

[Error] Timout when querying for status #1

Closed georg90 closed 7 years ago

georg90 commented 7 years ago

Hi!

Thank you for this great plugin! Exactly what I was looking for!

I am having trouble using it with my vacuum. I discover my vacuum just fine:

INFO:mirobo.vacuum:  IP 192.168.8.1: 845 - token: b'764e4b5a716672343239374562753632'

But when trying to query the status, I get this error:

sudo mirobo --ip 192.168.0.130 --token 764e4b5a716672343239374562753632 -d status
INFO:mirobo.cli:Debug mode active
DEBUG:mirobo.vacuum:192.168.0.130:54321 >>: {'id': 1, 'method': 'get_status'}
ERROR:mirobo.vacuum:got error when receiving: timed out
Traceback (most recent call last):
  File "/usr/local/bin/mirobo", line 11, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args[1:], **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/mirobo/cli.py", line 51, in status
    res = vac.status()
  File "/usr/local/lib/python3.6/site-packages/mirobo/vacuum.py", line 106, in status
    return VacuumStatus(self.send("get_status")[0])
  File "/usr/local/lib/python3.6/site-packages/mirobo/vacuum.py", line 79, in send
    data, addr = s.recvfrom(1024)
socket.timeout: timed out

Steps I took:

  1. Reset vacuum (delete from app)
  2. use ´discover´ function
  3. connect the robot to my wifi using the Mi Home App
  4. Try to query for status using mirobo

PS: I already increased the timout 15s inside the vacuum.py

rytilahti commented 7 years ago

Are you sure the IP belongs to the robot? Does the IP answer to pings? If you try nmap against it, do you see open ports? Considering that you tried increasing the timeout, this sounds odd.

georg90 commented 7 years ago

The Ip belongs to the robot..

ping 192.168.0.130
PING 192.168.0.130 (192.168.0.130): 56 data bytes
64 bytes from 192.168.0.130: icmp_seq=0 ttl=64 time=21.866 ms
64 bytes from 192.168.0.130: icmp_seq=1 ttl=64 time=2.533 ms
64 bytes from 192.168.0.130: icmp_seq=2 ttl=64 time=66.665 ms
--- 192.168.0.130 ping statistics ---
4 packets transmitted, 3 packets received, 25.0% packet loss
round-trip min/avg/max/stddev = 2.533/30.355/66.665/26.861 ms

But the only open port is an ssh port?

sudo nmap -n 192.168.0.130
Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-31 16:37 CEST
Nmap scan report for 192.168.0.130
Host is up (0.0044s latency).
Not shown: 999 closed ports
PORT   STATE    SERVICE
22/tcp filtered ssh
MAC Address: 28:6C:07:F7:77:18 (Xiaomi Electronics,co.)
Nmap done: 1 IP address (1 host up) scanned in 13.64 seconds

I pressed the reset button on the robot itself, and got when discovering:

mirobo --ip 192.168.0.130 --token 764e4b5a716672343239374562753632 discover
INFO:mirobo.vacuum:Sending discovery packet to broadcast address..
WARNING:mirobo.protocol:Unable to decrypt, returning raw bytes.
INFO:mirobo.vacuum:  IP 192.168.8.1: 845 - token: b'654d6c68624539454d74554863564350'
WARNING:mirobo.vacuum:error while reading discover results: timed out

After setting up the wifi inside the original app, I get this:

$mirobo --ip 192.168.0.130 --token 654d6c68624539454d74554863564350 discover
INFO:mirobo.vacuum:Sending discovery packet to broadcast address..
WARNING:mirobo.protocol:Unable to decrypt, returning raw bytes.
INFO:mirobo.vacuum:  IP 192.168.0.130: 845 - token: b'ffffffffffffffffffffffffffffffff'
WARNING:mirobo.vacuum:error while reading discover results: timed out

$mirobo --ip 192.168.0.130 --token 654d6c68624539454d74554863564350 status
ERROR:mirobo.vacuum:got error when receiving: timed out
Traceback (most recent call last):
  File "/usr/local/bin/mirobo", line 11, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/decorators.py", line 64, in new_func
    return ctx.invoke(f, obj, *args[1:], **kwargs)
  File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/mirobo/cli.py", line 51, in status
    res = vac.status()
  File "/usr/local/lib/python3.6/site-packages/mirobo/vacuum.py", line 106, in status
    return VacuumStatus(self.send("get_status")[0])
  File "/usr/local/lib/python3.6/site-packages/mirobo/vacuum.py", line 79, in send
    data, addr = s.recvfrom(1024)
socket.timeout: timed out

Which FW version are you running? Did you bind the robot to your wifi using the original app?

rytilahti commented 7 years ago

The nmap results look similar to mine, so the network connectivity doesn't seem to be the problem. For discovery it's not using the token at all, it's just ignored. My robot is bound with the original app yes, the FW version is 3.3.6_003047.

Maybe cou could try to use tcpdump or wireshark to see if the robot is answering anything? I'm running python 3.4 here, but that shouldn't be a problem.

edit: you could also try if calling "mirobo consumables" yields a timeout too? It's a bit odd that discover (even when connected) will respond just fine...

georg90 commented 7 years ago

I have the same FW version.

tcpdump results doesn't look promising..

Original app turning on cleaning:

sudo tcpdump -i en0 -n host 192.168.0.130 -vvv -w ~/Desktop/DumpFile03.pcap
Output:
17:57:18.076350 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
17:57:18.076956 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
17:57:18.280579 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
17:57:18.281042 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
17:57:18.820787 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
17:57:18.820792 IP 192.168.0.130.5353 > 224.0.0.251.5353: UDP, length 205
E.....@....................h............._miio._udp.local........x.".rockrobo-vacuum-v1_miio55408263...(.......x...path=/mydevice.(.!.....x.'.....1.rockrobo-vacuum-v1_miio55408263..w.......x.......w.......x.......w./.....x. .w..@....
mirobi status
17:56:06.681927 IP 192.168.0.164.53650 > 192.168.0.130.54321: UDP, length 80
E..l3...@..7...........1.X..!1.P........X.|.=Z..k.7t..W.^.*.'.li..uu.iZ..nk....}*l...|..V...4..)    .(.8.m\....
17:56:22.259312 IP 192.168.0.164.65101 > 192.168.0.130.54321: UDP, length 80
E..l....@..P.........M.1.Xz.!1.P........X.|&.......nd_.N7...'.li..uu.iZ..nk.tR.........s..z.#.x.=...22..Qd4.
mirobi discover
18:03:27.239550 ARP, Request who-has 192.168.0.164 tell 192.168.0.130, length 28
........(l..w...............
18:03:27.239594 ARP, Reply 192.168.0.164 is-at ac:bc:32:8c:a4:49, length 28
..........2..I....(l..w.....
18:03:27.242299 IP 192.168.0.130.54321 > 192.168.0.164.57804: UDP, length 32
E..<..@.@..:.........1...(UV!1. .....Mv.X.}.................

So we get an answer when using discover bit, but not on the functions.. Maybe the coding changed, and the token isn't enough anymore?

rytilahti commented 7 years ago

Those messages you see for original app turning it on are not related to control, but discovery (through mdns), confirms that the IP is correct though.

Do you have a different firmware version then? Mine says that it's already up to date, and I doubt they would have changed it so suddenly, but it's possible.

an0Nym0us63 commented 7 years ago

same issue here everything falls into timeout.

By the way i compiled un exe for discovering. As it can be easier for some people to connect to the vacuum wifi via a laptop to get the token

I was going to integrate the air purifier, but with the timeout issue happening every where it's weird

rytilahti commented 7 years ago

The timeout (also) happens when an incorrect message is sent to the device, e.g. when the token is invalid. I just commited a couple of changes, one allowing using -d multiple times, and made it print out more information when invoked with '-dd'. @georg90 could you do a discovery (it doesn't require specifying token & ip anymore) with -dd and paste the contents its printing?

@sarakha63 nice to hear about air purifier. Those timeouts are weird, but indicate most likely that there's a problem with the token or with the checksum. You could also try with -dd to see if the returned messages do look fine.

an0Nym0us63 commented 7 years ago

i also have the humidificator and lots of other device that do follow the same process

an0Nym0us63 commented 7 years ago

here it is
image

rytilahti commented 7 years ago

That's what is being sent, try the discovery to see what is being returned to you. Could you remove the comment on this line: https://github.com/rytilahti/python-mirobo/blob/master/mirobo/vacuum.py#L45 and do a rediscovery?

Maybe it's about the one hardcoded "serial" value which is causing it. In my robot it is 41997, but maybe it differs for you and that causes the problem. Maybe there are different revisions of the software, or that this value is somehow based on hardware and you happen to have a different revision? That is, assuming that this is working for someone else besides me at all :-)

an0Nym0us63 commented 7 years ago

i nice guess i'm checking that

an0Nym0us63 commented 7 years ago

mine is not 41997

it is 55069 I'm going to try

an0Nym0us63 commented 7 years ago

Also the type id is not the same mine is 0x034c

rytilahti commented 7 years ago

Ok, then that's not really a type, but something else. It was just a guess from my part (after reading some forums etc.). Or maybe it is a type, but your hardware/software is different..

an0Nym0us63 commented 7 years ago

[2017-03-31 20:34:34][DEBUG] : Got a response: Container: data = Container: length = 0 offset2 = 32 data = offset1 = 32 value = header = Container: length = 16 offset2 = 16 data = !1 ▒▒4e offset1 = 0 value = Container: length = 32 unknown = 0 devtype = Xiaomi Smart Mi Air Purifier serial = 55069 ts = 1970-01-01 04:43:33 checksum = ▒\▒tz▒1▒▒ I also notice the robot is not at the right time

an0Nym0us63 commented 7 years ago

Ah this is not the good one sorry [2017-03-31 20:34:34][DEBUG] : Got a response: Container: data = Container: length = 0 offset2 = 32 data = offset1 = 32 value = header = Container: length = 16 offset2 = 16 data = !1 L▒jXޡ: offset1 = 0 value = Container: length = 32 unknown = 0 devtype = 844 serial = 43370 ts = 2017-03-31 20:34:34 checksum = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ this is the good one sorry i have so much device lol

rytilahti commented 7 years ago

In the bottom of the robot there are some serial numbers etc, maybe we can pinpoint those to the required values. The purifier is detected correctly, right? If so, we are on the correct track with devid being a devid of some sort :)

Please try to change https://github.com/rytilahti/python-mirobo/blob/master/mirobo/protocol.py#L140 accordingly.

edit: for comparison, here's output from my (already connected) discover:

DEBUG:mirobo.vacuum:Got a response: Container: 
    data = Container: 
        value = b''
        offset1 = 32
        offset2 = 32
        data = b''
        length = 0
    header = Container: 
        value = Container: 
            length = 32
            unknown = 0
            devtype = Xiaomi Mi Robot Vacuum
            serial = 41997
            ts = 2017-03-31 20:39:04
        offset1 = 0
        offset2 = 16
        data = b'!1\x00 \x00\x00\x00\x00\x02\xf2\xa4\rX\xde\xa2H'
        length = 16
    checksum = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
an0Nym0us63 commented 7 years ago

Oh yes i have lots of device detected. No i added manualy the type with the code i had with discover, so the name is because i followed the same idea as you and added it to the dict

an0Nym0us63 commented 7 years ago

i don't get it even if i change the serial in the message struct in protocol file i alaways see 41997 for the serial

rytilahti commented 7 years ago

Those are currently hardcoded in vacuum.py it seems, my bad. Check out lines 66-68. It's all in a bit "proof-of-concept" state currently. The structure is being used (mostly) for reading.

an0Nym0us63 commented 7 years ago

ahhh ok didnt check there

an0Nym0us63 commented 7 years ago

Ok great needed to change both of them serial and type and i get my status ;) nice

an0Nym0us63 commented 7 years ago

so in my integration just have to add the devtype and the serial on the json result on discover and save it in the system. will investigate the purifier this week end

an0Nym0us63 commented 7 years ago

Fo info the purifier keeps giving its real token even after being connected

rytilahti commented 7 years ago

That's great to hear that it works! Could you please check what it says underneath your robot for CMIIT ID and the second one? In my case they are SDJQR01RR (device id I assume) and CMIIT 2016DP3912.

an0Nym0us63 commented 7 years ago

SDJQR01RR and 2016DP3912.

So i really thing the dict for devtype won't be usefull for now, and the vacuum class should just have serial and devtype to be added as argument, and the samething displayed upon disover and the lib will be just fine.

Maybe we will find that there are only 2 or 3 devtypes and the dict can come back. But for now im not sure it has sense. AS we don't know yet if there are 2 3 or 10 devtypes for the same device family.

But great i will come back on my attempts on Purifier Humidifcator and else

rytilahti commented 7 years ago

Yeah, that's true. I think the best solution for this would be to send a discovery message to the given device before sending any more messages. It would induce a small delay, but it'd make it easy to extract the required values and use them directly. Let's see what values @georg90 has and see how to proceed.

an0Nym0us63 commented 7 years ago

the dev type for @georg90 is 844 (decimal) the same as mine, lets see for the serial if it is the same

georg90 commented 7 years ago

Great job guys. That fixed it for me!

So using discover I get (first removing comment from L45):

$sudo mirobo -dd discover
INFO:mirobo.cli:Debug mode active
INFO:mirobo.vacuum:Sending discovery packet to broadcast address with timeout of 5s..
WARNING:mirobo.protocol:Unable to decrypt, returning raw bytes.
DEBUG:mirobo.vacuum:Got a response: Container: 
    data = Container: 
        data = b''
        value = b''
        offset1 = 32
        offset2 = 32
        length = 0
    header = Container: 
        data = b'!1\x00 \x00\x00\x00\x00\x03Mv\x87\x00\x00\x01g'
        value = Container: 
            length = 32
            unknown = 0
            devtype = 845
            serial = 30343
            ts = 1970-01-01 01:05:59
        offset1 = 0
        offset2 = 16
        length = 16
    checksum = b'NbrXjFW1Kc9SzuQw'
INFO:mirobo.vacuum:  IP 192.168.8.1: 845 - token: b'4e6272586a4657314b6339537a755177'
INFO:mirobo.vacuum:Discovery done

Now I can get information from the robot:

$sudo mirobo --ip 192.168.8.1 --token 4e6272586a4657314b6339537a755177 consumables
main: 2:17:05, side: 2:17:05, filter: 2:17:05, sensor dirty: 1:41:17
$sudo mirobo --ip 192.168.8.1 --token 4e6272586a4657314b6339537a755177 start
Starting cleaning: 0
$sudo mirobo --ip 192.168.8.1 --token 4e6272586a4657314b6339537a755177 stop
Stop cleaning: 0

What I did was using serial and devid from the discovery and changes this in line 66-68 from vacuum.py.. Thats it!

So I guess, when initially setting up the script, we need two more variables "serial" and "devid" to get it working correctly?

an0Nym0us63 commented 7 years ago

Yep and as i can see the devid and the serial seems really not the same so hard to deduct a logic to map it to a dev type. I guess we should really consider serial and devid as a totally dynamic value for now

an0Nym0us63 commented 7 years ago

@rytilahti do you know how was found the cmds app_start app_stop and so on.

I have succesfully gotten so datas sent with mihome for the purifier. I used the code to unpack it

image

but i would like to get the equivalent of app_start app_stop.

To try generating it trough the lib and see if it succeeds

Here is what i caught from mihome

213100600000000002d1d71d0001b401296bd8ff968cebf68e4588d39eab346691ca787891f3c8b94f748ce185ff6f2b66f6cfccbc5f105db0d573f40fbff68464ab41ed40ebc4644051e94c642bea78ff551e9dfab891045128d59da5111389

It seems to be parsed as a valid message by the lib. Just needs to get the equivalent of app_start and so on

an0Nym0us63 commented 7 years ago

Ok managed to make the purifier work, the only thing is that the timestamp inside the purifeir can be not set and is 1970 .....

So the idea is to query the device before to fullfill the ts field on the sent data with a date expected by the purifier

And i think this can be used for every device even the robot so we will be sure that a robot whose date can be wrong will still be controlable by the lib

an0Nym0us63 commented 7 years ago

Ok just managed to make the humidificator work too

rytilahti commented 7 years ago

Looks like you found my source for those commands too, great. Good to hear that you got your devices working too 👍

From https://github.com/OpenMiHome/mihome-binary-protocol/blob/master/doc/PROTOCOL.md :

Device ID: 32 bits
     Unique number. Possibly derived from the MAC address.
     except in the "Hello" packet, when it's 0xFFFFFFFF

If that's true, it's been just lucky to have similar MAC addresses for same line of devices.

This and the timestamp issue you encountered mean that it'll be the best when we just send an unicast discovery, as the client should return to that accordingly with all required fields filled in. The only problem I see with this is that we can't differentiate between devices (if not having a dictionary for common values).

avariorss commented 7 years ago

Great work with this bit of software - I'm excited to get this working!

I installed the package with: "pip3 install python-mirobo" on a RPi3 running raspbian.

I connected to MiRobot's WiFi and had wlan0 IP of 192.168.8.72.

When entering command "mirobo discover" the following message is returned:

Usage: mirobo [OPTIONS] COMMAND [ARGS]... Error: Missing option "--ip".

I then tried "mirobo --ip 192.168.8.1 --token ffff discover" and received:

INFO:mirobo.vacuum: IP 192.168.8.1: 845 - token: b'7a7a63597135446f3976334c49713792'

Having then setup WiFi in the original app I was getting the same timeout issues as described in the first message in this thread. I followed the instructions above and un-commented line 45 and was able to get the device ID and serial and replace these in line 66 with the binary numbers (devtype = 845 & serial = 25325).

Now it all works :-)

rytilahti commented 7 years ago

Nice to hear that it's working for you! This needs to be fixed by either allowing defining this "device id" (which includes both devtype and serial) or by sending a discovery packet on every connection try to the robot to gather this information.

rytilahti commented 7 years ago

The newest version (0.0.5) now sends multiple discovery packets during the discovery process for new robots to avoid dropped packets (the robot seem not to answer for every request).

It also send a hello to the given IP and fetches the "serial" and "devtype" for each separate request, so this should now work generally on all robots.

@sarakha63 your work on supporting other devices can be done in separate reports & pull requests, so I'm closing this one related to the vacuum now :-)