From 05949b7e3abe20c75263a333b4984d6317c0a291 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 31 Oct 2026 02:45:49 -1000 Subject: [PATCH] [remote_base] Add tests for transmit_raw and rc_switch transmit code generation (#22000) --- .../config/raw_rc_switch_actions.yaml | 117 +++++++++++++++ .../test_raw_rc_switch_actions.py | 135 ++++++++++++++++++ .../remote_transmitter/common-buttons.yaml | 35 +++++ 3 files changed, 287 insertions(+) create mode 100644 tests/component_tests/remote_transmitter/config/raw_rc_switch_actions.yaml create mode 100644 tests/component_tests/remote_transmitter/test_raw_rc_switch_actions.py diff --git a/tests/component_tests/remote_transmitter/config/raw_rc_switch_actions.yaml b/tests/component_tests/remote_transmitter/config/raw_rc_switch_actions.yaml new file mode 100644 index 00000000000..e1851ae8438 --- /dev/null +++ b/tests/component_tests/remote_transmitter/config/raw_rc_switch_actions.yaml @@ -0,0 +1,117 @@ +esphome: + name: test + +esp8266: + board: esp01_1m + +remote_transmitter: + id: tx + pin: GPIO4 + carrier_duty_percent: 50% + +script: + - id: send_scaled + parameters: + x: int + then: + - remote_transmitter.transmit_raw: + code: !lambda |- + x *= 2; + return {x, -1000}; + carrier_frequency: !lambda return x *= 1000; + repeat: + times: !lambda return x *= 1; + wait_time: !lambda x *= 3; return x; + - remote_transmitter.transmit_rc_switch_type_b: + address: !lambda x *= 2; return x; + channel: !lambda return x *= 1; + state: !lambda return x > 0; + protocol: !lambda x *= 1; return remote_base::rc_switch_protocol(x); + +button: + - platform: template + name: Raw static + on_press: + - remote_transmitter.transmit_raw: + code: [1000, -1000, 500, -500] + - platform: template + name: Raw static repeat + on_press: + - remote_transmitter.transmit_raw: + code: [1000, -1000, 500, -500] + carrier_frequency: 38kHz + repeat: + times: 2 + wait_time: 10ms + - platform: template + name: Raw lambda + on_press: + - remote_transmitter.transmit_raw: + code: !lambda return {2000, -1000}; + - platform: template + name: RC switch raw + on_press: + - remote_transmitter.transmit_rc_switch_raw: + code: "00101001100111110101xxxx" + protocol: 2 + - platform: template + name: RC switch raw lambda + on_press: + - remote_transmitter.transmit_rc_switch_raw: + code: !lambda return std::string("0010100110"); + repeat: + times: 2 + wait_time: 10ms + - platform: template + name: RC switch type A + on_press: + - remote_transmitter.transmit_rc_switch_type_a: + group: "11001" + device: "01000" + state: true + protocol: + pulse_length: 175 + - platform: template + name: RC switch type A lambda + on_press: + - remote_transmitter.transmit_rc_switch_type_a: + group: !lambda return std::string("11001"); + device: !lambda return std::string("01000"); + state: !lambda return false; + - platform: template + name: RC switch type B + on_press: + - remote_transmitter.transmit_rc_switch_type_b: + address: 4 + channel: 2 + state: true + - platform: template + name: RC switch type C + on_press: + - remote_transmitter.transmit_rc_switch_type_c: + family: "c" + group: 1 + device: 2 + state: true + - platform: template + name: RC switch type C lambda + on_press: + - remote_transmitter.transmit_rc_switch_type_c: + family: !lambda return std::string("C"); + group: !lambda return 1; + device: !lambda return 2; + state: !lambda return true; + - platform: template + name: RC switch type D + on_press: + - remote_transmitter.transmit_rc_switch_type_d: + group: "b" + device: 3 + state: false + - platform: template + name: RC switch type D lambda + on_press: + - remote_transmitter.transmit_rc_switch_type_d: + group: !lambda return std::string("b"); + device: !lambda return 3; + state: !lambda return false; diff --git a/tests/component_tests/remote_transmitter/test_raw_rc_switch_actions.py b/tests/component_tests/remote_transmitter/test_raw_rc_switch_actions.py new file mode 100644 index 00000000000..1703ddf6ede --- /dev/null +++ b/tests/component_tests/remote_transmitter/test_raw_rc_switch_actions.py @@ -0,0 +1,135 @@ +"""transmit_raw and the rc_switch transmit actions as generated code.""" + +from collections.abc import Callable +from pathlib import Path +import re + +import pytest + +CONFIG = "raw_rc_switch_actions.yaml" + + +@pytest.fixture +def main_cpp( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> str: + # Without the #line directives a lambda body is the user's text alone + main = generate_main(component_config_path(CONFIG)) + return re.sub(r"\n *(#line .*|// ESPHOME_LINE_DIRECTIVE_RESET)(?=\n)", "", main) + + +def _action(main_cpp: str, action_id: str) -> str: + """The setup statements of one action object.""" + marker = f"new({action_id}) " + start = main_cpp.index(marker) + # Ends at the next object constructed, or at the end of the file + end = main_cpp.find("new(", start + len(marker)) + return main_cpp[start : end if end != -1 else len(main_cpp)] + + +def test_raw_static_code_in_flash(main_cpp: str) -> None: + raw = _action(main_cpp, "remote_base_rawaction_id_2") + assert ( + "static constexpr int32_t int32_t_id_2[] PROGMEM = {1000, -1000, 500, -500};" + in raw + ) + assert "->set_code_static(int32_t_id_2, 4);" in raw + assert "->set_carrier_frequency([]() -> uint32_t {\n return 0;\n});" in raw + # Without repeat the call sends once + assert "set_send_times" not in raw + assert "set_send_wait" not in raw + + +def test_raw_repeat_and_carrier(main_cpp: str) -> None: + raw = _action(main_cpp, "remote_base_rawaction_id_3") + assert "->set_send_times([]() -> uint32_t {\n return 2;\n});" in raw + assert "->set_send_wait([]() -> uint32_t {\n return 10000;\n});" in raw + assert "->set_carrier_frequency([]() -> uint32_t {\n return 38000;\n});" in raw + + +def test_raw_lambda_code(main_cpp: str) -> None: + raw = _action(main_cpp, "remote_base_rawaction_id_4") + assert ( + "->set_code_template([]() -> std::vector {\n" + " return {2000, -1000};\n" + "});" + ) in raw + + +def test_rc_switch_raw(main_cpp: str) -> None: + constant = _action(main_cpp, "remote_base_rcswitchrawaction_id") + assert "return remote_base::rc_switch_protocol(2);" in constant + assert '->set_code(ESPHOME_F("00101001100111110101xxxx"));' in constant + # The rc_switch actions also send once without repeat + assert "set_send_times" not in constant + lambda_ = _action(main_cpp, "remote_base_rcswitchrawaction_id_2") + assert "return remote_base::rc_switch_protocol(1);" in lambda_ + assert ' return std::string("0010100110");' in lambda_ + assert "->set_send_times([]() -> uint32_t {\n return 2;\n});" in lambda_ + assert "->set_send_wait([]() -> uint32_t {\n return 10000;\n});" in lambda_ + + +def test_rc_switch_type_a(main_cpp: str) -> None: + constant = _action(main_cpp, "remote_base_rcswitchtypeaaction_id") + assert ( + "return remote_base::rc_switch_protocol_copy(rc_switch_custom_protocol);" + in constant + ) + assert '->set_group(ESPHOME_F("11001"));' in constant + assert '->set_device(ESPHOME_F("01000"));' in constant + assert "->set_state([]() -> bool {\n return true;\n});" in constant + lambda_ = _action(main_cpp, "remote_base_rcswitchtypeaaction_id_2") + assert '->set_group([]() -> std::string {\n return std::string("11001");' in ( + lambda_ + ) + assert '->set_device([]() -> std::string {\n return std::string("01000");' in ( + lambda_ + ) + assert "->set_state([]() -> bool {\n return false;" in lambda_ + + +def test_rc_switch_type_b(main_cpp: str) -> None: + constant = _action(main_cpp, "remote_base_rcswitchtypebaction_id_2") + assert "->set_address([]() -> uint8_t {\n return 4;\n});" in constant + assert "->set_channel([]() -> uint8_t {\n return 2;\n});" in constant + assert "->set_state([]() -> bool {\n return true;\n});" in constant + + +def test_rc_switch_type_c(main_cpp: str) -> None: + constant = _action(main_cpp, "remote_base_rcswitchtypecaction_id") + assert '->set_family(ESPHOME_F("c"));' in constant + assert "->set_group([]() -> uint8_t {\n return 1;\n});" in constant + assert "->set_device([]() -> uint8_t {\n return 2;\n});" in constant + lambda_ = _action(main_cpp, "remote_base_rcswitchtypecaction_id_2") + assert '->set_family([]() -> std::string {\n return std::string("C");' in ( + lambda_ + ) + + +def test_rc_switch_type_d(main_cpp: str) -> None: + constant = _action(main_cpp, "remote_base_rcswitchtypedaction_id") + assert '->set_group(ESPHOME_F("b"));' in constant + assert "->set_device([]() -> uint8_t {\n return 3;\n});" in constant + assert "->set_state([]() -> bool {\n return false;\n});" in constant + lambda_ = _action(main_cpp, "remote_base_rcswitchtypedaction_id_2") + assert '->set_group([]() -> std::string {\n return std::string("b");' in lambda_ + + +def test_lambdas_take_args_by_value(main_cpp: str) -> None: + """User lambdas get the script argument by value, so they may modify it.""" + raw = _action(main_cpp, "remote_base_rawaction_id") + assert ( + "[](int x) -> std::vector {\n x *= 2;\n return {x, -1000};\n}" + in raw + ) + assert "[](int x) -> uint32_t {\n return x *= 1000;\n}" in raw + assert "[](int x) -> uint32_t {\n return x *= 1;\n}" in raw + assert "[](int x) -> uint32_t {\n x *= 3; return x;\n}" in raw + type_b = _action(main_cpp, "remote_base_rcswitchtypebaction_id") + assert "[](int x) -> uint8_t {\n x *= 2; return x;\n}" in type_b + assert "[](int x) -> uint8_t {\n return x *= 1;\n}" in type_b + assert ( + "[](int x) -> remote_base::RCSwitchBase {\n" + " x *= 1; return remote_base::rc_switch_protocol(x);\n}" + ) in type_b diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index e09ada83018..350d5e3d7c5 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -1059,3 +1059,38 @@ button: - remote_transmitter.transmit_raw: code: [1000, -1000] carrier_frequency: !lambda return 38000; + +script: + # The lambdas modify their by-value argument + - id: remote_transmitter_send_scaled + parameters: + x: int + then: + - remote_transmitter.transmit_raw: + code: !lambda |- + x *= 2; + return {x, -1000}; + carrier_frequency: !lambda return x *= 1000; + repeat: + times: !lambda return x *= 1; + wait_time: !lambda x *= 3; return x; + - remote_transmitter.transmit_rc_switch_raw: + code: !lambda x *= 1; return std::string(x, '1'); + - remote_transmitter.transmit_rc_switch_type_a: + group: !lambda x *= 1; return std::string("11001"); + device: !lambda return std::string(x *= 1, '0'); + state: !lambda return (x *= 1) > 0; + - remote_transmitter.transmit_rc_switch_type_b: + address: !lambda x *= 2; return x; + channel: !lambda return x *= 1; + state: !lambda return x > 0; + protocol: !lambda x *= 1; return remote_base::rc_switch_protocol(x); + - remote_transmitter.transmit_rc_switch_type_c: + family: !lambda x *= 1; return std::string("a"); + group: !lambda return x *= 1; + device: !lambda x += 1; return x; + state: !lambda return x > 0; + - remote_transmitter.transmit_rc_switch_type_d: + group: !lambda x *= 1; return std::string("a"); + device: !lambda return x *= 1; + state: !lambda x *= 1; return true;