From 4fb7e945f86c143e69bb95e77213d45b2e278950 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 Dec 2023 16:59:24 -1000 Subject: [PATCH] Fix unexpected disconnects when outgoing buffer is full during keepalive (#5988) --- esphome/components/api/api_connection.cpp | 24 +++++++++++++++++++---- esphome/components/api/api_connection.h | 3 +++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index d5ab00a822..4ebfb7582e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -118,7 +118,9 @@ void APIConnection::loop() { this->list_entities_iterator_.advance(); this->initial_state_iterator_.advance(); - const uint32_t keepalive = 60000; + static uint32_t keepalive = 60000; + static uint8_t max_ping_retries = 60; + static uint16_t ping_retry_interval = 1000; const uint32_t now = millis(); if (this->sent_ping_) { // Disconnect if not responded within 2.5*keepalive @@ -126,10 +128,24 @@ void APIConnection::loop() { on_fatal_error(); ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str()); } - } else if (now - this->last_traffic_ > keepalive) { + } else if (now - this->last_traffic_ > keepalive && now > this->next_ping_retry_) { ESP_LOGVV(TAG, "Sending keepalive PING..."); - this->sent_ping_ = true; - this->send_ping_request(PingRequest()); + this->sent_ping_ = this->send_ping_request(PingRequest()); + if (!this->sent_ping_) { + this->next_ping_retry_ = now + ping_retry_interval; + this->ping_retries_++; + if (this->ping_retries_ >= max_ping_retries) { + on_fatal_error(); + ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(), + this->ping_retries_); + } else if (this->ping_retries_ >= 10) { + ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", + this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + } else { + ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms", + this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval); + } + } } #ifdef USE_ESP32_CAMERA diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 09b595bb71..9d01468807 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -140,6 +140,7 @@ class APIConnection : public APIServerConnection { void on_disconnect_response(const DisconnectResponse &value) override; void on_ping_response(const PingResponse &value) override { // we initiated ping + this->ping_retries_ = 0; this->sent_ping_ = false; } void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override; @@ -217,6 +218,8 @@ class APIConnection : public APIServerConnection { bool state_subscription_{false}; int log_subscription_{ESPHOME_LOG_LEVEL_NONE}; uint32_t last_traffic_; + uint32_t next_ping_retry_{0}; + uint8_t ping_retries_{0}; bool sent_ping_{false}; bool service_call_subscription_{false}; bool next_close_ = false;