diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 526f7f4b42..329349b531 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -384,26 +384,32 @@ void ESP32ImprovComponent::check_wifi_connection_() { this->connecting_sta_ = {}; this->cancel_timeout("wifi-connect-timeout"); - std::vector urls; + // Build URL list with minimal allocations + // Maximum 3 URLs: custom next_url + ESPHOME_MY_LINK + webserver URL + std::string url_strings[3]; + size_t url_count = 0; // Add next_url if configured (should be first per Improv BLE spec) std::string next_url = this->get_formatted_next_url_(); if (!next_url.empty()) { - urls.push_back(next_url); + url_strings[url_count++] = std::move(next_url); } // Add default URLs for backward compatibility - urls.emplace_back(ESPHOME_MY_LINK); + url_strings[url_count++] = ESPHOME_MY_LINK; #ifdef USE_WEBSERVER for (auto &ip : wifi::global_wifi_component->wifi_sta_ip_addresses()) { if (ip.is_ip4()) { - std::string webserver_url = "http://" + ip.str() + ":" + to_string(USE_WEBSERVER_PORT); - urls.push_back(webserver_url); + char url_buffer[64]; + snprintf(url_buffer, sizeof(url_buffer), "http://%s:%d", ip.str().c_str(), USE_WEBSERVER_PORT); + url_strings[url_count++] = url_buffer; break; } } #endif - std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls); + // Pass to build_rpc_response using vector constructor from iterators to avoid extra copies + std::vector data = improv::build_rpc_response( + improv::WIFI_SETTINGS, std::vector(url_strings, url_strings + url_count)); this->send_response_(data); } else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) { ESP_LOGD(TAG, "WiFi provisioned externally"); diff --git a/esphome/components/improv_base/improv_base.cpp b/esphome/components/improv_base/improv_base.cpp index 89ee5492b5..233098e6cd 100644 --- a/esphome/components/improv_base/improv_base.cpp +++ b/esphome/components/improv_base/improv_base.cpp @@ -6,6 +6,21 @@ namespace esphome { namespace improv_base { +static constexpr const char DEVICE_NAME_PLACEHOLDER[] = "{{device_name}}"; +static constexpr size_t DEVICE_NAME_PLACEHOLDER_LEN = sizeof(DEVICE_NAME_PLACEHOLDER) - 1; +static constexpr const char IP_ADDRESS_PLACEHOLDER[] = "{{ip_address}}"; +static constexpr size_t IP_ADDRESS_PLACEHOLDER_LEN = sizeof(IP_ADDRESS_PLACEHOLDER) - 1; + +static void replace_all_in_place(std::string &str, const char *placeholder, size_t placeholder_len, + const std::string &replacement) { + size_t pos = 0; + const size_t replacement_len = replacement.length(); + while ((pos = str.find(placeholder, pos)) != std::string::npos) { + str.replace(pos, placeholder_len, replacement); + pos += replacement_len; + } +} + std::string ImprovBase::get_formatted_next_url_() { if (this->next_url_.empty()) { return ""; @@ -14,28 +29,15 @@ std::string ImprovBase::get_formatted_next_url_() { std::string formatted_url = this->next_url_; // Replace all occurrences of {{device_name}} - const std::string device_name_placeholder = "{{device_name}}"; - const std::string &device_name = App.get_name(); - size_t pos = 0; - while ((pos = formatted_url.find(device_name_placeholder, pos)) != std::string::npos) { - formatted_url.replace(pos, device_name_placeholder.length(), device_name); - pos += device_name.length(); - } + replace_all_in_place(formatted_url, DEVICE_NAME_PLACEHOLDER, DEVICE_NAME_PLACEHOLDER_LEN, App.get_name()); // Replace all occurrences of {{ip_address}} - const std::string ip_address_placeholder = "{{ip_address}}"; - std::string ip_address_str; for (auto &ip : network::get_ip_addresses()) { if (ip.is_ip4()) { - ip_address_str = ip.str(); + replace_all_in_place(formatted_url, IP_ADDRESS_PLACEHOLDER, IP_ADDRESS_PLACEHOLDER_LEN, ip.str()); break; } } - pos = 0; - while ((pos = formatted_url.find(ip_address_placeholder, pos)) != std::string::npos) { - formatted_url.replace(pos, ip_address_placeholder.length(), ip_address_str); - pos += ip_address_str.length(); - } // Note: {{esphome_version}} is replaced at code generation time in Python