diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 949262098f..461a8a8c3a 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -639,6 +639,14 @@ bool APIServer::teardown() { #define USE_API_ACTION_CALL_TIMEOUT_MS 30000 // NOLINT #endif +// SSO-friendly action call key - hex format guarantees max 11 chars ("ac_ffffffff") +// which fits in any std::string SSO buffer (typically 12-15 bytes) +static inline std::string make_action_call_key(uint32_t id) { + char buf[12]; + size_t len = snprintf(buf, sizeof(buf), "ac_%x", id); + return std::string(buf, len); +} + uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConnection *conn) { uint32_t action_call_id = this->next_action_call_id_++; // Handle wraparound (skip 0 as it means "no call") @@ -648,18 +656,17 @@ uint32_t APIServer::register_active_action_call(uint32_t client_call_id, APIConn this->active_action_calls_.push_back({action_call_id, client_call_id, conn}); // Schedule automatic cleanup after timeout (client will have given up by then) - this->set_timeout(str_sprintf("action_call_%u", action_call_id), USE_API_ACTION_CALL_TIMEOUT_MS, - [this, action_call_id]() { - ESP_LOGD(TAG, "Action call %u timed out", action_call_id); - this->unregister_active_action_call(action_call_id); - }); + this->set_timeout(make_action_call_key(action_call_id), USE_API_ACTION_CALL_TIMEOUT_MS, [this, action_call_id]() { + ESP_LOGD(TAG, "Action call %u timed out", action_call_id); + this->unregister_active_action_call(action_call_id); + }); return action_call_id; } void APIServer::unregister_active_action_call(uint32_t action_call_id) { // Cancel the timeout for this action call - this->cancel_timeout(str_sprintf("action_call_%u", action_call_id)); + this->cancel_timeout(make_action_call_key(action_call_id)); // Swap-and-pop is more efficient than remove_if for unordered vectors for (size_t i = 0; i < this->active_action_calls_.size(); i++) { @@ -676,7 +683,7 @@ void APIServer::unregister_active_action_calls_for_connection(APIConnection *con for (size_t i = 0; i < this->active_action_calls_.size();) { if (this->active_action_calls_[i].connection == conn) { // Cancel the timeout for this action call - this->cancel_timeout(str_sprintf("action_call_%u", this->active_action_calls_[i].action_call_id)); + this->cancel_timeout(make_action_call_key(this->active_action_calls_[i].action_call_id)); std::swap(this->active_action_calls_[i], this->active_action_calls_.back()); this->active_action_calls_.pop_back();