3#ifdef USE_BLUETOOTH_PROXY
24 "BLE advertisement data array size mismatch");
54#ifdef USE_BLE_SCANNER_STATE_CALLBACK
64 const bool running = this->
hub_->scan_running();
73#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
84#ifdef USE_BLE_SCANNER_STATE_CALLBACK
97#ifdef USE_BLUETOOTH_PROXY_ADVERTISEMENT_FILTER
101 ESP_LOGVV(TAG,
"Filtered packet from %012" PRIX64,
raw.address);
107 adv.address =
raw.address;
109 adv.address_type =
raw.addr_type;
110 uint8_t
length =
raw.data_len >
sizeof(adv.data) ?
sizeof(adv.data) :
static_cast<uint8_t
>(
raw.data_len);
116 ESP_LOGVV(TAG,
"Queuing raw packet from %012" PRIX64
", length %d. RSSI: %d dB",
raw.address,
length,
raw.rssi);
124#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
126 ESP_LOGW(TAG,
"[%d] [%s] Connection request ignored, state: %s", connection->
get_connection_index(),
135#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
137 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" dropped, TCP buffer full", what,
address);
141 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" deferred, TCP buffer full", what,
address);
145 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" dropped, displaced by %012" PRIX64, what, owed,
address);
149 ESP_LOGW(TAG,
"Cannot %s GATT %s, not connected", action,
type);
176 char mac_str[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
178 const char *mac_out = mac_str[0] !=
'\0' ? mac_str :
"unavailable (adapter not up yet)";
180#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
185 " Configured scan: %s\n"
191 " Mode: advertisement-only (no GATT connections)\n"
192 " Configured scan: %s\n"
196#ifdef USE_BLUETOOTH_PROXY_ADVERTISEMENT_FILTER
201#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
207 ESP_LOGE(TAG,
"Connection registry full, dropping registration");
213 connection->
proxy_ =
this;
220 if (slot == find_value) {
227 ESP_LOGW(TAG,
"Connection slot accounting mismatch (find 0x%llx)", (
unsigned long long) find_value);
239 if (free_entry ==
nullptr && owed.
empty()) {
243 if (free_entry !=
nullptr) {
304 uint64_t conn_addr = connection->get_address();
314 if (free_slot ==
nullptr && conn_addr == 0)
315 free_slot = connection;
317 if (!reserve || free_slot ==
nullptr)
335 if (connection ==
nullptr) {
336 ESP_LOGW(TAG,
"No free connections available");
341 ESP_LOGE(TAG,
"[%d] [%s] Missing address type in connect request", connection->get_connection_index(),
342 connection->address_str());
348 connection->send_connected_reply_();
352 ESP_LOGW(TAG,
"[%d] [%s] Connection request while pending disconnect, cancelling pending disconnect",
353 connection->get_connection_index(), connection->address_str());
368 connection->initiate_connection(
static_cast<uint8_t
>(msg.
address_type));
374 if (connection ==
nullptr) {
380 connection->disconnect();
382 connection->set_address(0);
392 if (connection !=
nullptr) {
393 if (!connection->is_paired()) {
394 auto err = connection->pair();
395 if (err != CONN_OK) {
409 if (
ret == CONN_OK) {
413 if (connection !=
nullptr) {
414 connection->set_unpaired();
426 ESP_LOGE(TAG,
"V1 connections removed");
435 if (connection ==
nullptr) {
440 auto err = connection->read_characteristic(msg.
handle);
441 if (err != CONN_OK) {
442 connection->send_gatt_error_(msg.
handle, err);
448 if (connection ==
nullptr) {
454 if (err != CONN_OK) {
455 connection->send_gatt_error_(msg.
handle, err);
461 if (connection ==
nullptr) {
466 auto err = connection->read_descriptor(msg.
handle);
467 if (err != CONN_OK) {
468 connection->send_gatt_error_(msg.
handle, err);
474 if (connection ==
nullptr) {
480 if (err != CONN_OK) {
481 connection->send_gatt_error_(msg.
handle, err);
487 if (connection ==
nullptr || !connection->connected()) {
491 if (!connection->has_gatt_services()) {
492 ESP_LOGW(TAG,
"[%d] [%s] No GATT services found", connection->get_connection_index(), connection->address_str());
496 connection->send_services_done_();
499 if (connection->send_service_ > 0) {
504 ESP_LOGD(TAG,
"[%d] [%s] GetServices mid-stream, restarting", connection->get_connection_index(),
505 connection->address_str());
506 connection->send_service_ = 0;
509 if (connection->send_service_ == SERVICES_DONE_PENDING) {
514 ESP_LOGW(TAG,
"[%d] [%s] GetServices superseded an undelivered done; client timeout will retry",
515 connection->get_connection_index(), connection->address_str());
516 connection->send_service_ = DONE_SENDING_SERVICES;
519 if (connection->send_service_ == INIT_SENDING_SERVICES)
520 connection->send_service_ = 0;
525 if (connection ==
nullptr) {
530 auto err = connection->notify_characteristic(msg.
handle, msg.
enable);
531 if (err != CONN_OK) {
532 connection->send_gatt_error_(msg.
handle, err);
546 if (connection ==
nullptr || !connection->connected()) {
547 ESP_LOGW(TAG,
"[%d] [%s] Cannot set connection params, not connected",
548 connection ?
static_cast<int>(connection->get_connection_index()) : -1,
549 connection ? connection->address_str() :
"unknown");
550 resp.
error = GATT_NOT_CONNECTED;
559 constexpr uint32_t max_val = std::numeric_limits<uint16_t>::max();
560 resp.
error = connection->update_connection_params(
static_cast<uint16_t
>(std::min(msg.
min_interval, max_val)),
561 static_cast<uint16_t
>(std::min(msg.
max_interval, max_val)),
562 static_cast<uint16_t
>(std::min(msg.
latency, max_val)),
563 static_cast<uint16_t
>(std::min(msg.
timeout, max_val)));
576 if (this->
hub_->get_scan_active() == active) {
579 ESP_LOGD(TAG,
"Setting scanner mode to %s", active ?
"active" :
"passive");
580 this->
hub_->set_scan_active(active);
581 this->
hub_->stop_scan();
582 this->
hub_->set_scan_continuous(
589 if (this->
hub_->scan_active() != active) {
590 ESP_LOGD(TAG,
"Setting scanner mode to %s", active ?
"active" :
"passive");
591 if (!this->
hub_->request_scan_mode(active)) {
595 ESP_LOGW(TAG,
"Scanner mode %s not supported by this tracker", active ?
"active" :
"passive");
598#ifndef USE_BLE_SCANNER_STATE_CALLBACK
612#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
626#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
638#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
643 if (connection->get_address() != 0) {
644 connection->disconnect();
651#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
676#ifdef USE_BLE_SCANNER_STATE_CALLBACK
684 if (this->
hub_->scan_running() != this->last_scan_running_) {
713#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
716#ifdef USE_BLE_SCANNER_STATE_CALLBACK
727#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
734 connection->park_service_stream_();
735 connection->clear_owed_flags_();
748 char old_peername[socket::SOCKADDR_STR_LEN];
749 char new_peername[socket::SOCKADDR_STR_LEN];
750 ESP_LOGW(TAG,
"Subscription from %s (%s) replaces %s (%s)", api_connection->
get_name(),
751 api_connection->
get_peername_to(new_peername), this->api_connection_->get_name(),
752 this->api_connection_->get_peername_to(old_peername));
759#ifdef USE_BLE_SCANNER_STATE_CALLBACK
769 ESP_LOGV(TAG,
"API connection is not subscribed");
776#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
785 if (!api_connection->
send_message(this->connections_free_response_) && api_connection == this->api_connection_) {
788 ESP_LOGV(TAG,
"Connections-free update deferred, TCP buffer full");
798 call.connected = connected;
827 call.paired = paired;
843 this->pending_unpairing_.error() == CONN_OK) {
849 call.success = success;
875 call.success = success;
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.
const char * get_peername_to(std::span< char, socket::SOCKADDR_STR_LEN > buf) const
Get peer name (IP address) into caller-provided buffer, returns buf for convenience.
const char * get_name() const
bool send_message(const T &msg)
Returns false as soon as the TCP buffer is full.
std::array< uint64_t, BLUETOOTH_PROXY_MAX_CONNECTIONS > allocated
enums::BluetoothDeviceRequestType request_type
std::array< BluetoothLERawAdvertisement, BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE > advertisements
uint16_t advertisements_len
enums::BluetoothScannerMode mode
enums::BluetoothScannerState state
enums::BluetoothScannerMode configured_mode
uint64_t get_address() const
const char * address_str() const
void set_state(ClientState st)
uint8_t get_connection_index() const
void set_address(uint64_t address)
bluetooth_proxy::BluetoothProxy * proxy_
uint8_t connection_index_
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg)
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg)
void handle_gatt_not_connected_(uint64_t address, uint16_t handle, const char *action, const char *type)
AdvertisementFilter advertisement_filter_
void log_reply_deferred_(const char *what, uint64_t address)
A latched reply's leading edge; the drain's re-refusals stay quiet.
bool configured_scan_active_
void get_bluetooth_mac_address_pretty(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > output)
void on_raw_advertisement_(const ble_device_base::RawAdvertisement &raw)
bool adv_flush_toggle_
Wi-Fi only: flush on every other non-empty tick (~200 ms) so partial batches fill; an idle tick re-ar...
void log_not_connected_gatt_(const char *action, const char *type)
void send_scanner_state_(ble_device_base::ScannerState state)
void answer_device_disconnected_(uint64_t address)
Answer a request with connected=false.
void bluetooth_device_request(const api::BluetoothDeviceRequest &msg)
void send_device_unpairing(uint64_t address, bool success, conn_err_t error)
No default error: the drain rebuilds success as (error == CONN_OK), so a caller that omitted it would...
void replace_allocated_slot_(uint64_t find_value, uint64_t set_value)
PendingReply pending_unpairing_
bool send_gatt_services_done(uint64_t address)
Same convention as send_device_connection: false only on a refused frame.
void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg)
std::array< PendingReply, BLUETOOTH_PROXY_MAX_CONNECTIONS > pending_disconnections_
void bluetooth_scanner_set_mode(bool active)
void log_advertisement_flush_(bool sent)
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags)
void send_device_clear_cache(uint64_t address, bool success, conn_err_t error=CONN_OK)
void reset_connection_slot_(BluetoothConnection *connection, conn_err_t reason)
Free a connection slot after teardown: notify the API client and reset the streaming cursor.
void dump_config() override
void send_connections_free()
void unsubscribe_api_connection(api::APIConnection *api_connection)
void register_connection(BluetoothConnection *connection)
void send_device_pairing(uint64_t address, bool paired, conn_err_t error=CONN_OK)
bool scanner_state_pending_
ble_device_base::BLEHub * hub_
bool send_gatt_error(uint64_t address, uint16_t handle, conn_err_t error)
False only when the API refused the frame, so the reply is still owed.
api::BluetoothLERawAdvertisementsResponse response_
void send_device_disconnected_(uint64_t address, conn_err_t error=CONN_OK)
Send connected=false and pool it for the paced drain if refused.
void log_connection_info_(BluetoothConnection *connection, const char *message)
void reset_owed_replies_()
Drop everything the ending session was owed.
bool send_device_connection(uint64_t address, bool connected, uint16_t mtu=0, conn_err_t error=CONN_OK)
False only when a subscriber refused the frame; true = delivered or nobody subscribed.
void log_reply_displaced_(const char *what, uint64_t owed, uint64_t address)
A latched reply lost to a newer one for a different address.
BluetoothConnection * get_connection_(uint64_t address, bool reserve)
void bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg)
bool send_bluetooth_scanner_state_(ble_device_base::ScannerState state)
void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg)
bool connections_free_pending_
void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg)
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg)
void log_reply_dropped_(const char *what, uint64_t address)
Report a reply we deliberately do not latch, so no drop is silent.
void send_polled_scanner_state_()
uint32_t last_advertisement_flush_time_
void flush_pending_advertisements_()
Caller must ensure api_connection_ is non-null and API server is connected.
api::APIConnection * api_connection_
std::array< BluetoothConnection *, BLUETOOTH_PROXY_MAX_CONNECTIONS > connections_
void latch_pending_disconnection_(uint64_t address, conn_err_t error)
Pool a refused freed-slot notification for the paced drain.
api::BluetoothConnectionsFreeResponse connections_free_response_
void log_connection_request_ignored_(BluetoothConnection *connection, ClientState state)
uint8_t connection_count_
void log_slot_accounting_mismatch_()
void clear_pending_disconnection_(uint64_t address)
Drop any owed freed-slot notification for this address (client reconnected).
One owed address-keyed reply in a single word: 48-bit address low, 16-bit error on top.
constexpr uint64_t address() const
constexpr bool empty() const
constexpr bool matches(uint64_t address) const
constexpr void set(uint64_t address, conn_err_t error)
constexpr conn_err_t error() const
const LogString * message
@ BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR
@ BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE
@ BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT
@ BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR
@ BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE
@ BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE
@ BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT
@ BLUETOOTH_SCANNER_MODE_PASSIVE
@ BLUETOOTH_SCANNER_MODE_ACTIVE
@ BLUETOOTH_SCANNER_STATE_IDLE
@ BLUETOOTH_SCANNER_STATE_STOPPED
@ BLUETOOTH_SCANNER_STATE_STARTING
@ BLUETOOTH_SCANNER_STATE_STOPPING
@ BLUETOOTH_SCANNER_STATE_RUNNING
@ BLUETOOTH_SCANNER_STATE_FAILED
APIServer * global_api_server
const char * client_state_to_string(ClientState state)
ScannerState
Scanner lifecycle, wire-value aligned with the api enum so consumers cast directly (pinned by static_...
conn_err_t unpair_device(uint64_t address)
conn_err_t clear_gatt_cache(uint64_t address)
ESPHOME_LOG_TAG(TAG, "bluetooth_proxy")
BluetoothProxy * global_bluetooth_proxy
Application App
Global storage of Application pointer - only one Application can exist.
One raw advertisement as delivered by the controller — a borrowed view, valid only for the duration o...
bool is_set() const
A default-constructed slot is "no filter"; the proxy guards on this.
spi_device_handle_t handle