From 8137d7600a30792f755ef018591d0ef25c0894d8 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 1 Oct 2025 14:26:25 -0400 Subject: [PATCH 01/11] [rtttl] Fix warning (#10972) --- esphome/components/rtttl/rtttl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 5aedc74489..2c48105490 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -374,7 +374,7 @@ void Rtttl::loop() { this->last_note_ = millis(); } -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE static const LogString *state_to_string(State state) { switch (state) { case STATE_STOPPED: From 638c6cc14e2d64fc52ebdc29d76daf1f16ce92d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 20:26:47 +0200 Subject: [PATCH 02/11] [api] Reduce flash usage in user services by eliminating vector copy (#10971) --- esphome/components/api/user_services.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 5f040e8433..dba2d055bf 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -55,7 +55,7 @@ template class UserServiceBase : public UserServiceDescriptor { protected: virtual void execute(Ts... x) = 0; - template void execute_(std::vector args, seq type) { + template void execute_(const std::vector &args, seq type) { this->execute((get_execute_arg_value(args[S]))...); } From a9dc0628c42d3539e2e0ea5555c5d9a4afeb135a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:21:58 +0200 Subject: [PATCH 03/11] [mdns][openthread] Use std::array for mdns services and remove unnecessary copy --- esphome/components/mdns/__init__.py | 30 ++++++++++-- esphome/components/mdns/mdns_component.cpp | 50 +++++++------------- esphome/components/mdns/mdns_component.h | 16 ++++--- esphome/components/openthread/openthread.cpp | 11 +++-- esphome/components/openthread/openthread.h | 1 - 5 files changed, 58 insertions(+), 50 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index a84fe5a249..f023f84750 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -91,12 +91,36 @@ async def to_code(config): cg.add_define("USE_MDNS") + # Calculate compile-time service count + service_count = 0 + + # Check if API component is enabled (it may create a service at runtime) + if cg.is_defined("USE_API"): + service_count += 1 + + # Check for prometheus + if cg.is_defined("USE_PROMETHEUS"): + service_count += 1 + + # Check for web_server + if cg.is_defined("USE_WEBSERVER"): + service_count += 1 + + # Count extra services from config + extra_services_count = len(config[CONF_SERVICES]) + if extra_services_count > 0: + service_count += extra_services_count + cg.add_define("USE_MDNS_EXTRA_SERVICES") + + # Ensure at least 1 service (fallback service) + if service_count == 0: + service_count = 1 + + cg.add_define("MDNS_SERVICE_COUNT", service_count) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - if config[CONF_SERVICES]: - cg.add_define("USE_MDNS_EXTRA_SERVICES") - for service in config[CONF_SERVICES]: txt = [ cg.StructInitializer( diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 5d9788198f..5a5286c5bc 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -73,33 +73,11 @@ MDNS_STATIC_CONST_CHAR(NETWORK_THREAD, "thread"); void MDNSComponent::compile_records_() { this->hostname_ = App.get_name(); - - // Calculate exact capacity needed for services vector - size_t services_count = 0; -#ifdef USE_API - if (api::global_api_server != nullptr) { - services_count++; - } -#endif -#ifdef USE_PROMETHEUS - services_count++; -#endif -#ifdef USE_WEBSERVER - services_count++; -#endif -#ifdef USE_MDNS_EXTRA_SERVICES - services_count += this->services_extra_.size(); -#endif - // Reserve for fallback service if needed - if (services_count == 0) { - services_count = 1; - } - this->services_.reserve(services_count); + this->services_count_ = 0; #ifdef USE_API if (api::global_api_server != nullptr) { - this->services_.emplace_back(); - auto &service = this->services_.back(); + auto &service = this->services_[this->services_count_++]; service.service_type = MDNS_STR(SERVICE_ESPHOMELIB); service.proto = MDNS_STR(SERVICE_TCP); service.port = api::global_api_server->get_port(); @@ -178,30 +156,29 @@ void MDNSComponent::compile_records_() { #endif // USE_API #ifdef USE_PROMETHEUS - this->services_.emplace_back(); - auto &prom_service = this->services_.back(); + auto &prom_service = this->services_[this->services_count_++]; prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS); prom_service.proto = MDNS_STR(SERVICE_TCP); prom_service.port = USE_WEBSERVER_PORT; #endif #ifdef USE_WEBSERVER - this->services_.emplace_back(); - auto &web_service = this->services_.back(); + auto &web_service = this->services_[this->services_count_++]; web_service.service_type = MDNS_STR(SERVICE_HTTP); web_service.proto = MDNS_STR(SERVICE_TCP); web_service.port = USE_WEBSERVER_PORT; #endif #ifdef USE_MDNS_EXTRA_SERVICES - this->services_.insert(this->services_.end(), this->services_extra_.begin(), this->services_extra_.end()); + for (const auto &extra_service : this->services_extra_) { + this->services_[this->services_count_++] = extra_service; + } #endif #if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES) // Publish "http" service if not using native API or any other services // This is just to have *some* mDNS service so that .local resolution works - this->services_.emplace_back(); - auto &fallback_service = this->services_.back(); + auto &fallback_service = this->services_[this->services_count_++]; fallback_service.service_type = "_http"; fallback_service.proto = "_tcp"; fallback_service.port = USE_WEBSERVER_PORT; @@ -209,6 +186,12 @@ void MDNSComponent::compile_records_() { #endif } +#ifdef USE_MDNS_EXTRA_SERVICES +void MDNSComponent::add_extra_service(MDNSService service) { + this->services_[this->services_count_++] = std::move(service); +} +#endif + void MDNSComponent::dump_config() { ESP_LOGCONFIG(TAG, "mDNS:\n" @@ -216,7 +199,8 @@ void MDNSComponent::dump_config() { this->hostname_.c_str()); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE ESP_LOGV(TAG, " Services:"); - for (const auto &service : this->services_) { + for (uint8_t i = 0; i < this->services_count_; i++) { + const auto &service = this->services_[i]; ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(), const_cast &>(service.port).value()); for (const auto &record : service.txt_records) { @@ -227,8 +211,6 @@ void MDNSComponent::dump_config() { #endif } -std::vector MDNSComponent::get_services() { return this->services_; } - } // namespace mdns } // namespace esphome #endif diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h index f87ef08bcd..e653e9384f 100644 --- a/esphome/components/mdns/mdns_component.h +++ b/esphome/components/mdns/mdns_component.h @@ -1,14 +1,17 @@ #pragma once #include "esphome/core/defines.h" #ifdef USE_MDNS +#include #include -#include #include "esphome/core/automation.h" #include "esphome/core/component.h" namespace esphome { namespace mdns { +// Service count is calculated at compile time by Python codegen +// MDNS_SERVICE_COUNT will always be defined + struct MDNSTXTRecord { std::string key; TemplatableValue value; @@ -36,18 +39,17 @@ class MDNSComponent : public Component { float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; } #ifdef USE_MDNS_EXTRA_SERVICES - void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); } + void add_extra_service(MDNSService service); #endif - std::vector get_services(); + const std::array &get_services() const { return services_; } + uint8_t get_services_count() const { return services_count_; } void on_shutdown() override; protected: -#ifdef USE_MDNS_EXTRA_SERVICES - std::vector services_extra_{}; -#endif - std::vector services_{}; + std::array services_{}; + uint8_t services_count_{0}; std::string hostname_; void compile_records_(); }; diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index 5b5c113f83..3caec9b698 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -143,11 +143,12 @@ void OpenThreadSrpComponent::setup() { return; } - // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this - // component - this->mdns_services_ = this->mdns_->get_services(); - ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size()); - for (const auto &service : this->mdns_services_) { + // Use mdns services directly - they remain valid for the lifetime of the mdns component + const auto &mdns_services = this->mdns_->get_services(); + uint8_t mdns_count = this->mdns_->get_services_count(); + ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", mdns_count); + for (uint8_t i = 0; i < mdns_count; i++) { + const auto &service = mdns_services[i]; otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance); if (!entry) { ESP_LOGW(TAG, "Failed to allocate service entry"); diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index a9aff78e56..5d139c633d 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -57,7 +57,6 @@ class OpenThreadSrpComponent : public Component { protected: esphome::mdns::MDNSComponent *mdns_{nullptr}; - std::vector mdns_services_; std::vector> memory_pool_; void *pool_alloc_(size_t size); }; From 21d7dc2b9bca4c5e98f27e3820b0086c91931f8a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:25:11 +0200 Subject: [PATCH 04/11] [mdns][openthread] Use std::array for mdns services and remove unnecessary copy --- esphome/components/mdns/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index f023f84750..ced4753b75 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -95,15 +95,15 @@ async def to_code(config): service_count = 0 # Check if API component is enabled (it may create a service at runtime) - if cg.is_defined("USE_API"): + if "api" in CORE.config: service_count += 1 # Check for prometheus - if cg.is_defined("USE_PROMETHEUS"): + if "prometheus" in CORE.config: service_count += 1 # Check for web_server - if cg.is_defined("USE_WEBSERVER"): + if "web_server" in CORE.config: service_count += 1 # Count extra services from config From 518402f0310350c192b057fd21164d3c9354528b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:31:04 +0200 Subject: [PATCH 05/11] preen --- esphome/core/defines.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 7fc42ea334..12bf02d856 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -82,6 +82,7 @@ #define USE_LVGL_TILEVIEW #define USE_LVGL_TOUCHSCREEN #define USE_MDNS +#define MDNS_SERVICE_COUNT 3 #define USE_MEDIA_PLAYER #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER From 2eb35f83b79c7ea1466a1e47ee9d8d221f0a74f1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:31:55 +0200 Subject: [PATCH 06/11] preen --- esphome/components/mdns/mdns_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 5a5286c5bc..6dba821258 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -197,7 +197,7 @@ void MDNSComponent::dump_config() { "mDNS:\n" " Hostname: %s", this->hostname_.c_str()); -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGV(TAG, " Services:"); for (uint8_t i = 0; i < this->services_count_; i++) { const auto &service = this->services_[i]; From c12eba95908b59a6469130ee9440cd0bf5469a87 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:32:28 +0200 Subject: [PATCH 07/11] preen --- esphome/components/mdns/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index ced4753b75..550a39216b 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -113,10 +113,7 @@ async def to_code(config): cg.add_define("USE_MDNS_EXTRA_SERVICES") # Ensure at least 1 service (fallback service) - if service_count == 0: - service_count = 1 - - cg.add_define("MDNS_SERVICE_COUNT", service_count) + cg.add_define("MDNS_SERVICE_COUNT", max(1, service_count)) var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) From 03e0fbd65759d74651aff30fc69b73a0c4c92c3b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:33:22 +0200 Subject: [PATCH 08/11] preen --- esphome/components/mdns/__init__.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 550a39216b..a22085978d 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -92,19 +92,10 @@ async def to_code(config): cg.add_define("USE_MDNS") # Calculate compile-time service count - service_count = 0 - - # Check if API component is enabled (it may create a service at runtime) - if "api" in CORE.config: - service_count += 1 - - # Check for prometheus - if "prometheus" in CORE.config: - service_count += 1 - - # Check for web_server - if "web_server" in CORE.config: - service_count += 1 + # Each of these components may create a service at runtime + service_count = sum( + 1 for key in ("api", "prometheus", "web_server") if key in CORE.config + ) # Count extra services from config extra_services_count = len(config[CONF_SERVICES]) From 30df2cb9ee97ed294f6c3310c47ac65f2e9dba87 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:33:50 +0200 Subject: [PATCH 09/11] preen --- esphome/components/mdns/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index a22085978d..8c3193430c 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -95,12 +95,9 @@ async def to_code(config): # Each of these components may create a service at runtime service_count = sum( 1 for key in ("api", "prometheus", "web_server") if key in CORE.config - ) + ) + len(config[CONF_SERVICES]) - # Count extra services from config - extra_services_count = len(config[CONF_SERVICES]) - if extra_services_count > 0: - service_count += extra_services_count + if config[CONF_SERVICES]: cg.add_define("USE_MDNS_EXTRA_SERVICES") # Ensure at least 1 service (fallback service) From b4b8b43bd77f414f4d0784277dcc6ddac950e2ed Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:34:32 +0200 Subject: [PATCH 10/11] preen --- esphome/components/mdns/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 8c3193430c..12ec7353b5 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -17,6 +17,9 @@ from esphome.coroutine import CoroPriority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] +# Components that create mDNS services at runtime +COMPONENTS_WITH_MDNS_SERVICES = ("api", "prometheus", "web_server") + mdns_ns = cg.esphome_ns.namespace("mdns") MDNSComponent = mdns_ns.class_("MDNSComponent", cg.Component) MDNSTXTRecord = mdns_ns.struct("MDNSTXTRecord") @@ -92,9 +95,8 @@ async def to_code(config): cg.add_define("USE_MDNS") # Calculate compile-time service count - # Each of these components may create a service at runtime service_count = sum( - 1 for key in ("api", "prometheus", "web_server") if key in CORE.config + 1 for key in COMPONENTS_WITH_MDNS_SERVICES if key in CORE.config ) + len(config[CONF_SERVICES]) if config[CONF_SERVICES]: From de2838fa6683c09916c180d53cf8369f009d6c67 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Oct 2025 22:35:33 +0200 Subject: [PATCH 11/11] preen --- esphome/components/mdns/__init__.py | 2 ++ esphome/components/mdns/mdns_component.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 12ec7353b5..ce0241677d 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -18,6 +18,8 @@ CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] # Components that create mDNS services at runtime +# IMPORTANT: If you add a new component here, you must also update the corresponding +# #ifdef blocks in mdns_component.cpp compile_records_() method COMPONENTS_WITH_MDNS_SERVICES = ("api", "prometheus", "web_server") mdns_ns = cg.esphome_ns.namespace("mdns") diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index 6dba821258..58952e94e9 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -75,6 +75,9 @@ void MDNSComponent::compile_records_() { this->hostname_ = App.get_name(); this->services_count_ = 0; + // IMPORTANT: The #ifdef blocks below must match COMPONENTS_WITH_MDNS_SERVICES + // in mdns/__init__.py. If you add a new service here, update both locations. + #ifdef USE_API if (api::global_api_server != nullptr) { auto &service = this->services_[this->services_count_++];