19static const char *
const TAG =
"ble_device_base";
23static constexpr size_t BLE_ADV_MAX_LOG_BYTES = 62;
32 ret.uuid_.uuid16 = uuid;
39 ret.uuid_.uuid32 = uuid;
46 memcpy(
ret.uuid_.uuid128, data, 16);
53 for (
int i = 0; i < 16; i++)
54 ret.uuid_.uuid128[i] = data[15 - i];
64 if (parsed.has_value()) {
66 ret.uuid_.uuid16 = parsed.value();
71 if (parsed.has_value()) {
73 ret.uuid_.uuid32 = parsed.value();
78 memcpy(
ret.uuid_.uuid128,
reinterpret_cast<const uint8_t *
>(data), 16);
83 for (
size_t i = 0; i <
length; i += 2) {
86 uint8_t msb = data[i];
87 uint8_t lsb = data[i + 1];
92 ret.uuid_.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F);
95 ESP_LOGE(TAG,
"ERROR: UUID value not 4, 8, 16 or 36 bytes - %s", data);
104 if (uuid.len == ESP_UUID_LEN_16)
106 if (uuid.len == ESP_UUID_LEN_32)
113 switch (this->
type_) {
116 memset(&
ret.uuid, 0,
sizeof(
ret.uuid));
119 ret.len = ESP_UUID_LEN_16;
120 ret.uuid.uuid16 = this->
uuid_.uuid16;
123 ret.len = ESP_UUID_LEN_32;
124 ret.uuid.uuid32 = this->
uuid_.uuid32;
128 ret.len = ESP_UUID_LEN_128;
129 memcpy(
ret.uuid.uuid128, this->uuid_.uuid128, ESP_UUID_LEN_128);
140 uint8_t mac_lsb_first[MAC_ADDRESS_SIZE];
141 for (uint8_t i = 0; i < 6; i++)
142 mac_lsb_first[i] = scan_result.bda[5 - i];
143 this->
from_scan_result(mac_lsb_first, scan_result.rssi, scan_result.ble_addr_type, scan_result.ble_adv,
144 scan_result.adv_data_len + scan_result.scan_rsp_len);
159 switch (this->
type_) {
163 return (this->
uuid_.uuid16 >> 8) == data2 && (this->
uuid_.uuid16 & 0xFF) == data1;
165 for (uint8_t i = 0; i < 3; i++) {
166 bool a = ((this->
uuid_.uuid32 >> i * 8) & 0xFF) == data1;
167 bool b = ((this->
uuid_.uuid32 >> (i + 1) * 8) & 0xFF) == data2;
173 for (uint8_t i = 0; i < 15; i++) {
174 if (this->
uuid_.uuid128[i] == data1 && this->uuid_.uuid128[i + 1] == data2)
185 switch (this->
type_) {
187 memcpy(buf,
"None", 5);
201 for (
int shift = 28; shift >= 0; shift -= 4)
208 for (int8_t i = 15; i >= 0; i--) {
209 uint8_t
byte = this->
uuid_.uuid128[i];
212 if (i == 12 || i == 10 || i == 8 || i == 6)
224 static const uint8_t BASE[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
225 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
227 memcpy(out, this->
uuid_.uuid128, 16);
230 memcpy(out, BASE, 16);
233 for (
size_t i = 0; i <
len; i++)
234 out[12 + i] = (value >> (i * 8)) & 0xFF;
239 switch (this->
type_) {
260 return memcmp(a, b, 16) == 0;
272 if (!data.uuid.contains(0x4C, 0x00))
274 if (data.data.size() != 23)
279 if (data.data[0] != 0x02 || data.data[1] != 0x15) {
280 if (prefix_rejected !=
nullptr)
281 *prefix_rejected =
true;
292 bool prefix_rejected =
false;
293 uint8_t rejected_sub_type = 0;
294 uint8_t rejected_len = 0;
296 bool rejected =
false;
300 if (rejected && !prefix_rejected) {
301 prefix_rejected =
true;
302 rejected_sub_type = it.data[0];
303 rejected_len = it.data[1];
306 if (prefix_rejected) {
314 static uint64_t last_addr = 0;
317 const uint32_t since = now - last_log;
318 if (last_log == 0 || since > 60000 || (addr != last_addr && since > 1000)) {
322 ESP_LOGD(TAG,
"%s: 23-byte Apple frame without iBeacon prefix ignored (sub-type 0x%02X len 0x%02X)",
331 case BLE_ADDR_TYPE_PUBLIC:
333 case BLE_ADDR_TYPE_RANDOM:
335 case BLE_ADDR_TYPE_RPA_PUBLIC:
337 case BLE_ADDR_TYPE_RPA_RANDOM:
348 for (uint8_t i = 0; i < 6; i++)
353 this->
name_[0] =
'\0';
362#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
366 " Address: %s (%s)\n"
371 ESP_LOGVV(TAG,
" TX Power: %d", it);
374 ESP_LOGVV(TAG,
" Appearance: %u", *this->
appearance_);
377 ESP_LOGVV(TAG,
" Ad Flag: %u", *this->
ad_flag_);
379 char uuid_buf[UUID_STR_LEN];
381 ESP_LOGVV(TAG,
" Service UUID: %s", uuid.to_str(uuid_buf));
386 if (ibeacon.has_value()) {
388 " Manufacturer iBeacon:\n"
393 ibeacon.value().get_uuid().to_str(uuid_buf), ibeacon.value().get_major(), ibeacon.value().get_minor(),
394 ibeacon.value().get_signal_power());
396 ESP_LOGVV(TAG,
" Manufacturer ID: %s, data: %s", mfg_data.uuid.to_str(uuid_buf),
405 svc_data.uuid.to_str(uuid_buf),
413std::string ESPBTDevice::address_str()
const {
427 for (
int i = 0; i < 6; i++)
428 addr |=
static_cast<uint64_t
>(this->
address_[i]) << ((5 - i) * 8);
433#ifdef USE_BLE_DEVICE_IRK
439 uint8_t ecb_plaintext[16] = {0};
440 uint8_t ecb_ciphertext[16];
442 ecb_plaintext[13] = (addr64 >> 40) & 0xff;
443 ecb_plaintext[14] = (addr64 >> 32) & 0xff;
444 ecb_plaintext[15] = (addr64 >> 24) & 0xff;
446 return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
447 ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
459 while (offset <
len) {
460 uint8_t ad_len = payload[offset++];
463 if (offset + ad_len >
len)
465 uint8_t ad_type = payload[offset];
466 const uint8_t *ad_data = &payload[offset + 1];
467 uint8_t ad_data_len = ad_len - 1;
472 if (ad_data_len >= 1)
483 memcpy(this->
name_, ad_data, name_len);
484 this->
name_[name_len] =
'\0';
490 if (ad_data_len >= 1)
491 this->
tx_powers_.push_back(
static_cast<int8_t
>(ad_data[0]));
495 if (ad_data_len >= 2)
496 this->
appearance_ =
static_cast<uint16_t
>(ad_data[0]) | (
static_cast<uint16_t
>(ad_data[1]) << 8);
501 for (uint8_t i = 0; (i + 1) < ad_data_len; i += 2) {
502 uint16_t uuid = (
static_cast<uint16_t
>(ad_data[i + 1]) << 8) | ad_data[i];
509 for (uint8_t i = 0; (i + 3) < ad_data_len; i += 4) {
511 (
static_cast<uint32_t>(ad_data[i + 2]) << 16) | (
static_cast<uint32_t>(ad_data[i + 1]) << 8) |
519 for (uint8_t i = 0; (i + 15) < ad_data_len; i += 16)
524 if (ad_data_len >= 2) {
525 uint16_t company_id = (
static_cast<uint16_t
>(ad_data[1]) << 8) | ad_data[0];
528 sd.
data.assign(ad_data + 2, ad_data + ad_data_len);
534 if (ad_data_len >= 2) {
535 uint16_t uuid = (
static_cast<uint16_t
>(ad_data[1]) << 8) | ad_data[0];
538 sd.
data.assign(ad_data + 2, ad_data + ad_data_len);
544 if (ad_data_len >= 4) {
546 (
static_cast<uint32_t>(ad_data[1]) << 8) | ad_data[0];
549 sd.
data.assign(ad_data + 4, ad_data + ad_data_len);
555 if (ad_data_len >= 16) {
558 sd.
data.assign(ad_data + 16, ad_data + ad_data_len);
574#ifdef ESPHOME_LOG_HAS_DEBUG
582 this->already_discovered_.push_back(
address);
586 "Found device %s RSSI=%d\n"
593 ESP_LOGD(
tag,
" TX Power: %d", tx_power);
constexpr const char * c_str() const
constexpr bool empty() const
std::vector< uint64_t > already_discovered_
void log_device(const char *tag, const ESPBTDevice &device)
Log the device at DEBUG the first time its MAC is seen this scan period.
static optional< ESPBLEiBeacon > from_manufacturer_data(const ServiceData &data, bool *prefix_rejected=nullptr)
prefix_rejected: caller must initialise to false; set to true ONLY when a 23-byte Apple frame was ref...
struct PACKED esphome::ble_device_base::ESPBLEiBeacon::BeaconData beacon_data_
ESPDEPRECATED("Use address_str_to() instead. Removed in 2027.2.0.", "2026.8.0") std const char * address_str_to(char *buf) const
Return MAC as "XX:XX:XX:XX:XX:XX" string.
void parse_scan_rst(const esp32_ble::BLEScanResult &scan_result)
Historical esp32 ingest (esp32 builds only): parse an ESP-IDF scan result.
std::vector< ESPBTUUID > service_uuids_
std::vector< int8_t > tx_powers_
const std::vector< int8_t > & get_tx_powers() const
optional< uint16_t > appearance_
void from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data, uint16_t data_len)
Populate from a raw scan result delivered by a BLE tracker backend.
static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE
char name_[MAX_ADV_NAME_LEN+1]
void parse_adv_(const uint8_t *payload, uint16_t len)
std::vector< ServiceData > service_datas_
const esp32_ble::BLEScanResult * scan_result_
std::vector< ServiceData > manufacturer_datas_
optional< ESPBLEiBeacon > get_ibeacon() const
uint64_t address_uint64() const
Return MAC as packed uint64 (byte 0 in LSB — matches esp32's address_uint64).
const char * address_type_str() const
Human-readable address type ("PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM" or "UNKNOWN"),...
uint8_t address_[MAC_ADDRESS_SIZE]
static constexpr uint8_t MAX_ADV_NAME_LEN
StringRef get_name() const
Advertised name as a view into the fixed buffer (always NUL-terminated, so c_str() is safe); converts...
bool resolve_irk(const uint8_t *irk) const
Resolve a Resolvable Private Address against a 16-byte IRK (Bluetooth "ah" function,...
optional< uint8_t > ad_flag_
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Source compatibility with the historical esp32_ble API (esp32 builds only).
static ESPBTUUID from_uint16(uint16_t uuid)
static ESPBTUUID from_raw(const uint8_t *data)
Construct from raw 16-byte little-endian UUID.
bool contains(uint8_t data1, uint8_t data2) const
True if the UUID value contains the adjacent byte pair (data1, data2).
void to_128bit_(uint8_t out[16]) const
esp_bt_uuid_t get_uuid() const
static ESPBTUUID from_raw_reversed(const uint8_t *data)
Construct from raw 16-byte big-endian UUID (reversed on store).
union esphome::ble_device_base::ESPBTUUID::@21 uuid_
static ESPBTUUID from_uint32(uint32_t uuid)
ESPBTUUID as_128bit() const
Expand to the 128-bit Bluetooth Base UUID form.
bool operator==(const ESPBTUUID &other) const
const char * to_str(char *buf) const
Write "0xABCD" / "0xABCDEF01" / the dashed 128-bit form, or "None" for an unset UUID,...
void aes128_encrypt_block(const uint8_t key[16], const uint8_t in[16], uint8_t out[16])
AES-128 single-block encrypt (the same software cipher CCM uses).
ESPHOME_ALWAYS_INLINE char format_hex_pretty_char(uint8_t v)
Convert a nibble (0-15) to uppercase hex char (used for pretty printing)
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.
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
uint32_t IRAM_ATTR HOT millis()
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)