[remote_base] Keep constant abbwelcome payloads in shared flash tables (#20311)

This commit is contained in:
J. Nick Koston
2026-10-11 14:55:22 -10:00
committed by GitHub
parent 643f29fae6
commit 40425610d5
4 changed files with 74 additions and 39 deletions
+9 -12
View File
@@ -44,7 +44,7 @@ from esphome.const import (
CONF_WAND_ID,
CONF_ZERO,
)
from esphome.core import ID, coroutine
from esphome.core import coroutine
from esphome.cpp_generator import MockObj
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType, TemplateArgsType
@@ -2393,17 +2393,14 @@ async def abbwelcome_action(var, config, args):
await cg.templatable(config[CONF_MESSAGE_ID], args, cg.uint8)
)
)
if CONF_DATA in config:
data_ = config[CONF_DATA]
if cg.is_template(data_):
template_ = await cg.templatable(
data_, args, cg.std_vector.template(cg.uint8)
)
cg.add(var.set_data_template(template_))
else:
arr_id = ID(f"{var.base}_data", is_declaration=True, type=cg.uint8)
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data_))
cg.add(var.set_data_static(arr, len(data_)))
# TemplatableBytes starts in lambda mode with no function, so a transmit without data sets an empty payload
await automation.templatable_bytes(
config.get(CONF_DATA, []),
args,
var.set_data_template,
var.set_data_static,
"abbwelcome_data",
)
# Mirage
@@ -112,13 +112,14 @@ class ABBWelcomeData {
}
void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; }
uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; }
void set_data(std::vector<uint8_t> data) {
uint8_t size = std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(data.size()));
void set_data(const uint8_t *data, size_t len) {
uint8_t size = static_cast<uint8_t>(std::min<size_t>(MAX_DATA_LENGTH, len));
this->data_[2] &= (0xff ^ DATA_LENGTH_MASK);
this->data_[2] |= (size & DATA_LENGTH_MASK);
if (size)
std::copy_n(data.begin(), size, this->data_.begin() + 5 + 2 * this->get_address_length());
std::copy_n(data, size, this->data_.begin() + 5 + 2 * this->get_address_length());
}
void set_data(const std::vector<uint8_t> &data) { this->set_data(data.data(), data.size()); }
std::vector<uint8_t> get_data() const {
std::vector<uint8_t> data(this->data_.begin() + 5 + 2 * this->get_address_length(),
this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size());
@@ -233,14 +234,8 @@ template<typename... Ts> class ABBWelcomeAction : public RemoteTransmitterAction
TEMPLATABLE_VALUE(uint8_t, message_type)
TEMPLATABLE_VALUE(uint8_t, message_id)
TEMPLATABLE_VALUE(bool, auto_message_id)
void set_data_template(std::vector<uint8_t> (*func)(Ts...)) {
this->data_.func = func;
this->len_ = -1; // Sentinel value indicates template mode
}
void set_data_static(const uint8_t *data, size_t len) {
this->data_.data = data;
this->len_ = len; // Length >= 0 indicates static mode
}
TEMPLATABLE_BYTES(data)
void encode(RemoteTransmitData *dst, Ts... x) override {
ABBWelcomeData data;
data.set_three_byte_address(this->three_byte_address_.value(x...));
@@ -250,25 +245,11 @@ template<typename... Ts> class ABBWelcomeAction : public RemoteTransmitterAction
data.set_message_type(this->message_type_.value(x...));
data.set_message_id(this->message_id_.value(x...));
data.auto_message_id = this->auto_message_id_.value(x...);
std::vector<uint8_t> data_vec;
if (this->len_ > 0) {
// Static mode: copy from flash to vector
data_vec.assign(this->data_.data, this->data_.data + this->len_);
} else if (this->len_ < 0) {
// Template mode: call function
data_vec = this->data_.func(x...);
}
data.set_data(data_vec);
this->data_.template visit<MAX_DATA_LENGTH>(
[&data](const uint8_t *bytes, size_t len) { data.set_data(bytes, len); }, x...);
data.finalize();
ABBWelcomeProtocol().encode(dst, data);
}
protected:
ssize_t len_{0}; // <0 = template mode, >=0 = static mode with length
union Data {
std::vector<uint8_t> (*func)(Ts...); // Function pointer (stateless lambdas)
const uint8_t *data; // Pointer to static data in flash
} data_;
};
} // namespace esphome::remote_base
@@ -0,0 +1,36 @@
---
esphome:
name: test
on_boot:
then:
- remote_transmitter.transmit_abbwelcome:
source_address: 0x1234
destination_address: 0x5678
message_type: 0x01
data: [0x10, 0x20, 0x30]
- remote_transmitter.transmit_abbwelcome:
source_address: 0x1234
destination_address: 0x5678
message_type: 0x02
data: [0x10, 0x20, 0x30]
- remote_transmitter.transmit_abbwelcome:
source_address: 0x1234
destination_address: 0x5678
message_type: 0x03
data: []
- remote_transmitter.transmit_abbwelcome:
source_address: 0x1234
destination_address: 0x5678
message_type: 0x04
- remote_transmitter.transmit_abbwelcome:
source_address: 0x1234
destination_address: 0x5678
message_type: 0x05
data: !lambda return {0x09};
esp32:
board: esp32dev
remote_transmitter:
pin: 4
carrier_duty_percent: 100%
@@ -0,0 +1,21 @@
"""Tests for abbwelcome constant data in shared PROGMEM tables."""
from collections.abc import Callable
from pathlib import Path
import re
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."""
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