ESPHome  2024.12.2
abbwelcome_protocol.h
Go to the documentation of this file.
1 #pragma once
2 
4 #include "esphome/core/helpers.h"
5 #include "remote_base.h"
6 #include <array>
7 #include <cinttypes>
8 #include <utility>
9 #include <vector>
10 
11 namespace esphome {
12 namespace remote_base {
13 
14 static const uint8_t MAX_DATA_LENGTH = 15;
15 static const uint8_t DATA_LENGTH_MASK = 0x3f;
16 
17 /*
18 Message Format:
19  2 bytes: Sync (0x55FF)
20  1 bit: Retransmission flag (High means retransmission)
21  1 bit: Address length flag (Low means 2 bytes, High means 3 bytes)
22  2 bits: Unknown
23  4 bits: Data length (in bytes)
24  1 bit: Reply flag (High means this is a reply to a previous message with the same message type)
25  7 bits: Message type
26  2-3 bytes: Destination address
27  2-3 bytes: Source address
28  1 byte: Message ID (randomized, does not change for retransmissions)
29  0-? bytes: Data
30  1 byte: Checksum
31 */
32 
34  public:
35  // Make default
37  std::fill(std::begin(this->data_), std::end(this->data_), 0);
38  this->data_[0] = 0x55;
39  this->data_[1] = 0xff;
40  }
41  // Make from initializer_list
42  ABBWelcomeData(std::initializer_list<uint8_t> data) {
43  std::fill(std::begin(this->data_), std::end(this->data_), 0);
44  std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
45  }
46  // Make from vector
47  ABBWelcomeData(const std::vector<uint8_t> &data) {
48  std::fill(std::begin(this->data_), std::end(this->data_), 0);
49  std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
50  }
51  // Default copy constructor
52  ABBWelcomeData(const ABBWelcomeData &) = default;
53 
54  bool auto_message_id{false};
55 
56  uint8_t *data() { return this->data_.data(); }
57  const uint8_t *data() const { return this->data_.data(); }
58  uint8_t size() const {
59  return std::min(static_cast<uint8_t>(6 + (2 * this->get_address_length()) + (this->data_[2] & DATA_LENGTH_MASK)),
60  static_cast<uint8_t>(this->data_.size()));
61  }
62  bool is_valid() const {
63  return this->data_[0] == 0x55 && this->data_[1] == 0xff &&
64  ((this->data_[2] & DATA_LENGTH_MASK) <= MAX_DATA_LENGTH) &&
65  (this->data_[this->size() - 1] == this->calc_cs_());
66  }
67  void set_retransmission(bool retransmission) {
68  if (retransmission) {
69  this->data_[2] |= 0x80;
70  } else {
71  this->data_[2] &= 0x7f;
72  }
73  }
74  bool get_retransmission() const { return this->data_[2] & 0x80; }
75  // set_three_byte_address must be called before set_source_address, set_destination_address, set_message_id and
76  // set_data!
77  void set_three_byte_address(bool three_byte_address) {
78  if (three_byte_address) {
79  this->data_[2] |= 0x40;
80  } else {
81  this->data_[2] &= 0xbf;
82  }
83  }
84  uint8_t get_three_byte_address() const { return (this->data_[2] & 0x40); }
85  uint8_t get_address_length() const { return this->get_three_byte_address() ? 3 : 2; }
86  void set_message_type(uint8_t message_type) { this->data_[3] = message_type; }
87  uint8_t get_message_type() const { return this->data_[3]; }
89  if (this->get_address_length() == 2) {
90  this->data_[4] = (address >> 8) & 0xff;
91  this->data_[5] = address & 0xff;
92  } else {
93  this->data_[4] = (address >> 16) & 0xff;
94  this->data_[5] = (address >> 8) & 0xff;
95  this->data_[6] = address & 0xff;
96  }
97  }
98  uint32_t get_destination_address() const {
99  if (this->get_address_length() == 2) {
100  return (this->data_[4] << 8) + this->data_[5];
101  }
102  return (this->data_[4] << 16) + (this->data_[5] << 8) + this->data_[6];
103  }
104  void set_source_address(uint32_t address) {
105  if (this->get_address_length() == 2) {
106  this->data_[6] = (address >> 8) & 0xff;
107  this->data_[7] = address & 0xff;
108  } else {
109  this->data_[7] = (address >> 16) & 0xff;
110  this->data_[8] = (address >> 8) & 0xff;
111  this->data_[9] = address & 0xff;
112  }
113  }
114  uint32_t get_source_address() const {
115  if (this->get_address_length() == 2) {
116  return (this->data_[6] << 8) + this->data_[7];
117  }
118  return (this->data_[7] << 16) + (this->data_[8] << 8) + this->data_[9];
119  }
120  void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; }
121  uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; }
122  void set_data(std::vector<uint8_t> data) {
123  uint8_t size = std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(data.size()));
124  this->data_[2] &= (0xff ^ DATA_LENGTH_MASK);
125  this->data_[2] |= (size & DATA_LENGTH_MASK);
126  if (size)
127  std::copy_n(data.begin(), size, this->data_.begin() + 5 + 2 * this->get_address_length());
128  }
129  std::vector<uint8_t> get_data() const {
130  std::vector<uint8_t> data(this->data_.begin() + 5 + 2 * this->get_address_length(),
131  this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size());
132  return data;
133  }
134  uint8_t get_data_size() const {
135  return std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(this->data_[2] & DATA_LENGTH_MASK));
136  }
137  void finalize() {
138  if (this->auto_message_id && !this->get_retransmission() && !(this->data_[3] & 0x80)) {
139  this->set_message_id(static_cast<uint8_t>(random_uint32()));
140  }
141  this->data_[0] = 0x55;
142  this->data_[1] = 0xff;
143  this->data_[this->size() - 1] = this->calc_cs_();
144  }
145  std::string to_string(uint8_t max_print_bytes = 255) const {
146  std::string info;
147  if (this->is_valid()) {
148  info = str_sprintf(this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X"
149  : "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X",
150  this->get_source_address(), this->get_retransmission() ? "ยป" : ">",
151  this->get_destination_address(), this->get_message_type());
152  if (this->get_data_size())
153  info += str_sprintf(", Data: %s", format_hex_pretty(this->get_data()).c_str());
154  } else {
155  info = "[Invalid]";
156  }
157  uint8_t print_bytes = std::min(this->size(), max_print_bytes);
158  if (print_bytes)
159  info = str_sprintf("%s %s", format_hex_pretty(this->data_.data(), print_bytes).c_str(), info.c_str());
160  return info;
161  }
162  bool operator==(const ABBWelcomeData &rhs) const {
163  if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin()))
164  return true;
165  return (this->auto_message_id || rhs.auto_message_id) && this->is_valid() && rhs.is_valid() &&
166  (this->get_message_type() == rhs.get_message_type()) &&
167  (this->get_source_address() == rhs.get_source_address()) &&
168  (this->get_destination_address() == rhs.get_destination_address()) && (this->get_data() == rhs.get_data());
169  }
170  uint8_t &operator[](size_t idx) { return this->data_[idx]; }
171  const uint8_t &operator[](size_t idx) const { return this->data_[idx]; }
172 
173  protected:
174  std::array<uint8_t, 12 + MAX_DATA_LENGTH> data_;
175  // Calculate checksum
176  uint8_t calc_cs_() const;
177 };
178 
179 class ABBWelcomeProtocol : public RemoteProtocol<ABBWelcomeData> {
180  public:
181  void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override;
182  optional<ABBWelcomeData> decode(RemoteReceiveData src) override;
183  void dump(const ABBWelcomeData &data) override;
184 
185  protected:
186  void encode_byte_(RemoteTransmitData *dst, uint8_t data) const;
187  bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data);
188 };
189 
191  public:
192  bool matches(RemoteReceiveData src) override {
193  auto data = ABBWelcomeProtocol().decode(src);
194  return data.has_value() && data.value() == this->data_;
195  }
196  void set_source_address(const uint32_t source_address) { this->data_.set_source_address(source_address); }
197  void set_destination_address(const uint32_t destination_address) {
198  this->data_.set_destination_address(destination_address);
199  }
200  void set_retransmission(const bool retransmission) { this->data_.set_retransmission(retransmission); }
201  void set_three_byte_address(const bool three_byte_address) { this->data_.set_three_byte_address(three_byte_address); }
202  void set_message_type(const uint8_t message_type) { this->data_.set_message_type(message_type); }
203  void set_message_id(const uint8_t message_id) { this->data_.set_message_id(message_id); }
204  void set_auto_message_id(const bool auto_message_id) { this->data_.auto_message_id = auto_message_id; }
205  void set_data(const std::vector<uint8_t> &data) { this->data_.set_data(data); }
206  void finalize() { this->data_.finalize(); }
207 
208  protected:
210 };
211 
214 
215 template<typename... Ts> class ABBWelcomeAction : public RemoteTransmitterActionBase<Ts...> {
216  TEMPLATABLE_VALUE(uint32_t, source_address)
217  TEMPLATABLE_VALUE(uint32_t, destination_address)
218  TEMPLATABLE_VALUE(bool, retransmission)
219  TEMPLATABLE_VALUE(bool, three_byte_address)
220  TEMPLATABLE_VALUE(uint8_t, message_type)
221  TEMPLATABLE_VALUE(uint8_t, message_id)
222  TEMPLATABLE_VALUE(bool, auto_message_id)
223  void set_data_static(std::vector<uint8_t> data) { data_static_ = std::move(data); }
224  void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
225  this->data_func_ = func;
226  has_data_func_ = true;
227  }
228  void encode(RemoteTransmitData *dst, Ts... x) override {
230  data.set_three_byte_address(this->three_byte_address_.value(x...));
231  data.set_source_address(this->source_address_.value(x...));
232  data.set_destination_address(this->destination_address_.value(x...));
233  data.set_retransmission(this->retransmission_.value(x...));
234  data.set_message_type(this->message_type_.value(x...));
235  data.set_message_id(this->message_id_.value(x...));
236  data.auto_message_id = this->auto_message_id_.value(x...);
237  if (has_data_func_) {
238  data.set_data(this->data_func_(x...));
239  } else {
240  data.set_data(this->data_static_);
241  }
242  data.finalize();
243  ABBWelcomeProtocol().encode(dst, data);
244  }
245 
246  protected:
247  std::function<std::vector<uint8_t>(Ts...)> data_func_{};
248  std::vector<uint8_t> data_static_{};
249  bool has_data_func_{false};
250 };
251 
252 } // namespace remote_base
253 } // namespace esphome
bool operator==(const ABBWelcomeData &rhs) const
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:369
optional< ABBWelcomeData > decode(RemoteReceiveData src) override
void set_message_type(const uint8_t message_type)
void set_message_id(const uint8_t message_id)
void set_data(const std::vector< uint8_t > &data)
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition: helpers.cpp:193
uint16_t x
Definition: tt21100.cpp:17
bool matches(RemoteReceiveData src) override
std::array< uint8_t, 12+MAX_DATA_LENGTH > data_
std::string to_string(uint8_t max_print_bytes=255) const
void set_destination_address(const uint32_t destination_address)
std::vector< uint8_t > get_data() const
void set_destination_address(uint32_t address)
void set_source_address(const uint32_t source_address)
void set_data(std::vector< uint8_t > data)
void set_retransmission(const bool retransmission)
void set_message_type(uint8_t message_type)
std::string str_sprintf(const char *fmt,...)
Definition: helpers.cpp:320
void set_three_byte_address(const bool three_byte_address)
ABBWelcomeData(const std::vector< uint8_t > &data)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src) override
void set_retransmission(bool retransmission)
uint8_t address
Definition: bl0906.h:211
void set_auto_message_id(const bool auto_message_id)
const uint8_t & operator[](size_t idx) const
uint8_t end[39]
Definition: sun_gtil2.cpp:31
ABBWelcomeData(std::initializer_list< uint8_t > data)
void set_three_byte_address(bool three_byte_address)