3#ifdef USE_BLUETOOTH_PROXY
17static const char *
const TAG =
"bluetooth_proxy";
23static_assert(
sizeof(((api::BluetoothLERawAdvertisement *)
nullptr)->data) == 62,
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
98 adv.address =
raw.address;
100 adv.address_type =
raw.addr_type;
101 uint8_t
length =
raw.data_len >
sizeof(adv.data) ?
sizeof(adv.data) :
static_cast<uint8_t
>(
raw.data_len);
107 ESP_LOGVV(TAG,
"Queuing raw packet from %012" PRIX64
", length %d. RSSI: %d dB",
raw.address,
length,
raw.rssi);
115#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
117 ESP_LOGW(TAG,
"[%d] [%s] Connection request ignored, state: %s", connection->
get_connection_index(),
126#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
128 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" dropped, TCP buffer full", what,
address);
132 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" deferred, TCP buffer full", what,
address);
136 ESP_LOGW(TAG,
"%s reply for %012" PRIX64
" dropped, displaced by %012" PRIX64, what, owed,
address);
140 ESP_LOGW(TAG,
"Cannot %s GATT %s, not connected", action,
type);
167 char mac_str[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
169 const char *mac_out = mac_str[0] !=
'\0' ? mac_str :
"unavailable (adapter not up yet)";
171#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
176 " Configured scan: %s\n"
182 " Mode: advertisement-only (no GATT connections)\n"
183 " Configured scan: %s\n"
189#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
195 ESP_LOGE(TAG,
"Connection registry full, dropping registration");
201 connection->
proxy_ =
this;
208 if (slot == find_value) {
215 ESP_LOGW(TAG,
"Connection slot accounting mismatch (find 0x%llx)", (
unsigned long long) find_value);
227 if (free_entry ==
nullptr && owed.
empty()) {
231 if (free_entry !=
nullptr) {
292 uint64_t conn_addr = connection->get_address();
302 if (free_slot ==
nullptr && conn_addr == 0)
303 free_slot = connection;
305 if (!reserve || free_slot ==
nullptr)
323 if (connection ==
nullptr) {
324 ESP_LOGW(TAG,
"No free connections available");
329 ESP_LOGE(TAG,
"[%d] [%s] Missing address type in connect request", connection->get_connection_index(),
330 connection->address_str());
334 if (connection->state() == ClientState::CONNECTED || connection->state() == ClientState::ESTABLISHED) {
336 connection->send_connected_reply_();
339 }
else if (connection->state() == ClientState::DISCONNECTING && connection->cancel_teardown()) {
340 ESP_LOGW(TAG,
"[%d] [%s] Connection request while pending disconnect, cancelling pending disconnect",
341 connection->get_connection_index(), connection->address_str());
343 }
else if (connection->state() != ClientState::INIT) {
356 connection->initiate_connection(
static_cast<uint8_t
>(msg.
address_type));
362 if (connection ==
nullptr) {
367 if (connection->state() != ClientState::IDLE) {
368 connection->disconnect();
370 connection->set_address(0);
380 if (connection !=
nullptr) {
381 if (!connection->is_paired()) {
382 auto err = connection->pair();
383 if (err != CONN_OK) {
397 if (
ret == CONN_OK) {
401 if (connection !=
nullptr) {
402 connection->set_unpaired();
414 ESP_LOGE(TAG,
"V1 connections removed");
423 if (connection ==
nullptr) {
428 auto err = connection->read_characteristic(msg.
handle);
429 if (err != CONN_OK) {
430 connection->send_gatt_error_(msg.
handle, err);
436 if (connection ==
nullptr) {
442 if (err != CONN_OK) {
443 connection->send_gatt_error_(msg.
handle, err);
449 if (connection ==
nullptr) {
454 auto err = connection->read_descriptor(msg.
handle);
455 if (err != CONN_OK) {
456 connection->send_gatt_error_(msg.
handle, err);
462 if (connection ==
nullptr) {
468 if (err != CONN_OK) {
469 connection->send_gatt_error_(msg.
handle, err);
475 if (connection ==
nullptr || !connection->connected()) {
479 if (!connection->has_gatt_services()) {
480 ESP_LOGW(TAG,
"[%d] [%s] No GATT services found", connection->get_connection_index(), connection->address_str());
484 connection->send_services_done_();
487 if (connection->send_service_ > 0) {
492 ESP_LOGD(TAG,
"[%d] [%s] GetServices mid-stream, restarting", connection->get_connection_index(),
493 connection->address_str());
494 connection->send_service_ = 0;
497 if (connection->send_service_ == SERVICES_DONE_PENDING) {
502 ESP_LOGW(TAG,
"[%d] [%s] GetServices superseded an undelivered done; client timeout will retry",
503 connection->get_connection_index(), connection->address_str());
504 connection->send_service_ = DONE_SENDING_SERVICES;
507 if (connection->send_service_ == INIT_SENDING_SERVICES)
508 connection->send_service_ = 0;
513 if (connection ==
nullptr) {
518 auto err = connection->notify_characteristic(msg.
handle, msg.
enable);
519 if (err != CONN_OK) {
520 connection->send_gatt_error_(msg.
handle, err);
534 if (connection ==
nullptr || !connection->connected()) {
535 ESP_LOGW(TAG,
"[%d] [%s] Cannot set connection params, not connected",
536 connection ?
static_cast<int>(connection->get_connection_index()) : -1,
537 connection ? connection->address_str() :
"unknown");
538 resp.
error = GATT_NOT_CONNECTED;
547 constexpr uint32_t max_val = std::numeric_limits<uint16_t>::max();
548 resp.
error = connection->update_connection_params(
static_cast<uint16_t
>(std::min(msg.
min_interval, max_val)),
549 static_cast<uint16_t
>(std::min(msg.
max_interval, max_val)),
550 static_cast<uint16_t
>(std::min(msg.
latency, max_val)),
551 static_cast<uint16_t
>(std::min(msg.
timeout, max_val)));
564 if (this->
hub_->get_scan_active() == active) {
567 ESP_LOGD(TAG,
"Setting scanner mode to %s", active ?
"active" :
"passive");
568 this->
hub_->set_scan_active(active);
569 this->
hub_->stop_scan();
570 this->
hub_->set_scan_continuous(
577 if (this->
hub_->scan_active() != active) {
578 ESP_LOGD(TAG,
"Setting scanner mode to %s", active ?
"active" :
"passive");
579 if (!this->
hub_->request_scan_mode(active)) {
583 ESP_LOGW(TAG,
"Scanner mode %s not supported by this tracker", active ?
"active" :
"passive");
586#ifndef USE_BLE_SCANNER_STATE_CALLBACK
600#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
614#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
626#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
631 if (connection->get_address() != 0) {
632 connection->disconnect();
639#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
664#ifdef USE_BLE_SCANNER_STATE_CALLBACK
672 if (this->
hub_->scan_running() != this->last_scan_running_) {
701#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
704#ifdef USE_BLE_SCANNER_STATE_CALLBACK
715#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
722 connection->park_service_stream_();
723 connection->clear_owed_flags_();
736 char old_peername[socket::SOCKADDR_STR_LEN];
737 char new_peername[socket::SOCKADDR_STR_LEN];
738 ESP_LOGW(TAG,
"Subscription from %s (%s) replaces %s (%s)", api_connection->
get_name(),
739 api_connection->
get_peername_to(new_peername), this->api_connection_->get_name(),
740 this->api_connection_->get_peername_to(old_peername));
747#ifdef USE_BLE_SCANNER_STATE_CALLBACK
757 ESP_LOGV(TAG,
"API connection is not subscribed");
764#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS
773 if (!api_connection->
send_message(this->connections_free_response_) && api_connection == this->api_connection_) {
776 ESP_LOGV(TAG,
"Connections-free update deferred, TCP buffer full");
786 call.connected = connected;
815 call.paired = paired;
831 this->pending_unpairing_.error() == CONN_OK) {
837 call.success = success;
863 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)
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)
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...
spi_device_handle_t handle