diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 4168761c74..897cf8c41f 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -52,11 +52,6 @@ void APIServer::setup() { #endif #endif - // Schedule reboot if no clients connect within timeout - if (this->reboot_timeout_ != 0) { - this->schedule_reboot_timeout_(); - } - this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections if (this->socket_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); @@ -112,16 +107,6 @@ void APIServer::setup() { #endif } -void APIServer::schedule_reboot_timeout_() { - this->status_set_warning(); - this->set_timeout("api_reboot", this->reboot_timeout_, []() { - if (!global_api_server->is_connected()) { - ESP_LOGE(TAG, "No clients; rebooting"); - App.reboot(); - } - }); -} - void APIServer::loop() { // Accept new clients only if the socket exists and has incoming connections if (this->socket_ && this->socket_->ready()) { @@ -147,15 +132,24 @@ void APIServer::loop() { this->clients_.emplace_back(conn); conn->start(); - // Clear warning status and cancel reboot when first client connects + // First client connected - clear warning and update timestamp if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) { this->status_clear_warning(); - this->cancel_timeout("api_reboot"); + this->last_connected_ = App.get_loop_component_start_time(); } } } if (this->clients_.empty()) { + // Check reboot timeout - done in loop to avoid scheduler heap churn + // (cancelled scheduler items sit in heap memory until their scheduled time) + if (this->reboot_timeout_ != 0) { + const uint32_t now = App.get_loop_component_start_time(); + if (now - this->last_connected_ > this->reboot_timeout_) { + ESP_LOGE(TAG, "No client connected; rebooting"); + App.reboot(); + } + } return; } @@ -194,9 +188,10 @@ void APIServer::loop() { } this->clients_.pop_back(); - // Schedule reboot when last client disconnects + // Last client disconnected - set warning and start tracking for reboot timeout if (this->clients_.empty() && this->reboot_timeout_ != 0) { - this->schedule_reboot_timeout_(); + this->status_set_warning(); + this->last_connected_ = App.get_loop_component_start_time(); } // Don't increment client_index since we need to process the swapped element } diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 3089bb1d35..eb495afde7 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -202,7 +202,6 @@ class APIServer : public Component, #endif protected: - void schedule_reboot_timeout_(); #ifdef USE_API_NOISE bool update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, const LogString *fail_log_msg, const psk_t &active_psk, bool make_active); @@ -218,6 +217,7 @@ class APIServer : public Component, // 4-byte aligned types uint32_t reboot_timeout_{300000}; + uint32_t last_connected_{0}; // Vectors and strings (12 bytes each on 32-bit) std::vector> clients_;