25 if ((value_type == 0x1001) && (value_length == 3)) {
30 else if ((value_type == 0x0003) && (value_length == 1)) {
34 else if ((value_type == 0x1004) && (value_length == 2)) {
39 else if ((value_type == 0x1006) && (value_length == 2)) {
44 else if (((value_type == 0x1007) || (value_type == 0x000F)) && (value_length == 3)) {
47 result.
is_light = illuminance >= 100;
48 if (value_type == 0x0F)
52 else if ((value_type == 0x1008) && (value_length == 1)) {
56 else if ((value_type == 0x1009) && (value_length == 2)) {
57 const uint16_t conductivity =
encode_uint16(data[1], data[0]);
61 else if ((value_type == 0x100A || value_type == 0x4803) && (value_length == 1)) {
65 else if ((value_type == 0x100D) && (value_length == 4)) {
72 else if ((value_type == 0x1010) && (value_length == 2)) {
73 const uint16_t formaldehyde =
encode_uint16(data[1], data[0]);
77 else if ((value_type == 0x1012) && (value_length == 1)) {
81 else if ((value_type == 0x1013) && (value_length == 1)) {
85 else if ((value_type == 0x1017) && (value_length == 4)) {
89 }
else if ((value_type == 0x1018) && (value_length == 1)) {
93 else if ((value_type == 0x4C01) && (value_length == 4)) {
100 else if ((value_type == 0x4C02) && (value_length == 1)) {
104 else if ((value_type == 0x4C08) && (value_length == 4)) {
107 std::memcpy(&humidity, &int_number,
sizeof(humidity));
173 ESP_LOGVV(TAG,
"parse_xiaomi_header(): no service data UUID magic bytes.");
178 if (
raw.size() < 5) {
179 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data too short (%d).",
raw.size());
187 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data has no DATA flag.");
191 static uint8_t last_frame_count = 0;
192 if (last_frame_count ==
raw[4]) {
193 ESP_LOGVV(TAG,
"parse_xiaomi_header(): duplicate data packet received (%d).",
static_cast<int>(last_frame_count));
197 last_frame_count =
raw[4];
203 if (device_uuid == 0x0098) {
205 result.
name =
"HHCCJCY01";
206 }
else if (device_uuid == 0x01aa) {
208 result.
name =
"LYWSDCGQ";
209 }
else if (device_uuid == 0x015d) {
211 result.
name =
"HHCCPOT002";
212 }
else if (device_uuid == 0x02df) {
214 result.
name =
"JQJCY01YM";
215 }
else if (device_uuid == 0x03dd) {
217 result.
name =
"MUE4094RT";
219 }
else if (device_uuid == 0x0347 ||
220 device_uuid == 0x0B48) {
222 result.
name =
"CGG1";
223 }
else if (device_uuid == 0x03bc) {
225 result.
name =
"GCLS002";
226 }
else if (device_uuid == 0x045b) {
228 result.
name =
"LYWSD02";
229 }
else if (device_uuid == 0x2542) {
231 result.
name =
"LYWSD02MMC";
232 if (
raw.size() == 19)
234 }
else if (device_uuid == 0x040a) {
236 result.
name =
"WX08ZM";
237 }
else if (device_uuid == 0x0576) {
239 result.
name =
"CGD1";
240 }
else if (device_uuid == 0x066F) {
242 result.
name =
"CGDK2";
243 }
else if (device_uuid == 0x055b) {
245 result.
name =
"LYWSD03MMC";
246 }
else if (device_uuid == 0x1203) {
248 result.
name =
"XMWSDJ04MMC";
249 if (
raw.size() == 19)
251 }
else if (device_uuid == 0x07f6) {
253 result.
name =
"MJYD02YLA";
254 if (
raw.size() == 19)
256 }
else if (device_uuid == 0x06d3) {
258 result.
name =
"MHOC303";
259 }
else if (device_uuid == 0x0387) {
261 result.
name =
"MHOC401";
262 }
else if (device_uuid == 0x0A83) {
264 result.
name =
"CGPR1";
265 if (
raw.size() == 19)
267 }
else if (device_uuid == 0x0A8D) {
269 result.
name =
"RTCGQ02LM";
270 if (
raw.size() == 19)
273 ESP_LOGVV(TAG,
"parse_xiaomi_header(): unknown device, no magic bytes.");
281 if ((
raw.size() != 19) && ((
raw.size() < 22) || (
raw.size() > 24))) {
282 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): data packet has wrong size (%d)!",
raw.size());
283#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
290 uint8_t mac_reverse[6] = {0};
291 mac_reverse[5] = (uint8_t) (
address >> 40);
292 mac_reverse[4] = (uint8_t) (
address >> 32);
293 mac_reverse[3] = (uint8_t) (
address >> 24);
294 mac_reverse[2] = (uint8_t) (
address >> 16);
295 mac_reverse[1] = (uint8_t) (
address >> 8);
296 mac_reverse[0] = (uint8_t) (
address >> 0);
311 int cipher_pos = (
raw.size() == 19) ? 5 : 11;
313 const uint8_t *v =
raw.data();
315 memcpy(vector.key, bindkey, vector.keysize);
316 memcpy(vector.ciphertext, v + cipher_pos, vector.datasize);
317 memcpy(vector.tag, v +
raw.size() - vector.tagsize, vector.tagsize);
318 memcpy(vector.iv, mac_reverse, 6);
319 memcpy(vector.iv + 6, v + 2, 3);
320 memcpy(vector.iv + 9, v +
raw.size() - 7, 3);
322#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
324 uint8_t ct_with_tag[
sizeof(vector.ciphertext) +
sizeof(vector.tag)];
325 memcpy(ct_with_tag, vector.ciphertext, vector.datasize);
326 memcpy(ct_with_tag + vector.datasize, vector.tag, vector.tagsize);
327 size_t ct_with_tag_size = vector.datasize + vector.tagsize;
329 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
330 psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
331 psa_set_key_bits(&attributes, vector.keysize * 8);
332 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
333 psa_set_key_algorithm(&attributes, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize));
335 mbedtls_svc_key_id_t key_id;
336 if (psa_import_key(&attributes, vector.key, vector.keysize, &key_id) != PSA_SUCCESS) {
337 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): psa_import_key() failed.");
341 size_t plaintext_length;
342 psa_status_t
status = psa_aead_decrypt(key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize),
343 vector.iv, vector.ivsize, vector.authdata, vector.authsize, ct_with_tag,
344 ct_with_tag_size, vector.plaintext, vector.datasize, &plaintext_length);
345 psa_destroy_key(key_id);
346 bool decrypt_ok = (
status == PSA_SUCCESS && plaintext_length == vector.datasize);
348 mbedtls_ccm_context ctx;
349 mbedtls_ccm_init(&ctx);
351 int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, vector.key, vector.keysize * 8);
353 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): mbedtls_ccm_setkey() failed.");
354 mbedtls_ccm_free(&ctx);
358 ret = mbedtls_ccm_auth_decrypt(&ctx, vector.datasize, vector.iv, vector.ivsize, vector.authdata, vector.authsize,
359 vector.ciphertext, vector.plaintext, vector.tag, vector.tagsize);
360 mbedtls_ccm_free(&ctx);
361 bool decrypt_ok = (ret == 0);
365 uint8_t mac_address[6] = {0};
366 memcpy(mac_address, mac_reverse + 5, 1);
367 memcpy(mac_address + 1, mac_reverse + 4, 1);
368 memcpy(mac_address + 2, mac_reverse + 3, 1);
369 memcpy(mac_address + 3, mac_reverse + 2, 1);
370 memcpy(mac_address + 4, mac_reverse + 1, 1);
371 memcpy(mac_address + 5, mac_reverse, 1);
372 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption failed.");
373#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
374 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
378 ESP_LOGVV(TAG,
" MAC address : %s", mac_buf);
382 ESP_LOGVV(TAG,
" Cipher : %s",
format_hex_pretty_to(hex_buf, vector.ciphertext, vector.datasize));
388 uint8_t *p = vector.plaintext;
389 for (std::vector<uint8_t>::iterator it =
raw.begin() + cipher_pos; it !=
raw.begin() + cipher_pos + vector.datasize;
397 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption passed.");
398#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
401 ESP_LOGVV(TAG,
" Plaintext : %s, Packet : %d",
408 if (!result.has_value()) {
409 ESP_LOGVV(TAG,
"report_xiaomi_results(): no results available.");
413 ESP_LOGD(TAG,
"Got Xiaomi %s (%s):", result->name.c_str(),
address);
415 if (result->temperature.has_value()) {
416 ESP_LOGD(TAG,
" Temperature: %.1f°C", *result->temperature);
418 if (result->humidity.has_value()) {
419 ESP_LOGD(TAG,
" Humidity: %.1f%%", *result->humidity);
421 if (result->battery_level.has_value()) {
422 ESP_LOGD(TAG,
" Battery Level: %.0f%%", *result->battery_level);
424 if (result->conductivity.has_value()) {
425 ESP_LOGD(TAG,
" Conductivity: %.0fµS/cm", *result->conductivity);
427 if (result->illuminance.has_value()) {
428 ESP_LOGD(TAG,
" Illuminance: %.0flx", *result->illuminance);
430 if (result->moisture.has_value()) {
431 ESP_LOGD(TAG,
" Moisture: %.0f%%", *result->moisture);
433 if (result->tablet.has_value()) {
434 ESP_LOGD(TAG,
" Mosquito tablet: %.0f%%", *result->tablet);
436 if (result->is_active.has_value()) {
437 ESP_LOGD(TAG,
" Repellent: %s", (*result->is_active) ?
"on" :
"off");
439 if (result->has_motion.has_value()) {
440 ESP_LOGD(TAG,
" Motion: %s", (*result->has_motion) ?
"yes" :
"no");
442 if (result->is_light.has_value()) {
443 ESP_LOGD(TAG,
" Light: %s", (*result->is_light) ?
"on" :
"off");
445 if (result->button_press.has_value()) {
446 ESP_LOGD(TAG,
" Button: %s", (*result->button_press) ?
"pressed" :
"");