From 349f45f582e1f7bb7da963db19f89dd589383373 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:26:16 -1000 Subject: [PATCH] [binary_sensor] Move send_state_internal and invalidate_state to .cpp set_new_state is a template method (~189 bytes when instantiated). Inlining callers like send_state_internal and invalidate_state causes the template code to be duplicated at every call site (publish_state, Filter::output, BinarySensorInvalidateAction::play, etc.), adding ~384 bytes of flash. Keep these as out-of-line definitions in the .cpp so the template is instantiated once. --- esphome/components/binary_sensor/binary_sensor.cpp | 4 ++++ esphome/components/binary_sensor/binary_sensor.h | 3 ++- esphome/core/entity_base.h | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 564cb48f64..fe7e169fea 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -32,6 +32,10 @@ void BinarySensor::publish_initial_state(bool new_state) { this->invalidate_state(); this->publish_state(new_state); } +// Defined out-of-line to prevent set_new_state template from being inlined at each call site, +// which would duplicate ~189 bytes of template code per caller (publish_state, Filter::output, etc.) +void BinarySensor::send_state_internal(bool new_state) { this->set_new_state(new_state); } +void BinarySensor::invalidate_state() { this->set_new_state({}); } void BinarySensor::on_state_changed_(const optional &old_state, const optional &new_state, bool had_state) { StatefulEntityBase::on_state_changed_(old_state, new_state, had_state); diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 31e8f92b96..7cd8a44a0f 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -57,7 +57,8 @@ class BinarySensor : public StatefulEntityBase { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void send_state_internal(bool new_state) { this->set_new_state(new_state); } + /// Defined in .cpp to avoid inlining set_new_state template code at every call site. + void send_state_internal(bool new_state); /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index b6785a9aab..202f386234 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -320,7 +320,8 @@ template class StatefulEntityBase : public EntityBase { /// Return the current state if available, otherwise return the provided default. T get_state_default(T default_value) const { return this->has_state() ? this->get_state() : default_value; } /// Clear the state — sets has_state() to false and fires callbacks with nullopt. - void invalidate_state() { this->set_new_state({}); } + /// Defined out-of-line in subclass .cpp to avoid inlining set_new_state template code at every call site. + void invalidate_state(); template void add_full_state_callback(F &&callback) { this->full_state_callbacks_.add(std::forward(callback));