ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
esp32_ble_tracker.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
3#include "esp32_ble_tracker.h"
6#include "esphome/core/hal.h"
8#include "esphome/core/log.h"
9
10#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
11#include <esp_bt.h>
12#endif
13#include <esp_bt_defs.h>
14#include <esp_bt_main.h>
15#include <esp_gap_ble_api.h>
16#include <freertos/FreeRTOS.h>
17#include <freertos/FreeRTOSConfig.h>
18#include <freertos/task.h>
19#include <nvs_flash.h>
20#include <cinttypes>
21
22#ifdef USE_OTA
24#endif
25
26#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
27#include <esp_coexist.h>
28#endif
29
30// bt_trace.h
31#undef TAG
32
34
35static const char *const TAG = "esp32_ble_tracker";
36
37ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
38
40 switch (state) {
42 return "INIT";
44 return "DISCONNECTING";
46 return "IDLE";
48 return "DISCOVERED";
50 return "CONNECTING";
52 return "CONNECTED";
54 return "ESTABLISHED";
55 default:
56 return "UNKNOWN";
57 }
58}
59
61
63 if (this->parent_->is_failed()) {
64 this->mark_failed();
65 ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE");
66 return;
67 }
68
70
71#ifdef USE_OTA_STATE_LISTENER
73#endif
74}
75
76#ifdef USE_OTA_STATE_LISTENER
78 if (state == ota::OTA_STARTED) {
80 this->stop_scan();
81#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
82 for (auto *client : this->clients_) {
83 client->disconnect();
84 }
85#endif
87 this->scan_continuous_before_ota_ = false;
88 this->scan_continuous_ = true;
89 // Do not restart scanning immediately here; allow loop() to
90 // safely restart scanning once the scanner and all clients are idle.
91 }
92}
93#endif
94
96 if (!this->parent_->is_active()) {
97 this->ble_was_disabled_ = true;
98 return;
99 } else if (this->ble_was_disabled_) {
100 this->ble_was_disabled_ = false;
101 // If the BLE stack was disabled, we need to start the scan again.
102 if (this->scan_continuous_) {
103 this->start_scan();
104 }
105 }
106
107 // Check for scan timeout - moved here from scheduler to avoid false reboots
108 // when the loop is blocked. This must run every iteration for safety.
110 switch (this->scan_timeout_state_) {
112 // Robust time comparison that handles rollover correctly
113 // This works because unsigned arithmetic wraps around predictably
114 if ((App.get_loop_component_start_time() - this->scan_start_time_) > this->scan_timeout_ms_) {
115 // First time we've seen the timeout exceeded - wait one more loop iteration
116 // This ensures all components have had a chance to process pending events
117 // This is because esp32_ble may not have run yet and called
118 // gap_scan_event_handler yet when the loop unblocks
119 ESP_LOGW(TAG, "Scan timeout exceeded");
121 }
122 break;
123 }
125 // We've waited at least one full loop iteration, and scan is still running
126 ESP_LOGE(TAG, "Scan never terminated, rebooting");
127 App.reboot();
128 break;
130 break;
131 }
132 }
133
134 // Fast path: skip expensive client state counting and processing
135 // if no state has changed since last loop iteration.
136 //
137 // How state changes ensure we reach the code below:
138 // - handle_scanner_failure_(): scanner_state_ becomes FAILED via set_scanner_state_(), or
139 // scan_set_param_failed_ requires scanner_state_==RUNNING which can only be reached via
140 // set_scanner_state_(RUNNING) in gap_scan_start_complete_() (scan params are set during
141 // STARTING, not RUNNING, so version is always incremented before this condition is true)
142 // - start_scan_(): scanner_state_ becomes IDLE via set_scanner_state_() in cleanup_scan_state_()
143 // - try_promote_discovered_clients_(): client enters DISCOVERED via set_state(), or
144 // connecting client finishes (state change), or scanner reaches RUNNING/IDLE
145 //
146 // All conditions that affect the logic below are tied to state changes that increment
147 // state_version_, so the fast path is safe.
148 if (this->state_version_ == this->last_processed_version_) {
149 return;
150 }
152
153 // State changed - do full processing
155 if (counts != this->client_state_counts_) {
156 this->client_state_counts_ = counts;
157 ESP_LOGD(TAG, "connecting: %d, discovered: %d, disconnecting: %d, active: %d",
158 this->client_state_counts_.connecting, this->client_state_counts_.discovered,
159 this->client_state_counts_.disconnecting, this->client_state_counts_.active);
160 }
161
162 // Scanner failure: reached when set_scanner_state_(FAILED) or scan_set_param_failed_ set
166 }
167 /*
168
169 Avoid starting the scanner if:
170 - we are already scanning
171 - we are connecting to a device
172 - we are disconnecting from a device
173
174 Otherwise the scanner could fail to ever start again
175 and our only way to recover is to reboot.
176
177 https://github.com/espressif/esp-idf/issues/6688
178
179 */
180
181 // Start scan: reached when scanner_state_ becomes IDLE (via set_scanner_state_()) and
182 // no clients are in the transient CONNECTING / DISCOVERED / DISCONNECTING states
183 // (their state changes increment version when they finish). CONNECTED / ESTABLISHED
184 // clients do NOT block this branch — the coex revert below has its own active-count gate.
185 if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting && !counts.discovered) {
186#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
187 // Only revert to BALANCE when no connections are active. Established connections
188 // continue to need PREFER_BT so peer GATT responses can reach us while WiFi traffic
189 // (advertisement upload, log streaming) competes for the shared radio. Reverting too
190 // early causes Bluedroid to time out at ~20s and synthesize status=133.
191 if (!counts.active) {
192 this->update_coex_preference_(false);
193 }
194#endif
195 if (this->scan_continuous_) {
196 this->start_scan_(false); // first = false
197 }
198 }
199 // Promote discovered clients: reached when a client's state becomes DISCOVERED (via set_state()),
200 // or when a blocking condition clears (connecting client finishes, scanner reaches RUNNING/IDLE).
201 // All these trigger state_version_ increment, so we'll process and check promotion eligibility.
202 // We check both RUNNING and IDLE states because:
203 // - RUNNING: gap_scan_event_handler initiates stop_scan_() but promotion can happen immediately
204 // - IDLE: Scanner has already stopped (naturally or by gap_scan_event_handler)
205 if (counts.discovered && !counts.connecting &&
206 (this->scanner_state_ == ScannerState::RUNNING || this->scanner_state_ == ScannerState::IDLE)) {
208 }
209}
210
212
214 ESP_LOGD(TAG, "Stopping scan.");
215 this->scan_continuous_ = false;
216 this->stop_scan_();
217}
218
220
223 // If scanner is already idle, there's nothing to stop - this is not an error
224 if (this->scanner_state_ != ScannerState::IDLE) {
225 ESP_LOGE(TAG, "Cannot stop scan: %s", this->scanner_state_to_string_(this->scanner_state_));
226 }
227 return;
228 }
229 // Reset timeout state machine when stopping scan
232 esp_err_t err = esp_ble_gap_stop_scanning();
233 if (err != ESP_OK) {
234 ESP_LOGE(TAG, "esp_ble_gap_stop_scanning failed: %d", err);
235 return;
236 }
237}
238
240 if (!this->parent_->is_active()) {
241 ESP_LOGW(TAG, "Cannot start scan while ESP32BLE is disabled.");
242 return;
243 }
244 if (this->scanner_state_ != ScannerState::IDLE) {
245 this->log_unexpected_state_("start scan", ScannerState::IDLE);
246 return;
247 }
249 ESP_LOGV(TAG, "Starting scan, set scanner state to STARTING.");
250 if (!first) {
251#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
252 for (auto *listener : this->listeners_)
253 listener->on_scan_end();
254#endif
255#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT
256 for (auto *listener : this->neutral_listeners_)
257 listener->on_scan_end();
258#endif
259 }
260#ifdef USE_ESP32_BLE_DEVICE
261 this->already_discovered_.clear();
262#endif
263 this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE;
264 this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
265 this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
266 this->scan_params_.scan_interval = this->scan_interval_;
267 this->scan_params_.scan_window = this->scan_window_;
268
269 // Start timeout monitoring in loop() instead of using scheduler
270 // This prevents false reboots when the loop is blocked
272 this->scan_timeout_ms_ = this->scan_duration_ * 2000;
274
275 esp_err_t err = esp_ble_gap_set_scan_params(&this->scan_params_);
276 if (err != ESP_OK) {
277 ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", err);
278 return;
279 }
280 err = esp_ble_gap_start_scanning(this->scan_duration_);
281 if (err != ESP_OK) {
282 ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", err);
283 return;
284 }
285}
286
288#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
289 client->app_id = ++this->app_id_;
290 // Give client a pointer to our state_version_ so it can notify us of state changes.
291 // This enables loop() fast-path optimization - we skip expensive work when no state changed.
292 // Safe because ESP32BLETracker (singleton) outlives all registered clients.
294 this->clients_.push_back(client);
296#endif
297}
298
300#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT
301 // Neutral BLEHub path (migrated sensors): parsed-advertisement consumers only.
302 this->neutral_listeners_.push_back(listener);
303 this->parse_advertisements_ = true;
304#endif
305}
306
308 get_mac_address_raw(out); // WiFi base MAC, MSB-first
309 // BT MAC = base MAC + 2 on the last octet only, wrapping without carry —
310 // exactly ESP-IDF's esp_read_mac(ESP_MAC_BT): mac[5] += MAC_ADDR_UNIVERSE_BT_OFFSET.
311 out[5] += 2;
312}
313
315#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
316 listener->set_parent(this);
317 this->listeners_.push_back(listener);
319#endif
320}
321
323 this->raw_advertisements_ = false;
324 this->parse_advertisements_ = false;
325#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT
326 // Neutral (BLEHub) listeners are parsed-advertisement consumers and are not in
327 // listeners_; without this, any later esp32-path registration (e.g. the proxy's
328 // GATT clients) would recompute the flags and silently drop parsed dispatch.
329 if (!this->neutral_listeners_.empty())
330 this->parse_advertisements_ = true;
331#endif
332#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
333 for (auto *listener : this->listeners_) {
334 if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
335 this->parse_advertisements_ = true;
336 } else {
337 this->raw_advertisements_ = true;
338 }
339 }
340#endif
341#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
342 for (auto *client : this->clients_) {
343 if (client->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) {
344 this->parse_advertisements_ = true;
345 } else {
346 this->raw_advertisements_ = true;
347 }
348 }
349#endif
350}
351
352void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
353 // Note: This handler is called from the main loop context, not directly from the BT task.
354 // The esp32_ble component queues events via enqueue_ble_event() and processes them in loop().
355 switch (event) {
356 case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
357 this->gap_scan_set_param_complete_(param->scan_param_cmpl);
358 break;
359 case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
360 this->gap_scan_start_complete_(param->scan_start_cmpl);
361 break;
362 case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
363 this->gap_scan_stop_complete_(param->scan_stop_cmpl);
364 break;
365 default:
366 break;
367 }
368 // Forward all events to clients (scan results are handled separately via gap_scan_event_handler)
369#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
370 for (auto *client : this->clients_) {
371 client->gap_event_handler(event, param);
372 }
373#endif
374}
375
376void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
377 // Note: This handler is called from the main loop context via esp32_ble's event queue.
378 // We process advertisements immediately instead of buffering them.
379 ESP_LOGVV(TAG, "gap_scan_result - event %d", scan_result.search_evt);
380
381 if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
382 // Process the scan result immediately
383 this->process_scan_result_(scan_result);
384 } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
385 // Scan finished on its own
387 this->log_unexpected_state_("scan complete", ScannerState::RUNNING);
388 }
389 // Scan completed naturally, perform cleanup and transition to IDLE
390 this->cleanup_scan_state_(false);
391 }
392}
393
394void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param &param) {
395 // Called from main loop context via gap_event_handler after being queued from BT task
396 ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status);
397 if (param.status == ESP_BT_STATUS_DONE) {
398 this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
399 } else {
400 this->scan_set_param_failed_ = param.status;
401 }
402}
403
404void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param &param) {
405 // Called from main loop context via gap_event_handler after being queued from BT task
406 ESP_LOGV(TAG, "gap_scan_start_complete - status %d", param.status);
407 this->scan_start_failed_ = param.status;
409 this->log_unexpected_state_("start complete", ScannerState::STARTING);
410 }
411 if (param.status == ESP_BT_STATUS_SUCCESS) {
412 this->scan_start_fail_count_ = 0;
414 } else {
416 if (this->scan_start_fail_count_ != std::numeric_limits<uint8_t>::max()) {
418 }
419 }
420}
421
422void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param &param) {
423 // Called from main loop context via gap_event_handler after being queued from BT task
424 // This allows us to safely transition to IDLE state and perform cleanup without race conditions
425 ESP_LOGV(TAG, "gap_scan_stop_complete - status %d", param.status);
427 this->log_unexpected_state_("stop complete", ScannerState::STOPPING);
428 }
429
430 // Perform cleanup and transition to IDLE
431 this->cleanup_scan_state_(true);
432}
433
434void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
435 esp_ble_gattc_cb_param_t *param) {
436#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
437 for (auto *client : this->clients_) {
438 client->gattc_event_handler(event, gattc_if, param);
439 }
440#endif
441}
442
444 this->scanner_state_ = state;
445 this->state_version_++;
446 for (auto *listener : this->scanner_state_listeners_) {
447 listener->on_scanner_state(state);
448 }
449}
450
452 ESP_LOGCONFIG(TAG, "BLE Tracker:");
453 ESP_LOGCONFIG(TAG,
454 " Scan Duration: %" PRIu32 " s\n"
455 " Scan Interval: %.1f ms\n"
456 " Scan Window: %.1f ms\n"
457 " Scan Type: %s\n"
458 " Continuous Scanning: %s",
459 this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
460 this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
461 ESP_LOGCONFIG(TAG,
462 " Scanner State: %s\n"
463 " Connecting: %d, discovered: %d, disconnecting: %d, active: %d",
465 this->client_state_counts_.discovered, this->client_state_counts_.disconnecting,
466 this->client_state_counts_.active);
467 if (this->scan_start_fail_count_) {
468 ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_);
469 }
470}
471
472#ifdef USE_ESP32_BLE_DEVICE
474 const uint64_t address = device.address_uint64();
475 for (auto &disc : this->already_discovered_) {
476 if (disc == address)
477 return;
478 }
479 this->already_discovered_.push_back(address);
480
481 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
482 ESP_LOGD(TAG, "Found device %s RSSI=%d", device.address_str_to(addr_buf), device.get_rssi());
483
484 const char *address_type_s;
485 switch (device.get_address_type()) {
486 case BLE_ADDR_TYPE_PUBLIC:
487 address_type_s = "PUBLIC";
488 break;
489 case BLE_ADDR_TYPE_RANDOM:
490 address_type_s = "RANDOM";
491 break;
492 case BLE_ADDR_TYPE_RPA_PUBLIC:
493 address_type_s = "RPA_PUBLIC";
494 break;
495 case BLE_ADDR_TYPE_RPA_RANDOM:
496 address_type_s = "RPA_RANDOM";
497 break;
498 default:
499 address_type_s = "UNKNOWN";
500 break;
501 }
502
503 ESP_LOGD(TAG, " Address Type: %s", address_type_s);
504 if (!device.get_name().empty()) {
505 ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str());
506 }
507 for (auto &tx_power : device.get_tx_powers()) {
508 ESP_LOGD(TAG, " TX Power: %d", tx_power);
509 }
510}
511
512// resolve_irk() is provided by ble_device_base (portable software AES).
513
514#endif // USE_ESP32_BLE_DEVICE
515
516void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
517 // Process raw advertisements
518 if (this->raw_advertisements_) {
519#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
520 for (auto *listener : this->listeners_) {
521 listener->parse_devices(&scan_result, 1);
522 }
523#endif
524#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
525 for (auto *client : this->clients_) {
526 client->parse_devices(&scan_result, 1);
527 }
528#endif
529 }
530
531 // Process parsed advertisements
532 if (this->parse_advertisements_) {
533#ifdef USE_ESP32_BLE_DEVICE
534 ESPBTDevice device;
535 device.parse_scan_rst(scan_result);
536
537 bool found = false;
538#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
539 for (auto *listener : this->listeners_) {
540 if (listener->parse_device(device))
541 found = true;
542 }
543#endif
544#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT
545 for (auto *listener : this->neutral_listeners_) {
546 if (listener->parse_device(device))
547 found = true;
548 }
549#endif
550
551#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
552 for (auto *client : this->clients_) {
553 if (client->parse_device(device)) {
554 found = true;
555 }
556 }
557#endif
558
559 if (!found && !this->scan_continuous_) {
560 this->print_bt_device_info(device);
561 }
562#endif // USE_ESP32_BLE_DEVICE
563 }
564}
565
566void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
567 ESP_LOGV(TAG, "Scan %scomplete, set scanner state to IDLE.", is_stop_complete ? "stop " : "");
568#ifdef USE_ESP32_BLE_DEVICE
569 this->already_discovered_.clear();
570#endif
571 // Reset timeout state machine instead of cancelling scheduler timeout
573
574#ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT
575 for (auto *listener : this->listeners_)
576 listener->on_scan_end();
577#endif
578#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT
579 for (auto *listener : this->neutral_listeners_)
580 listener->on_scan_end();
581#endif
582
584}
585
587 this->stop_scan_();
588 if (this->scan_start_fail_count_ == std::numeric_limits<uint8_t>::max()) {
589 ESP_LOGE(TAG, "Scan could not restart after %d attempts, rebooting to restore stack (IDF)",
590 std::numeric_limits<uint8_t>::max());
591 App.reboot();
592 }
593 if (this->scan_start_failed_) {
594 ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_);
595 this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS;
596 }
597 if (this->scan_set_param_failed_) {
598 ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_);
599 this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
600 }
601}
602
604 // Only promote the first discovered client to avoid multiple simultaneous connections
605#ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT
606 for (auto *client : this->clients_) {
607 if (client->state() != ClientState::DISCOVERED) {
608 continue;
609 }
610
612 ESP_LOGD(TAG, "Stopping scan to make connection");
613 this->stop_scan_();
614 // Don't wait for scan stop complete - promote immediately.
615 // This is safe because ESP-IDF processes BLE commands sequentially through its internal mailbox queue.
616 // This guarantees that the stop scan command will be fully processed before any subsequent connect command,
617 // preventing race conditions or overlapping operations.
618 }
619
620 ESP_LOGD(TAG, "Promoting client to connect");
621#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
622 this->update_coex_preference_(true);
623#endif
624 client->connect();
625 break;
626 }
627#endif
628}
629
631 switch (state) {
633 return "IDLE";
635 return "STARTING";
637 return "RUNNING";
639 return "STOPPING";
641 return "FAILED";
642 default:
643 return "UNKNOWN";
644 }
645}
646
647void ESP32BLETracker::log_unexpected_state_(const char *operation, ScannerState expected_state) const {
648 ESP_LOGE(TAG, "Unexpected state: %s on %s, expected: %s", this->scanner_state_to_string_(this->scanner_state_),
649 operation, this->scanner_state_to_string_(expected_state));
650}
651
652#ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
654#ifndef CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
655 if (force_ble && !this->coex_prefer_ble_) {
656 ESP_LOGD(TAG, "Setting coexistence to Bluetooth to make connection.");
657 this->coex_prefer_ble_ = true;
658 esp_coex_preference_set(ESP_COEX_PREFER_BT); // Prioritize Bluetooth
659 } else if (!force_ble && this->coex_prefer_ble_) {
660 ESP_LOGD(TAG, "Setting coexistence preference to balanced.");
661 this->coex_prefer_ble_ = false;
662 esp_coex_preference_set(ESP_COEX_PREFER_BALANCE); // Reset to default
663 }
664#endif // CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID
665}
666#endif
667
668} // namespace esphome::esp32_ble_tracker
669
670#endif // USE_ESP32
uint8_t address
Definition bl0906.h:4
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
const char * address_str_to(char *buf) const
Buffer overload: writes "XX:XX:XX:XX:XX:XX\0" into buf (>= 18 bytes), returns buf.
void parse_scan_rst(const esp32_ble::BLEScanResult &scan_result)
Historical esp32 ingest (esp32 builds only): parse an ESP-IDF scan result.
const std::vector< int8_t > & get_tx_powers() const
Definition ble_device.h:191
esp_ble_addr_type_t get_address_type() const
Definition ble_device.h:176
uint64_t address_uint64() const
Return MAC as packed uint64 (byte 0 in LSB — matches esp32's address_uint64).
const std::string & get_name() const
Definition ble_device.h:186
void try_promote_discovered_clients_()
Try to promote discovered clients to ready to connect.
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
std::vector< uint64_t > already_discovered_
Vector of addresses that have already been printed in print_bt_device_info.
uint8_t state_version_
Version counter for loop() fast-path optimization.
StaticVector< ESPBTClient *, ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT > clients_
void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT event is received.
ClientStateCounts count_client_states_() const
Count clients in each state.
uint8_t last_processed_version_
Last state_version_ value when loop() did full processing.
void gap_scan_event_handler(const BLEScanResult &scan_result)
std::vector< BLEScannerStateListener * > scanner_state_listeners_
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
esp_ble_scan_params_t scan_params_
A structure holding the ESP BLE scan parameters.
StaticVector< ESPBTDeviceListener *, ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT > listeners_
void register_listener(ESPBTDeviceListener *listener)
uint32_t scan_timeout_ms_
Precomputed timeout value: scan_duration_ * 2000.
void update_coex_preference_(bool force_ble)
Update BLE coexistence preference.
const char * scanner_state_to_string_(ScannerState state) const
Convert scanner state enum to string for logging.
void gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT event is received.
uint32_t scan_duration_
The interval in seconds to perform scans.
void setup() override
Setup the FreeRTOS task and the Bluetooth stack.
void handle_scanner_failure_()
Handle scanner failure states.
void cleanup_scan_state_(bool is_stop_complete)
Common cleanup logic when transitioning scanner to IDLE state.
void set_scanner_state_(ScannerState state)
Called to set the scanner state. Will also call callbacks to let listeners know when state is changed...
void print_bt_device_info(const ESPBTDevice &device)
StaticVector< ble_device_base::ESPBTDeviceListener *, ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT > neutral_listeners_
void process_scan_result_(const BLEScanResult &scan_result)
Process a single scan result immediately.
void gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param &param)
Called when a ESP_GAP_BLE_SCAN_START_COMPLETE_EVT event is received.
void log_unexpected_state_(const char *operation, ScannerState expected_state) const
Log an unexpected scanner state.
void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override
void start_scan_(bool first)
Start a single scan by setting up the parameters and doing some esp-idf calls.
Base class for BLE GATT clients that connect to remote devices.
void set_tracker_state_version(uint8_t *version)
Called by ESP32BLETracker::register_client() to enable state change notifications.
void add_global_state_listener(OTAGlobalStateListener *listener)
bool state
Definition fan.h:2
ESP32BLETracker * global_esp32_ble_tracker
const char * client_state_to_string(ClientState state)
OTAGlobalCallback * get_global_ota_callback()
constexpr float AFTER_BLUETOOTH
Definition component.h:49
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition helpers.cpp:74
Application App
Global storage of Application pointer - only one Application can exist.