From e212ed024d80bf6b03b0ce5786ca5c294f791e27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Oct 2025 10:00:43 -0700 Subject: [PATCH 1/3] [sntp] Replace std::vector with std::array to save heap memory (#11525) --- esphome/components/sntp/sntp_component.cpp | 6 +++--- esphome/components/sntp/sntp_component.h | 14 +++++++++----- esphome/components/sntp/time.py | 5 +++++ esphome/core/defines.h | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 1cca5e8043..331a9b3509 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -27,7 +27,7 @@ void SNTPComponent::setup() { esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); size_t i = 0; for (auto &server : this->servers_) { - esp_sntp_setservername(i++, server.c_str()); + esp_sntp_setservername(i++, server); } esp_sntp_set_sync_interval(this->get_update_interval()); esp_sntp_set_time_sync_notification_cb([](struct timeval *tv) { @@ -42,7 +42,7 @@ void SNTPComponent::setup() { size_t i = 0; for (auto &server : this->servers_) { - sntp_setservername(i++, server.c_str()); + sntp_setservername(i++, server); } #if defined(USE_ESP8266) @@ -59,7 +59,7 @@ void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, "SNTP Time:"); size_t i = 0; for (auto &server : this->servers_) { - ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server.c_str()); + ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server); } } void SNTPComponent::update() { diff --git a/esphome/components/sntp/sntp_component.h b/esphome/components/sntp/sntp_component.h index dd4c71e082..8f2e411c18 100644 --- a/esphome/components/sntp/sntp_component.h +++ b/esphome/components/sntp/sntp_component.h @@ -2,10 +2,14 @@ #include "esphome/core/component.h" #include "esphome/components/time/real_time_clock.h" +#include namespace esphome { namespace sntp { +// Server count is calculated at compile time by Python codegen +// SNTP_SERVER_COUNT will always be defined + /// The SNTP component allows you to configure local timekeeping via Simple Network Time Protocol. /// /// \note @@ -14,10 +18,7 @@ namespace sntp { /// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html class SNTPComponent : public time::RealTimeClock { public: - SNTPComponent(const std::vector &servers) : servers_(servers) {} - - // Note: set_servers() has been removed and replaced by a constructor - calling set_servers after setup would - // have had no effect anyway, and making the strings immutable avoids the need to strdup their contents. + SNTPComponent(const std::array &servers) : servers_(servers) {} void setup() override; void dump_config() override; @@ -29,7 +30,10 @@ class SNTPComponent : public time::RealTimeClock { void time_synced(); protected: - std::vector servers_; + // Store const char pointers to string literals + // ESP8266: strings in rodata (RAM), but avoids std::string overhead (~24 bytes each) + // Other platforms: strings in flash + std::array servers_; bool has_time_{false}; #if defined(USE_ESP32) diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index 1c8ee402ad..d27fc9991d 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -43,6 +43,11 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): servers = config[CONF_SERVERS] + + # Define server count at compile time + cg.add_define("SNTP_SERVER_COUNT", len(servers)) + + # Pass string literals to constructor - stored in flash/rodata by compiler var = cg.new_Pvariable(config[CONF_ID], servers) await cg.register_component(var, config) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 39698c1004..8095ffed4a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -87,6 +87,7 @@ #define USE_MDNS_STORE_SERVICES #define MDNS_SERVICE_COUNT 3 #define MDNS_DYNAMIC_TXT_COUNT 3 +#define SNTP_SERVER_COUNT 3 #define USE_MEDIA_PLAYER #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER From 6c9f93fbf807e1bda8a9aac992fe82fa66340d37 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Oct 2025 10:40:05 -0700 Subject: [PATCH 2/3] touch ups --- esphome/components/template/select/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/template/select/__init__.py b/esphome/components/template/select/__init__.py index 93aa2c8b05..40ba557d9b 100644 --- a/esphome/components/template/select/__init__.py +++ b/esphome/components/template/select/__init__.py @@ -73,15 +73,18 @@ async def to_code(config): cg.add(var.set_template(template_)) else: - cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) + # Only set if non-default to avoid bloating setup() function + if config[CONF_OPTIMISTIC]: + cg.add(var.set_optimistic(True)) initial_option_index = config[CONF_OPTIONS].index(config[CONF_INITIAL_OPTION]) # Only set if non-zero to avoid bloating setup() function # (initial_option_index_ is zero-initialized in the header) if initial_option_index != 0: cg.add(var.set_initial_option_index(initial_option_index)) - if CONF_RESTORE_VALUE in config: - cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) + # Only set if True (default is False) + if CONF_RESTORE_VALUE in config and config[CONF_RESTORE_VALUE]: + cg.add(var.set_restore_value(True)) if CONF_SET_ACTION in config: await automation.build_automation( From f0aa530069937660131f8feeedc04acf2290317a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 25 Oct 2025 10:42:20 -0700 Subject: [PATCH 3/3] preen --- esphome/components/template/select/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/template/select/__init__.py b/esphome/components/template/select/__init__.py index 40ba557d9b..0e9c240547 100644 --- a/esphome/components/template/select/__init__.py +++ b/esphome/components/template/select/__init__.py @@ -83,7 +83,7 @@ async def to_code(config): cg.add(var.set_initial_option_index(initial_option_index)) # Only set if True (default is False) - if CONF_RESTORE_VALUE in config and config[CONF_RESTORE_VALUE]: + if config.get(CONF_RESTORE_VALUE): cg.add(var.set_restore_value(True)) if CONF_SET_ACTION in config: