From ebc4421ba66b1cfe06138cf0539c43992cee766b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Mar 2026 14:01:08 -1000 Subject: [PATCH] Move custom mode pointers to private, consolidate empty vector to cpp - Move supported_custom_fan_modes_ and supported_custom_presets_ pointers and ensure helpers to private section - Move static EMPTY_VECTOR from inline header getters to a single file-scope constant in climate_traits.cpp (avoids duplication per TU) - Add 2026.11.0 removal comments on all compat code paths --- esphome/components/climate/climate.h | 20 +++++++++---------- esphome/components/climate/climate_traits.cpp | 15 ++++++++++++++ esphome/components/climate/climate_traits.h | 12 ++++------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 8e36936957..861a57a673 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -366,6 +366,16 @@ class Climate : public EntityBase { LazyCallbackManager control_callback_{}; ESPPreferenceObject rtc_; +#ifdef USE_CLIMATE_VISUAL_OVERRIDES + float visual_min_temperature_override_{NAN}; + float visual_max_temperature_override_{NAN}; + float visual_target_temperature_step_override_{NAN}; + float visual_current_temperature_step_override_{NAN}; + float visual_min_humidity_override_{NAN}; + float visual_max_humidity_override_{NAN}; +#endif + + private: /// Lazy-allocate custom mode vectors (never freed — entity lives forever). std::vector &ensure_custom_fan_modes_() { if (!this->supported_custom_fan_modes_) { @@ -383,16 +393,6 @@ class Climate : public EntityBase { std::vector *supported_custom_fan_modes_{nullptr}; std::vector *supported_custom_presets_{nullptr}; -#ifdef USE_CLIMATE_VISUAL_OVERRIDES - float visual_min_temperature_override_{NAN}; - float visual_max_temperature_override_{NAN}; - float visual_target_temperature_step_override_{NAN}; - float visual_current_temperature_step_override_{NAN}; - float visual_min_humidity_override_{NAN}; - float visual_max_humidity_override_{NAN}; -#endif - - private: /** The active custom fan mode (private - enforces use of safe setters). * * Points to an entry in supported_custom_fan_modes_ or nullptr. diff --git a/esphome/components/climate/climate_traits.cpp b/esphome/components/climate/climate_traits.cpp index 9bf2d9acd3..3af0e609a8 100644 --- a/esphome/components/climate/climate_traits.cpp +++ b/esphome/components/climate/climate_traits.cpp @@ -2,6 +2,21 @@ namespace esphome::climate { +// Compat: shared empty vector for getters when no custom modes are set. +// Remove in 2026.11.0 when deprecated ClimateTraits setters are removed +// and getters can return const vector * instead of const vector &. +static const std::vector EMPTY_CUSTOM_MODES; // NOLINT + +const std::vector &ClimateTraits::get_supported_custom_fan_modes() const { + // Compat: return empty ref when pointer is null. Remove in 2026.11.0 (change return to const vector *). + return this->supported_custom_fan_modes_ ? *this->supported_custom_fan_modes_ : EMPTY_CUSTOM_MODES; +} + +const std::vector &ClimateTraits::get_supported_custom_presets() const { + // Compat: return empty ref when pointer is null. Remove in 2026.11.0 (change return to const vector *). + return this->supported_custom_presets_ ? *this->supported_custom_presets_ : EMPTY_CUSTOM_MODES; +} + int8_t ClimateTraits::get_target_temperature_accuracy_decimals() const { return step_to_accuracy_decimals(this->visual_target_temperature_step_); } diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index fdca2638c2..86ebf33c48 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -168,10 +168,8 @@ class ClimateTraits { this->supported_custom_fan_modes_ = new std::vector(modes); // NOLINT } - const std::vector &get_supported_custom_fan_modes() const { - static const std::vector EMPTY_VECTOR; - return this->supported_custom_fan_modes_ ? *this->supported_custom_fan_modes_ : EMPTY_VECTOR; - } + // Compat: returns const ref with empty fallback. In 2026.11.0 change to return const vector *. + const std::vector &get_supported_custom_fan_modes() const; bool supports_custom_fan_mode(const char *custom_fan_mode) const { return this->supported_custom_fan_modes_ && vector_contains(*this->supported_custom_fan_modes_, custom_fan_mode); } @@ -200,10 +198,8 @@ class ClimateTraits { this->supported_custom_presets_ = new std::vector(presets); // NOLINT } - const std::vector &get_supported_custom_presets() const { - static const std::vector EMPTY_VECTOR; - return this->supported_custom_presets_ ? *this->supported_custom_presets_ : EMPTY_VECTOR; - } + // Compat: returns const ref with empty fallback. In 2026.11.0 change to return const vector *. + const std::vector &get_supported_custom_presets() const; bool supports_custom_preset(const char *custom_preset) const { return this->supported_custom_presets_ && vector_contains(*this->supported_custom_presets_, custom_preset); }