diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 95b87776e3..a1c38cbfaf 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -67,6 +67,10 @@ void BLEClientBase::loop() { (millis() - this->disconnecting_started_) > DISCONNECTING_TIMEOUT) { ESP_LOGE(TAG, "[%d] [%s] Timeout waiting for CLOSE_EVT after disconnect, forcing IDLE", this->connection_index_, this->address_str_); + // release_services() must be called before set_idle_() — if we entered DISCONNECTING + // via unconditional_disconnect() (which doesn't call release_services()), and ESP-IDF + // never delivered CLOSE_EVT/DISCONNECT_EVT, services would leak without this call. + this->release_services(); this->set_idle_(); } } @@ -230,6 +234,7 @@ void BLEClientBase::log_connection_params_(const char *param_type) { void BLEClientBase::handle_connection_result_(esp_err_t ret) { if (ret) { this->log_gattc_warning_("esp_ble_gattc_open", ret); + // Don't use set_idle_() here — CONNECT_EVT never fired so conn_id_ is still UNSET_CONN_ID. this->set_state(espbt::ClientState::IDLE); } } diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index b7fc772ba0..7bc72ae765 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -120,7 +120,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { uint16_t conn_id_{UNSET_CONN_ID}; uint16_t mtu_{23}; - // Group 6: 1-byte types and small enums + // Group 7: 1-byte types and small enums esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC}; espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; uint8_t connection_index_;