From 0de26965439d8c8e2b3c38ac32b3ee6128edc62b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 Jun 2025 11:37:11 -0500 Subject: [PATCH] Optimize memory usage by lazy-allocating raw callbacks in sensors --- esphome/components/sensor/sensor.cpp | 10 ++++++++-- esphome/components/sensor/sensor.h | 5 +++-- esphome/components/text_sensor/text_sensor.cpp | 11 +++++++++-- esphome/components/text_sensor/text_sensor.h | 7 +++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 14a8b3d490..962794b62d 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -21,6 +21,7 @@ std::string state_class_to_string(StateClass state_class) { } Sensor::Sensor() : state(NAN), raw_state(NAN) {} +Sensor::~Sensor() { delete this->raw_callback_; } int8_t Sensor::get_accuracy_decimals() { if (this->accuracy_decimals_.has_value()) @@ -38,7 +39,9 @@ StateClass Sensor::get_state_class() { void Sensor::publish_state(float state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_ != nullptr) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state); @@ -51,7 +54,10 @@ void Sensor::publish_state(float state) { void Sensor::add_on_state_callback(std::function &&callback) { this->callback_.add(std::move(callback)); } void Sensor::add_on_raw_state_callback(std::function &&callback) { - this->raw_callback_.add(std::move(callback)); + if (this->raw_callback_ == nullptr) { + this->raw_callback_ = new CallbackManager(); // NOLINT + } + this->raw_callback_->add(std::move(callback)); } void Sensor::add_filter(Filter *filter) { diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index ab9ff1565c..d8099116ac 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -61,6 +61,7 @@ std::string state_class_to_string(StateClass state_class); class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBase_UnitOfMeasurement { public: explicit Sensor(); + ~Sensor(); /// Get the accuracy in decimals, using the manual override if set. int8_t get_accuracy_decimals(); @@ -152,8 +153,8 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa void internal_send_state_to_frontend(float state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + CallbackManager *raw_callback_{nullptr}; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters. diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index f10cd50267..000e6c2dd2 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -6,9 +6,13 @@ namespace text_sensor { static const char *const TAG = "text_sensor"; +TextSensor::~TextSensor() { delete this->raw_callback_; } + void TextSensor::publish_state(const std::string &state) { this->raw_state = state; - this->raw_callback_.call(state); + if (this->raw_callback_ != nullptr) { + this->raw_callback_->call(state); + } ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str()); @@ -53,7 +57,10 @@ void TextSensor::add_on_state_callback(std::function callback this->callback_.add(std::move(callback)); } void TextSensor::add_on_raw_state_callback(std::function callback) { - this->raw_callback_.add(std::move(callback)); + if (this->raw_callback_ == nullptr) { + this->raw_callback_ = new CallbackManager(); // NOLINT + } + this->raw_callback_->add(std::move(callback)); } std::string TextSensor::get_state() const { return this->state; } diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index bd72ea70e3..de2702383e 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -33,6 +33,9 @@ namespace text_sensor { class TextSensor : public EntityBase, public EntityBase_DeviceClass { public: + TextSensor() = default; + ~TextSensor(); + /// Getter-syntax for .state. std::string get_state() const; /// Getter-syntax for .raw_state @@ -72,8 +75,8 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass { void internal_send_state_to_frontend(const std::string &state); protected: - CallbackManager raw_callback_; ///< Storage for raw state callbacks. - CallbackManager callback_; ///< Storage for filtered state callbacks. + CallbackManager *raw_callback_{nullptr}; ///< Storage for raw state callbacks (lazy allocated). + CallbackManager callback_; ///< Storage for filtered state callbacks. Filter *filter_list_{nullptr}; ///< Store all active filters.