[core] Migrate entities to use lazy callbacks (#12580)
Some checks failed
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Test components batch (${{ matrix.components }}) (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled
Stale / stale (push) Has been cancelled
Lock closed issues and PRs / lock (push) Has been cancelled
Publish Release / Initialize build (push) Has been cancelled
Publish Release / Build and publish to PyPi (push) Has been cancelled
Publish Release / Build ESPHome amd64 (push) Has been cancelled
Publish Release / Build ESPHome arm64 (push) Has been cancelled
Publish Release / Publish ESPHome docker to dockerhub (push) Has been cancelled
Publish Release / Publish ESPHome docker to ghcr (push) Has been cancelled
Publish Release / Publish ESPHome ha-addon to dockerhub (push) Has been cancelled
Publish Release / Publish ESPHome ha-addon to ghcr (push) Has been cancelled
Publish Release / deploy-ha-addon-repo (push) Has been cancelled
Publish Release / deploy-esphome-schema (push) Has been cancelled
Publish Release / version-notifier (push) Has been cancelled
Synchronise Device Classes from Home Assistant / Sync Device Classes (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Some checks failed
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Test components batch (${{ matrix.components }}) (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled
Stale / stale (push) Has been cancelled
Lock closed issues and PRs / lock (push) Has been cancelled
Publish Release / Initialize build (push) Has been cancelled
Publish Release / Build and publish to PyPi (push) Has been cancelled
Publish Release / Build ESPHome amd64 (push) Has been cancelled
Publish Release / Build ESPHome arm64 (push) Has been cancelled
Publish Release / Publish ESPHome docker to dockerhub (push) Has been cancelled
Publish Release / Publish ESPHome docker to ghcr (push) Has been cancelled
Publish Release / Publish ESPHome ha-addon to dockerhub (push) Has been cancelled
Publish Release / Publish ESPHome ha-addon to ghcr (push) Has been cancelled
Publish Release / deploy-ha-addon-repo (push) Has been cancelled
Publish Release / deploy-esphome-schema (push) Has been cancelled
Publish Release / version-notifier (push) Has been cancelled
Synchronise Device Classes from Home Assistant / Sync Device Classes (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
This commit is contained in:
@@ -132,13 +132,13 @@ class AlarmControlPanel : public EntityBase {
|
|||||||
// the call control function
|
// the call control function
|
||||||
virtual void control(const AlarmControlPanelCall &call) = 0;
|
virtual void control(const AlarmControlPanelCall &call) = 0;
|
||||||
// state callback - triggers check get_state() for specific state
|
// state callback - triggers check get_state() for specific state
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
// clear callback - fires when leaving TRIGGERED state
|
// clear callback - fires when leaving TRIGGERED state
|
||||||
CallbackManager<void()> cleared_callback_{};
|
LazyCallbackManager<void()> cleared_callback_{};
|
||||||
// chime callback
|
// chime callback
|
||||||
CallbackManager<void()> chime_callback_{};
|
LazyCallbackManager<void()> chime_callback_{};
|
||||||
// ready callback
|
// ready callback
|
||||||
CallbackManager<void()> ready_callback_{};
|
LazyCallbackManager<void()> ready_callback_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alarm_control_panel
|
} // namespace alarm_control_panel
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Button : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
*/
|
*/
|
||||||
virtual void press_action() = 0;
|
virtual void press_action() = 0;
|
||||||
|
|
||||||
CallbackManager<void()> press_callback_{};
|
LazyCallbackManager<void()> press_callback_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::button
|
} // namespace esphome::button
|
||||||
|
|||||||
@@ -326,8 +326,8 @@ class Climate : public EntityBase {
|
|||||||
|
|
||||||
void dump_traits_(const char *tag);
|
void dump_traits_(const char *tag);
|
||||||
|
|
||||||
CallbackManager<void(Climate &)> state_callback_{};
|
LazyCallbackManager<void(Climate &)> state_callback_{};
|
||||||
CallbackManager<void(ClimateCall &)> control_callback_{};
|
LazyCallbackManager<void(ClimateCall &)> control_callback_{};
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
#ifdef USE_CLIMATE_VISUAL_OVERRIDES
|
#ifdef USE_CLIMATE_VISUAL_OVERRIDES
|
||||||
float visual_min_temperature_override_{NAN};
|
float visual_min_temperature_override_{NAN};
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class Cover : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
|
|
||||||
optional<CoverRestoreState> restore_state_();
|
optional<CoverRestoreState> restore_state_();
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
|
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class DateTimeBase : public EntityBase {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CallbackManager<void()> state_callback_;
|
LazyCallbackManager<void()> state_callback_;
|
||||||
|
|
||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
time::RealTimeClock *rtc_;
|
time::RealTimeClock *rtc_;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class Event : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
LazyCallbackManager<void(const std::string &event_type)> event_callback_;
|
||||||
FixedVector<const char *> types_;
|
FixedVector<const char *> types_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ class Fan : public EntityBase {
|
|||||||
const char *find_preset_mode_(const char *preset_mode);
|
const char *find_preset_mode_(const char *preset_mode);
|
||||||
const char *find_preset_mode_(const char *preset_mode, size_t len);
|
const char *find_preset_mode_(const char *preset_mode, size_t len);
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
FanRestoreMode restore_mode_;
|
FanRestoreMode restore_mode_;
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class Lock : public EntityBase {
|
|||||||
*/
|
*/
|
||||||
virtual void control(const LockCall &call) = 0;
|
virtual void control(const LockCall &call) = 0;
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
Deduplicator<LockState> publish_dedup_;
|
Deduplicator<LockState> publish_dedup_;
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class MediaPlayer : public EntityBase {
|
|||||||
|
|
||||||
virtual void control(const MediaPlayerCall &call) = 0;
|
virtual void control(const MediaPlayerCall &call) = 0;
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media_player
|
} // namespace media_player
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Number : public EntityBase {
|
|||||||
*/
|
*/
|
||||||
virtual void control(float value) = 0;
|
virtual void control(float value) = 0;
|
||||||
|
|
||||||
CallbackManager<void(float)> state_callback_;
|
LazyCallbackManager<void(float)> state_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::number
|
} // namespace esphome::number
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class Select : public EntityBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CallbackManager<void(size_t)> state_callback_;
|
LazyCallbackManager<void(size_t)> state_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::select
|
} // namespace esphome::select
|
||||||
|
|||||||
@@ -76,9 +76,7 @@ StateClass Sensor::get_state_class() {
|
|||||||
|
|
||||||
void Sensor::publish_state(float state) {
|
void Sensor::publish_state(float state) {
|
||||||
this->raw_state = state;
|
this->raw_state = state;
|
||||||
if (this->raw_callback_) {
|
this->raw_callback_.call(state);
|
||||||
this->raw_callback_->call(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
|
ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state);
|
||||||
|
|
||||||
@@ -91,10 +89,7 @@ void Sensor::publish_state(float state) {
|
|||||||
|
|
||||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
||||||
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
||||||
if (!this->raw_callback_) {
|
this->raw_callback_.add(std::move(callback));
|
||||||
this->raw_callback_ = make_unique<CallbackManager<void(float)>>();
|
|
||||||
}
|
|
||||||
this->raw_callback_->add(std::move(callback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sensor::add_filter(Filter *filter) {
|
void Sensor::add_filter(Filter *filter) {
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
|
|||||||
void internal_send_state_to_frontend(float state);
|
void internal_send_state_to_frontend(float state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<CallbackManager<void(float)>> raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
LazyCallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
|
||||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
LazyCallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||||
|
|
||||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||||
|
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ class Switch : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
// Pointer first (4 bytes)
|
// Pointer first (4 bytes)
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
|
|
||||||
// CallbackManager (12 bytes on 32-bit - contains vector)
|
// LazyCallbackManager (4 bytes on 32-bit - nullptr when empty)
|
||||||
CallbackManager<void(bool)> state_callback_{};
|
LazyCallbackManager<void(bool)> state_callback_{};
|
||||||
|
|
||||||
// Small types grouped together
|
// Small types grouped together
|
||||||
Deduplicator<bool> publish_dedup_; // 2 bytes (bool has_value_ + bool last_value_)
|
Deduplicator<bool> publish_dedup_; // 2 bytes (bool has_value_ + bool last_value_)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Text : public EntityBase {
|
|||||||
*/
|
*/
|
||||||
virtual void control(const std::string &value) = 0;
|
virtual void control(const std::string &value) = 0;
|
||||||
|
|
||||||
CallbackManager<void(const std::string &)> state_callback_;
|
LazyCallbackManager<void(const std::string &)> state_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace text
|
} // namespace text
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ void TextSensor::publish_state(const std::string &state) {
|
|||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
this->raw_state = state;
|
this->raw_state = state;
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
if (this->raw_callback_) {
|
this->raw_callback_.call(state);
|
||||||
this->raw_callback_->call(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str());
|
ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str());
|
||||||
|
|
||||||
@@ -77,10 +75,7 @@ void TextSensor::add_on_state_callback(std::function<void(std::string)> callback
|
|||||||
this->callback_.add(std::move(callback));
|
this->callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
void TextSensor::add_on_raw_state_callback(std::function<void(std::string)> callback) {
|
void TextSensor::add_on_raw_state_callback(std::function<void(std::string)> callback) {
|
||||||
if (!this->raw_callback_) {
|
this->raw_callback_.add(std::move(callback));
|
||||||
this->raw_callback_ = make_unique<CallbackManager<void(std::string)>>();
|
|
||||||
}
|
|
||||||
this->raw_callback_->add(std::move(callback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextSensor::get_state() const { return this->state; }
|
std::string TextSensor::get_state() const { return this->state; }
|
||||||
|
|||||||
@@ -65,9 +65,8 @@ class TextSensor : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
void internal_send_state_to_frontend(const std::string &state);
|
void internal_send_state_to_frontend(const std::string &state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<CallbackManager<void(std::string)>>
|
LazyCallbackManager<void(std::string)> raw_callback_; ///< Storage for raw state callbacks.
|
||||||
raw_callback_; ///< Storage for raw state callbacks (lazy allocated).
|
LazyCallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.
|
||||||
CallbackManager<void(std::string)> callback_; ///< Storage for filtered state callbacks.
|
|
||||||
|
|
||||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
UpdateState state_{UPDATE_STATE_UNKNOWN};
|
UpdateState state_{UPDATE_STATE_UNKNOWN};
|
||||||
UpdateInfo update_info_;
|
UpdateInfo update_info_;
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
std::unique_ptr<Trigger<const UpdateInfo &>> update_available_trigger_{nullptr};
|
std::unique_ptr<Trigger<const UpdateInfo &>> update_available_trigger_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ class Valve : public EntityBase, public EntityBase_DeviceClass {
|
|||||||
|
|
||||||
optional<ValveRestoreState> restore_state_();
|
optional<ValveRestoreState> restore_state_();
|
||||||
|
|
||||||
CallbackManager<void()> state_callback_{};
|
LazyCallbackManager<void()> state_callback_{};
|
||||||
|
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -934,6 +934,50 @@ template<typename... Ts> class CallbackManager<void(Ts...)> {
|
|||||||
std::vector<std::function<void(Ts...)>> callbacks_;
|
std::vector<std::function<void(Ts...)>> callbacks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... X> class LazyCallbackManager;
|
||||||
|
|
||||||
|
/** Lazy-allocating callback manager that only allocates memory when callbacks are registered.
|
||||||
|
*
|
||||||
|
* This is a drop-in replacement for CallbackManager that saves memory when no callbacks
|
||||||
|
* are registered (common case after the Controller Registry eliminated per-entity callbacks
|
||||||
|
* from API and web_server components).
|
||||||
|
*
|
||||||
|
* Memory overhead comparison (32-bit systems):
|
||||||
|
* - CallbackManager: 12 bytes (empty std::vector)
|
||||||
|
* - LazyCallbackManager: 4 bytes (nullptr unique_ptr)
|
||||||
|
*
|
||||||
|
* @tparam Ts The arguments for the callbacks, wrapped in void().
|
||||||
|
*/
|
||||||
|
template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
|
||||||
|
public:
|
||||||
|
/// Add a callback to the list. Allocates the underlying CallbackManager on first use.
|
||||||
|
void add(std::function<void(Ts...)> &&callback) {
|
||||||
|
if (!this->callbacks_) {
|
||||||
|
this->callbacks_ = make_unique<CallbackManager<void(Ts...)>>();
|
||||||
|
}
|
||||||
|
this->callbacks_->add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call all callbacks in this manager. No-op if no callbacks registered.
|
||||||
|
void call(Ts... args) {
|
||||||
|
if (this->callbacks_) {
|
||||||
|
this->callbacks_->call(args...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of registered callbacks.
|
||||||
|
size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
|
||||||
|
|
||||||
|
/// Check if any callbacks are registered.
|
||||||
|
bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
|
||||||
|
|
||||||
|
/// Call all callbacks in this manager.
|
||||||
|
void operator()(Ts... args) { this->call(args...); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unique_ptr<CallbackManager<void(Ts...)>> callbacks_;
|
||||||
|
};
|
||||||
|
|
||||||
/// Helper class to deduplicate items in a series of values.
|
/// Helper class to deduplicate items in a series of values.
|
||||||
template<typename T> class Deduplicator {
|
template<typename T> class Deduplicator {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user