Skip to content

Instantly share code, notes, and snippets.

@patrick3399
Last active March 6, 2026 15:22
Show Gist options
  • Select an option

  • Save patrick3399/df2307d453ba17b9f0ad96a60e8f1962 to your computer and use it in GitHub Desktop.

Select an option

Save patrick3399/df2307d453ba17b9f0ad96a60e8f1962 to your computer and use it in GitHub Desktop.
ESPHome MiBeacon v5 AES-CCM Remote Decoder
substitutions:
devicename: "mi decode"
upper_devicename: "Mi Decode"
comment: ""
bindkey: "fa0fd744af52860c2ac77a61a29ada89"
key_press_delay: "2s"
esp32_ble_tracker:
on_ble_advertise:
- mac_address: AA:BB:CC:DD:EE:DD
then:
- lambda: |-
// MiBeacon v5 AES-CCM Remote Decoder
// Service UUID: 0xFE95
// Product ID: 0x3F3A (8Key) giot.remote.v58kwm
// Product ID: 0x3F36 (4Key) giot.remote.v54kwm
static const char* BINDKEY = "${bindkey}";
static uint8_t last_counter = 0;
for (auto &svc : x.get_service_datas()) {
if (!svc.uuid.contains(0x95, 0xFE)) continue;
auto& data = svc.data;
// Check: 22 bytes, encrypted (bit 3), product ID 0x3F3A
if (data.size() != 22 || !(data[0] & 0x08) ||
(data[2] | (data[3] << 8)) != 0x3F3A) continue;
// Deduplicate by frame counter
if (data[4] == last_counter) continue;
last_counter = data[4];
// Build nonce: MAC[5-10] + PID[2-3] + Counter[4] + Payload[15-17]
uint8_t nonce[12];
memcpy(nonce, &data[5], 6);
memcpy(nonce + 6, &data[2], 3);
memcpy(nonce + 9, &data[15], 3);
// Convert bindkey hex to bytes
uint8_t key[16];
for (int i = 0; i < 16; i++) {
sscanf(BINDKEY + 2*i, "%2hhx", &key[i]);
}
// Decrypt with AES-CCM
mbedtls_ccm_context ctx;
mbedtls_ccm_init(&ctx);
mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, 128);
uint8_t plaintext[4];
const uint8_t aad[] = {0x11};
int ret = mbedtls_ccm_auth_decrypt(&ctx, 4, nonce, 12, aad, 1,
&data[11], plaintext,
&data[18], 4);
mbedtls_ccm_free(&ctx);
if (ret == 0 && plaintext[0] == 0x0C && plaintext[1] == 0x4A) {
int btn = plaintext[3];
ESP_LOGI("v58kwm", "Button %d pressed", btn);
// Trigger corresponding sensor
switch(btn) {
case 1: id(remote_key_1).publish_state(true); break;
case 2: id(remote_key_2).publish_state(true); break;
case 3: id(remote_key_3).publish_state(true); break;
case 4: id(remote_key_4).publish_state(true); break;
case 5: id(remote_key_5).publish_state(true); break;
case 6: id(remote_key_6).publish_state(true); break;
case 7: id(remote_key_7).publish_state(true); break;
case 8: id(remote_key_8).publish_state(true); break;
}
}
}
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
sdkconfig_options:
CONFIG_MBEDTLS_CCM_C: y
esphome:
name: $devicename
friendly_name: $upper_devicename
comment: $comment
platformio_options:
build_src_flags:
- "-include mbedtls/ccm.h"
- "-include mbedtls/cipher.h"
on_boot:
- priority: -100
then:
- binary_sensor.template.publish:
id: remote_key_1
state: false
- binary_sensor.template.publish:
id: remote_key_2
state: false
- binary_sensor.template.publish:
id: remote_key_3
state: false
- binary_sensor.template.publish:
id: remote_key_4
state: false
- binary_sensor.template.publish:
id: remote_key_5
state: false
- binary_sensor.template.publish:
id: remote_key_6
state: false
- binary_sensor.template.publish:
id: remote_key_7
state: false
- binary_sensor.template.publish:
id: remote_key_8
state: false
binary_sensor:
- platform: template
name: "Button 1"
id: remote_key_1
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_1
state: false
- platform: template
name: "Button 2"
id: remote_key_2
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_2
state: false
- platform: template
name: "Button 3"
id: remote_key_3
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_3
state: false
- platform: template
name: "Button 4"
id: remote_key_4
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_4
state: false
- platform: template
name: "Button 5"
id: remote_key_5
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_5
state: false
- platform: template
name: "Button 6"
id: remote_key_6
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_6
state: false
- platform: template
name: "Button 7"
id: remote_key_7
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_7
state: false
- platform: template
name: "Button 8"
id: remote_key_8
on_press:
then:
- delay: ${key_press_delay}
- binary_sensor.template.publish:
id: remote_key_8
state: false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment