From e8aa7cff369ae76995ba7176c2c00ad6d0d886ca Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 Jun 2025 05:08:23 -0500 Subject: [PATCH] Improve shutdown reliability when tx buffer is full (#9043) --- esphome/components/api/api_connection.cpp | 8 ++++++++ esphome/components/api/api_connection.h | 4 ++++ esphome/components/api/api_server.cpp | 8 ++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9ef00546c8..93ba9248b4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1886,6 +1886,12 @@ uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnectio return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single); } +uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single) { + DisconnectRequest req; + return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single); +} + uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { // Use generated ESTIMATED_SIZE constants from each message type switch (message_type) { @@ -2021,6 +2027,8 @@ uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { return ListEntitiesServicesResponse::ESTIMATED_SIZE; case ListEntitiesDoneResponse::MESSAGE_TYPE: return ListEntitiesDoneResponse::ESTIMATED_SIZE; + case DisconnectRequest::MESSAGE_TYPE: + return DisconnectRequest::ESTIMATED_SIZE; default: // Fallback for unknown message types return 24; diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 6e85aba298..34c7dcd880 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -426,6 +426,10 @@ class APIConnection : public APIServerConnection { static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); + // Method for DisconnectRequest batching + static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, + bool is_single); + // Helper function to get estimated message size for buffer pre-allocation static uint16_t get_estimated_message_size(uint16_t message_type); diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 378aa63981..17c83c54f1 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -496,11 +496,15 @@ void APIServer::on_shutdown() { this->socket_ = nullptr; } + // Change batch delay to 5ms for quick flushing during shutdown + this->batch_delay_ = 5; + // Send disconnect requests to all connected clients for (auto &c : this->clients_) { if (!c->send_message(DisconnectRequest())) { - // If we can't send the disconnect request, mark for immediate closure - c->next_close_ = true; + // If we can't send the disconnect request directly (tx_buffer full), + // schedule it in the batch so it will be sent with the 5ms timer + c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE); } } }