#pragma once #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "uart_mock.h" namespace esphome::uart_mock { // This pattern is similar to UARTWriteAction but calls inject_rx instead of write_array, and is parented to VirtualUART // instead of UARTComponent template class MockUartInjectRXAction : public Action, public Parented { public: void set_data_template(std::vector (*func)(Ts...)) { // Stateless lambdas (generated by ESPHome) implicitly convert to function pointers this->code_.func = func; this->len_ = -1; // Sentinel value indicates template mode } // Store pointer to static data in flash (no RAM copy) void set_data_static(const uint8_t *data, size_t len) { this->code_.data = data; this->len_ = len; // Length >= 0 indicates static mode } void set_delay(uint32_t delay_ms) { this->delay_ms_ = delay_ms; } void play(const Ts &...x) override { if (this->len_ >= 0) { // Static mode: use pointer and length if (this->delay_ms_ > 0) { std::vector data(this->code_.data, this->code_.data + this->len_); this->parent_->inject_to_rx_buffer_delayed(data, this->delay_ms_); } else { this->parent_->inject_to_rx_buffer(this->code_.data, static_cast(this->len_)); } } else { // Template mode: call function auto val = this->code_.func(x...); if (this->delay_ms_ > 0) { this->parent_->inject_to_rx_buffer_delayed(val, this->delay_ms_); } else { this->parent_->inject_to_rx_buffer(val); } } } protected: uint32_t delay_ms_{0}; ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length union Code { std::vector (*func)(Ts...); // Function pointer (stateless lambdas) const uint8_t *data; // Pointer to static data in flash } code_; }; class MockUartTXTrigger : public Trigger> { public: explicit MockUartTXTrigger(MockUartComponent *parent) { parent->set_tx_hook([this](std::vector data) { this->trigger(data); }); } }; } // namespace esphome::uart_mock