Compare commits

...
7 changed files with 43 additions and 29 deletions

No files matched your search

@@ -189,8 +189,7 @@ def register_start_scan_action(name: str, tracker_class: MockObjClass) -> None:
if (continuous := config.get(CONF_CONTINUOUS)) is None:
mode = tracker.configured_continuous()
elif cg.is_template(continuous):
lambda_ = await cg.process_lambda(continuous, args, return_type=cg.bool_)
mode = automation.immediate_lambda_call(lambda_)
mode = await automation.call_user_lambda(continuous, args, cg.bool_)
else:
mode = continuous
text = str(cg.statement(ble_device_base_ns.start_scan(tracker, mode)))
+7 -8
View File
@@ -64,7 +64,7 @@ from esphome.core import (
coroutine,
coroutine_with_priority,
)
from esphome.cpp_generator import MockObj
from esphome.cpp_generator import MockObj, StaticCastExpression
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType, SafeExpType, TemplateArgsType
from esphome.util import Registry, SimpleRegistry
@@ -472,14 +472,13 @@ async def _transmit_value(
type_: SafeExpType,
const_fn: Callable[[Any], Any] = cg.safe_exp,
) -> str:
"""Argument text for one templatable transmit value.
A lambda keeps the trigger's own argument types and is always called, so its body may
modify its by-value arguments.
"""
"""Argument text for one templatable transmit value."""
if isinstance(value, Lambda):
lamb = await cg.process_lambda(value, args, return_type=type_)
return str(automation.immediate_lambda_call(lamb))
expr = await automation.call_user_lambda(value, args, type_)
if isinstance(expr, cg.RawExpression):
# Used as an operand (.transmit, [0]): the cast binds as a whole and converts like the lambda's return
expr = StaticCastExpression(type_, expr)
return str(expr)
return str(const_fn(value))
+13 -9
View File
@@ -7,6 +7,7 @@ from esphome.components.const import CONF_ON_STOP
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE, CONF_PARAMETERS, CONF_RESTART
from esphome.core import CORE, EsphomeError
from esphome.cpp_generator import AssignmentExpression, call_lambda
from esphome.types import ConfigType
CODEOWNERS = ["@esphome/core"]
@@ -242,9 +243,13 @@ async def script_execute_action_to_code(config, action_id, template_arg, args):
raise EsphomeError(
f"Missing parameter: '{name}' in script.execute {config[CONF_ID]}"
)
arg = await cg.templatable(
config_args[name], args, type, convert(str(type))
)
value = config_args[name]
if cg.is_template(value):
arg = await automation.call_user_lambda(value, args, type)
else:
arg = call_lambda(
await cg.templatable(value, args, type, convert(str(type)))
)
script_args.append(arg)
return script_args
@@ -255,14 +260,13 @@ async def script_execute_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
# Separate statements, because the order of evaluation inside one call is not defined
names = [cg.MockObj(f"esphome__arg_{index}") for index in range(len(script_args))]
statements = [
f"auto esphome__arg_{index} = {automation.immediate_lambda_call(arg)};"
for index, arg in enumerate(script_args)
str(cg.statement(AssignmentExpression("auto", "", name, arg)))
for name, arg in zip(names, script_args, strict=True)
]
names = ", ".join(
f"std::move(esphome__arg_{index})" for index in range(len(script_args))
)
statements.append(f"{automation.parent_ref(paren)}->execute({names});")
moved = [cg.std_ns.move(name) for name in names]
statements.append(str(cg.statement(automation.parent_ref(paren).execute(*moved))))
return automation.apply_statements_action(action_id, template_arg, args, statements)
@@ -106,8 +106,8 @@ def test_rc_switch_raw(bodies: list[str]) -> None:
lambda_ = bodies[6]
_in_order(
lambda_,
"const std::string esphome__code = []() -> std::string {\n"
' return std::string("0010100110");',
# A lambda naming no argument is reduced to its expression
'const std::string esphome__code = static_cast<std::string>(std::string("0010100110"));',
"remote_base::rc_switch_protocol(1).transmit(esphome__call.get_data(), "
"remote_base::decode_binary_string(esphome__code), esphome__code.size());",
"esphome__call.set_send_times(2);",
@@ -140,9 +140,9 @@ def test_rc_switch_type_a(bodies: list[str]) -> None:
_in_order(
bodies[8],
"const auto esphome__group = remote_base::decode_binary_string("
'[]() -> std::string {\n return std::string("11001");',
'static_cast<std::string>(std::string("11001")));',
"const auto esphome__device = remote_base::decode_binary_string(",
"const auto esphome__state = []() -> bool {\n return false;",
"const auto esphome__state = static_cast<bool>(false);",
"remote_base::RCSwitchBase::type_a_code(esphome__group, esphome__device, "
"esphome__state, &esphome__code, &esphome__nbits);",
"remote_base::rc_switch_protocol(1).transmit(",
@@ -163,11 +163,12 @@ def test_rc_switch_type_c(bodies: list[str]) -> None:
) in bodies[10]
_in_order(
bodies[11],
# A bare expression is cast to the lambda's type, so it binds as an operand
"const auto esphome__family = static_cast<uint8_t>(tolower("
"[]() -> std::string {\n return std::string(\"C\");\n }()[0]) - 'a');",
"const auto esphome__group = []() -> uint8_t {",
"const auto esphome__device = []() -> uint8_t {",
"const auto esphome__state = []() -> bool {",
"static_cast<std::string>(std::string(\"C\"))[0]) - 'a');",
"const auto esphome__group = static_cast<uint8_t>(1);",
"const auto esphome__device = static_cast<uint8_t>(2);",
"const auto esphome__state = static_cast<bool>(true);",
"remote_base::RCSwitchBase::type_c_code(esphome__family, esphome__group, "
"esphome__device, esphome__state, &esphome__code, &esphome__nbits);",
)
@@ -180,7 +181,7 @@ def test_rc_switch_type_d(bodies: list[str]) -> None:
) in bodies[12]
assert (
"const auto esphome__group = static_cast<uint8_t>(tolower("
"[]() -> std::string {\n return std::string(\"b\");\n }()[0]) - 'a');"
"static_cast<std::string>(std::string(\"b\"))[0]) - 'a');"
) in bodies[13]
@@ -4,6 +4,8 @@ esphome:
then:
- rp2_ble_tracker.start_scan:
continuous: true
- rp2_ble_tracker.start_scan:
continuous: !lambda return millis() > 0;
# Bare form: passes the configured scan_parameters mode (asserted in the codegen test).
- rp2_ble_tracker.start_scan:
- rp2_ble_tracker.stop_scan
@@ -42,6 +42,11 @@ def test_trigger_codegen(
main_cpp.count("::rp2_ble_tracker_rp2bletracker_id->configured_continuous());")
== 1
)
# A continuous lambda naming no trigger argument is reduced to its expression
assert (
"ble_device_base::start_scan(::rp2_ble_tracker_rp2bletracker_id, "
"static_cast<bool>(millis() > 0));"
) in main_cpp
assert main_cpp.count("->stop_scan();") == 1
# scan_parameters continuous: false reaches the YAML-mode setter, not the
# runtime override.
@@ -105,7 +105,11 @@ def test_parameters_by_value(generate_main) -> None:
)
body = _function_body(main_cpp, name)
assert body.count("[](int x, std::string s) -> void {") == 2
assert body.count("[](int x, std::string s) -> int {") == 2
# A lambda passed to another script copies only the parameters it names
assert (
body.count("[](int x, const std::remove_cvref_t<std::string> & s) -> int {")
== 2
)
assert "x *= 2;\n ESP_LOGI(" in body
assert '"BY_VALUE again %d", x *= 2' in body
assert "return x *= 2;" in body