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 Xiaomi Air Purifier availability blinking ({'code': -9999, 'message': 'user ack timeout'}) and Unable to discover a device at address #757

Closed
mrfoxbit opened this issue Jul 13, 2020 · 19 comments · Fixed by #1363
Labels

Comments

@mrfoxbit
Copy link

My device is Xiaomi Air Purifier 3 (zhimi.airpurifier.ma4), python-miio 0.5.2.1

image

It just doesn't respond very often.

2020-07-04 17:48:52 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: {'code': -9999, 'message': 'user ack timeout'}
2020-07-04 18:24:04 ERROR (SyncWorker_10) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 18:24:04 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 18:39:28 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: {'code': -9999, 'message': 'user ack timeout'}
2020-07-04 19:03:17 ERROR (SyncWorker_9) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 19:03:17 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 19:05:21 ERROR (SyncWorker_14) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 19:05:21 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 19:21:22 ERROR (SyncWorker_4) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 19:21:22 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 20:26:52 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: {'code': -9999, 'message': 'user ack timeout'}
2020-07-04 20:39:16 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: {'code': -9999, 'message': 'user ack timeout'}
2020-07-04 20:51:18 ERROR (SyncWorker_13) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 20:51:18 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:01:02 ERROR (SyncWorker_16) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 21:01:02 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:22:13 ERROR (SyncWorker_7) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 21:22:13 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:25:19 ERROR (SyncWorker_5) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 21:25:19 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:39:46 ERROR (SyncWorker_9) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 21:39:46 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:51:14 ERROR (SyncWorker_10) [miio.miioprotocol] Unable to discover a device at address 192.168.100.227
2020-07-04 21:51:14 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: Unable to discover the device 192.168.100.227
2020-07-04 21:52:02 ERROR (MainThread) [homeassistant.components.xiaomi_miio.fan] Got exception while fetching the state: {'code': -9999, 'message': 'user ack timeout'}

I uesd miiocli --debug airpurifiermiot --ip <ip> --token <token> status and this is result (sometime it show: Unable to discover the device 192.168.100.227)

INFO:miio.cli:Debug mode active
DEBUG:miio.protocol:Unable to decrypt, returning raw bytes: b''
DEBUG:miio.miioprotocol:Got a response: Container: 
    data = Container: 
        data = b'' (total 0)
        value = b'' (total 0)
        offset1 = 32
        offset2 = 32
        length = 0
    header = Container: 
        data = b'!1\x00 \x00\x00\x00\x00\x10\x16\xdf\xcd\x00\x11\xdbp' (total 16)
        value = Container: 
            length = 32
            unknown = 0
            device_id = b'\x10\x16\xdf\xcd' (total 4)
            ts = 1970-01-14 13:04:48
        offset1 = 0
        offset2 = 16
        length = 16
    checksum = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' (total 16)
DEBUG:miio.miioprotocol:Discovered 1016dfcd with ts: 1970-01-14 13:04:48, token: b'ffffffffffffffffffffffffffffffff'
DEBUG:miio.miioprotocol:192.168.100.227:54321 >>: {'id': 1, 'method': 'get_properties', 'params': [{'did': 'power', 'siid': 2, 'piid': 2}, {'did': 'fan_level', 'siid': 2, 'piid': 4}, {'did': 'mode', 'siid': 2, 'piid': 5}, {'did': 'humidity', 'siid': 3, 'piid': 7}, {'did': 'temperature', 'siid': 3, 'piid': 8}, {'did': 'aqi', 'siid': 3, 'piid': 6}, {'did': 'filter_life_remaining', 'siid': 4, 'piid': 3}, {'did': 'filter_hours_used', 'siid': 4, 'piid': 5}, {'did': 'buzzer', 'siid': 5, 'piid': 1}, {'did': 'buzzer_volume', 'siid': 5, 'piid': 2}, {'did': 'led_brightness', 'siid': 6, 'piid': 1}, {'did': 'led', 'siid': 6, 'piid': 6}, {'did': 'child_lock', 'siid': 7, 'piid': 1}, {'did': 'favorite_level', 'siid': 10, 'piid': 10}, {'did': 'favorite_rpm', 'siid': 10, 'piid': 7}]}
DEBUG:miio.miioprotocol:192.168.100.227:54321 (ts: 1970-01-14 13:04:49, id: 1) << {'id': 1, 'result': [{'did': 'power', 'siid': 2, 'piid': 2, 'code': 0, 'value': False}, {'did': 'fan_level', 'siid': 2, 'piid': 4, 'code': 0, 'value': 2}, {'did': 'mode', 'siid': 2, 'piid': 5, 'code': 0, 'value': 2}, {'did': 'humidity', 'siid': 3, 'piid': 7, 'code': 0, 'value': 67}, {'did': 'temperature', 'siid': 3, 'piid': 8, 'code': 0, 'value': 28.5}, {'did': 'aqi', 'siid': 3, 'piid': 6, 'code': 0, 'value': 4}, {'did': 'filter_life_remaining', 'siid': 4, 'piid': 3, 'code': 0, 'value': 82}, {'did': 'filter_hours_used', 'siid': 4, 'piid': 5, 'code': 0, 'value': 613}, {'did': 'buzzer', 'siid': 5, 'piid': 1, 'code': 0, 'value': False}, {'did': 'buzzer_volume', 'siid': 5, 'piid': 2, 'code': -4001}, {'did': 'led_brightness', 'siid': 6, 'piid': 1, 'code': 0, 'value': 1}, {'did': 'led', 'siid': 6, 'piid': 6, 'code': 0, 'value': True}, {'did': 'child_lock', 'siid': 7, 'piid': 1, 'code': 0, 'value': False}, {'did': 'favorite_level', 'siid': 10, 'piid': 10, 'code': 0, 'value': 10}, {'did': 'favorite_rpm', 'siid': 10, 'piid': 7, 'code': 0, 'value': 1600}], 'exe_time': 260}
DEBUG:miio.miioprotocol:192.168.100.227:54321 >>: {'id': 2, 'method': 'get_properties', 'params': [{'did': 'motor_speed', 'siid': 10, 'piid': 8}, {'did': 'use_time', 'siid': 12, 'piid': 1}, {'did': 'purify_volume', 'siid': 13, 'piid': 1}, {'did': 'average_aqi', 'siid': 13, 'piid': 2}, {'did': 'filter_rfid_tag', 'siid': 14, 'piid': 1}, {'did': 'filter_rfid_product_id', 'siid': 14, 'piid': 3}, {'did': 'app_extra', 'siid': 15, 'piid': 1}]}
DEBUG:miio.miioprotocol:192.168.100.227:54321 (ts: 1970-01-14 13:04:50, id: 2) << {'id': 2, 'result': [{'did': 'motor_speed', 'siid': 10, 'piid': 8, 'code': 0, 'value': 0}, {'did': 'use_time', 'siid': 12, 'piid': 1, 'code': 0, 'value': 2192400}, {'did': 'purify_volume', 'siid': 13, 'piid': 1, 'code': 0, 'value': 85696}, {'did': 'average_aqi', 'siid': 13, 'piid': 2, 'code': 0, 'value': 21}, {'did': 'filter_rfid_tag', 'siid': 14, 'piid': 1, 'code': 0, 'value': '81:66:6a:da:85:a:4'}, {'did': 'filter_rfid_product_id', 'siid': 14, 'piid': 3, 'code': 0, 'value': '0:0:30:33'}, {'did': 'app_extra', 'siid': 15, 'piid': 1, 'code': 0, 'value': 0}], 'exe_time': 280}
Power: off
AQI: 4 μg/m³
Average AQI: 21 μg/m³
Humidity: 67 %
Temperature: 28.5 °C
Fan Level: 2
Mode: OperationMode.Favorite
LED: True
LED brightness: LedBrightness.Dim
Buzzer: False
Buzzer vol.: None
Child lock: False
Favorite level: 10
Filter life remaining: 82 %
Filter hours used: 613
Use time: 2192400 s
Purify volume: 85696 m³
Motor speed: 0 rpm
Filter RFID product id: 0:0:30:33
Filter RFID tag: 81:66:6a:da:85:a:4
Filter type: FilterType.Regular

Originally posted by @hoangtuit in #577 (comment)

@rytilahti rytilahti added the bug label Jul 19, 2020
@mihaicristianpetrescu
Copy link

mihaicristianpetrescu commented Jul 25, 2020

+1
For me after about 5-6 times I ask for the status it just fails every single call afterwards.
miiocli airpurifiermiot --ip xxx --token xxx info -> this always works
miiocli airpurifiermiot --ip xxx --token xxx status -> this stop working as well after 5-6 calls with the error:
{
"code": -9999
"message": "user ack timeout"
}

@rytilahti
Copy link
Owner

@mihaicristianpetrescu requesting using miiocli does not currently save the sequence id, so that may explain your problem, see my response here: #762 (comment) . The issue with homeassistant is likely something else, as homeassistant keep using the same instance and the sequence numbers should be handled correctly.

@mihaicristianpetrescu
Copy link

This was just a test, I'm using the python lib in my workflow.
How to save the sequence id for miiocli and for python lilb in order to reuse the same one?
There's no parameter for it anywhere. I'm not using homeassistant, but directly via the python lib.

I'm making an AirPurifierMiot(ip, token) object and then calling the status() method and that fails with the same error I posted above in Python.

@rytilahti
Copy link
Owner

Take a look at #762 (comment) . In the current git master the id is available through raw_id property (since #776), in previous versions you have to access it with device_obj._protocol.raw_id.

@mihaicristianpetrescu
Copy link

I have looked at the comment, but I don't understand where I'm suppose to get the sequence id from?
When is that generated and how?
The airpurifier_miot.py file doesn't have any raw_id or _protocol_raw_id attribute.

@rytilahti
Copy link
Owner

The grand parent class of class is Device (https://github.com/rytilahti/python-miio/blob/master/miio/device.py#L122) which itself hides the protocol implementation behind MiIOProtocol which does the sequence generation (

def _id(self) -> int:
).

The parameter start_id (

start_id: int = 0,
) takes the wanted number as input, otherwise it starts from 1.

@mihaicristianpetrescu
Copy link

I got to the Device.py and you're right. It start from 0 as defult and keeps on growing. So if I just use the same one over and over, as in 0 it should be fine, right?

@mihaicristianpetrescu
Copy link

After a couple of status() calls it continuously gives me "miio.exceptions.DeviceException: Unable to discover the device ip" for every new call I make.

@rytilahti
Copy link
Owner

No, you cannot use the sequence numbers. After all, the reason for them is to make it possible for the device to ignore duplicate/already handled messages.

On top of that, it is possible that some devices simply stop responding to the messages if they think the client is spamming them with messages, or if they think that the message is otherwise invalid, e.g., based on the timestamps. Considering that these devices are pretty much blackboxes, it is really hard to debug such issues, so the two following things are done by the library based on empirical findings:

  1. Incrementing the sequence number as needed as long as the instance is alive
  2. Using the timestamp from the last response and incrementing it by one second for the next request.

Now that I wrote that down, I realized that it's possible that diverging timestamps could play a role if longer time period has passed since the last request. But I don't think that's a problem in your case...

For those "unable to discover" messages, are you simply running a script in a loop (with incrementing sequence numbers) that calls status()? If not, are you setting the sequence id correctly like shown in the comment I linked?

@mihaicristianpetrescu
Copy link

So I have a thread that calls status() every 3 seconds, and it uses the same object of AirPurifierMiot that has been instanced with value 1 as the sequence_number. It seems to be working fine now. Otherwise, if I don't use the sequence_number (same one) and I kill the process and restart it, it will stop working after 5-6 something kills/restarts.

So having the same sequence_number seems to solve this issue. If I keep changing the sequence number per request...then I will have to unplug and plug back in the air purifier in order to make the library work agian.

@rytilahti
Copy link
Owner

Okay, glad to hear it that you got it working, but I don't completely understand what you are doing without any code pieces... There should be no need for you to manually do any sequence number management, as long as you are using the same instance.

Here's how I think the logic in miio device implementations works:

pkt = recv_and_verify()  # get a packet, decrypt it & so on..
if pkt.sequence <= last_received_sequence:
    invalid_packets += 1 # ignore the packet as we already received it
    return
if pkt.ts + <some time period> < current_ts:
    invalid_packets += 1 # the timestamp of the packet was way too far in the past (or in future?), ignore
    return
if invalid_packets > THRESHOLD:
   # too many invalid packets from the client, ignore the client for
    return
<do the stuff>

where the invalid_packets get cleared or decremented at some point.

mirobo which is aimed only for the rockrobo vacuums, takes care of saving and restoring the sequence number (as that was the device I used to develop this). What miiocli does is that it begins always with 1 on every invocation, so some devices can be ignoring requests after the initial one. This is not an issue if you are using the library directly, as you or homeassistant integrations do, or that's been my understanding for now.

@mihaicristianpetrescu
Copy link

I'm using the python-miio library as a standalone library in my project. I have a service that run a python script in which I have an object instance of the AirPurifierMiot on which I then run commands.
I was trying to use the "set_mode(OperationMode)", but "OperationMode" isn't recognised as a known data type. How can I import and use it in a python script? I have the python-miio library installed via pip3

@rytilahti
Copy link
Owner

rytilahti commented Aug 3, 2020

That's just regular python: https://github.com/rytilahti/python-miio/blob/master/miio/airpurifier_miot.py#L54

So something like this should work:

from miio.airpurifier_miot import OperationMode

dev.set_mode(OperationMode.Silent)

edit: to add, please create a new issue (or simply join the matrix chat linked in the README) for further discussions, this is about the 'user ack timeout' error that is still unsolved :-)

@mihaicristianpetrescu
Copy link

Indeed, you are correct. I just downloaded modified the library, exposed the inner class via init, repackaged and installed.
But it does work like you suggested 👍
Brilliant python library so far!

In rergards to 'user ack timeout' I was getting it not related to any outside platform (such as homeassistant), but via miiocli or python execution due to the lack of start_id.
Everything is working fine if after a device reboot, every new connection that is made to the device is created using the same start_id.

@rytilahti
Copy link
Owner

Glad you got it working :-)

Regarding to the 'user ack timeout', I'm not sure I'm understanding you correctly. Do you mean that if you store the sequence id and initialize it again later on, it is working just fine?

With homeassistant, the instance is kept alive as long as homeassistant is kept running, so the the value gets increment automatically and it should in principle work (or that's working with some other devices so, at least).

What I find odd is that the error happens only sometimes, to my understanding. A couple for those who have the 'user ack timeout' problem with homeassistant:

  • Does this start happening first after some specific amount of requests (sequence ids, or time)?
  • Do you use the official app?

I'm wondering if the device could be confused if two devices are using different set of ids, or if the maximum id is different from other devices (they get rolled currently automatically after 9999, iirc) .

@mihaicristianpetrescu
Copy link

So am I, getting more Xiaomi devices now to play with :)

If I use the same sequence (ex:1) for every new class initialisation I'm doing for the same device it works just fine. If I don't use the same one, after I initialise the class a couple of times (script restart) it will get blocked by the device and I have to restart the device (unplug, plug back into the AC).

Incrementing the sequence id is like KRYPTONITE!

I want to be able to restart my python script (since it has the heart of the server) anytime I please without having to deal with unwanted consequences like having to reboot all my smart appliances due to that.

After 5-6 or so class initialisations (where I don't use the sequence id) it locks up.

@colethegamer123
Copy link

I have totally the same issue since I bought my Air Purifier 3H from Xiaomi.

@liangxrbiu
Copy link

liangxrbiu commented Sep 29, 2021

hello,i have the same question.i install the python-miio,and use the token_extractor.py to get the token and ip,then run the miiocli -d device --ip 192.168.1.102 --token f88e12052063b21be1a7923c7f508aea raw_command set_properties"[{'did':'MYDID','siid':2,'piid':1}](i found it on the #901) ,and the result is:

INFO:miio.cli:Debug mode active
Running command raw_command
DEBUG:miio.miioprotocol:Got a response: Container:
data = Container:
data = b'' (total 0)
value = b'' (total 0)
offset1 = 32
offset2 = 32
length = 0
header = Container:
data = b'!1\x00 \x00\x00\x00\x00\x19\xda\x01h\x00\x00\x04h' (total 16)
value = Container:
length = 32
unknown = 0
device_id = unhexlify('19da0168')
ts = 1970-01-01 00:18:48
offset1 = 0
offset2 = 16
length = 16
checksum = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' (total 16)
DEBUG:miio.miioprotocol:Discovered 19da0168 with ts: 1970-01-01 00:18:48, token: b'ffffffffffffffffffffffffffffffff'
DEBUG:miio.miioprotocol:192.168.1.102:54321 >>: {'id': 1, 'method': "get_properties[{'did':'MYDID','siid':2,'piid':1}]", 'params': []}
DEBUG:miio.miioprotocol:192.168.1.102:54321 (ts: 1970-01-01 00:18:52, id: 1) << {'id': 1, 'error': {'code': -9999, 'message': 'user ack timeout'}, 'exe_time': 4010}
DEBUG:miio.click_common:Exception: {'code': -9999, 'message': 'user ack timeout'}
Traceback (most recent call last):
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\click_common.py", line 59, in call
return self.main(*args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 782, in main
rv = self.invoke(ctx)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 610, in invoke
return callback(*args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\click_common.py", line 280, in wrap
kwargs["result"] = func(*args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\decorators.py", line 73, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\click\core.py", line 610, in invoke
return callback(*args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\click_common.py", line 245, in command_callback
return miio_command.call(miio_device, *args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\click_common.py", line 193, in call
return method(*args, **kwargs)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\device.py", line 116, in raw_command
return self.send(command, parameters)
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\device.py", line 98, in send
command, parameters, retry_count, extra_parameters=extra_parameters
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\miioprotocol.py", line 214, in send
self._handle_error(payload["error"])
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\miioprotocol.py", line 274, in _handle_error
raise DeviceError(error)
miio.exceptions.DeviceError: {'code': -9999, 'message': 'user ack timeout'}
Error: {'code': -9999, 'message': 'user ack timeout'}

i dont know what's wrong and how to solve it

@rytilahti
Copy link
Owner

This might be a common issue on these devices, maybe a similar fix that has been incorporated by homeassistant should be included directly in python-miio: home-assistant/core#56288

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants