ESPHome 2026.5.0
Loading...
Searching...
No Matches
abbwelcome_protocol.h
Go to the documentation of this file.
1#pragma once
2
5#include "remote_base.h"
6#include <array>
7#include <cinttypes>
8#include <utility>
9#include <vector>
10
11namespace esphome::remote_base {
12
13static constexpr uint8_t MAX_DATA_LENGTH = 15;
14static constexpr uint8_t DATA_LENGTH_MASK = 0x3f;
15
16/*
17Message Format:
18 2 bytes: Sync (0x55FF)
19 1 bit: Retransmission flag (High means retransmission)
20 1 bit: Address length flag (Low means 2 bytes, High means 3 bytes)
21 2 bits: Unknown
22 4 bits: Data length (in bytes)
23 1 bit: Reply flag (High means this is a reply to a previous message with the same message type)
24 7 bits: Message type
25 2-3 bytes: Destination address
26 2-3 bytes: Source address
27 1 byte: Message ID (randomized, does not change for retransmissions)
28 0-? bytes: Data
29 1 byte: Checksum
30*/
31
33 public:
34 // Make default
35 ABBWelcomeData() : data_{0x55, 0xff} {}
36 // Make from initializer_list
37 ABBWelcomeData(std::initializer_list<uint8_t> data) : data_{} {
38 std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
39 }
40 // Make from vector
41 ABBWelcomeData(const std::vector<uint8_t> &data) : data_{} {
42 std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
43 }
44 // Default copy constructor
45 ABBWelcomeData(const ABBWelcomeData &) = default;
46
47 bool auto_message_id{false};
48
49 uint8_t *data() { return this->data_.data(); }
50 const uint8_t *data() const { return this->data_.data(); }
51 uint8_t size() const {
52 return std::min(static_cast<uint8_t>(6 + (2 * this->get_address_length()) + (this->data_[2] & DATA_LENGTH_MASK)),
53 static_cast<uint8_t>(this->data_.size()));
54 }
55 bool is_valid() const {
56 return this->data_[0] == 0x55 && this->data_[1] == 0xff &&
57 ((this->data_[2] & DATA_LENGTH_MASK) <= MAX_DATA_LENGTH) &&
58 (this->data_[this->size() - 1] == this->calc_cs_());
59 }
60 void set_retransmission(bool retransmission) {
61 if (retransmission) {
62 this->data_[2] |= 0x80;
63 } else {
64 this->data_[2] &= 0x7f;
65 }
66 }
67 bool get_retransmission() const { return this->data_[2] & 0x80; }
68 // set_three_byte_address must be called before set_source_address, set_destination_address, set_message_id and
69 // set_data!
70 void set_three_byte_address(bool three_byte_address) {
71 if (three_byte_address) {
72 this->data_[2] |= 0x40;
73 } else {
74 this->data_[2] &= 0xbf;
75 }
76 }
77 uint8_t get_three_byte_address() const { return (this->data_[2] & 0x40); }
78 uint8_t get_address_length() const { return this->get_three_byte_address() ? 3 : 2; }
79 void set_message_type(uint8_t message_type) { this->data_[3] = message_type; }
80 uint8_t get_message_type() const { return this->data_[3]; }
82 if (this->get_address_length() == 2) {
83 this->data_[4] = (address >> 8) & 0xff;
84 this->data_[5] = address & 0xff;
85 } else {
86 this->data_[4] = (address >> 16) & 0xff;
87 this->data_[5] = (address >> 8) & 0xff;
88 this->data_[6] = address & 0xff;
89 }
90 }
92 if (this->get_address_length() == 2) {
93 return (this->data_[4] << 8) + this->data_[5];
94 }
95 return (this->data_[4] << 16) + (this->data_[5] << 8) + this->data_[6];
96 }
98 if (this->get_address_length() == 2) {
99 this->data_[6] = (address >> 8) & 0xff;
100 this->data_[7] = address & 0xff;
101 } else {
102 this->data_[7] = (address >> 16) & 0xff;
103 this->data_[8] = (address >> 8) & 0xff;
104 this->data_[9] = address & 0xff;
105 }
106 }
108 if (this->get_address_length() == 2) {
109 return (this->data_[6] << 8) + this->data_[7];
110 }
111 return (this->data_[7] << 16) + (this->data_[8] << 8) + this->data_[9];
112 }
113 void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; }
114 uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; }
115 void set_data(std::vector<uint8_t> data) {
116 uint8_t size = std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(data.size()));
117 this->data_[2] &= (0xff ^ DATA_LENGTH_MASK);
118 this->data_[2] |= (size & DATA_LENGTH_MASK);
119 if (size)
120 std::copy_n(data.begin(), size, this->data_.begin() + 5 + 2 * this->get_address_length());
121 }
122 std::vector<uint8_t> get_data() const {
123 std::vector<uint8_t> data(this->data_.begin() + 5 + 2 * this->get_address_length(),
124 this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size());
125 return data;
126 }
127 uint8_t get_data_size() const {
128 return std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(this->data_[2] & DATA_LENGTH_MASK));
129 }
130 void finalize() {
131 if (this->auto_message_id && !this->get_retransmission() && !(this->data_[3] & 0x80)) {
132 this->set_message_id(static_cast<uint8_t>(random_uint32()));
133 }
134 this->data_[0] = 0x55;
135 this->data_[1] = 0xff;
136 this->data_[this->size() - 1] = this->calc_cs_();
137 }
138 // Buffer size: max raw hex output (27*3-1=80) + space(1) + type_info(27) + data(52) + null(1) = 161, rounded up
139 static constexpr size_t FORMAT_BUFFER_SIZE = 192;
140
141 template<size_t N> char *format_to(char (&buffer)[N], uint8_t max_print_bytes = 255) const {
142 static_assert(N >= FORMAT_BUFFER_SIZE, "Buffer too small for format_to()");
143 return this->format_to_internal_(buffer, max_print_bytes);
144 }
145 bool operator==(const ABBWelcomeData &rhs) const {
146 if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin()))
147 return true;
148 return (this->auto_message_id || rhs.auto_message_id) && this->is_valid() && rhs.is_valid() &&
149 (this->get_message_type() == rhs.get_message_type()) &&
150 (this->get_source_address() == rhs.get_source_address()) &&
151 (this->get_destination_address() == rhs.get_destination_address()) && (this->get_data() == rhs.get_data());
152 }
153 uint8_t &operator[](size_t idx) { return this->data_[idx]; }
154 const uint8_t &operator[](size_t idx) const { return this->data_[idx]; }
155
156 protected:
157 std::array<uint8_t, 12 + MAX_DATA_LENGTH> data_;
158 // Calculate checksum
159 uint8_t calc_cs_() const;
160 // Internal format implementation - buffer guaranteed >= FORMAT_BUFFER_SIZE by caller
161 char *format_to_internal_(char *buffer, uint8_t max_print_bytes) const {
162 char *ptr = buffer;
163 char *end = buffer + FORMAT_BUFFER_SIZE;
164
165 uint8_t print_bytes = std::min(this->size(), max_print_bytes);
166 if (print_bytes) {
167 char raw_hex[format_hex_pretty_size(12 + MAX_DATA_LENGTH)];
168 format_hex_pretty_to(raw_hex, this->data_.data(), print_bytes, '.');
169 ptr += snprintf(ptr, end - ptr, "%s ", raw_hex);
170 }
171
172 if (this->is_valid()) {
173 ptr += snprintf(ptr, end - ptr,
174 this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X"
175 : "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X",
176 this->get_source_address(), this->get_retransmission() ? "ยป" : ">",
178 if (this->get_data_size()) {
179 char data_hex[format_hex_pretty_size(MAX_DATA_LENGTH)];
180 format_hex_pretty_to(data_hex, this->data_.data() + 5 + 2 * this->get_address_length(), this->get_data_size(),
181 '.');
182 snprintf(ptr, end - ptr, ", Data: %s", data_hex);
183 }
184 } else {
185 snprintf(ptr, end - ptr, "[Invalid]");
186 }
187
188 return buffer;
189 }
190};
191
192class ABBWelcomeProtocol : public RemoteProtocol<ABBWelcomeData> {
193 public:
194 void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override;
195 optional<ABBWelcomeData> decode(RemoteReceiveData src) override;
196 void dump(const ABBWelcomeData &data) override;
197
198 protected:
199 void encode_byte_(RemoteTransmitData *dst, uint8_t data) const;
200 bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data);
201};
202
204 public:
205 bool matches(RemoteReceiveData src) override {
206 auto data = ABBWelcomeProtocol().decode(src);
207 return data.has_value() && data.value() == this->data_;
208 }
209 void set_source_address(const uint32_t source_address) { this->data_.set_source_address(source_address); }
210 void set_destination_address(const uint32_t destination_address) {
211 this->data_.set_destination_address(destination_address);
212 }
213 void set_retransmission(const bool retransmission) { this->data_.set_retransmission(retransmission); }
214 void set_three_byte_address(const bool three_byte_address) { this->data_.set_three_byte_address(three_byte_address); }
215 void set_message_type(const uint8_t message_type) { this->data_.set_message_type(message_type); }
216 void set_message_id(const uint8_t message_id) { this->data_.set_message_id(message_id); }
217 void set_auto_message_id(const bool auto_message_id) { this->data_.auto_message_id = auto_message_id; }
218 void set_data(const std::vector<uint8_t> &data) { this->data_.set_data(data); }
219 void finalize() { this->data_.finalize(); }
220
221 protected:
223};
224
227
228template<typename... Ts> class ABBWelcomeAction : public RemoteTransmitterActionBase<Ts...> {
229 TEMPLATABLE_VALUE(uint32_t, source_address)
230 TEMPLATABLE_VALUE(uint32_t, destination_address)
231 TEMPLATABLE_VALUE(bool, retransmission)
232 TEMPLATABLE_VALUE(bool, three_byte_address)
233 TEMPLATABLE_VALUE(uint8_t, message_type)
234 TEMPLATABLE_VALUE(uint8_t, message_id)
235 TEMPLATABLE_VALUE(bool, auto_message_id)
236 void set_data_template(std::vector<uint8_t> (*func)(Ts...)) {
237 this->data_.func = func;
238 this->len_ = -1; // Sentinel value indicates template mode
239 }
240 void set_data_static(const uint8_t *data, size_t len) {
241 this->data_.data = data;
242 this->len_ = len; // Length >= 0 indicates static mode
243 }
244 void encode(RemoteTransmitData *dst, Ts... x) override {
245 ABBWelcomeData data;
246 data.set_three_byte_address(this->three_byte_address_.value(x...));
247 data.set_source_address(this->source_address_.value(x...));
248 data.set_destination_address(this->destination_address_.value(x...));
249 data.set_retransmission(this->retransmission_.value(x...));
250 data.set_message_type(this->message_type_.value(x...));
251 data.set_message_id(this->message_id_.value(x...));
252 data.auto_message_id = this->auto_message_id_.value(x...);
253 std::vector<uint8_t> data_vec;
254 if (this->len_ > 0) {
255 // Static mode: copy from flash to vector
256 data_vec.assign(this->data_.data, this->data_.data + this->len_);
257 } else if (this->len_ < 0) {
258 // Template mode: call function
259 data_vec = this->data_.func(x...);
260 }
261 data.set_data(data_vec);
262 data.finalize();
263 ABBWelcomeProtocol().encode(dst, data);
264 }
265
266 protected:
267 ssize_t len_{0}; // <0 = template mode, >=0 = static mode with length
268 union Data {
269 std::vector<uint8_t> (*func)(Ts...); // Function pointer (stateless lambdas)
270 const uint8_t *data; // Pointer to static data in flash
272};
273
274} // namespace esphome::remote_base
uint8_t address
Definition bl0906.h:4
union esphome::remote_base::ABBWelcomeAction::Data data_
bool matches(RemoteReceiveData src) override
void set_retransmission(const bool retransmission)
void set_destination_address(const uint32_t destination_address)
void set_auto_message_id(const bool auto_message_id)
void set_message_type(const uint8_t message_type)
void set_data(const std::vector< uint8_t > &data)
void set_source_address(const uint32_t source_address)
void set_three_byte_address(const bool three_byte_address)
void set_data(std::vector< uint8_t > data)
bool operator==(const ABBWelcomeData &rhs) const
ABBWelcomeData(std::initializer_list< uint8_t > data)
void set_retransmission(bool retransmission)
void set_destination_address(uint32_t address)
void set_three_byte_address(bool three_byte_address)
char * format_to_internal_(char *buffer, uint8_t max_print_bytes) const
const uint8_t & operator[](size_t idx) const
std::array< uint8_t, 12+MAX_DATA_LENGTH > data_
char * format_to(char(&buffer)[N], uint8_t max_print_bytes=255) const
std::vector< uint8_t > get_data() const
void set_message_type(uint8_t message_type)
ABBWelcomeData(const ABBWelcomeData &)=default
ABBWelcomeData(const std::vector< uint8_t > &data)
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override
void encode_byte_(RemoteTransmitData *dst, uint8_t data) const
void dump(const ABBWelcomeData &data) override
bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data)
optional< ABBWelcomeData > decode(RemoteReceiveData src) override
__int64 ssize_t
Definition httplib.h:178
std::string size_t len
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:341
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1386
static void uint32_t
uint8_t end[39]
Definition sun_gtil2.cpp:17
uint16_t x
Definition tt21100.cpp:5