diff --git a/esphome/components/ble_device_base/ble_device.cpp b/esphome/components/ble_device_base/ble_device.cpp index c025af4d28..9c3e1d4397 100644 --- a/esphome/components/ble_device_base/ble_device.cpp +++ b/esphome/components/ble_device_base/ble_device.cpp @@ -264,6 +264,21 @@ optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData // ESPBTDevice // --------------------------------------------------------------------------- +const char *ESPBTDevice::address_type_str() const { + switch (this->address_type_) { + case BLE_ADDR_TYPE_PUBLIC: + return "PUBLIC"; + case BLE_ADDR_TYPE_RANDOM: + return "RANDOM"; + case BLE_ADDR_TYPE_RPA_PUBLIC: + return "RPA_PUBLIC"; + case BLE_ADDR_TYPE_RPA_RANDOM: + return "RPA_RANDOM"; + default: + return "UNKNOWN"; + } +} + void ESPBTDevice::from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data, uint16_t data_len) { // Ingest is BLE controller order (LSB-first); store in printable (MSB-first) @@ -282,29 +297,13 @@ void ESPBTDevice::from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_ty this->parse_adv_(data, data_len); #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - ESP_LOGVV(TAG, "Parse Result:"); - const char *address_type; - switch (this->address_type_) { - case BLE_ADDR_TYPE_PUBLIC: - address_type = "PUBLIC"; - break; - case BLE_ADDR_TYPE_RANDOM: - address_type = "RANDOM"; - break; - case BLE_ADDR_TYPE_RPA_PUBLIC: - address_type = "RPA_PUBLIC"; - break; - case BLE_ADDR_TYPE_RPA_RANDOM: - address_type = "RPA_RANDOM"; - break; - default: - address_type = "UNKNOWN"; - break; - } char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; - ESP_LOGVV(TAG, " Address: %s (%s)", this->address_str_to(addr_buf), address_type); - ESP_LOGVV(TAG, " RSSI: %d", this->rssi_); - ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str()); + ESP_LOGVV(TAG, + "Parse Result:\n" + " Address: %s (%s)\n" + " RSSI: %d\n" + " Name: '%s'", + this->address_str_to(addr_buf), this->address_type_str(), this->rssi_, this->name_.c_str()); for (auto &it : this->tx_powers_) { ESP_LOGVV(TAG, " TX Power: %d", it); } @@ -322,20 +321,26 @@ void ESPBTDevice::from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_ty for (auto &mfg_data : this->manufacturer_datas_) { auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(mfg_data); if (ibeacon.has_value()) { - ESP_LOGVV(TAG, " Manufacturer iBeacon:"); - ESP_LOGVV(TAG, " UUID: %s", ibeacon.value().get_uuid().to_str(uuid_buf)); - ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major()); - ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor()); - ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power()); + ESP_LOGVV(TAG, + " Manufacturer iBeacon:\n" + " UUID: %s\n" + " Major: %u\n" + " Minor: %u\n" + " TXPower: %d", + ibeacon.value().get_uuid().to_str(uuid_buf), ibeacon.value().get_major(), ibeacon.value().get_minor(), + ibeacon.value().get_signal_power()); } else { ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", mfg_data.uuid.to_str(uuid_buf), format_hex_pretty_to(hex_buf, mfg_data.data.data(), mfg_data.data.size())); } } for (auto &svc_data : this->service_datas_) { - ESP_LOGVV(TAG, " Service data:"); - ESP_LOGVV(TAG, " UUID: %s", svc_data.uuid.to_str(uuid_buf)); - ESP_LOGVV(TAG, " Data: %s", format_hex_pretty_to(hex_buf, svc_data.data.data(), svc_data.data.size())); + ESP_LOGVV(TAG, + " Service data:\n" + " UUID: %s\n" + " Data: %s", + svc_data.uuid.to_str(uuid_buf), + format_hex_pretty_to(hex_buf, svc_data.data.data(), svc_data.data.size())); } ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty_to(hex_buf, data, data_len)); #endif // ESPHOME_LOG_HAS_VERY_VERBOSE @@ -493,4 +498,33 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) { } } +// --------------------------------------------------------------------------- +// DiscoveredDeviceLog +// --------------------------------------------------------------------------- + +void DiscoveredDeviceLog::log_device(const char *tag, const ESPBTDevice &device) { +#ifdef ESPHOME_LOG_HAS_DEBUG + // Everything here feeds ESP_LOGD: below DEBUG the whole body (including the + // dedup vector growth) would be pure overhead, so compile it out entirely. + const uint64_t address = device.address_uint64(); + for (auto &disc : this->already_discovered_) { + if (disc == address) + return; + } + this->already_discovered_.push_back(address); + + char addr_buf[ESPBTDevice::MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGD(tag, + "Found device %s RSSI=%d\n" + " Address Type: %s", + device.address_str_to(addr_buf), device.get_rssi(), device.address_type_str()); + if (!device.get_name().empty()) { + ESP_LOGD(tag, " Name: '%s'", device.get_name().c_str()); + } + for (auto &tx_power : device.get_tx_powers()) { + ESP_LOGD(tag, " TX Power: %d", tx_power); + } +#endif // ESPHOME_LOG_HAS_DEBUG +} + } // namespace esphome::ble_device_base diff --git a/esphome/components/ble_device_base/ble_device.h b/esphome/components/ble_device_base/ble_device.h index 6b52a8f842..94678091cc 100644 --- a/esphome/components/ble_device_base/ble_device.h +++ b/esphome/components/ble_device_base/ble_device.h @@ -181,6 +181,9 @@ class ESPBTDevice { #else uint8_t get_address_type() const { return this->address_type_; } #endif + /// Human-readable address type ("PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM" or + /// "UNKNOWN"), backed by the shared BLE_ADDR_TYPE_* constants above. + const char *address_type_str() const; int get_rssi() const { return rssi_; } const std::string &get_name() const { return name_; } @@ -224,6 +227,24 @@ class ESPBTDevice { optional ad_flag_{}; }; +// --------------------------------------------------------------------------- +// DiscoveredDeviceLog — shared per-scan-period "Found device" DEBUG logger +// --------------------------------------------------------------------------- + +/// Per-scan-period "Found device" DEBUG logger, deduplicated by MAC address. +/// Shared by all tracker backends so the output format and dedup behaviour stay +/// identical by construction (single implementation instead of per-chip copies). +class DiscoveredDeviceLog { + public: + /// Log the device at DEBUG the first time its MAC is seen this scan period. + void log_device(const char *tag, const ESPBTDevice &device); + /// Reset the per-period dedup list (call when a scan period ends). + void clear() { this->already_discovered_.clear(); } + + protected: + std::vector already_discovered_; +}; + // --------------------------------------------------------------------------- // ESPBTDeviceListener — base class for BLE consumers (sensors, proxy, triggers) // --------------------------------------------------------------------------- diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 141aa6729d..0c1a98c4be 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -258,7 +258,7 @@ void ESP32BLETracker::start_scan_(bool first) { #endif } #ifdef USE_ESP32_BLE_DEVICE - this->already_discovered_.clear(); + this->discovered_log_.clear(); #endif this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE; this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC; @@ -471,42 +471,9 @@ void ESP32BLETracker::dump_config() { #ifdef USE_ESP32_BLE_DEVICE void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { - const uint64_t address = device.address_uint64(); - for (auto &disc : this->already_discovered_) { - if (disc == address) - return; - } - this->already_discovered_.push_back(address); - - char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; - ESP_LOGD(TAG, "Found device %s RSSI=%d", device.address_str_to(addr_buf), device.get_rssi()); - - const char *address_type_s; - switch (device.get_address_type()) { - case BLE_ADDR_TYPE_PUBLIC: - address_type_s = "PUBLIC"; - break; - case BLE_ADDR_TYPE_RANDOM: - address_type_s = "RANDOM"; - break; - case BLE_ADDR_TYPE_RPA_PUBLIC: - address_type_s = "RPA_PUBLIC"; - break; - case BLE_ADDR_TYPE_RPA_RANDOM: - address_type_s = "RPA_RANDOM"; - break; - default: - address_type_s = "UNKNOWN"; - break; - } - - ESP_LOGD(TAG, " Address Type: %s", address_type_s); - if (!device.get_name().empty()) { - ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str()); - } - for (auto &tx_power : device.get_tx_powers()) { - ESP_LOGD(TAG, " TX Power: %d", tx_power); - } + // Shared implementation in ble_device_base — identical output and per-period + // MAC dedup on every tracker backend. + this->discovered_log_.log_device(TAG, device); } // resolve_irk() is provided by ble_device_base (portable software AES). @@ -566,7 +533,7 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) { ESP_LOGV(TAG, "Scan %scomplete, set scanner state to IDLE.", is_stop_complete ? "stop " : ""); #ifdef USE_ESP32_BLE_DEVICE - this->already_discovered_.clear(); + this->discovered_log_.clear(); #endif // Reset timeout state machine instead of cancelling scheduler timeout this->scan_timeout_state_ = ScanTimeoutState::INACTIVE; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index c20962eb25..01ae22e710 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -343,8 +343,8 @@ class ESP32BLETracker final : public Component, #endif ble_device_base::RawAdvertisementCallback raw_advertisement_callback_{nullptr}; #ifdef USE_ESP32_BLE_DEVICE - /// Vector of addresses that have already been printed in print_bt_device_info - std::vector already_discovered_; + /// Per-period "Found device" DEBUG log with MAC dedup (shared ble_device_base impl) + ble_device_base::DiscoveredDeviceLog discovered_log_; #endif // Group 2: Structs (aligned to 4 bytes)