diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index e21bec2d7e..5b07dd2915 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -131,7 +131,7 @@ async def map_filter_to_code(config, filter_id): ) for conf in config ] - return cg.new_Pvariable(filter_id, mappings) + return cg.new_Pvariable(filter_id, cg.TemplateArguments(len(mappings)), mappings) validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") diff --git a/esphome/components/text_sensor/filter.cpp b/esphome/components/text_sensor/filter.cpp index 5fdd323510..d4e6b5b9bb 100644 --- a/esphome/components/text_sensor/filter.cpp +++ b/esphome/components/text_sensor/filter.cpp @@ -87,17 +87,15 @@ bool substitute_filter_apply(const Substitution *substitutions, size_t count, st return true; } -// Map -MapFilter::MapFilter(const std::initializer_list &mappings) : mappings_(mappings) {} - -bool MapFilter::new_value(std::string &value) { - for (const auto &mapping : this->mappings_) { - if (value == mapping.from) { - value.assign(mapping.to); +// Map — non-template helper +bool map_filter_apply(const Substitution *mappings, size_t count, std::string &value) { + for (size_t i = 0; i < count; i++) { + if (value == mappings[i].from) { + value.assign(mappings[i].to); return true; } } - return true; // Pass through if no match + return true; } } // namespace esphome::text_sensor diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index 2a90a6ad50..4c241edcc1 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -144,6 +144,9 @@ template class SubstituteFilter : public Filter { std::array substitutions_{}; }; +/// Non-template helper (implementation in filter.cpp) +bool map_filter_apply(const Substitution *mappings, size_t count, std::string &value); + /** A filter that maps values from one set to another * * Uses linear search instead of std::map for typical small datasets (2-20 mappings). @@ -167,14 +170,23 @@ template class SubstituteFilter : public Filter { * - Faster for typical ESPHome usage (2-10 mappings common, 20+ rare) * * Break-even point: ~35-40 mappings, but ESPHome configs rarely exceed 20 + * + * N is set by code generation to match the exact number of mappings configured in YAML. */ -class MapFilter : public Filter { +template class MapFilter : public Filter { public: - explicit MapFilter(const std::initializer_list &mappings); - bool new_value(std::string &value) override; + explicit MapFilter(const std::initializer_list &mappings) { + size_t i = 0; + for (const auto &m : mappings) { + if (i >= N) + break; + this->mappings_[i++] = m; + } + } + bool new_value(std::string &value) override { return map_filter_apply(this->mappings_.data(), N, value); } protected: - FixedVector mappings_; + std::array mappings_{}; }; } // namespace esphome::text_sensor