24 if ((value_type == 0x1001) && (value_length == 3)) {
29 else if ((value_type == 0x0003) && (value_length == 1)) {
33 else if ((value_type == 0x1004) && (value_length == 2)) {
38 else if ((value_type == 0x1006) && (value_length == 2)) {
43 else if (((value_type == 0x1007) || (value_type == 0x000F)) && (value_length == 3)) {
46 result.
is_light = illuminance >= 100;
47 if (value_type == 0x0F)
51 else if ((value_type == 0x1008) && (value_length == 1)) {
55 else if ((value_type == 0x1009) && (value_length == 2)) {
56 const uint16_t conductivity =
encode_uint16(data[1], data[0]);
60 else if ((value_type == 0x100A || value_type == 0x4803) && (value_length == 1)) {
64 else if ((value_type == 0x100D) && (value_length == 4)) {
71 else if ((value_type == 0x1010) && (value_length == 2)) {
72 const uint16_t formaldehyde =
encode_uint16(data[1], data[0]);
76 else if ((value_type == 0x1012) && (value_length == 1)) {
80 else if ((value_type == 0x1013) && (value_length == 1)) {
84 else if ((value_type == 0x1017) && (value_length == 4)) {
88 }
else if ((value_type == 0x1018) && (value_length == 1)) {
92 else if ((value_type == 0x4C01) && (value_length == 4)) {
99 else if ((value_type == 0x4C02) && (value_length == 1)) {
103 else if ((value_type == 0x4C08) && (value_length == 4)) {
106 std::memcpy(&humidity, &int_number,
sizeof(humidity));
172 ESP_LOGVV(TAG,
"parse_xiaomi_header(): no service data UUID magic bytes.");
177 if (
raw.size() < 5) {
178 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data too short (%d).",
raw.size());
186 ESP_LOGVV(TAG,
"parse_xiaomi_header(): service data has no DATA flag.");
190 static uint8_t last_frame_count = 0;
191 if (last_frame_count ==
raw[4]) {
192 ESP_LOGVV(TAG,
"parse_xiaomi_header(): duplicate data packet received (%d).",
static_cast<int>(last_frame_count));
196 last_frame_count =
raw[4];
202 if (device_uuid == 0x0098) {
204 result.
name =
"HHCCJCY01";
205 }
else if (device_uuid == 0x01aa) {
207 result.
name =
"LYWSDCGQ";
208 }
else if (device_uuid == 0x015d) {
210 result.
name =
"HHCCPOT002";
211 }
else if (device_uuid == 0x02df) {
213 result.
name =
"JQJCY01YM";
214 }
else if (device_uuid == 0x03dd) {
216 result.
name =
"MUE4094RT";
218 }
else if (device_uuid == 0x0347 ||
219 device_uuid == 0x0B48) {
221 result.
name =
"CGG1";
222 }
else if (device_uuid == 0x03bc) {
224 result.
name =
"GCLS002";
225 }
else if (device_uuid == 0x045b) {
227 result.
name =
"LYWSD02";
228 }
else if (device_uuid == 0x2542) {
230 result.
name =
"LYWSD02MMC";
231 if (
raw.size() == 19)
233 }
else if (device_uuid == 0x040a) {
235 result.
name =
"WX08ZM";
236 }
else if (device_uuid == 0x0576) {
238 result.
name =
"CGD1";
239 }
else if (device_uuid == 0x066F) {
241 result.
name =
"CGDK2";
242 }
else if (device_uuid == 0x055b) {
244 result.
name =
"LYWSD03MMC";
245 }
else if (device_uuid == 0x1203) {
247 result.
name =
"XMWSDJ04MMC";
248 if (
raw.size() == 19)
250 }
else if (device_uuid == 0x07f6) {
252 result.
name =
"MJYD02YLA";
253 if (
raw.size() == 19)
255 }
else if (device_uuid == 0x06d3) {
257 result.
name =
"MHOC303";
258 }
else if (device_uuid == 0x0387) {
260 result.
name =
"MHOC401";
261 }
else if (device_uuid == 0x0A83) {
263 result.
name =
"CGPR1";
264 if (
raw.size() == 19)
266 }
else if (device_uuid == 0x0A8D) {
268 result.
name =
"RTCGQ02LM";
269 if (
raw.size() == 19)
272 ESP_LOGVV(TAG,
"parse_xiaomi_header(): unknown device, no magic bytes.");
280 if ((
raw.size() != 19) && ((
raw.size() < 22) || (
raw.size() > 24))) {
281 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): data packet has wrong size (%d)!",
raw.size());
282#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
289 uint8_t mac_reverse[6] = {0};
290 mac_reverse[5] = (uint8_t) (
address >> 40);
291 mac_reverse[4] = (uint8_t) (
address >> 32);
292 mac_reverse[3] = (uint8_t) (
address >> 24);
293 mac_reverse[2] = (uint8_t) (
address >> 16);
294 mac_reverse[1] = (uint8_t) (
address >> 8);
295 mac_reverse[0] = (uint8_t) (
address >> 0);
310 int cipher_pos = (
raw.size() == 19) ? 5 : 11;
312 const uint8_t *v =
raw.data();
314 memcpy(vector.key, bindkey, vector.keysize);
315 memcpy(vector.ciphertext, v + cipher_pos, vector.datasize);
316 memcpy(vector.tag, v +
raw.size() - vector.tagsize, vector.tagsize);
317 memcpy(vector.iv, mac_reverse, 6);
318 memcpy(vector.iv + 6, v + 2, 3);
319 memcpy(vector.iv + 9, v +
raw.size() - 7, 3);
321#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
323 uint8_t ct_with_tag[
sizeof(vector.ciphertext) +
sizeof(vector.tag)];
324 memcpy(ct_with_tag, vector.ciphertext, vector.datasize);
325 memcpy(ct_with_tag + vector.datasize, vector.tag, vector.tagsize);
326 size_t ct_with_tag_size = vector.datasize + vector.tagsize;
328 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
329 psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
330 psa_set_key_bits(&attributes, vector.keysize * 8);
331 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
332 psa_set_key_algorithm(&attributes, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize));
334 mbedtls_svc_key_id_t key_id;
335 if (psa_import_key(&attributes, vector.key, vector.keysize, &key_id) != PSA_SUCCESS) {
336 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): psa_import_key() failed.");
340 size_t plaintext_length;
341 psa_status_t
status = psa_aead_decrypt(key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, vector.tagsize),
342 vector.iv, vector.ivsize, vector.authdata, vector.authsize, ct_with_tag,
343 ct_with_tag_size, vector.plaintext, vector.datasize, &plaintext_length);
344 psa_destroy_key(key_id);
345 bool decrypt_ok = (
status == PSA_SUCCESS && plaintext_length == vector.datasize);
347 mbedtls_ccm_context ctx;
348 mbedtls_ccm_init(&ctx);
350 int ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, vector.key, vector.keysize * 8);
352 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): mbedtls_ccm_setkey() failed.");
353 mbedtls_ccm_free(&ctx);
357 ret = mbedtls_ccm_auth_decrypt(&ctx, vector.datasize, vector.iv, vector.ivsize, vector.authdata, vector.authsize,
358 vector.ciphertext, vector.plaintext, vector.tag, vector.tagsize);
359 mbedtls_ccm_free(&ctx);
360 bool decrypt_ok = (ret == 0);
364 uint8_t mac_address[6] = {0};
365 memcpy(mac_address, mac_reverse + 5, 1);
366 memcpy(mac_address + 1, mac_reverse + 4, 1);
367 memcpy(mac_address + 2, mac_reverse + 3, 1);
368 memcpy(mac_address + 3, mac_reverse + 2, 1);
369 memcpy(mac_address + 4, mac_reverse + 1, 1);
370 memcpy(mac_address + 5, mac_reverse, 1);
371 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption failed.");
372#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
373 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
377 ESP_LOGVV(TAG,
" MAC address : %s", mac_buf);
381 ESP_LOGVV(TAG,
" Cipher : %s",
format_hex_pretty_to(hex_buf, vector.ciphertext, vector.datasize));
387 uint8_t *p = vector.plaintext;
388 for (std::vector<uint8_t>::iterator it =
raw.begin() + cipher_pos; it !=
raw.begin() + cipher_pos + vector.datasize;
396 ESP_LOGVV(TAG,
"decrypt_xiaomi_payload(): authenticated decryption passed.");
397#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
400 ESP_LOGVV(TAG,
" Plaintext : %s, Packet : %d",
407 if (!result.has_value()) {
408 ESP_LOGVV(TAG,
"report_xiaomi_results(): no results available.");
412 ESP_LOGD(TAG,
"Got Xiaomi %s (%s):", result->name.c_str(),
address);
414 if (result->temperature.has_value()) {
415 ESP_LOGD(TAG,
" Temperature: %.1f°C", *result->temperature);
417 if (result->humidity.has_value()) {
418 ESP_LOGD(TAG,
" Humidity: %.1f%%", *result->humidity);
420 if (result->battery_level.has_value()) {
421 ESP_LOGD(TAG,
" Battery Level: %.0f%%", *result->battery_level);
423 if (result->conductivity.has_value()) {
424 ESP_LOGD(TAG,
" Conductivity: %.0fµS/cm", *result->conductivity);
426 if (result->illuminance.has_value()) {
427 ESP_LOGD(TAG,
" Illuminance: %.0flx", *result->illuminance);
429 if (result->moisture.has_value()) {
430 ESP_LOGD(TAG,
" Moisture: %.0f%%", *result->moisture);
432 if (result->tablet.has_value()) {
433 ESP_LOGD(TAG,
" Mosquito tablet: %.0f%%", *result->tablet);
435 if (result->is_active.has_value()) {
436 ESP_LOGD(TAG,
" Repellent: %s", (*result->is_active) ?
"on" :
"off");
438 if (result->has_motion.has_value()) {
439 ESP_LOGD(TAG,
" Motion: %s", (*result->has_motion) ?
"yes" :
"no");
441 if (result->is_light.has_value()) {
442 ESP_LOGD(TAG,
" Light: %s", (*result->is_light) ?
"on" :
"off");
444 if (result->button_press.has_value()) {
445 ESP_LOGD(TAG,
" Button: %s", (*result->button_press) ?
"pressed" :
"");