8 #include "mbedtls/ccm.h" 11 namespace xiaomi_ble {
13 static const char *
const TAG =
"xiaomi_ble";
17 if ((value_type == 0x1001) && (value_length == 3)) {
22 else if ((value_type == 0x0003) && (value_length == 1)) {
26 else if ((value_type == 0x1004) && (value_length == 2)) {
31 else if ((value_type == 0x1006) && (value_length == 2)) {
36 else if (((value_type == 0x1007) || (value_type == 0x000F)) && (value_length == 3)) {
37 const uint32_t illuminance =
encode_uint24(data[2], data[1], data[0]);
39 result.
is_light = illuminance >= 100;
40 if (value_type == 0x0F)
44 else if ((value_type == 0x1008) && (value_length == 1)) {
48 else if ((value_type == 0x1009) && (value_length == 2)) {
49 const uint16_t conductivity =
encode_uint16(data[1], data[0]);
53 else if ((value_type == 0x100A) && (value_length == 1)) {
57 else if ((value_type == 0x100D) && (value_length == 4)) {
64 else if ((value_type == 0x1010) && (value_length == 2)) {
65 const uint16_t formaldehyde =
encode_uint16(data[1], data[0]);
69 else if ((value_type == 0x1012) && (value_length == 1)) {
73 else if ((value_type == 0x1013) && (value_length == 1)) {
77 else if ((value_type == 0x1017) && (value_length == 4)) {
78 const uint32_t idle_time =
encode_uint32(data[3], data[2], data[1], data[0]);
81 }
else if ((value_type == 0x1018) && (value_length == 1)) {
93 ESP_LOGVV(TAG,
"parse_xiaomi_message(): payload is encrypted, stop reading message.");
103 const uint8_t *payload = message.data() + result.
raw_offset;
104 uint8_t payload_length = message.size() - result.
raw_offset;
105 uint8_t payload_offset = 0;
106 bool success =
false;
108 if (payload_length < 4) {
109 ESP_LOGVV(TAG,
"parse_xiaomi_message(): payload has wrong size (%d)!", payload_length);
113 while (payload_length > 3) {
114 if (payload[payload_offset + 1] != 0x10 && payload[payload_offset + 1] != 0x00) {
115 ESP_LOGVV(TAG,
"parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
119 const uint8_t value_length = payload[payload_offset + 2];
120 if ((value_length < 1) || (value_length > 4) || (payload_length < (3 + value_length))) {
121 ESP_LOGVV(TAG,
"parse_xiaomi_message(): value has wrong size (%d)!", value_length);
125 const uint16_t value_type =
encode_uint16(payload[payload_offset + 1], payload[payload_offset + 0]);
126 const uint8_t *data = &payload[payload_offset + 3];
131 payload_length -= 3 + value_length;
132 payload_offset += 3 + value_length;
141 ESP_LOGVV(TAG,
"parse_xiaomi_header(): no service data UUID magic bytes.");
151 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data has no DATA flag.");
155 static uint8_t last_frame_count = 0;
156 if (last_frame_count ==
raw[4]) {
157 ESP_LOGVV(TAG,
"parse_xiaomi_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
161 last_frame_count =
raw[4];
167 if (device_uuid == 0x0098) {
169 result.
name =
"HHCCJCY01";
170 }
else if (device_uuid == 0x01aa) {
172 result.
name =
"LYWSDCGQ";
173 }
else if (device_uuid == 0x015d) {
175 result.
name =
"HHCCPOT002";
176 }
else if (device_uuid == 0x02df) {
178 result.
name =
"JQJCY01YM";
179 }
else if (device_uuid == 0x03dd) {
181 result.
name =
"MUE4094RT";
183 }
else if (device_uuid == 0x0347 ||
184 device_uuid == 0x0B48) {
186 result.
name =
"CGG1";
187 }
else if (device_uuid == 0x03bc) {
189 result.
name =
"GCLS002";
190 }
else if (device_uuid == 0x045b) {
192 result.
name =
"LYWSD02";
193 }
else if (device_uuid == 0x040a) {
195 result.
name =
"WX08ZM";
196 }
else if (device_uuid == 0x0576) {
198 result.
name =
"CGD1";
199 }
else if (device_uuid == 0x066F) {
201 result.
name =
"CGDK2";
202 }
else if (device_uuid == 0x055b) {
204 result.
name =
"LYWSD03MMC";
205 }
else if (device_uuid == 0x07f6) {
207 result.
name =
"MJYD02YLA";
208 if (
raw.size() == 19)
210 }
else if (device_uuid == 0x06d3) {
212 result.
name =
"MHOC303";
213 }
else if (device_uuid == 0x0387) {
215 result.
name =
"MHOC401";
216 }
else if (device_uuid == 0x0A83) {
218 result.
name =
"CGPR1";
219 if (
raw.size() == 19)
221 }
else if (device_uuid == 0x0A8D) {
223 result.
name =
"RTCGQ02LM";
224 if (
raw.size() == 19)
227 ESP_LOGVV(TAG,
"parse_xiaomi_header(): unknown device, no magic bytes.");
235 if (!((raw.size() == 19) || ((raw.size() >= 22) && (raw.size() <= 24)))) {
236 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): data packet has wrong size (%d)!", raw.size());
237 ESP_LOGVV(TAG,
" Packet : %s",
format_hex_pretty(raw.data(), raw.size()).c_str());
241 uint8_t mac_reverse[6] = {0};
242 mac_reverse[5] = (uint8_t) (address >> 40);
243 mac_reverse[4] = (uint8_t) (address >> 32);
244 mac_reverse[3] = (uint8_t) (address >> 24);
245 mac_reverse[2] = (uint8_t) (address >> 16);
246 mac_reverse[1] = (uint8_t) (address >> 8);
247 mac_reverse[0] = (uint8_t) (address >> 0);
261 vector.datasize = (raw.size() == 19) ? raw.size() - 12 : raw.size() - 18;
262 int cipher_pos = (raw.size() == 19) ? 5 : 11;
264 const uint8_t *v = raw.data();
266 memcpy(vector.key, bindkey, vector.keysize);
267 memcpy(vector.ciphertext, v + cipher_pos, vector.datasize);
268 memcpy(vector.tag, v + raw.size() - vector.tagsize, vector.tagsize);
269 memcpy(vector.iv, mac_reverse, 6);
270 memcpy(vector.iv + 6, v + 2, 3);
271 memcpy(vector.iv + 9, v + raw.size() - 7, 3);
273 mbedtls_ccm_context ctx;
274 mbedtls_ccm_init(&ctx);
276 int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, vector.key, vector.keysize * 8);
278 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): mbedtls_ccm_setkey() failed.");
279 mbedtls_ccm_free(&ctx);
283 ret = mbedtls_ccm_auth_decrypt(&ctx, vector.datasize, vector.iv, vector.ivsize, vector.authdata, vector.authsize,
284 vector.ciphertext, vector.plaintext, vector.tag, vector.tagsize);
286 uint8_t mac_address[6] = {0};
287 memcpy(mac_address, mac_reverse + 5, 1);
288 memcpy(mac_address + 1, mac_reverse + 4, 1);
289 memcpy(mac_address + 2, mac_reverse + 3, 1);
290 memcpy(mac_address + 3, mac_reverse + 2, 1);
291 memcpy(mac_address + 4, mac_reverse + 1, 1);
292 memcpy(mac_address + 5, mac_reverse, 1);
293 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption failed.");
295 ESP_LOGVV(TAG,
" Packet : %s",
format_hex_pretty(raw.data(), raw.size()).c_str());
296 ESP_LOGVV(TAG,
" Key : %s",
format_hex_pretty(vector.key, vector.keysize).c_str());
298 ESP_LOGVV(TAG,
" Cipher : %s",
format_hex_pretty(vector.ciphertext, vector.datasize).c_str());
299 ESP_LOGVV(TAG,
" Tag : %s",
format_hex_pretty(vector.tag, vector.tagsize).c_str());
300 mbedtls_ccm_free(&ctx);
305 uint8_t *p = vector.plaintext;
306 for (std::vector<uint8_t>::iterator it = raw.begin() + cipher_pos; it != raw.begin() + cipher_pos + vector.datasize;
314 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption passed.");
315 ESP_LOGVV(TAG,
" Plaintext : %s, Packet : %d",
format_hex_pretty(raw.data() + cipher_pos, vector.datasize).c_str(),
316 static_cast<int>(raw[4]));
318 mbedtls_ccm_free(&ctx);
324 ESP_LOGVV(TAG,
"report_xiaomi_results(): no results available.");
328 ESP_LOGD(TAG,
"Got Xiaomi %s (%s):", result->name.c_str(), address.c_str());
331 ESP_LOGD(TAG,
" Temperature: %.1f°C", *result->temperature);
334 ESP_LOGD(TAG,
" Humidity: %.1f%%", *result->humidity);
337 ESP_LOGD(TAG,
" Battery Level: %.0f%%", *result->battery_level);
340 ESP_LOGD(TAG,
" Conductivity: %.0fµS/cm", *result->conductivity);
343 ESP_LOGD(TAG,
" Illuminance: %.0flx", *result->illuminance);
346 ESP_LOGD(TAG,
" Moisture: %.0f%%", *result->moisture);
349 ESP_LOGD(TAG,
" Mosquito tablet: %.0f%%", *result->tablet);
352 ESP_LOGD(TAG,
" Repellent: %s", (*result->is_active) ?
"on" :
"off");
355 ESP_LOGD(TAG,
" Motion: %s", (*result->has_motion) ?
"yes" :
"no");
358 ESP_LOGD(TAG,
" Light: %s", (*result->is_light) ?
"on" :
"off");
361 ESP_LOGD(TAG,
" Button: %s", (*result->button_press) ?
"pressed" :
"");
optional< float > illuminance
optional< float > temperature
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result)
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
optional< float > formaldehyde
bool decrypt_xiaomi_payload(std::vector< uint8_t > &raw, const uint8_t *bindkey, const uint64_t &address)
optional< bool > button_press
optional< float > humidity
optional< bool > is_active
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
enum esphome::xiaomi_ble::XiaomiParseResult::@106 type
optional< float > battery_level
bool parse_xiaomi_message(const std::vector< uint8_t > &message, XiaomiParseResult &result)
optional< XiaomiParseResult > parse_xiaomi_header(const esp32_ble_tracker::ServiceData &service_data)
optional< float > idle_time
bool report_xiaomi_results(const optional< XiaomiParseResult > &result, const std::string &address)
bool contains(uint8_t data1, uint8_t data2) const
optional< bool > is_light
optional< bool > has_motion
optional< float > moisture
optional< float > conductivity