From 26c000c41784e973a456c925cd63aa256aa8ed15 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Aug 2026 09:36:17 -0500 Subject: [PATCH] Collapse shim-era indirection left by the merge of the two halves - Fold the one-line event-handler forwarders, the state accessor renames, and the report helper into direct calls; drop the dead forward declaration and the tracker_client() accessor (the backend is the tracker client) - Hoist DEFAULT_ATT_MTU next to the other shared GATT constants and use it in all three engines - Emit BLUETOOTH_PROXY_MAX_CONNECTIONS once, in _connections_to_code - Drop the unused esp_gatt_common_api.h include and DOMAIN constant - Comment accuracy: stale shim/esp32-parity wording, registry roles, request_gatt_client() caller --- .../components/ble_device_base/__init__.py | 4 +- .../ble_device_base/ble_client_state.h | 3 + .../bluetooth_connection/__init__.py | 12 ++- .../bluetooth_connection_bluedroid.cpp | 73 ++++++++----------- .../bluetooth_connection_bluedroid.h | 16 +--- .../bluetooth_connection_hub.cpp | 7 +- .../bluetooth_connection_hub.h | 4 +- .../bluetooth_connection_rp2.h | 5 +- .../components/bluetooth_proxy/__init__.py | 22 +++--- 9 files changed, 59 insertions(+), 87 deletions(-) diff --git a/esphome/components/ble_device_base/__init__.py b/esphome/components/ble_device_base/__init__.py index fa66448867..c2520ba0cf 100644 --- a/esphome/components/ble_device_base/__init__.py +++ b/esphome/components/ble_device_base/__init__.py @@ -163,8 +163,8 @@ _request_gatt_connection_slot = cg.slot_counter(GATT_CLIENT_COUNT_DEFINE) def request_gatt_client() -> None: """Compile in the neutral GATT client contract (ble_gatt_client.h) and - claim one connection slot. Called by bluetooth_proxy once per connection - it instantiates on a hub platform.""" + claim one connection slot. Called by bluetooth_connection.new_gatt_backend() + once per backend instance.""" cg.add_define("USE_BLE_GATT_CLIENT") _request_gatt_connection_slot() diff --git a/esphome/components/ble_device_base/ble_client_state.h b/esphome/components/ble_device_base/ble_client_state.h index 013853b2e2..a8909d643d 100644 --- a/esphome/components/ble_device_base/ble_client_state.h +++ b/esphome/components/ble_device_base/ble_client_state.h @@ -22,6 +22,9 @@ static constexpr int GATT_ERR_NO_MEMORY = -2; /// delivers its disconnect completion. static constexpr uint32_t GATT_DISCONNECT_TIMEOUT_MS = 10000; +/// ATT MTU before negotiation completes (Bluetooth spec default). +static constexpr uint16_t DEFAULT_ATT_MTU = 23; + // Preferred connection parameters shared by every platform's GATT client so // the backends cannot drift (units: interval 1.25 ms, timeout 10 ms; latency // 0). FAST covers connection setup and service discovery; MEDIUM is the diff --git a/esphome/components/bluetooth_connection/__init__.py b/esphome/components/bluetooth_connection/__init__.py index 7523bd9050..e1b1b842b9 100644 --- a/esphome/components/bluetooth_connection/__init__.py +++ b/esphome/components/bluetooth_connection/__init__.py @@ -15,8 +15,6 @@ from esphome.const import PLATFORM_ESP32, PLATFORM_RP2, PlatformFramework from esphome.core import CORE from esphome.types import ConfigType -DOMAIN = "bluetooth_connection" - def AUTO_LOAD() -> list[str]: """ble_device_base plus the platform BLE stack the build's backend @@ -39,8 +37,8 @@ bluetooth_connection_ns = cg.esphome_ns.namespace("bluetooth_connection") # raising this needs an upstream change (the layer itself supports N). RP2_MAX_CONNECTIONS = 1 -# Hub platforms with a GATT backend, mapped to their slot limit — the single -# registry of which hub platforms run the connection-capable proxy. +# Slot limits for the hub platforms running the connection-capable proxy; +# the backend registry itself is _PLATFORM_BACKENDS below. HUB_MAX_CONNECTIONS: dict[str, int] = {PLATFORM_RP2: RP2_MAX_CONNECTIONS} # The hub-platform wrapper and the backend codegen classes. @@ -70,9 +68,9 @@ def _rp2_schema_fragment() -> cv.Schema: async def _esp32_register(backend: cg.MockObj, config: ConfigType) -> None: from esphome.components import esp32_ble_tracker - # The tracker's promote loop owns connect timing; the backend's - # tracker-facing shim registers as a raw client. - await esp32_ble_tracker.register_raw_client(backend.tracker_client(), config) + # The tracker's promote loop owns connect timing; the backend registers + # as a raw client (it is the tracker's ESPBTClient). + await esp32_ble_tracker.register_raw_client(backend, config) async def _rp2_register(backend: cg.MockObj, config: ConfigType) -> None: diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp index da784e0682..d8e1654b90 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp @@ -16,8 +16,6 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include - #include namespace esphome::bluetooth_connection { @@ -34,21 +32,12 @@ using esp32_ble_tracker::ClientState; using esp32_ble_tracker::ConnectionType; static constexpr uint16_t UNSET_CONN_ID = 0xFFFF; -// Wire default before any MTU exchange (Bluetooth spec ATT_MTU minimum). -static constexpr uint16_t DEFAULT_ATT_MTU = 23; // Bounds one characteristic's descriptor walk against a stack that never // reports end-of-range. static constexpr uint16_t MAX_DESCRIPTORS_PER_CHARACTERISTIC = 64; // ---- tracker surface ---- -bool BluedroidGattClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { - return this->handle_gattc_event_(event, gattc_if, param); -} -void BluedroidGattClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - this->handle_gap_event_(event, param); -} void BluedroidGattClient::connect() { this->tracker_connect_(); } void BluedroidGattClient::disconnect() { this->gatt_disconnect(); } @@ -62,10 +51,10 @@ void BluedroidGattClient::setup() { void BluedroidGattClient::loop() { if (!esp32_ble::global_ble->is_active()) { // Stack down: re-register the app on the next enable. - this->set_state_(ClientState::INIT); + this->set_state(ClientState::INIT); return; } - auto st = this->state_(); + auto st = this->state(); if (st == ClientState::INIT) { auto ret = esp_ble_gattc_app_register(this->app_id); if (ret) { @@ -73,7 +62,7 @@ void BluedroidGattClient::loop() { this->mark_failed(); } // Do not wait for REG_EVT; a dropped event must not wedge the slot. - this->set_state_(ClientState::IDLE); + this->set_state(ClientState::IDLE); } else if (st == ClientState::IDLE) { // The loop only drives the bootstrap and the disconnect safety timeout. this->disable_loop(); @@ -84,7 +73,7 @@ void BluedroidGattClient::loop() { // lost CLOSE/DISCONNECT would otherwise leak the table and the cache. this->release_services(); this->set_idle_(); - this->report_connection_state_(false, 0, ESP_GATT_CONN_TIMEOUT); + this->listener_->on_connection_state(false, 0, ESP_GATT_CONN_TIMEOUT); } } @@ -100,7 +89,7 @@ void BluedroidGattClient::dump_config() { int BluedroidGattClient::connect(uint64_t address, uint8_t addr_type) { // Only from idle: clobbering DISCONNECTING would open a new link the // stale CLOSE_EVT then tears down. - if (this->state_() != ClientState::IDLE) { + if (this->state() != ClientState::IDLE) { ESP_LOGW(TAG, "[%d] Connect rejected, slot busy", this->connection_index_); return ESP_GATT_BUSY; } @@ -108,12 +97,12 @@ int BluedroidGattClient::connect(uint64_t address, uint8_t addr_type) { this->remote_addr_type_ = addr_type; // Hand the request to the tracker's promote loop: it stops the scan, raises // coex, and calls tracker_connect_() - the tracker owns connect timing here. - this->set_state_(ClientState::DISCOVERED); + this->set_state(ClientState::DISCOVERED); return 0; } void BluedroidGattClient::tracker_connect_() { - auto st = this->state_(); + auto st = this->state(); if (st == ClientState::CONNECTING || st == ClientState::CONNECTED || st == ClientState::ESTABLISHED) { ESP_LOGW(TAG, "[%d] Connection already in progress", this->connection_index_); return; @@ -126,7 +115,7 @@ void BluedroidGattClient::tracker_connect_() { this->services_released_ = false; this->seen_mtu_ = false; this->enable_loop(); - this->set_state_(ClientState::CONNECTING); + this->set_state(ClientState::CONNECTING); if (this->connection_type_ == ConnectionType::V3_WITHOUT_CACHE) { // Fast params for the discovery phase; stepped down at SEARCH_CMPL. esp_ble_gap_set_prefer_conn_params(this->remote_bda_, FAST_MIN_CONN_INTERVAL, FAST_MAX_CONN_INTERVAL, 0, @@ -140,13 +129,13 @@ void BluedroidGattClient::tracker_connect_() { if (ret) { this->log_gattc_warning_("esp_ble_gattc_open", ret); // CONNECT_EVT never fired, so conn_id_ is legitimately unset: plain IDLE. - this->set_state_(ClientState::IDLE); - this->report_connection_state_(false, 0, ret); + this->set_state(ClientState::IDLE); + this->listener_->on_connection_state(false, 0, ret); } } int BluedroidGattClient::gatt_disconnect() { - auto st = this->state_(); + auto st = this->state(); if (st == ClientState::DISCONNECTING) { return 0; } @@ -157,7 +146,7 @@ int BluedroidGattClient::gatt_disconnect() { } if (st == ClientState::DISCOVERED) { // Parked for the tracker promote loop, never opened. - this->set_state_(ClientState::IDLE); + this->set_state(ClientState::IDLE); return ble_device_base::GATT_ERR_NOT_CONNECTED; } if (st == ClientState::CONNECTING || this->conn_id_ == UNSET_CONN_ID) { @@ -428,21 +417,17 @@ bool BluedroidGattClient::check_addr_(const esp_bd_addr_t &addr) const { } void BluedroidGattClient::set_idle_() { - this->set_state_(ClientState::IDLE); + this->set_state(ClientState::IDLE); this->conn_id_ = UNSET_CONN_ID; } void BluedroidGattClient::set_disconnecting_() { this->disconnecting_started_ = millis(); - this->set_state_(ClientState::DISCONNECTING); + this->set_state(ClientState::DISCONNECTING); // The loop may be disabled while idle; the safety timeout needs it. this->enable_loop(); } -void BluedroidGattClient::report_connection_state_(bool connected, uint16_t mtu, int error) { - this->listener_->on_connection_state(connected, mtu, error); -} - esp_err_t BluedroidGattClient::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout, const char *param_type) { esp_ble_conn_update_params_t conn_params = {{0}}; @@ -645,7 +630,7 @@ void BluedroidGattClient::stream_service_batch(BluetoothConnection &conn) { // ---- events ---- void BluedroidGattClient::handle_open_evt_(esp_ble_gattc_cb_param_t *param) { - auto st = this->state_(); + auto st = this->state(); if (st == ClientState::IDLE) { // IDF can deliver OPEN_EVT after esp_ble_gattc_open already returned an // error and the slot went IDLE; do not resurrect it. @@ -659,7 +644,7 @@ void BluedroidGattClient::handle_open_evt_(esp_ble_gattc_cb_param_t *param) { this->log_gattc_warning_("Connection open", param->open.status); // Never established, CLOSE_EVT may not follow. this->set_idle_(); - this->report_connection_state_(false, 0, param->open.status); + this->listener_->on_connection_state(false, 0, param->open.status); return; } if (this->disconnect_pending()) { @@ -667,17 +652,17 @@ void BluedroidGattClient::handle_open_evt_(esp_ble_gattc_cb_param_t *param) { this->unconditional_disconnect_(); return; } - this->set_state_(ClientState::CONNECTED); + this->set_state(ClientState::CONNECTED); ESP_LOGI(TAG, "[%d] Connection open", this->connection_index_); if (this->connection_type_ == ConnectionType::V3_WITH_CACHE) { - this->set_state_(ClientState::ESTABLISHED); + this->set_state(ClientState::ESTABLISHED); // No discovery phase: report immediately; the MTU report below is // suppressed by seen_mtu_ (HA tolerates a post-connect MTU of 23 here, // matching the previous esp32 behavior). this->seen_mtu_ = true; // Cached path never exchanged an MTU; HA has always seen the default. - this->report_connection_state_(true, DEFAULT_ATT_MTU, 0); - if (this->state_() != ClientState::DISCONNECTING) { + this->listener_->on_connection_state(true, ble_device_base::DEFAULT_ATT_MTU, 0); + if (this->state() != ClientState::DISCONNECTING) { // Settled; set_disconnecting_() re-enables the loop for the net. this->disable_loop(); } @@ -685,12 +670,12 @@ void BluedroidGattClient::handle_open_evt_(esp_ble_gattc_cb_param_t *param) { } void BluedroidGattClient::handle_disconnect_evt_(esp_ble_gattc_cb_param_t *param) { - if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER && this->state_() == ClientState::CONNECTED) { + if (param->disconnect.reason == ESP_GATT_CONN_TERMINATE_PEER_USER && this->state() == ClientState::CONNECTED) { ESP_LOGW(TAG, "[%d] Remote closed during discovery", this->connection_index_); } else { ESP_LOGD(TAG, "[%d] DISCONNECT_EVT reason=0x%02x", this->connection_index_, param->disconnect.reason); } - if (this->state_() == ClientState::IDLE) { + if (this->state() == ClientState::IDLE) { // Active close delivers CLOSE_EVT first; never walk back to DISCONNECTING. return; } @@ -702,7 +687,7 @@ void BluedroidGattClient::handle_disconnect_evt_(esp_ble_gattc_cb_param_t *param this->set_disconnecting_(); } -bool BluedroidGattClient::handle_gattc_event_(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, +bool BluedroidGattClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) return false; @@ -746,8 +731,8 @@ bool BluedroidGattClient::handle_gattc_event_(esp_gattc_cb_event_t event, esp_ga if (!this->seen_mtu_) { this->seen_mtu_ = true; // The connected report waited for the MTU; forwarded, not stored. - this->report_connection_state_(true, - param->cfg_mtu.status == ESP_GATT_OK ? param->cfg_mtu.mtu : DEFAULT_ATT_MTU, 0); + this->listener_->on_connection_state( + true, param->cfg_mtu.status == ESP_GATT_OK ? param->cfg_mtu.mtu : ble_device_base::DEFAULT_ATT_MTU, 0); } break; } @@ -764,16 +749,16 @@ bool BluedroidGattClient::handle_gattc_event_(esp_gattc_cb_event_t event, esp_ga this->set_idle_(); // The one connected=false report: the wrapper frees the slot on it, // so it must not fire before the controller finished closing. - this->report_connection_state_(false, 0, param->close.reason); + this->listener_->on_connection_state(false, 0, param->close.reason); break; } case ESP_GATTC_SEARCH_CMPL_EVT: { if (this->conn_id_ != param->search_cmpl.conn_id) return false; ESP_LOGI(TAG, "[%d] Service discovery complete", this->connection_index_); - this->set_state_(ClientState::ESTABLISHED); + this->set_state(ClientState::ESTABLISHED); this->handle_search_cmpl_(); - if (this->state_() != ClientState::DISCONNECTING) { + if (this->state() != ClientState::DISCONNECTING) { // Settled; a failed count started a teardown that needs the loop. this->disable_loop(); } @@ -820,7 +805,7 @@ bool BluedroidGattClient::handle_gattc_event_(esp_gattc_cb_event_t event, esp_ga return true; } -void BluedroidGattClient::handle_gap_event_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { +void BluedroidGattClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_SEC_REQ_EVT: { if (!this->check_addr_(param->ble_security.auth_cmpl.bd_addr)) diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h index b06d695cd2..58e9e49f77 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h @@ -2,10 +2,8 @@ // ble_device_base::BLEGattConnection alias for the hub BluetoothConnection // wrapper. Not a BLEClientBase: the tracker's promote loop owns // scan-stop/coex/one-connect-at-a-time, so the contract's connect() only -// parks the address in DISCOVERED and the real esp_ble_gattc_open happens in -// the tracker-invoked shim connect(). The shim exists because the tracker's -// ESPBTClient::disconnect() returns void while the contract's returns int - -// one class cannot carry both. +// parks the address in DISCOVERED; the real esp_ble_gattc_open happens in +// the tracker-invoked connect() override. #pragma once @@ -22,7 +20,6 @@ namespace esphome::bluetooth_connection { -class BluedroidGattClient; #ifdef USE_BLUETOOTH_PROXY class BluetoothConnection; #endif @@ -41,7 +38,6 @@ class BluedroidGattClient final : public esp32_ble_tracker::ESPBTClient, public // Wired by codegen before setup and invariant for the device lifetime. void set_listener(ble_device_base::GattClientListener *listener) { this->listener_ = listener; } - esp32_ble_tracker::ESPBTClient *tracker_client() { return this; } // ---- esp32_ble_tracker::ESPBTClient ---- bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -65,7 +61,8 @@ class BluedroidGattClient final : public esp32_ble_tracker::ESPBTClient, public int pair(); int update_connection_params(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout); // On-demand table for direct consumers; the proxy streams instead, so the - // materializer compiles only under USE_BLE_GATT_SERVICE_TABLE. + // materializer compiles only under USE_BLE_GATT_SERVICE_TABLE (emitted by + // direct-consumer codegen, never by the proxy). #ifdef USE_BLE_GATT_SERVICE_TABLE ble_device_base::GattServiceTable get_service_table(); #else @@ -83,19 +80,14 @@ class BluedroidGattClient final : public esp32_ble_tracker::ESPBTClient, public void set_connection_type(ble_device_base::ConnectionType ct) { this->connection_type_ = ct; } protected: - esp32_ble_tracker::ClientState state_() const { return this->state(); } - void set_state_(esp32_ble_tracker::ClientState st) { this->set_state(st); } bool check_addr_(const esp_bd_addr_t &addr) const; void tracker_connect_(); - bool handle_gattc_event_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); - void handle_gap_event_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void handle_open_evt_(esp_ble_gattc_cb_param_t *param); void handle_disconnect_evt_(esp_ble_gattc_cb_param_t *param); void handle_search_cmpl_(); void unconditional_disconnect_(); void set_idle_(); void set_disconnecting_(); - void report_connection_state_(bool connected, uint16_t mtu, int error); esp_err_t update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout, const char *param_type); int check_and_log_error_(const char *operation, esp_err_t err); diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp index 487ee45e7b..deb2fb692c 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp @@ -148,8 +148,7 @@ void BluetoothConnection::on_connection_state(bool connected, uint16_t mtu, int return; } // V3_WITHOUT_CACHE: discover services first — the connected response is - // sent when discovery completes, mirroring the esp32 flow (MTU + services - // before the response). + // sent when discovery completes (MTU + services before the response). this->state_ = ClientState::CONNECTED; int err = this->backend_->discover_services(); if (err != 0) { @@ -337,8 +336,8 @@ void BluetoothConnection::send_service_for_discovery_() { } // The subscriber vanished mid-stream: park the cursor at done WITHOUT - // sending services-done (esp32 parity — a resubscribing client gets - // silence and its 30 s timeout, never an authoritative partial list) and + // sending services-done (a resubscribing client gets silence and its + // 30 s timeout, never an authoritative partial list) and // free the table; the api-gone sweep tears the connection down anyway. auto *api_conn = this->proxy_->get_api_connection(); if (api_conn == nullptr) { diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_hub.h b/esphome/components/bluetooth_connection/bluetooth_connection_hub.h index 59aa1d6bb6..6801e90281 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_hub.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection_hub.h @@ -43,7 +43,7 @@ class BluetoothConnection final : public ble_device_base::GattClientListener { /// Start connecting: record the API address type (BLE_ADDR_TYPE_* code /// space) and open the connection through the backend. Failures report - /// through the same reset path a failed open takes on esp32. + /// through the same reset path a failed open takes. void initiate_connection(uint8_t address_type) { this->remote_addr_type_ = address_type; this->start_connect_(); @@ -126,7 +126,7 @@ class BluetoothConnection final : public ble_device_base::GattClientListener { // Group 2: 2-byte types int16_t send_service_{INIT_SENDING_SERVICES}; - uint16_t mtu_{23}; + uint16_t mtu_{ble_device_base::DEFAULT_ATT_MTU}; // Group 3: 8-byte and 4-byte types uint64_t address_{0}; diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_rp2.h b/esphome/components/bluetooth_connection/bluetooth_connection_rp2.h index 2bf88d7db9..5afb1b55fa 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_rp2.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection_rp2.h @@ -92,8 +92,7 @@ class RP2GattClient final : public Component, public Parented list[str]: # Assistant) assumes an ESPHome proxy can scan actively, so a passive-only # proxy would be misdriven — bk72xx follows once the API carries a feature # flag clients can trust (FEATURE_ACTIVE_SCAN + a version flag, separate PRs). -# Coupled to bluetooth_connection: platforms with a GATT backend are also -# listed in its HUB_MAX_CONNECTIONS and its FILTER_SOURCE_FILES hub entry. +# Coupled to bluetooth_connection: platforms here are also listed in its +# _PLATFORM_BACKENDS registry, HUB_MAX_CONNECTIONS, and FILTER_SOURCE_FILES +# hub entry. _HUB_PLATFORMS = (PLATFORM_LN882X, PLATFORM_RP2) DEPENDENCIES = ["api"] @@ -200,7 +201,12 @@ def _rp2_config_schema() -> cv.All: async def _connections_to_code(var: cg.MockObj, config: ConfigType) -> None: """One wrapper + backend pair per slot; the platform-specific backend registration lives in bluetooth_connection.new_gatt_backend().""" - for connection_conf in config.get(CONF_CONNECTIONS, []): + connections = config.get(CONF_CONNECTIONS, []) + # The api component sizes BluetoothConnectionsFreeResponse.allocated with + # this define whenever a proxy is present (zero on advertisement-only + # hubs); sized here so it can never diverge from the loop below. + cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", len(connections)) + for connection_conf in connections: backend = await bluetooth_connection.new_gatt_backend(connection_conf) connection = cg.new_Pvariable(connection_conf[CONF_ID]) cg.add(connection.set_backend(backend)) @@ -361,10 +367,6 @@ async def _to_code_esp32(config: ConfigType) -> None: # registration into the proxy; the other hubs are polled instead. cg.add_define("USE_BLE_SCANNER_STATE_CALLBACK") - # Define max connections for protobuf fixed array - connection_count = len(config.get(CONF_CONNECTIONS, [])) - cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", connection_count) - await _connections_to_code(var, config) if config.get(CONF_CACHE_SERVICES): @@ -379,12 +381,6 @@ async def _to_code_ble_hub(config: ConfigType) -> None: hub = await cg.get_variable(config[ble_device_base.CONF_BLE_HUB_ID]) cg.add(var.set_ble_hub(hub)) - # The api component sizes BluetoothConnectionsFreeResponse.allocated with - # this define whenever a proxy is present. Zero on advertisement-only hubs. - # Sized from the instantiated connections so the define can never diverge - # from the loop below (the define sizes fixed storage in the proxy). - slots = len(config.get(CONF_CONNECTIONS, ())) - cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", slots) await _connections_to_code(var, config)