31 if ((value_type == 0x1001) && (value_length == 3)) {
36 else if ((value_type == 0x0003) && (value_length == 1)) {
40 else if ((value_type == 0x1004) && (value_length == 2)) {
45 else if ((value_type == 0x1006) && (value_length == 2)) {
50 else if (((value_type == 0x1007) || (value_type == 0x000F)) && (value_length == 3)) {
53 result.
is_light = illuminance >= 100;
54 if (value_type == 0x0F)
58 else if ((value_type == 0x1008) && (value_length == 1)) {
62 else if ((value_type == 0x1009) && (value_length == 2)) {
63 const uint16_t conductivity =
encode_uint16(data[1], data[0]);
67 else if ((value_type == 0x100A || value_type == 0x4803) && (value_length == 1)) {
71 else if ((value_type == 0x100D) && (value_length == 4)) {
78 else if ((value_type == 0x1010) && (value_length == 2)) {
79 const uint16_t formaldehyde =
encode_uint16(data[1], data[0]);
83 else if ((value_type == 0x1012) && (value_length == 1)) {
87 else if ((value_type == 0x1013) && (value_length == 1)) {
91 else if ((value_type == 0x1017) && (value_length == 4)) {
95 }
else if ((value_type == 0x1018) && (value_length == 1)) {
99 else if ((value_type == 0x4C01) && (value_length == 4)) {
106 else if ((value_type == 0x4C02) && (value_length == 1)) {
110 else if ((value_type == 0x4C08) && (value_length == 4)) {
113 std::memcpy(&humidity, &int_number,
sizeof(humidity));
125 ESP_LOGVV(TAG,
"parse_xiaomi_message(): payload is encrypted, stop reading message.");
136 ESP_LOGVV(TAG,
"parse_xiaomi_message(): raw_offset (%d) exceeds message size (%d)!", result.
raw_offset,
142 uint8_t payload_offset = 0;
143 bool success =
false;
145 if (payload_length < 4) {
146 ESP_LOGVV(TAG,
"parse_xiaomi_message(): payload has wrong size (%d)!", payload_length);
150 while (payload_length > 3) {
151 if (payload[payload_offset + 1] != 0x10 && payload[payload_offset + 1] != 0x00 &&
152 payload[payload_offset + 1] != 0x4C && payload[payload_offset + 1] != 0x48) {
153 ESP_LOGVV(TAG,
"parse_xiaomi_message(): fixed byte not found, stop parsing residual data.");
157 const uint8_t value_length = payload[payload_offset + 2];
158 if ((value_length < 1) || (value_length > 4) || (payload_length < (3 + value_length))) {
159 ESP_LOGVV(TAG,
"parse_xiaomi_message(): value has wrong size (%d)!", value_length);
163 const uint16_t value_type =
encode_uint16(payload[payload_offset + 1], payload[payload_offset + 0]);
164 const uint8_t *data = &payload[payload_offset + 3];
169 payload_length -= 3 + value_length;
170 payload_offset += 3 + value_length;
179 ESP_LOGVV(TAG,
"parse_xiaomi_header(): no service data UUID magic bytes.");
184 if (
raw.size() < 5) {
185 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data too short (%d).",
raw.size());
193 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data has no DATA flag.");
197 static uint8_t last_frame_count = 0;
198 if (last_frame_count ==
raw[4]) {
199 ESP_LOGVV(TAG,
"parse_xiaomi_header(): duplicate data packet received (%d).",
static_cast<int>(last_frame_count));
203 last_frame_count =
raw[4];
209 if (device_uuid == 0x0098) {
211 result.
name =
"HHCCJCY01";
212 }
else if (device_uuid == 0x01aa) {
214 result.
name =
"LYWSDCGQ";
215 }
else if (device_uuid == 0x015d) {
217 result.
name =
"HHCCPOT002";
218 }
else if (device_uuid == 0x02df) {
220 result.
name =
"JQJCY01YM";
221 }
else if (device_uuid == 0x03dd) {
223 result.
name =
"MUE4094RT";
225 }
else if (device_uuid == 0x0347 ||
226 device_uuid == 0x0B48) {
228 result.
name =
"CGG1";
229 }
else if (device_uuid == 0x03bc) {
231 result.
name =
"GCLS002";
232 }
else if (device_uuid == 0x045b) {
234 result.
name =
"LYWSD02";
235 }
else if (device_uuid == 0x2542) {
237 result.
name =
"LYWSD02MMC";
238 if (
raw.size() == 19)
240 }
else if (device_uuid == 0x040a) {
242 result.
name =
"WX08ZM";
243 }
else if (device_uuid == 0x0576) {
245 result.
name =
"CGD1";
246 }
else if (device_uuid == 0x066F) {
248 result.
name =
"CGDK2";
249 }
else if (device_uuid == 0x055b) {
251 result.
name =
"LYWSD03MMC";
252 }
else if (device_uuid == 0x1203) {
254 result.
name =
"XMWSDJ04MMC";
255 if (
raw.size() == 19)
257 }
else if (device_uuid == 0x07f6) {
259 result.
name =
"MJYD02YLA";
260 if (
raw.size() == 19)
262 }
else if (device_uuid == 0x06d3) {
264 result.
name =
"MHOC303";
265 }
else if (device_uuid == 0x0387) {
267 result.
name =
"MHOC401";
268 }
else if (device_uuid == 0x0A83) {
270 result.
name =
"CGPR1";
271 if (
raw.size() == 19)
273 }
else if (device_uuid == 0x0A8D) {
275 result.
name =
"RTCGQ02LM";
276 if (
raw.size() == 19)
279 ESP_LOGVV(TAG,
"parse_xiaomi_header(): unknown device, no magic bytes.");
287 if ((
raw.size() != 19) && ((
raw.size() < 22) || (
raw.size() > 24))) {
288 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): data packet has wrong size (%d)!",
raw.size());
289#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
296 uint8_t mac_reverse[MAC_ADDRESS_SIZE] = {0};
297 mac_reverse[5] = (uint8_t) (
address >> 40);
298 mac_reverse[4] = (uint8_t) (
address >> 32);
299 mac_reverse[3] = (uint8_t) (
address >> 24);
300 mac_reverse[2] = (uint8_t) (
address >> 16);
301 mac_reverse[1] = (uint8_t) (
address >> 8);
302 mac_reverse[0] = (uint8_t) (
address >> 0);
317 int cipher_pos = (
raw.size() == 19) ? 5 : 11;
319 const uint8_t *v =
raw.data();
321 memcpy(vector.key, bindkey, vector.keysize);
322 memcpy(vector.ciphertext, v + cipher_pos, vector.datasize);
323 memcpy(vector.tag, v +
raw.size() - vector.tagsize, vector.tagsize);
324 memcpy(vector.iv, mac_reverse, 6);
325 memcpy(vector.iv + 6, v + 2, 3);
326 memcpy(vector.iv + 9, v +
raw.size() - 7, 3);
328#ifdef XIAOMI_CRYPTO_PSA
330 uint8_t ct_with_tag[
sizeof(vector.ciphertext) +
sizeof(vector.tag)];
331 memcpy(ct_with_tag, vector.ciphertext, vector.datasize);
332 memcpy(ct_with_tag + vector.datasize, vector.tag, vector.tagsize);
333 size_t ct_with_tag_size = vector.datasize + vector.tagsize;
335 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
336 psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
337 psa_set_key_bits(&attributes, vector.keysize * 8);
338 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
339 psa_set_key_algorithm(&attributes, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize));
341 mbedtls_svc_key_id_t key_id;
342 if (psa_import_key(&attributes, vector.key, vector.keysize, &key_id) != PSA_SUCCESS) {
343 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): psa_import_key() failed.");
347 size_t plaintext_length;
348 psa_status_t
status = psa_aead_decrypt(key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize),
349 vector.iv, vector.ivsize, vector.authdata, vector.authsize, ct_with_tag,
350 ct_with_tag_size, vector.plaintext, vector.datasize, &plaintext_length);
351 psa_destroy_key(key_id);
352 bool decrypt_ok = (
status == PSA_SUCCESS && plaintext_length == vector.datasize);
356 vector.authsize, vector.ciphertext, vector.datasize,
357 vector.plaintext, vector.tag, vector.tagsize);
361 uint8_t mac_address[MAC_ADDRESS_SIZE] = {0};
362 memcpy(mac_address, mac_reverse + 5, 1);
363 memcpy(mac_address + 1, mac_reverse + 4, 1);
364 memcpy(mac_address + 2, mac_reverse + 3, 1);
365 memcpy(mac_address + 3, mac_reverse + 2, 1);
366 memcpy(mac_address + 4, mac_reverse + 1, 1);
367 memcpy(mac_address + 5, mac_reverse, 1);
368 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption failed.");
369#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
370 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
374 ESP_LOGVV(TAG,
" MAC address : %s", mac_buf);
378 ESP_LOGVV(TAG,
" Cipher : %s",
format_hex_pretty_to(hex_buf, vector.ciphertext, vector.datasize));
384 uint8_t *p = vector.plaintext;
385 for (std::vector<uint8_t>::iterator it =
raw.begin() + cipher_pos; it !=
raw.begin() + cipher_pos + vector.datasize;
393 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption passed.");
394#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
397 ESP_LOGVV(TAG,
" Plaintext : %s, Packet : %d",
404 if (!result.has_value()) {
405 ESP_LOGVV(TAG,
"report_xiaomi_results(): no results available.");
409 ESP_LOGD(TAG,
"Got Xiaomi %s (%s):", result->name.c_str(),
address);
411 if (result->temperature.has_value()) {
412 ESP_LOGD(TAG,
" Temperature: %.1f°C", *result->temperature);
414 if (result->humidity.has_value()) {
415 ESP_LOGD(TAG,
" Humidity: %.1f%%", *result->humidity);
417 if (result->battery_level.has_value()) {
418 ESP_LOGD(TAG,
" Battery Level: %.0f%%", *result->battery_level);
420 if (result->conductivity.has_value()) {
421 ESP_LOGD(TAG,
" Conductivity: %.0fµS/cm", *result->conductivity);
423 if (result->illuminance.has_value()) {
424 ESP_LOGD(TAG,
" Illuminance: %.0flx", *result->illuminance);
426 if (result->moisture.has_value()) {
427 ESP_LOGD(TAG,
" Moisture: %.0f%%", *result->moisture);
429 if (result->tablet.has_value()) {
430 ESP_LOGD(TAG,
" Mosquito tablet: %.0f%%", *result->tablet);
432 if (result->is_active.has_value()) {
433 ESP_LOGD(TAG,
" Repellent: %s", (*result->is_active) ?
"on" :
"off");
435 if (result->has_motion.has_value()) {
436 ESP_LOGD(TAG,
" Motion: %s", (*result->has_motion) ?
"yes" :
"no");
438 if (result->is_light.has_value()) {
439 ESP_LOGD(TAG,
" Light: %s", (*result->is_light) ?
"on" :
"off");
441 if (result->button_press.has_value()) {
442 ESP_LOGD(TAG,
" Button: %s", (*result->button_press) ?
"pressed" :
"");