From 6629de81b8ad649e7fe7da8aa4b793218f3efc55 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Sep 2026 07:56:27 -0500 Subject: [PATCH] [esp32_ble_client] Settle a live link and clear the interface before the stack goes down Without the replayed close events a connected ble_client kept its services across a ble.disable cycle and grew a second copy on reconnect. Free them and report the disconnect from the hook, forget the old interface, and refuse an open before the app is registered on the new stack, matching the proxy backend. --- .../esp32_ble_client/ble_client_base.cpp | 15 +++++++++++++++ .../components/esp32_ble_client/ble_client_base.h | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 8b4e4348b0..e290f9f2dd 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -73,6 +73,15 @@ void BLEClientBase::loop() { float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } void BLEClientBase::ble_before_disabled_event_handler() { + auto st = this->state(); + if (st != espbt::ClientState::IDLE && st != espbt::ClientState::INIT) { + // No CLOSE_EVT will come: free the services and settle the link. + this->release_services(); + this->set_idle_(); + this->on_disconnect_complete(ESP_GATT_CONN_TERMINATE_LOCAL_HOST); + } + // The interface belongs to the torn-down stack. + this->gattc_if_ = ESP_GATT_IF_NONE; this->set_state(espbt::ClientState::INIT); // An idle client runs no loop; the INIT branch must run to register again. this->enable_loop(); @@ -123,6 +132,12 @@ void BLEClientBase::connect() { this->connection_index_, this->address_str_); return; } + if (this->gattc_if_ == ESP_GATT_IF_NONE) { + // Bluedroid drops an open on an unknown interface without any event. + ESP_LOGW(TAG, "[%d] [%s] Connect rejected, GATT app not registered", this->connection_index_, this->address_str_); + this->set_state(espbt::ClientState::IDLE); + return; + } ESP_LOGI(TAG, "[%d] [%s] 0x%02x Connecting", this->connection_index_, this->address_str_, this->remote_addr_type_); this->paired_ = false; // A registration whose event never arrived must not block this connection's release. diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index dd0958d2b8..01c3de0c8b 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -115,7 +115,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { #endif // Group 3: 4-byte types - int gattc_if_; + int gattc_if_{ESP_GATT_IF_NONE}; esp_gatt_status_t status_{ESP_GATT_OK}; // Group 4: Arrays