diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 30d3e23bf6f..9d5b6ad49ce 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -303,19 +303,20 @@ async def cover_control_to_code(config, action_id, template_arg, args): # All configured fields are folded into a single stateless lambda whose # constants live in flash; the action stores only a function pointer. - # CONF_STATE and CONF_POSITION are mutually exclusive in the schema and - # both map to set_position. - fields: list[tuple[object, str, object]] = [] - if (stop := config.get(CONF_STOP)) is not None: - fields.append((stop, "set_stop", cg.bool_)) - if (position := config.get(CONF_STATE, config.get(CONF_POSITION))) is not None: - fields.append((position, "set_position", cg.float_)) - if (tilt := config.get(CONF_TILT)) is not None: - fields.append((tilt, "set_tilt", cg.float_)) + # CONF_STATE and CONF_POSITION are cv.Exclusive in the schema, so at most + # one is present and both dispatch to set_position. + FIELDS = ( + (CONF_STOP, "set_stop", cg.bool_), + (CONF_STATE, "set_position", cg.float_), + (CONF_POSITION, "set_position", cg.float_), + (CONF_TILT, "set_tilt", cg.float_), + ) fwd_args = ", ".join(name for _, name in args) body_lines: list[str] = [] - for value, setter, type_ in fields: + for conf_key, setter, type_ in FIELDS: + if (value := config.get(conf_key)) is None: + continue if isinstance(value, Lambda): inner = await cg.process_lambda(value, args, return_type=type_) body_lines.append(f"call.{setter}(({inner})({fwd_args}));") diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index 8b39dc5b631..1af003d75d6 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -135,21 +135,20 @@ async def cover_template_publish_to_code(config, action_id, template_arg, args): # constants live in flash; the action stores only a function pointer. # The lambda mutates Cover fields directly (no CoverCall) since publish # is a state push, not a control request. - # CONF_STATE and CONF_POSITION both map to position. - fields: list[tuple[object, str, object]] = [] - position_value = config.get(CONF_STATE, config.get(CONF_POSITION)) - if position_value is not None: - fields.append((position_value, "position", cg.float_)) - if CONF_TILT in config: - fields.append((config[CONF_TILT], "tilt", cg.float_)) - if CONF_CURRENT_OPERATION in config: - fields.append( - (config[CONF_CURRENT_OPERATION], "current_operation", cover.CoverOperation) - ) + # CONF_STATE and CONF_POSITION are cv.Exclusive in the schema, so at most + # one is present and both map to the position field. + FIELDS = ( + (CONF_STATE, "position", cg.float_), + (CONF_POSITION, "position", cg.float_), + (CONF_TILT, "tilt", cg.float_), + (CONF_CURRENT_OPERATION, "current_operation", cover.CoverOperation), + ) fwd_args = ", ".join(name for _, name in args) body_lines: list[str] = [] - for value, field, type_ in fields: + for conf_key, field, type_ in FIELDS: + if (value := config.get(conf_key)) is None: + continue if isinstance(value, Lambda): inner = await cg.process_lambda(value, args, return_type=type_) body_lines.append(f"cover->{field} = ({inner})({fwd_args});")