From a3ccb656b25fae9df16791a90364f38bb24cfe36 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 17:09:44 -1000 Subject: [PATCH] Replace forward-declared App with throttle_check_and_update helper Forward declaration fails when filter.h is included before application.h (incomplete type error). Instead, extract the throttle time check into a non-template helper in filter.cpp that accesses App directly. This keeps the same call overhead as the old ThrottleFilter (one function call) but the helper does the time check AND updates last_input, so the template new_value() body has no App dependency at all. --- esphome/components/sensor/filter.cpp | 9 +++++++++ esphome/components/sensor/filter.h | 15 ++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 0b2c45289a..7afc43b8aa 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -222,6 +222,15 @@ MultiplyFilter::MultiplyFilter(TemplatableValue multiplier) : multiplier_ optional MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); } +bool throttle_check_and_update(uint32_t &last_input, uint32_t min_time_between_inputs) { + const uint32_t now = App.get_loop_component_start_time(); + if (last_input == 0 || now - last_input >= min_time_between_inputs) { + last_input = now; + return true; + } + return false; +} + // ValueListFilter helper (non-template, shared by all ValueListFilter instantiations) bool value_list_matches_any(Sensor *parent, float sensor_value, const TemplatableValue *values, size_t count) { int8_t accuracy = parent->get_accuracy_decimals(); diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index cc9644f6e1..9e97ad432a 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -332,14 +332,9 @@ class MultiplyFilter : public Filter { /// Non-template helper for value matching (implementation in filter.cpp) bool value_list_matches_any(Sensor *parent, float sensor_value, const TemplatableValue *values, size_t count); -} // namespace esphome::sensor -// Forward declaration — avoids circular include of application.h. -// Template bodies are only instantiated in main.cpp where Application is fully defined. -namespace esphome { -class Application; -extern Application App; -} // namespace esphome -namespace esphome::sensor { +/// Returns true if throttle should allow the value through (time expired or first input). +/// Updates last_input in-place. Implementation in filter.cpp (accesses App without circular include). +bool throttle_check_and_update(uint32_t &last_input, uint32_t min_time_between_inputs); /** Base class for filters that compare sensor values against a fixed list of configured values. * @@ -395,10 +390,8 @@ template class ThrottleWithPriorityFilter : public ValueListFilter : ValueListFilter(prioritized_values), min_time_between_inputs_(min_time_between_inputs) {} optional new_value(float value) override { - const uint32_t now = App.get_loop_component_start_time(); - if (this->last_input_ == 0 || now - this->last_input_ >= this->min_time_between_inputs_ || + if (throttle_check_and_update(this->last_input_, this->min_time_between_inputs_) || this->value_matches_any_(value)) { - this->last_input_ = now; return value; } return {};