We work with the CO₂ Xiaomi ClearGrass Air Detector sensor locally, without Chinese servers



For a long time I planned to introduce a carbon dioxide sensor CO₂ in home automation. In terms of price / quality / function / appearance, Xiaomi ClearGrass Air Detector turned out to be the best for me. The air quality analyzer contains sensors:

  1. CO₂
  2. tVOC (volatile organic compounds)
  3. PM2.5
  4. Temperature
  5. Humidity

ClearGrass has a high-quality screen with large viewing angles and a battery for 6 hours of battery life. The price in the region of $ 130 for such a device translates it into the must-have segment! Great review can be read on mysku.ru .

The analyzer can be added to the native qingping + or MiHome application , in both cases the data goes through Chinese servers, which categorically did not suit me. I decided to figure out how to get data from the sensor locally without using third-party remote servers.

1. Study traffic


The first step was to see how ClearGrass transfers data to the qingping + application . ClearGrass connects to the Internet via Wi-Fi. To listen to traffic, I raised an access point on the Raspberry Pi Wi-Fi and started tcpdump to collect information:

sudo tcpdump -i wlan0 -vv -s0 -X -n port 1883 -s 65535 -w cleargrass.pcap

Analysis of traffic showed that ClearGrass accesses approximately 5 different IP addresses, and on 154.8.191.174 it transmits air quality data unencrypted using the MQTT protocol .



2. We wrap traffic from ClearGrass on the Raspberry Pi


Having experimented a bit with iptables, I came to this rule:

sudo iptables -i wlan0 -t nat -A PREROUTING -s 192.168.115.19 -j REDIRECT

It reads like this: "All new traffic on the wlan0 interface from 192.168.115.19 (IP ClearGrass) should be redirected locally." I am not a big connoisseur of iptables, so I will be glad to suggestions and improvements. There is a minus in this rule, if the analyzer is already connected to the Raspberry Pi, then the traffic will not be redirected. First you need to run the rule and only then connect ClearGrass to the Raspberry Pi via Wi-Fi.

As a result, having picked up the MQTT mosquitto broker on the Raspberry Pi, I saw that the analyzer transmits air quality data once a minute.

3. MQTT nano-broker on JS for home automation Z-Way


As a home automation server, I use Z-Way, which supports many Z-Wave devices and the ability to write scripts in JS.



Unfortunately for Z-Way there is no MQTT broker in JS (unlike systems based on node.js), so I decided to write a minimal broker that only accepts data from this analyzer and can’t do anything else. Without particularly reading the documentation, I looked at the communication between the analyzer and mosquitto and compiled the following sequence:

MQTT PROTOCOL

Connect Command (sensor -> broker)
	0x10 - Connect Command

Connect Ack (broker -> sensor)
	0x20 - Connect Ack
	0x02 - Len 2
	0x00
	0x00 - Connection Accepted

Subscribe Request (sensor -> broker)
	0x82 - 0b1000 0010; 0b1000 - Subscribe Request

Subscribe Ack (broker -> sensor)
	0x90 - 0b1001 0000; 0b1001 - Subscribe Ack
	0x03 - Len 3
	0x00
	0x08 - Message identifier 8
	0x00 - Fire and Forget

Ping Request (sensor -> broker)
	0xC0 - Ping Request
	0x00 - Len 0

Ping Response (broker -> sensor)
	0xD0 - Ping Response
	0x00 - Len 0

Publish Message (sensor -> broker)
	0x30 - Publish Message
	0x96
	0x04 - Len 534

As a result, a simple JS script was born:

mqttSocket.reusable();
mqttSocket.bind(1883);
mqttSocket.onrecv = function(data, host, port) {
	var arr = new Uint8Array(data);

	switch(arr[0]) {
		// PING
		case 0xC0:
			console.log("---------- MQTT PING RESPONSE");
			this.send([0xD0, 0x00]);
			break;
		// CONNECT
		case 0x10:
			console.log("---------- MQTT CONNECT ACK");
			this.send([0x20, 0x02, 0x00, 0x00]);
			break;
		// SUBSCRIBE
		case 0x82:
			console.log("---------- MQTT SUBSCRIBE ACK");
			this.send([0x90, 0x03, arr[2], arr[3], 0x00]);
			break;
		// PUBLISH
		case 0x30:
			var sensorPayload = self.getPayload(arr);
			var sensorMessage = sensorPayload.substr(sensorPayload.indexOf('{'), sensorPayload.lastIndexOf('}'));
			var sensorObj = JSON.parse(sensorMessage);
			console.logJS("---------- MQTT MESSAGE:", sensorObj);
			console.logJS("---------- CO2: ", sensorObj.data.co2);
			self.vDevCO2.set("metrics:level", sensorObj.data.co2);
			break;
	}
};
mqttSocket.listen();

Of course, while much is not taken into account, for example, in one premise, both PING and MESSAGE may come, but I will miss some of this. Perhaps in the future I will use the aedes code base to create an MQTT broker for Z-Way. And at the moment, the goal was the fundamental opportunity to get locally air quality data from the Xiaomi ClearGrass Air Detector analyzer and this goal was achieved.

In the future I want to install the Z-Wave version of the TION S3 breather and control it based on data from ClearGrass.

All Articles