mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
[remote_base] Generate the byte payload transmit actions with no C++ class (#20568)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import re
|
||||
|
||||
_PROTOCOL_CALL_BODY = re.compile(
|
||||
r"auto apply_call = \S+->protocol_call<remote_base::\w+>\(\);\n(.*?)apply_call\.perform\(\);",
|
||||
r"auto apply_call = \S+->(?:protocol|encode)_call<remote_base::\w+>\(\);\n(.*?)apply_call\.perform\(\);",
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
remote_transmitter:
|
||||
pin: GPIO4
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: ABBWelcome fixed id
|
||||
on_press:
|
||||
- remote_transmitter.transmit_abbwelcome:
|
||||
source_address: 0x123456
|
||||
destination_address: 0x56789A
|
||||
three_byte_address: true
|
||||
message_type: 0x01
|
||||
message_id: 0x42
|
||||
data: [0x10]
|
||||
- platform: template
|
||||
name: AEHA lambda
|
||||
on_press:
|
||||
- remote_transmitter.transmit_aeha:
|
||||
address: !lambda return 0x8008;
|
||||
carrier_frequency: 36700Hz
|
||||
data: !lambda return {0x00, 0x02};
|
||||
- platform: template
|
||||
name: Midea lambda
|
||||
on_press:
|
||||
- remote_transmitter.transmit_midea:
|
||||
code: !lambda return {0xA2, 0x08, 0xFF, 0xFF, 0xFF};
|
||||
@@ -9,13 +9,17 @@ def test_constant_data_shares_progmem_tables(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""Equal data shares one table, empty or missing data is an empty table, lambdas stay templates."""
|
||||
"""Equal data shares one table, empty data is a null table, lambdas are called inline."""
|
||||
main_cpp = generate_main(component_config_path("abbwelcome_payload_tables.yaml"))
|
||||
|
||||
tables = re.findall(
|
||||
r"static constexpr uint8_t (\w+)\[\] PROGMEM = (\{[^}]*\});", main_cpp
|
||||
)
|
||||
assert [v for _, v in tables] == ["{0x10, 0x20, 0x30}"]
|
||||
assert main_cpp.count(f"set_data_static({tables[0][0]}, 3);") == 2
|
||||
assert main_cpp.count("set_data_static(nullptr, 0);") == 2
|
||||
assert main_cpp.count("set_data_template(") == 1
|
||||
assert main_cpp.count(f"apply_call.data.set_data({tables[0][0]}, 3);") == 2
|
||||
# Empty data is a null table; missing data leaves the default empty payload
|
||||
assert main_cpp.count("apply_call.data.set_data(nullptr, 0);") == 1
|
||||
assert (
|
||||
main_cpp.count("apply_call.data.set_data([]() -> std::vector<uint8_t> {") == 1
|
||||
)
|
||||
assert "std::vector<uint8_t>{" not in main_cpp
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
"""The byte payload transmit actions as generated code."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
from tests.component_tests.remote_transmitter import protocol_call_bodies
|
||||
|
||||
|
||||
def test_byte_payloads(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
main_cpp = generate_main(component_config_path("byte_payloads.yaml"))
|
||||
abb, aeha, midea = protocol_call_bodies(main_cpp)
|
||||
# The address length goes first, a configured id turns the automatic one off
|
||||
assert abb.index("set_three_byte_address(true);") < abb.index("set_source_address(")
|
||||
assert abb.index("auto_message_id = true;") < abb.index(
|
||||
"set_fixed_message_id(0x42);"
|
||||
)
|
||||
assert abb.rstrip().endswith("apply_call.data.finalize();")
|
||||
# The carrier is set before the payload is encoded
|
||||
assert aeha.index("set_carrier_frequency(36700);") < aeha.index(
|
||||
"apply_call.encode("
|
||||
)
|
||||
assert (
|
||||
"apply_call.encode(static_cast<uint16_t>(0x8008), []() -> std::vector<uint8_t> {"
|
||||
in aeha
|
||||
)
|
||||
assert (
|
||||
"apply_call.data = remote_base::MideaData([]() -> std::vector<uint8_t> {"
|
||||
in midea
|
||||
)
|
||||
assert midea.rstrip().endswith("apply_call.data.finalize();")
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Constant IR byte codes live in shared PROGMEM tables; lambdas stay lambdas."""
|
||||
"""Constant IR byte codes are encoded from shared PROGMEM tables; lambdas are called inline."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
@@ -16,9 +16,10 @@ def test_ir_codes_use_shared_progmem_tables(
|
||||
)
|
||||
assert len(tables) == 4 # Midea (shared by two actions), AEHA, Haier, Mirage
|
||||
midea = tables[0]
|
||||
assert main_cpp.count(f"->set_code_static({midea}, 5);") == 2
|
||||
assert re.search(r"->set_data_static\(remote_base_code\w*, 6\);", main_cpp)
|
||||
assert re.search(r"->set_code_static\(remote_base_code\w*, 13\);", main_cpp)
|
||||
assert re.search(r"->set_code_static\(remote_base_code\w*, 14\);", main_cpp)
|
||||
assert "->set_code_template([](" in main_cpp
|
||||
assert main_cpp.count(f"apply_call.data = remote_base::MideaData({midea}, 5);") == 2
|
||||
assert re.search(r"apply_call\.encode\(0x8008, remote_base_code\w*, 6\);", main_cpp)
|
||||
assert re.search(r"apply_call\.encode\(remote_base_code\w*, 13\);", main_cpp)
|
||||
assert re.search(r"apply_call\.encode\(remote_base_code\w*, 14\);", main_cpp)
|
||||
assert "apply_call.encode([]() -> std::vector<uint8_t> {" in main_cpp
|
||||
# Constant codes are read from the table, never copied into a vector
|
||||
assert "std::vector<uint8_t>{" not in main_cpp
|
||||
|
||||
@@ -62,27 +62,15 @@ RawTimings with_trailing_space(RawTimings timings) {
|
||||
return timings;
|
||||
}
|
||||
|
||||
template<typename A> RawTimings encode(A &action) {
|
||||
RemoteTransmitData dst;
|
||||
action.encode(&dst);
|
||||
return dst.get_data();
|
||||
}
|
||||
|
||||
// Exposes the protected encode() of an action for testing
|
||||
template<typename A> class Open : public A {
|
||||
public:
|
||||
using A::encode;
|
||||
};
|
||||
|
||||
// A constant and a lambda code must both encode to the golden timings
|
||||
template<typename A>
|
||||
void expect_static_and_lambda_match(const uint8_t *code, size_t len, std::vector<uint8_t> (*lambda)(),
|
||||
const RawTimings &golden) {
|
||||
Open<A> fixed, from_lambda;
|
||||
fixed.set_code_static(code, len);
|
||||
from_lambda.set_code_template(lambda);
|
||||
EXPECT_EQ(encode(fixed), golden);
|
||||
EXPECT_EQ(encode(from_lambda), golden);
|
||||
// A constant table and a lambda's vector take the two encoder overloads generated actions call
|
||||
template<typename P, typename... Lead>
|
||||
void expect_pointer_and_vector_match(const uint8_t *code, size_t len, const std::vector<uint8_t> &bytes,
|
||||
const RawTimings &golden, const Lead &...lead) {
|
||||
RemoteTransmitData fixed, from_vector;
|
||||
P().encode(&fixed, lead..., code, len);
|
||||
P().encode(&from_vector, lead..., bytes);
|
||||
EXPECT_EQ(fixed.get_data(), golden);
|
||||
EXPECT_EQ(from_vector.get_data(), golden);
|
||||
}
|
||||
|
||||
template<typename P> void expect_decodes_to(const RawTimings &timings, const std::vector<uint8_t> &code) {
|
||||
@@ -94,25 +82,16 @@ template<typename P> void expect_decodes_to(const RawTimings &timings, const std
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(IrCodeEncodersTest, HaierStaticAndLambdaMatchGolden) {
|
||||
expect_static_and_lambda_match<HaierAction<>>(HAIER_CODE, sizeof(HAIER_CODE), haier_code, HAIER_GOLDEN);
|
||||
TEST(IrCodeEncodersTest, HaierPointerAndVectorMatchGolden) {
|
||||
expect_pointer_and_vector_match<HaierProtocol>(HAIER_CODE, sizeof(HAIER_CODE), haier_code(), HAIER_GOLDEN);
|
||||
}
|
||||
|
||||
TEST(IrCodeEncodersTest, MirageStaticAndLambdaMatchGolden) {
|
||||
expect_static_and_lambda_match<MirageAction<>>(MIRAGE_CODE, sizeof(MIRAGE_CODE), mirage_code, MIRAGE_GOLDEN);
|
||||
TEST(IrCodeEncodersTest, MiragePointerAndVectorMatchGolden) {
|
||||
expect_pointer_and_vector_match<MirageProtocol>(MIRAGE_CODE, sizeof(MIRAGE_CODE), mirage_code(), MIRAGE_GOLDEN);
|
||||
}
|
||||
|
||||
TEST(IrCodeEncodersTest, AehaStaticAndLambdaMatchGolden) {
|
||||
Open<AEHAAction<>> fixed, lambda;
|
||||
for (auto *action : {&fixed, &lambda}) {
|
||||
// TemplatableFn fields take functions, not raw constants
|
||||
action->set_address([]() -> uint16_t { return AEHA_ADDRESS; });
|
||||
action->set_carrier_frequency([]() -> uint32_t { return 38000; });
|
||||
}
|
||||
fixed.set_data_static(AEHA_CODE, sizeof(AEHA_CODE));
|
||||
lambda.set_data_template(aeha_code);
|
||||
EXPECT_EQ(encode(fixed), AEHA_GOLDEN);
|
||||
EXPECT_EQ(encode(lambda), AEHA_GOLDEN);
|
||||
TEST(IrCodeEncodersTest, AehaPointerAndVectorMatchGolden) {
|
||||
expect_pointer_and_vector_match<AEHAProtocol>(AEHA_CODE, sizeof(AEHA_CODE), aeha_code(), AEHA_GOLDEN, AEHA_ADDRESS);
|
||||
}
|
||||
|
||||
TEST(IrCodeEncodersTest, StructEncodersMatchGolden) {
|
||||
|
||||
Reference in New Issue
Block a user