[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
This commit is contained in:
J. Nick Koston
2026-04-07 19:10:19 -10:00
parent c79d0e807b
commit a6538d56d9
6 changed files with 28 additions and 11 deletions
+2 -1
View File
@@ -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]))
+8 -1
View File
@@ -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_))
@@ -35,8 +35,6 @@ template<typename... Ts> class TotoAction : public RemoteTransmitterActionBase<T
data.rc_code_1 = this->rc_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);
}
};
+3 -3
View File
@@ -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)
+1 -2
View File
@@ -108,8 +108,7 @@ template<typename... Ts> class StartSingleValveAction : public Action<Ts...> {
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<typename V> void set_valve_to_start(V valve_to_start) { this->valve_to_start_ = valve_to_start; }
TEMPLATABLE_VALUE(uint32_t, valve_run_duration)
+14 -2
View File
@@ -110,9 +110,21 @@ template<typename T, typename... X> class TemplatableValue {
// Accept stateless lambdas (convertible to function pointer)
template<typename F> TemplatableValue(F f) requires std::convertible_to<F, T (*)(X...)> : tag_(FN) { this->f_ = f; }
// Reject stateful lambdas at compile time
// Convertible return type (e.g., int -> uint8_t) — casting trampoline
template<typename F>
TemplatableValue(F) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) = delete;
[[deprecated("Lambda return type does not match TemplatableValue<T> — use the correct type in "
"codegen")]] TemplatableValue(F) requires(!std::convertible_to<F, T (*)(X...)>) &&
std::invocable<F, X...> &&std::convertible_to<std::invoke_result_t<F, X...>, T> &&std::is_empty_v<F>
&&std::default_initializable<F> : tag_(FN) {
this->f_ = [](X... x) -> T { return static_cast<T>(F{}(x...)); };
}
// Reject any callable that didn't match the above
template<typename F>
TemplatableValue(F) requires std::invocable<F, X...> &&
(!std::convertible_to<F, T (*)(X...)>) &&(!std::is_empty_v<F> ||
!std::convertible_to<std::invoke_result_t<F, X...>, T> ||
!std::default_initializable<F>) = delete;
TemplatableValue(const TemplatableValue &other) : tag_(other.tag_) {
if (this->tag_ == VALUE) {