Problem

When python-miio (0.5.4) is used to query status of a Xiaomi Air Purifier 3 (zhimi.airpurifier.mb3) the AQI returned by the library may appear constant and differ from the AQI displayed on the device.

import time
from datetime import datetime
from miio import AirPurifierMiot

purifierBedroom = AirPurifierMiot("192.168.1.56", "<<token>>")


def log(str):
	now = datetime.now().isoformat()
	print("[{}] {}".format(now, str), flush=True)
	
while True:
	log(purifierBedroom.status().aqi)
	time.sleep(3)
pi@raspberrypi:~/samba/xiaomi $ python3 test.py
[2021-03-03T19:56:42.091184] 4
[2021-03-03T19:56:46.818952] 4
[2021-03-03T19:56:51.089959] 4
[2021-03-03T19:56:55.088681] 4
...
[2021-03-03T19:57:42.908315] 4
[2021-03-03T19:57:47.488642] 4
[2021-03-03T19:57:51.914245] 4
[2021-03-03T19:57:55.887381] 4

Solution

Let’s set the aqi-updata-heartbeat property at the beginning of the loop:

while True:
	purifierBedroom.raw_command("set_properties", [{"did": "aqi-updata-heartbeat", "value":60, "siid": 13, "piid":9 }])
	log(purifierBedroom.status().aqi)
	time.sleep(3)
pi@raspberrypi:~/samba/xiaomi $ python3 test.py
[2021-03-03T19:58:20.125930] 4
[2021-03-03T19:58:24.521098] 5
[2021-03-03T19:58:28.628670] 4
[2021-03-03T19:58:33.571845] 4
[2021-03-03T19:58:38.807365] 4
[2021-03-03T19:58:43.050492] 4
[2021-03-03T19:58:48.471507] 6
...
[2021-03-03T19:59:14.869361] 3
[2021-03-03T19:59:20.429442] 4
[2021-03-03T19:59:24.588200] 4
[2021-03-03T19:59:29.109418] 4
[2021-03-03T19:59:33.449647] 3

How

I was inspired by this github post by mouth4war. Let’s change our loop a bit again to get the aqi-updata-heartbeat property from the device.

while True:
	log(purifierBedroom.raw_command("get_properties", [{"did": "aqi-updata-heartbeat", "siid": 13, "piid":9 }]))
	time.sleep(3)

Normally the property is set to 0 and the AQI reported in the API is not correct. However, when the Xiaomi Home app is started on the mobile phone and the Air Purifier 3 device is selected, the property suddenly changes to 60 and AQI reporting is fixed. Over time the property decreases so it seems that it performs some kind of countdown. This behavior suggests that the property being greater than zero tells the device to keep reporting the correct AQI so that it can be displayed by the mobile application.

pi@raspberrypi:~/samba/xiaomi $ python3 test.py
[2021-03-03T20:20:08.717466] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 0}]
[2021-03-03T20:20:12.243629] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 0}]
[2021-03-03T20:20:15.950367] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 0}]
#mobile app started 
[2021-03-03T20:20:19.395743] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 60}]
[2021-03-03T20:20:23.196622] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 55}]
[2021-03-03T20:20:26.535611] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 50}]
[2021-03-03T20:20:29.875356] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 50}]
[2021-03-03T20:20:33.457382] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 45}]
[2021-03-03T20:20:37.072436] [{'did': 'aqi-updata-heartbeat', 'siid': 13, 'piid': 9, 'code': 0, 'value': 40}]

Changing the value using python-miio seems to trick the purifier into thinking that the mobile app is started and make it report the AQI correctly.