diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index e4cc0cb12a..c5281c5b4f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -206,6 +206,11 @@ BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool rese return connection; if (reserve && conn_addr == 0) { + if (address == this->pending_disconnection_address_) { + // The client re-requested this address: it already acted on the + // disconnect, and a late resend would shadow the new connection. + this->pending_disconnection_address_ = 0; + } connection->send_service_ = INIT_SENDING_SERVICES; connection->set_address(address); // All connections must start at INIT @@ -487,6 +492,13 @@ void BluetoothProxy::loop() { return; this->last_advertisement_flush_time_ = now; + if (this->pending_disconnection_address_ != 0 && this->api_connection_ != nullptr) { + // Paced resend of a dropped freed-slot notification; a repeat failure + // re-latches through send_device_connection itself. + uint64_t addr = this->pending_disconnection_address_; + this->pending_disconnection_address_ = 0; + this->send_device_connection(addr, false, 0, this->pending_disconnection_error_); + } if (this->connections_free_pending_ && this->api_connection_ != nullptr) { // Resend a dropped slot-state update, paced by the 100 ms gate so the // retry does not hammer the congestion it exists to survive; the @@ -633,7 +645,12 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui call.connected = connected; call.mtu = mtu; call.error = error; - this->api_connection_->send_message(call); + if (!this->api_connection_->send_message(call) && !connected) { + // The freed-slot notification must eventually arrive; a lost + // connected=true is covered by the client's own connect timeout. + this->pending_disconnection_address_ = address; + this->pending_disconnection_error_ = error; + } } void BluetoothProxy::send_connections_free() { if (this->api_connection_ != nullptr) { diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 26f99fcca2..584f390f35 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -251,6 +251,10 @@ class BluetoothProxy final : public Component { // Pre-allocated response message - always ready to send api::BluetoothConnectionsFreeResponse connections_free_response_; + // One-deep retry latch for a dropped connected=false notification (the + // freed-slot half of the pair the connections-free retry covers). + uint64_t pending_disconnection_address_{0}; + conn_err_t pending_disconnection_error_{0}; // Group 4: 1-byte types grouped together bool active_;