13static constexpr size_t DEVICE_ID_POS = 2;
14static constexpr size_t FRAME_COUNT_POS = 4;
15static constexpr size_t CIPHER_POS = 5;
16static constexpr size_t CIPHER_SIZE = 12;
17static constexpr size_t COUNTER_POS = CIPHER_POS + CIPHER_SIZE;
18static constexpr size_t TAG_POS = COUNTER_POS + 3;
19static constexpr size_t TAG_SIZE = 4;
20static constexpr size_t FRAME_SIZE = TAG_POS + TAG_SIZE;
22static constexpr uint8_t FRAME_ENCRYPTED_DATA = 0x48;
23static constexpr uint8_t OBJECT_SIZE = 9;
24static constexpr uint16_t OBJECT_S200_MEASUREMENT = 0x4E16;
25static constexpr uint16_t OBJECT_S400_MEASUREMENT = 0x6E16;
26static constexpr uint32_t STABILIZED_RESET_ID = 0;
27static constexpr uint32_t STABILIZED_RESET_MS = 1000;
34 uint8_t mac[MAC_ADDRESS_SIZE];
36 char mac_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
41 LOG_SENSOR(
" ",
"Weight", this->
weight_);
46 LOG_BINARY_SENSOR(
" ",
"Stabilized", this->
stabilized_);
50 uint8_t nonce[MAC_ADDRESS_SIZE + 6];
51 for (
size_t i = 0; i < MAC_ADDRESS_SIZE; i++)
52 nonce[i] =
static_cast<uint8_t
>(this->
address_ >> (i * 8));
53 memcpy(nonce + MAC_ADDRESS_SIZE, frame + DEVICE_ID_POS, 3);
54 memcpy(nonce + MAC_ADDRESS_SIZE + 3, frame + COUNTER_POS, 3);
55 static constexpr uint8_t AUTH_DATA[1] = {0x11};
57 frame + CIPHER_POS, CIPHER_SIZE, plaintext, frame + TAG_POS, TAG_SIZE);
72 const uint16_t weight = packed & 0x7FF;
73 const uint8_t heart_rate = (packed >> 11) & 0x7F;
74 const uint16_t impedance = packed >> 18;
75 ESP_LOGD(TAG,
"weight=%u heart_rate=%u impedance=%u", weight, heart_rate, impedance);
77 if (weight != 0 && this->
weight_ !=
nullptr)
79 if (heart_rate > 0 && heart_rate < 127 && this->
heart_rate_ !=
nullptr)
82 if (weight == 0 && heart_rate == 0) {
87 }
else if (impedance != 0) {
102 if (service_data.data.size() != FRAME_SIZE || !service_data.uuid.contains(0x95, 0xFE))
104 const uint8_t *frame = service_data.data.data();
106 if ((frame[0] & FRAME_ENCRYPTED_DATA) != FRAME_ENCRYPTED_DATA)
111 uint8_t
object[CIPHER_SIZE];
112 if (!this->
decrypt_(frame,
object)) {
113 ESP_LOGW(TAG,
"Decryption failed, wrong bindkey?");
120 const uint16_t value_type =
encode_uint16(
object[1],
object[0]);
121 const uint8_t *data =
object + 3;
123 const bool sized =
object[2] == OBJECT_SIZE;
124 if (sized && value_type == OBJECT_S400_MEASUREMENT) {
126 }
else if (sized && value_type == OBJECT_S200_MEASUREMENT) {
128 if (packed != 0 && this->
weight_ !=
nullptr)
131 ESP_LOGVV(TAG,
"Unknown object 0x%04X, length %u", value_type,
object[2]);
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
void publish_state(bool new_state)
Publish a new state to the front-end.
uint64_t address_uint64() const
Return MAC as packed uint64 (byte 0 in LSB — matches esp32's address_uint64).
const std::vector< ServiceData > & get_service_datas() const
void publish_state(float state)
Publish a new state to the front-end.
XiaomiBodyScale(uint64_t address, const char *bindkey)
sensor::Sensor * heart_rate_
void publish_s400_(uint32_t packed)
binary_sensor::BinarySensor * stabilized_
bool parse_device(const ble_device_base::ESPBTDevice &device) override
void dump_config() override
sensor::Sensor * profile_id_
uint16_t last_frame_count_
void publish_stabilized_(bool stabilized)
sensor::Sensor * impedance_low_
bool decrypt_(const uint8_t *frame, uint8_t *plaintext) const
sensor::Sensor * impedance_high_
void uint64_to_mac_msb_first(uint64_t address, uint8_t out[6])
Unpack a uint64 BLE address into printable (MSB-first) byte order — the order bd_addr_t / esp_bd_addr...
bool aes_ccm_auth_decrypt(const uint8_t key[16], const uint8_t *nonce, size_t nonce_len, const uint8_t *aad, size_t aad_len, const uint8_t *ciphertext, size_t ct_len, uint8_t *plaintext, const uint8_t *tag, size_t tag_len)
ESPHOME_LOG_TAG(TAG, "xiaomi_body_scale")
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
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.
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)