rytilahti / python-miio

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

Support for Xiaomi Air Purifier 3 (zhimi.airpurifier.ma4) #577

Closed inviicta closed 4 years ago

inviicta commented 5 years ago

Hi, I got my purifier and I tried to setup python-miio library and write something easy to start. And I got error:

raise DeviceError(error) miio.exceptions.DeviceError: {'code': -5001, 'message': 'command error'}

So i assume it is not supported device. Any plans to add it to the device list?

rytilahti commented 5 years ago

It uses different API that is not currently implemented, see https://github.com/rytilahti/python-miio/issues/543 for details.

petrkotek commented 5 years ago

Same issue for Xiaomi Air Purifier 3/3H (zhimi.airpurifier.mb3).

I started experimenting with it & managed to get some features working:

export AIR_IP=192.168.0.xx
export TOKEN=77xxxxxxxxxxxxxxxxxxff
$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN status
Power: False
Temperature: 26.200001 °C
Humidity: 43 %
Favorite level: 7
Filter hours used: 12
Filter RFID product id: 0:0:31:31
Filter RFID tag: 80:66:6a:da:38:bd:4

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN on
Powering on
[{'did': 'MYDID', 'siid': 2, 'piid': 2, 'code': 0}]

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_favorite_level 14
Setting favorite level to 14
[{'did': 'MYDID', 'siid': 10, 'piid': 10, 'code': 0}]

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN status
Power: True
Temperature: 26.200001 °C
Humidity: 44 %
Favorite level: 14
Filter hours used: 12
Filter RFID product id: 0:0:31:31
Filter RFID tag: 80:66:6a:da:38:bd:4

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_mode auto
Setting mode to '0'
[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0}]

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_mode favorite
Setting mode to '2'
[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0}]

$ python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_mode silent
Setting mode to '1'
[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0}]

Code is here: https://github.com/rytilahti/python-miio/compare/master...petrkotek:air-purifier-3h-support?expand=1

I'm hoping to productionize it upcoming days and open a PR.

In the meantime, would you be maybe able to give it a go with your purifier and tell me if it works for you too?

Note: my firmware version as per Mi Home app is 2.0.5.0006. I'm getting a prompt to upgrade to 1.3.1.0009 -- yes, looks like a downgrade rather than upgrade; not going to risk it at this stage.

inviicta commented 5 years ago

Hi, sure. I will try to do it in the evening. I will let you know. :)

My purifier got an update lately and currently has version: 2.0.7.0021.

inviicta commented 5 years ago

Hi!

I had a lot of troubles to get your code working, but finally it is. :)

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN status
Power: True
Temperature: 23.6 °C
Humidity: 61 %
Favorite level: 14
Filter hours used: 162
Filter RFID product id: 0:0:30:33
Filter RFID tag: 80:64:d1:ba:37:b2:4

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN off
Powering off
[{'did': 'MYDID', 'siid': 2, 'piid': 2, 'code': 0}]

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN on 
Powering on
[{'did': 'MYDID', 'siid': 2, 'piid': 2, 'code': 0}]

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_mode silent
Setting mode to '1'
[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0}]

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN set_mode auto  
Setting mode to '0'
[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0}]

Is it possible to get the value of the currect pollution?

petrkotek commented 5 years ago

Awesome! Sure, I'll add more properties in next two days or so.

Btw, Did you have to change some code or did you have troubles with git or Python environment in general?

inviicta commented 5 years ago

Just human issue between laptop and chair :D

I have edited your code inside airpurifier_miot.py:

class AirPurifierMiot(Device):
    """Main class representing the air purifier miot."""

    @command(
        default_output=format_output(
            "",
            "Power: {result.power}\n"
            "AQI: {result.aqi} μg/m³\n"
            "Average AQI: {result.average_aqi} μg/m³\n"
            "Temperature: {result.temperature} °C\n" "Humidity: {result.humidity} %\n"

And here:

    def status(self) -> AirPurifierMiotStatus:
        """Retrieve properties."""

        properties = [
            {"did": "power", "siid": 2, "piid": 2},
            {"did": "favorite_level", "siid": 10, "piid": 10},
            {"did": "aqi", "siid": 13, "piid": 8},
            {"did": "average_aqi", "siid": 13, "piid": 2},
            {"did": "humidity", "siid": 3, "piid": 7},
            {"did": "temperature", "siid": 3, "piid": 8},

And now it is showing also in status command value of AQI. I need to check proper PIID and SIID, but it looks like it is working just fine. :)

% python miio/cli.py airpurifiermiot --ip $AIR_IP --token $TOKEN status
Power: True
AQI: 0 μg/m³
Average AQI: 6 μg/m³
Temperature: 23.6 °C
Humidity: 62 %
Favorite level: 14
Filter hours used: 163
Filter RFID product id: 0:0:30:33
Filter RFID tag: 80:64:d1:ba:37:b2:4

Good job! :)

djmalin commented 4 years ago

inviicta hello pleace add your code here, i have this same zhimi.airpurifier.ma4, what do you add in the config file?

petrkotek commented 4 years ago

@djmalin which config file are you referring to?

Note that currently, only my branch (https://github.com/petrkotek/python-miio/tree/air-purifier-3h-support) has partial (very limited) support for it. Over the next several days, I'd like to finalise it and hopefully get it reviewed. Once that's done and merged, I'll be looking into how to get that support into Home Assistant. Is Home Assistant config (or other home automation platform) the config you were referring to?

djmalin commented 4 years ago

this is very good news for me, I am holding my thumb to make it happen

petrkotek commented 4 years ago

I got all the important things working locally (controlling mode, led, led_brightness, favourite_level fan_level, child_lock, buzzer; reading aqi, humidity, temperature, filter info etc.).

See https://github.com/rytilahti/python-miio/pull/585 for details.

petrkotek commented 4 years ago

"Preparation PR" has been merged (https://github.com/rytilahti/python-miio/pull/592). The next PR will finally include the support for this Air Purifier 3.

No promises, however, I think it could be merged in a week or two.

adamtmitchell commented 4 years ago

Note: my firmware version as per Mi Home app is 2.0.5.0006. I'm getting a prompt to upgrade to 1.3.1.0009 -- yes, looks like a downgrade rather than upgrade; not going to risk it at this stage.

Not really the right place for this i know, but did you ever go through with this "update"?

petrkotek commented 4 years ago

Not really the right place for this i know, but did you ever go through with this "update"?

No worries @adamtmitchell. I have not updated yet.

djmalin commented 4 years ago

how too add this integration from hassio? Home Assistant 0.104.0, System | HassOS 3.8 python_version | 3.7.6

adamtmitchell commented 4 years ago

@petrkotek thanks. I decided to go ahead with it (as i thought there was a chance it might make the temp reading a bit more accurate), and can tell you it completed and came back as up to date on 2.0.5.0009, so it seems it was just the first part of the build number on the update was displayed as wrong. Not sure it has helped with my readings though.

supermichael commented 4 years ago

@petrkotek Does the Xiaomi Air Purifier 3H expose its token during discovery or did you have to extract it out of an android backup / log? I am not clear on this one.

cisco-devnet commented 4 years ago

@petrkotek Does the Xiaomi Air Purifier 3H expose its token during discovery or did you have to extract it out of an android backup / log? I am not clear on this one.

@supermichael I don't how it looks like in your cases, but in my case 3h exposed its token during discovery (the connected to its open network), and then i can use this token and this miot API to manage device, but, just when i send call to connect to my wifi network (ssid, wpa-psk) it properly will connect to this new wifi network and... change the token - as I am not able to manage it using this first token any more. Also I heard that using mi home app on the phone - to find a device, the internet connection, bluetooth and location need to be turned on. But I didn't do such experiment, as ideally my devices shouldn't ever connect to Internet, nor using any app on any tablet/phone connected to Internet. Maybe someone with more knowledge and time will sniff/analyze change of the behavior of this 3h devices.

supermichael commented 4 years ago

@petrkotek Does the Xiaomi Air Purifier 3H expose its token during discovery or did you have to extract it out of an android backup / log? I am not clear on this one.

@supermichael I don't how it looks like in your cases, but in my case 3h exposed its token during discovery (the connected to its open network), and then i can use this token and this miot API to manage device, but, just when i send call to connect to my wifi network (ssid, wpa-psk) it properly will connect to this new wifi network and... change the token - as I am not able to manage it using this first token any more. Also I heard that using mi home app on the phone - to find a device, the internet connection, bluetooth and location need to be turned on. But I didn't do such experiment, as ideally my devices shouldn't ever connect to Internet, nor using any app on any tablet/phone connected to Internet. Maybe someone with more knowledge and time will sniff/analyze change of the behavior of this 3h devices.

I assume that the token is somehow based on the wifi ssid/mac address. So the way I found to be somewhat efficient was:

  1. Setting up the device "as you normally would" over the mi home app and connecting it to the home network (and revoking all app-priviliges after that).
  2. The generated token for the new network can be retrieved via a sketchy russian app (https://www.kapiba.ru/2017/11/mi-home.html under profile - share device after clicking on the Air Purifier once, hence "initializing" it somehow). Since I dont want this on my smartphone I used an android emulator for step 2. I could not manage to do step 1 in an emulator since this needs "special access" to change the wifi (assumption).
  3. I had a valid token and could communicate with the device (zhimi.airpurifier.mb3). Direct communication over lan is possible - i have all traffic from 3H to wan restricted by a firewall.

Hope this helps a bit.

SeraphimSerapis commented 4 years ago

I extracted the token using the Android app (5.4.49) -- HASS throws the following error, which seems to be related to this library: Got exception while fetching the state: {'code': -5001, 'message': 'command error'}.

Did anyone manage to get this device up and running with Home Assistant yet? Seems like it has not been added to the model definitions yet.

mouth4war commented 4 years ago

@petrkotek thanks. I decided to go ahead with it (as i thought there was a chance it might make the temp reading a bit more accurate), and can tell you it completed and came back as up to date on 2.0.5.0009, so it seems it was just the first part of the build number on the update was displayed as wrong. Not sure it has helped with my readings though.

@adamtmitchell Seems to me that the purifier is reporting temperature in 0.5C increments. Humidity seems to work fine, though. I have the latest firmware 2.0.5.0009 too.

EDIT: I checked the app logs because it looked like while the app is open, the air purifier was reporting AQI and temperature quicker. So it seems like aqi-updata-heartbeat (siid 13, pii 9) affects the update interval for both AQI and temperature. While the app is open, it is set to 60.

EDIT 2: My 3Hs are all going offline sporadically throughout the day. Cause is unknown. Seems like the reported AQI and temperature changes once (from 20 to 20.200001 and from 21.5 to 21.5000001) and then the purifier is messed up and keeps going offline.

n3dj0 commented 4 years ago

I'm a bit late to this discussion but the only way I managed to get the token was this: YouTube: How to get Xiaomi Token for any device - Air Purifier, Roborock S50 Vacuum - Homekit Integration

I've tried almost all of the methods from here: Github: Obtain Mi Home device token

mrfoxbit commented 4 years ago

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