From a3a1a8799ae00335d99abe5813627dbbfe9d4764 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:36:17 -1000 Subject: [PATCH 1/6] [sensor] Deprecate .raw_state, guard raw_callback_ behind USE_SENSOR_FILTER Deprecate direct access to sensor .raw_state member in favor of get_raw_state(). This prepares for eventually compiling out raw_state on builds without sensor filters, saving 4 bytes per sensor instance. Guard raw_callback_ and add_on_raw_state_callback behind USE_SENSOR_FILTER, saving 4 bytes per sensor on builds without filters immediately. Guard SensorRawStateTrigger behind USE_SENSOR_FILTER and ensure on_raw_value automation sets USE_SENSOR_FILTER define. Fix nextion sensor to stop directly writing .raw_state/.state members - the publish=false path was redundantly setting already-current values. Migrate haier hon_climate from .raw_state to get_raw_state(). --- esphome/components/haier/hon_climate.cpp | 2 +- .../nextion/sensor/nextion_sensor.cpp | 4 ---- esphome/components/sensor/__init__.py | 1 + esphome/components/sensor/automation.h | 2 ++ esphome/components/sensor/sensor.cpp | 10 +++++++-- esphome/components/sensor/sensor.h | 22 +++++++++++++------ 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 92defe560e..1cee95bf16 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -748,7 +748,7 @@ void HonClimate::update_sub_sensor_(SubSensorType type, float value) { if (type < SubSensorType::SUB_SENSOR_TYPE_COUNT) { size_t index = (size_t) type; if ((this->sub_sensors_[index] != nullptr) && - ((!this->sub_sensors_[index]->has_state()) || (this->sub_sensors_[index]->raw_state != value))) + ((!this->sub_sensors_[index]->has_state()) || (this->sub_sensors_[index]->get_raw_state() != value))) this->sub_sensors_[index]->publish_state(value); } } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 03b7261239..9ea12cf808 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -85,10 +85,6 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { } this->publish_state(published_state); - } else { - this->raw_state = state; - this->state = state; - this->set_has_state(true); } } this->update_component_settings(); diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 9f3c1484b0..44ba87f86a 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -901,6 +901,7 @@ async def _build_sensor_automations(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_RAW_VALUE, []): + cg.add_define("USE_SENSOR_FILTER") trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_VALUE_RANGE, []): diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index b4de712727..bcd2ca0f0d 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -13,12 +13,14 @@ class SensorStateTrigger : public Trigger { } }; +#ifdef USE_SENSOR_FILTER class SensorRawStateTrigger : public Trigger { public: explicit SensorRawStateTrigger(Sensor *parent) { parent->add_on_raw_state_callback([this](float value) { this->trigger(value); }); } }; +#endif template class SensorPublishAction : public Action { public: diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index b4e59dfeb5..f5601823c1 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -4,6 +4,10 @@ #include "esphome/core/log.h" #include "esphome/core/progmem.h" +// Suppress deprecation warnings for internal access to .state and .raw_state +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + namespace esphome::sensor { static const char *const TAG = "sensor"; @@ -64,7 +68,9 @@ StateClass Sensor::get_state_class() { void Sensor::publish_state(float state) { this->raw_state = state; +#ifdef USE_SENSOR_FILTER this->raw_callback_.call(state); +#endif ESP_LOGV(TAG, "'%s': Received new state %f", this->name_.c_str(), state); @@ -110,8 +116,6 @@ void Sensor::clear_filters() { this->filter_list_ = nullptr; } #endif // USE_SENSOR_FILTER -float Sensor::get_state() const { return this->state; } -float Sensor::get_raw_state() const { return this->raw_state; } void Sensor::internal_send_state_to_frontend(float state) { this->set_has_state(true); @@ -124,4 +128,6 @@ void Sensor::internal_send_state_to_frontend(float state) { #endif } +#pragma GCC diagnostic pop + } // namespace esphome::sensor diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index b3bd962036..c71704cd74 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -95,9 +95,12 @@ class Sensor : public EntityBase { #endif /// Getter-syntax for .state. - float get_state() const; + float get_state() const { return this->state; } /// Getter-syntax for .raw_state - float get_raw_state() const; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + float get_raw_state() const { return this->raw_state; } +#pragma GCC diagnostic pop /** Publish a new state to the front-end. * @@ -112,10 +115,12 @@ class Sensor : public EntityBase { // (In most use cases you won't need these) /// Add a callback that will be called every time a filtered value arrives. template void add_on_state_callback(F &&callback) { this->callback_.add(std::forward(callback)); } +#ifdef USE_SENSOR_FILTER /// Add a callback that will be called every time the sensor sends a raw value. template void add_on_raw_state_callback(F &&callback) { this->raw_callback_.add(std::forward(callback)); } +#endif /** This member variable stores the last state that has passed through all filters. * @@ -126,17 +131,20 @@ class Sensor : public EntityBase { */ float state; - /** This member variable stores the current raw state of the sensor, without any filters applied. - * - * Unlike .state,this will be updated immediately when publish_state is called. - */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /// @deprecated Use get_raw_state() instead. This member will be removed in ESPHome 2027.1.0. + ESPDEPRECATED("Use get_raw_state() instead of .raw_state. Will be removed in 2027.1.0", "2026.7.0") float raw_state; +#pragma GCC diagnostic pop void internal_send_state_to_frontend(float state); protected: +#ifdef USE_SENSOR_FILTER LazyCallbackManager raw_callback_; ///< Storage for raw state callbacks. - LazyCallbackManager callback_; ///< Storage for filtered state callbacks. +#endif + LazyCallbackManager callback_; ///< Storage for filtered state callbacks. #ifdef USE_SENSOR_FILTER Filter *filter_list_{nullptr}; ///< Store all active filters. From c082ba7d64c440e8eab408bb4dfc266519879078 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:44:32 -1000 Subject: [PATCH 2/6] [sensor] Fix deprecation version to 2026.4.0, removal to 2026.10.0 --- esphome/components/sensor/sensor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index c71704cd74..4baf188a9c 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -133,8 +133,8 @@ class Sensor : public EntityBase { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - /// @deprecated Use get_raw_state() instead. This member will be removed in ESPHome 2027.1.0. - ESPDEPRECATED("Use get_raw_state() instead of .raw_state. Will be removed in 2027.1.0", "2026.7.0") + /// @deprecated Use get_raw_state() instead. This member will be removed in ESPHome 2026.10.0. + ESPDEPRECATED("Use get_raw_state() instead of .raw_state. Will be removed in 2026.10.0", "2026.4.0") float raw_state; #pragma GCC diagnostic pop From 30b5c3fb715b6db7e47db167aa8435be45442efc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:49:20 -1000 Subject: [PATCH 3/6] [sensor] Keep add_on_raw_state_callback always available Delegate to callback_ when USE_SENSOR_FILTER is off since raw state equals filtered state without filters. This avoids requiring external components to know about USE_SENSOR_FILTER. --- esphome/components/sensor/sensor.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 4baf188a9c..73fd10ea6f 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -115,12 +115,15 @@ class Sensor : public EntityBase { // (In most use cases you won't need these) /// Add a callback that will be called every time a filtered value arrives. template void add_on_state_callback(F &&callback) { this->callback_.add(std::forward(callback)); } -#ifdef USE_SENSOR_FILTER /// Add a callback that will be called every time the sensor sends a raw value. + /// Without filters, raw state equals filtered state so this delegates to the regular callback. template void add_on_raw_state_callback(F &&callback) { +#ifdef USE_SENSOR_FILTER this->raw_callback_.add(std::forward(callback)); - } +#else + this->callback_.add(std::forward(callback)); #endif + } /** This member variable stores the last state that has passed through all filters. * From 9578376f4662d9651613c10163018b9a2ce52ac9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:51:01 -1000 Subject: [PATCH 4/6] [sensor] Remove USE_SENSOR_FILTER guard from SensorRawStateTrigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add_on_raw_state_callback is always available now (delegates to callback_ without filters). SensorRawStateTrigger doesn't need the guard, and on_raw_value doesn't need to set USE_SENSOR_FILTER. Without filters raw == filtered, so the trigger fires with the correct value either way — no wasted RAM on filter infrastructure. --- esphome/components/sensor/__init__.py | 1 - esphome/components/sensor/automation.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 44ba87f86a..9f3c1484b0 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -901,7 +901,6 @@ async def _build_sensor_automations(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_RAW_VALUE, []): - cg.add_define("USE_SENSOR_FILTER") trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_VALUE_RANGE, []): diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index bcd2ca0f0d..b4de712727 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -13,14 +13,12 @@ class SensorStateTrigger : public Trigger { } }; -#ifdef USE_SENSOR_FILTER class SensorRawStateTrigger : public Trigger { public: explicit SensorRawStateTrigger(Sensor *parent) { parent->add_on_raw_state_callback([this](float value) { this->trigger(value); }); } }; -#endif template class SensorPublishAction : public Action { public: From 84c58403f7230f78186a18cb966193adbe60f695 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 18:34:21 -1000 Subject: [PATCH 5/6] [sensor] Clarify compile-time vs runtime in raw_callback comment --- esphome/components/sensor/sensor.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 73fd10ea6f..ee0fc62828 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -116,7 +116,8 @@ class Sensor : public EntityBase { /// Add a callback that will be called every time a filtered value arrives. template void add_on_state_callback(F &&callback) { this->callback_.add(std::forward(callback)); } /// Add a callback that will be called every time the sensor sends a raw value. - /// Without filters, raw state equals filtered state so this delegates to the regular callback. + /// When USE_SENSOR_FILTER is not enabled, delegates to the regular callback + /// since raw state equals filtered state without filter support compiled in. template void add_on_raw_state_callback(F &&callback) { #ifdef USE_SENSOR_FILTER this->raw_callback_.add(std::forward(callback)); From cab07c537ec268b05404fcba34bf2cbffc4187e2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 18:56:15 -1000 Subject: [PATCH 6/6] [sensor] Narrow pragma scope to only functions accessing deprecated raw_state Scope the deprecation warning suppression to just the constructor and publish_state() instead of the entire file, so unrelated deprecation warnings aren't accidentally hidden. --- esphome/components/sensor/sensor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index f5601823c1..f3657b1239 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -4,10 +4,6 @@ #include "esphome/core/log.h" #include "esphome/core/progmem.h" -// Suppress deprecation warnings for internal access to .state and .raw_state -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - namespace esphome::sensor { static const char *const TAG = "sensor"; @@ -44,7 +40,10 @@ const LogString *state_class_to_string(StateClass state_class) { return StateClassStrings::get_log_str(static_cast(state_class), 0); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" Sensor::Sensor() : state(NAN), raw_state(NAN) {} +#pragma GCC diagnostic pop int8_t Sensor::get_accuracy_decimals() { if (this->sensor_flags_.has_accuracy_override) @@ -66,6 +65,8 @@ StateClass Sensor::get_state_class() { return StateClass::STATE_CLASS_NONE; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" void Sensor::publish_state(float state) { this->raw_state = state; #ifdef USE_SENSOR_FILTER @@ -84,6 +85,7 @@ void Sensor::publish_state(float state) { } #endif } +#pragma GCC diagnostic pop #ifdef USE_SENSOR_FILTER void Sensor::add_filter(Filter *filter) { @@ -128,6 +130,4 @@ void Sensor::internal_send_state_to_frontend(float state) { #endif } -#pragma GCC diagnostic pop - } // namespace esphome::sensor