From a6538d56d93e3bfcd411fc058e0ce9350e671f87 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Apr 2026 19:10:19 -1000 Subject: [PATCH] [multiple] Fix codegen type mismatches and raw value setter calls - sprinkler: fix codegen to use cg.size_t for valve_number (was cg.uint8) - remote_base/toto: move send_times/send_wait defaults to codegen - remote_base/abbwelcome: wrap auto_message_id bool via cg.templatable - http_request: wrap capture_response bool via cg.templatable - core/automation.h: add casting trampoline to TemplatableValue (same as TemplatableFn) for codegen with mismatched return types --- esphome/components/http_request/__init__.py | 3 ++- esphome/components/remote_base/__init__.py | 9 ++++++++- esphome/components/remote_base/toto_protocol.h | 2 -- esphome/components/sprinkler/__init__.py | 6 +++--- esphome/components/sprinkler/automation.h | 3 +-- esphome/core/automation.h | 16 ++++++++++++++-- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index ce1a3fcecc..90879c459e 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -307,7 +307,8 @@ async def http_request_action_to_code(config, action_id, template_arg, args): capture_response = config[CONF_CAPTURE_RESPONSE] if capture_response: - cg.add(var.set_capture_response(capture_response)) + template_ = await cg.templatable(capture_response, args, cg.bool_) + cg.add(var.set_capture_response(template_)) cg.add_define("USE_HTTP_REQUEST_RESPONSE") cg.add(var.set_max_response_buffer_size(config[CONF_MAX_RESPONSE_BUFFER_SIZE])) diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 99eda76f81..042ac9d46a 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -2123,7 +2123,8 @@ async def abbwelcome_action(var, config, args): await cg.templatable(config[CONF_MESSAGE_TYPE], args, cg.uint8) ) ) - cg.add(var.set_auto_message_id(CONF_MESSAGE_ID not in config)) + template_ = await cg.templatable(CONF_MESSAGE_ID not in config, args, cg.bool_) + cg.add(var.set_auto_message_id(template_)) if CONF_MESSAGE_ID in config: cg.add( var.set_message_id( @@ -2231,3 +2232,9 @@ async def Toto_action(var, config, args): cg.add(var.set_rc_code_2(template_)) template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) cg.add(var.set_command(template_)) + # Set toto-specific defaults (only if user didn't configure repeat) + if CONF_REPEAT not in config: + template_ = await cg.templatable(3, args, cg.uint32) + cg.add(var.set_send_times(template_)) + template_ = await cg.templatable(36000, args, cg.uint32) + cg.add(var.set_send_wait(template_)) diff --git a/esphome/components/remote_base/toto_protocol.h b/esphome/components/remote_base/toto_protocol.h index 6a635b0f7c..53d453f7e3 100644 --- a/esphome/components/remote_base/toto_protocol.h +++ b/esphome/components/remote_base/toto_protocol.h @@ -35,8 +35,6 @@ template class TotoAction : public RemoteTransmitterActionBaserc_code_1_.value(x...); data.rc_code_2 = this->rc_code_2_.value(x...); data.command = this->command_.value(x...); - this->set_send_times(this->send_times_.value_or(x..., 3)); - this->set_send_wait(this->send_wait_.value_or(x..., 36000)); TotoProtocol().encode(dst, data); } }; diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index fb2beb5b16..efa5b0bf15 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -455,7 +455,7 @@ async def sprinkler_set_multiplier_to_code(config, action_id, template_arg, args async def sprinkler_set_queued_valve_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.uint8) + template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.size_t) cg.add(var.set_valve_number(template_)) template_ = await cg.templatable(config[CONF_RUN_DURATION], args, cg.uint32) cg.add(var.set_valve_run_duration(template_)) @@ -487,7 +487,7 @@ async def sprinkler_set_valve_run_duration_to_code( ): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.uint8) + template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.size_t) cg.add(var.set_valve_number(template_)) template_ = await cg.templatable(config[CONF_RUN_DURATION], args, cg.uint32) cg.add(var.set_valve_run_duration(template_)) @@ -525,7 +525,7 @@ async def sprinkler_start_full_cycle_to_code(config, action_id, template_arg, ar async def sprinkler_start_single_valve_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.uint8) + template_ = await cg.templatable(config[CONF_VALVE_NUMBER], args, cg.size_t) cg.add(var.set_valve_to_start(template_)) if CONF_RUN_DURATION in config: template_ = await cg.templatable(config[CONF_RUN_DURATION], args, cg.uint32) diff --git a/esphome/components/sprinkler/automation.h b/esphome/components/sprinkler/automation.h index ed091ac5d7..c6fe2e4e02 100644 --- a/esphome/components/sprinkler/automation.h +++ b/esphome/components/sprinkler/automation.h @@ -108,8 +108,7 @@ template class StartSingleValveAction : public Action { public: explicit StartSingleValveAction(Sprinkler *a_sprinkler) : sprinkler_(a_sprinkler) {} - // valve_to_start uses TemplatableValue (not TemplatableFn) because it is set - // from both codegen (lambdas) and C++ (raw values in sprinkler.cpp). + // TemplatableValue (not TemplatableFn) — also set from C++ with raw values in sprinkler.cpp template void set_valve_to_start(V valve_to_start) { this->valve_to_start_ = valve_to_start; } TEMPLATABLE_VALUE(uint32_t, valve_run_duration) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index e448faad48..7d5981c3b8 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -110,9 +110,21 @@ template class TemplatableValue { // Accept stateless lambdas (convertible to function pointer) template TemplatableValue(F f) requires std::convertible_to : tag_(FN) { this->f_ = f; } - // Reject stateful lambdas at compile time + // Convertible return type (e.g., int -> uint8_t) — casting trampoline template - TemplatableValue(F) requires std::invocable &&(!std::convertible_to) = delete; + [[deprecated("Lambda return type does not match TemplatableValue — use the correct type in " + "codegen")]] TemplatableValue(F) requires(!std::convertible_to) && + std::invocable &&std::convertible_to, T> &&std::is_empty_v + &&std::default_initializable : tag_(FN) { + this->f_ = [](X... x) -> T { return static_cast(F{}(x...)); }; + } + + // Reject any callable that didn't match the above + template + TemplatableValue(F) requires std::invocable && + (!std::convertible_to) &&(!std::is_empty_v || + !std::convertible_to, T> || + !std::default_initializable) = delete; TemplatableValue(const TemplatableValue &other) : tag_(other.tag_) { if (this->tag_ == VALUE) {