From c5e4a608849e2bb956bd20da40d1229c1543b4f5 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 17 Jan 2026 08:35:40 +1100 Subject: [PATCH] [select] Add condition for testing select option (#13267) Co-authored-by: J. Nick Koston --- esphome/components/select/__init__.py | 45 +++++++++++++++++++++- esphome/components/select/automation.h | 30 +++++++++++++++ tests/components/template/common-base.yaml | 12 ++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 7c50fe02c0..c51131a292 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -8,17 +8,20 @@ from esphome.const import ( CONF_ICON, CONF_ID, CONF_INDEX, + CONF_LAMBDA, CONF_MODE, CONF_MQTT_ID, CONF_ON_VALUE, CONF_OPERATION, CONF_OPTION, + CONF_OPTIONS, CONF_TRIGGER_ID, CONF_WEB_SERVER, ) -from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity -from esphome.cpp_generator import MockObjClass +from esphome.cpp_generator import MockObjClass, TemplateArguments +from esphome.cpp_types import global_ns CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True @@ -38,6 +41,9 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action) SelectSetIndexAction = select_ns.class_("SelectSetIndexAction", automation.Action) SelectOperationAction = select_ns.class_("SelectOperationAction", automation.Action) +# Conditions +SelectIsCondition = select_ns.class_("SelectIsCondition", automation.Condition) + # Enums SelectOperation = select_ns.enum("SelectOperation") SELECT_OPERATION_OPTIONS = { @@ -165,6 +171,41 @@ async def select_set_index_to_code(config, action_id, template_arg, args): return var +@automation.register_condition( + "select.is", + SelectIsCondition, + OPERATION_BASE_SCHEMA.extend( + { + cv.Optional(CONF_OPTIONS): cv.All( + cv.ensure_list(cv.string_strict), cv.Length(min=1) + ), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ).add_extra(cv.has_exactly_one_key(CONF_OPTIONS, CONF_LAMBDA)), +) +async def select_is_to_code(config, condition_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + if options := config.get(CONF_OPTIONS): + # List of constant options + # Create a constexpr and pass that with a template length + arr_id = ID( + f"{condition_id}_data", + is_declaration=True, + type=global_ns.namespace("constexpr char * const"), + ) + arg = cg.static_const_array(arr_id, cg.ArrayInitializer(*options)) + template_arg = TemplateArguments(len(options), *template_arg) + else: + # Lambda + arg = await cg.process_lambda( + config[CONF_LAMBDA], + [(global_ns.namespace("StringRef &").operator("const"), "current")] + args, + return_type=cg.bool_, + ) + template_arg = TemplateArguments(0, *template_arg) + return cg.new_Pvariable(condition_id, template_arg, paren, arg) + + @automation.register_action( "select.operation", SelectOperationAction, diff --git a/esphome/components/select/automation.h b/esphome/components/select/automation.h index dda5403557..81e8a3561d 100644 --- a/esphome/components/select/automation.h +++ b/esphome/components/select/automation.h @@ -66,4 +66,34 @@ template class SelectOperationAction : public Action { Select *select_; }; +template class SelectIsCondition : public Condition { + public: + SelectIsCondition(Select *parent, const char *const *option_list) : parent_(parent), option_list_(option_list) {} + + bool check(const Ts &...x) override { + auto current = this->parent_->current_option(); + for (size_t i = 0; i != N; i++) { + if (current == this->option_list_[i]) { + return true; + } + } + return false; + } + + protected: + Select *parent_; + const char *const *option_list_; +}; + +template class SelectIsCondition<0, Ts...> : public Condition { + public: + SelectIsCondition(Select *parent, std::function &&f) + : parent_(parent), f_(f) {} + + bool check(const Ts &...x) override { return this->f_(this->parent_->current_option(), x...); } + + protected: + Select *parent_; + std::function f_; +}; } // namespace esphome::select diff --git a/tests/components/template/common-base.yaml b/tests/components/template/common-base.yaml index 134ad4d046..3b888c3d19 100644 --- a/tests/components/template/common-base.yaml +++ b/tests/components/template/common-base.yaml @@ -53,6 +53,17 @@ binary_sensor: // Garage Door is closed. return false; } + - platform: template + id: select_binary_sensor + name: Select is one or two + condition: + any: + - select.is: + id: template_select + options: [one, two] + - select.is: + id: template_select + lambda: return current == id(template_text).state; - platform: template id: other_binary_sensor name: "Garage Door Closed" @@ -320,6 +331,7 @@ valve: text: - platform: template + id: template_text name: "Template text" optimistic: true min_length: 0