ESPHome  2022.11.3
tuya.cpp
Go to the documentation of this file.
1 #include "tuya.h"
3 #include "esphome/core/helpers.h"
4 #include "esphome/core/log.h"
5 #include "esphome/core/util.h"
6 #include "esphome/core/gpio.h"
7 
8 namespace esphome {
9 namespace tuya {
10 
11 static const char *const TAG = "tuya";
12 static const int COMMAND_DELAY = 10;
13 static const int RECEIVE_TIMEOUT = 300;
14 static const int MAX_RETRIES = 5;
15 
16 void Tuya::setup() {
17  this->set_interval("heartbeat", 15000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); });
18  if (this->status_pin_.has_value()) {
19  this->status_pin_.value()->digital_write(false);
20  }
21 }
22 
23 void Tuya::loop() {
24  while (this->available()) {
25  uint8_t c;
26  this->read_byte(&c);
27  this->handle_char_(c);
28  }
30 }
31 
33  ESP_LOGCONFIG(TAG, "Tuya:");
35  if (this->init_failed_) {
36  ESP_LOGCONFIG(TAG, " Initialization failed. Current init_state: %u", static_cast<uint8_t>(this->init_state_));
37  } else {
38  ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u",
39  static_cast<uint8_t>(this->init_state_));
40  }
41  ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device.");
42  return;
43  }
44  for (auto &info : this->datapoints_) {
45  if (info.type == TuyaDatapointType::RAW) {
46  ESP_LOGCONFIG(TAG, " Datapoint %u: raw (value: %s)", info.id, format_hex_pretty(info.value_raw).c_str());
47  } else if (info.type == TuyaDatapointType::BOOLEAN) {
48  ESP_LOGCONFIG(TAG, " Datapoint %u: switch (value: %s)", info.id, ONOFF(info.value_bool));
49  } else if (info.type == TuyaDatapointType::INTEGER) {
50  ESP_LOGCONFIG(TAG, " Datapoint %u: int value (value: %d)", info.id, info.value_int);
51  } else if (info.type == TuyaDatapointType::STRING) {
52  ESP_LOGCONFIG(TAG, " Datapoint %u: string value (value: %s)", info.id, info.value_string.c_str());
53  } else if (info.type == TuyaDatapointType::ENUM) {
54  ESP_LOGCONFIG(TAG, " Datapoint %u: enum (value: %d)", info.id, info.value_enum);
55  } else if (info.type == TuyaDatapointType::BITMASK) {
56  ESP_LOGCONFIG(TAG, " Datapoint %u: bitmask (value: %x)", info.id, info.value_bitmask);
57  } else {
58  ESP_LOGCONFIG(TAG, " Datapoint %u: unknown", info.id);
59  }
60  }
61  if ((this->status_pin_reported_ != -1) || (this->reset_pin_reported_ != -1)) {
62  ESP_LOGCONFIG(TAG, " GPIO Configuration: status: pin %d, reset: pin %d", this->status_pin_reported_,
63  this->reset_pin_reported_);
64  }
65  if (this->status_pin_.has_value()) {
66  LOG_PIN(" Status Pin: ", this->status_pin_.value());
67  }
68  ESP_LOGCONFIG(TAG, " Product: '%s'", this->product_.c_str());
69  this->check_uart_settings(9600);
70 }
71 
73  uint32_t at = this->rx_message_.size() - 1;
74  auto *data = &this->rx_message_[0];
75  uint8_t new_byte = data[at];
76 
77  // Byte 0: HEADER1 (always 0x55)
78  if (at == 0)
79  return new_byte == 0x55;
80  // Byte 1: HEADER2 (always 0xAA)
81  if (at == 1)
82  return new_byte == 0xAA;
83 
84  // Byte 2: VERSION
85  // no validation for the following fields:
86  uint8_t version = data[2];
87  if (at == 2)
88  return true;
89  // Byte 3: COMMAND
90  uint8_t command = data[3];
91  if (at == 3)
92  return true;
93 
94  // Byte 4: LENGTH1
95  // Byte 5: LENGTH2
96  if (at <= 5) {
97  // no validation for these fields
98  return true;
99  }
100 
101  uint16_t length = (uint16_t(data[4]) << 8) | (uint16_t(data[5]));
102 
103  // wait until all data is read
104  if (at - 6 < length)
105  return true;
106 
107  // Byte 6+LEN: CHECKSUM - sum of all bytes (including header) modulo 256
108  uint8_t rx_checksum = new_byte;
109  uint8_t calc_checksum = 0;
110  for (uint32_t i = 0; i < 6 + length; i++)
111  calc_checksum += data[i];
112 
113  if (rx_checksum != calc_checksum) {
114  ESP_LOGW(TAG, "Tuya Received invalid message checksum %02X!=%02X", rx_checksum, calc_checksum);
115  return false;
116  }
117 
118  // valid message
119  const uint8_t *message_data = data + 6;
120  ESP_LOGV(TAG, "Received Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version,
121  format_hex_pretty(message_data, length).c_str(), static_cast<uint8_t>(this->init_state_));
122  this->handle_command_(command, version, message_data, length);
123 
124  // return false to reset rx buffer
125  return false;
126 }
127 
128 void Tuya::handle_char_(uint8_t c) {
129  this->rx_message_.push_back(c);
130  if (!this->validate_message_()) {
131  this->rx_message_.clear();
132  } else {
134  }
135 }
136 
137 void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len) {
138  TuyaCommandType command_type = (TuyaCommandType) command;
139 
140  if (this->expected_response_.has_value() && this->expected_response_ == command_type) {
141  this->expected_response_.reset();
142  this->command_queue_.erase(command_queue_.begin());
143  this->init_retries_ = 0;
144  }
145 
146  switch (command_type) {
148  ESP_LOGV(TAG, "MCU Heartbeat (0x%02X)", buffer[0]);
149  this->protocol_version_ = version;
150  if (buffer[0] == 0) {
151  ESP_LOGI(TAG, "MCU restarted");
153  }
157  }
158  break;
160  // check it is a valid string made up of printable characters
161  bool valid = true;
162  for (size_t i = 0; i < len; i++) {
163  if (!std::isprint(buffer[i])) {
164  valid = false;
165  break;
166  }
167  }
168  if (valid) {
169  this->product_ = std::string(reinterpret_cast<const char *>(buffer), len);
170  } else {
171  this->product_ = R"({"p":"INVALID"})";
172  }
176  }
177  break;
178  }
180  if (len >= 2) {
181  this->status_pin_reported_ = buffer[0];
182  this->reset_pin_reported_ = buffer[1];
183  }
184  if (this->init_state_ == TuyaInitState::INIT_CONF) {
185  // If mcu returned status gpio, then we can omit sending wifi state
186  if (this->status_pin_reported_ != -1) {
189  bool is_pin_equals =
190  this->status_pin_.has_value() && this->status_pin_.value()->get_pin() == this->status_pin_reported_;
191  // Configure status pin toggling (if reported and configured) or WIFI_STATE periodic send
192  if (is_pin_equals) {
193  ESP_LOGV(TAG, "Configured status pin %i", this->status_pin_reported_);
194  this->set_interval("wifi", 1000, [this] { this->set_status_pin_(); });
195  } else {
196  ESP_LOGW(TAG, "Supplied status_pin does not equals the reported pin %i. TuyaMcu will work in limited mode.",
197  this->status_pin_reported_);
198  }
199  } else {
201  ESP_LOGV(TAG, "Configured WIFI_STATE periodic send");
202  this->set_interval("wifi", 1000, [this] { this->send_wifi_status_(); });
203  }
204  }
205  break;
206  }
208  if (this->init_state_ == TuyaInitState::INIT_WIFI) {
211  }
212  break;
214  ESP_LOGE(TAG, "WIFI_RESET is not handled");
215  break;
217  ESP_LOGE(TAG, "WIFI_SELECT is not handled");
218  break;
220  break;
224  this->set_timeout("datapoint_dump", 1000, [this] { this->dump_config(); });
225  this->initialized_callback_.call();
226  }
227  this->handle_datapoints_(buffer, len);
228  break;
230  break;
232  this->send_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_TEST, .payload = std::vector<uint8_t>{0x00, 0x00}});
233  break;
235 #ifdef USE_TIME
236  if (this->time_id_.has_value()) {
237  this->send_local_time_();
238  auto *time_id = *this->time_id_;
239  time_id->add_on_time_sync_callback([this] { this->send_local_time_(); });
240  } else {
241  ESP_LOGW(TAG, "LOCAL_TIME_QUERY is not handled because time is not configured");
242  }
243 #else
244  ESP_LOGE(TAG, "LOCAL_TIME_QUERY is not handled");
245 #endif
246  break;
247  default:
248  ESP_LOGE(TAG, "Invalid command (0x%02X) received", command);
249  }
250 }
251 
252 void Tuya::handle_datapoints_(const uint8_t *buffer, size_t len) {
253  while (len >= 4) {
254  TuyaDatapoint datapoint{};
255  datapoint.id = buffer[0];
256  datapoint.type = (TuyaDatapointType) buffer[1];
257  datapoint.value_uint = 0;
258 
259  size_t data_size = (buffer[2] << 8) + buffer[3];
260  const uint8_t *data = buffer + 4;
261  size_t data_len = len - 4;
262  if (data_size > data_len) {
263  ESP_LOGW(TAG, "Datapoint %u is truncated and cannot be parsed (%zu > %zu)", datapoint.id, data_size, data_len);
264  return;
265  }
266 
267  datapoint.len = data_size;
268 
269  switch (datapoint.type) {
271  datapoint.value_raw = std::vector<uint8_t>(data, data + data_size);
272  ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, format_hex_pretty(datapoint.value_raw).c_str());
273  break;
275  if (data_size != 1) {
276  ESP_LOGW(TAG, "Datapoint %u has bad boolean len %zu", datapoint.id, data_size);
277  return;
278  }
279  datapoint.value_bool = data[0];
280  ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, ONOFF(datapoint.value_bool));
281  break;
283  if (data_size != 4) {
284  ESP_LOGW(TAG, "Datapoint %u has bad integer len %zu", datapoint.id, data_size);
285  return;
286  }
287  datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]);
288  ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_int);
289  break;
291  datapoint.value_string = std::string(reinterpret_cast<const char *>(data), data_size);
292  ESP_LOGD(TAG, "Datapoint %u update to %s", datapoint.id, datapoint.value_string.c_str());
293  break;
295  if (data_size != 1) {
296  ESP_LOGW(TAG, "Datapoint %u has bad enum len %zu", datapoint.id, data_size);
297  return;
298  }
299  datapoint.value_enum = data[0];
300  ESP_LOGD(TAG, "Datapoint %u update to %d", datapoint.id, datapoint.value_enum);
301  break;
303  switch (data_size) {
304  case 1:
305  datapoint.value_bitmask = encode_uint32(0, 0, 0, data[0]);
306  break;
307  case 2:
308  datapoint.value_bitmask = encode_uint32(0, 0, data[0], data[1]);
309  break;
310  case 4:
311  datapoint.value_bitmask = encode_uint32(data[0], data[1], data[2], data[3]);
312  break;
313  default:
314  ESP_LOGW(TAG, "Datapoint %u has bad bitmask len %zu", datapoint.id, data_size);
315  return;
316  }
317  ESP_LOGD(TAG, "Datapoint %u update to %#08X", datapoint.id, datapoint.value_bitmask);
318  break;
319  default:
320  ESP_LOGW(TAG, "Datapoint %u has unknown type %#02hhX", datapoint.id, static_cast<uint8_t>(datapoint.type));
321  return;
322  }
323 
324  len -= data_size + 4;
325  buffer = data + data_size;
326 
327  // drop update if datapoint is in ignore_mcu_datapoint_update list
328  bool skip = false;
329  for (auto i : this->ignore_mcu_update_on_datapoints_) {
330  if (datapoint.id == i) {
331  ESP_LOGV(TAG, "Datapoint %u found in ignore_mcu_update_on_datapoints list, dropping MCU update", datapoint.id);
332  skip = true;
333  break;
334  }
335  }
336  if (skip)
337  continue;
338 
339  // Update internal datapoints
340  bool found = false;
341  for (auto &other : this->datapoints_) {
342  if (other.id == datapoint.id) {
343  other = datapoint;
344  found = true;
345  }
346  }
347  if (!found) {
348  this->datapoints_.push_back(datapoint);
349  }
350 
351  // Run through listeners
352  for (auto &listener : this->listeners_) {
353  if (listener.datapoint_id == datapoint.id)
354  listener.on_datapoint(datapoint);
355  }
356  }
357 }
358 
360  uint8_t len_hi = (uint8_t)(command.payload.size() >> 8);
361  uint8_t len_lo = (uint8_t)(command.payload.size() & 0xFF);
362  uint8_t version = 0;
363 
365  switch (command.cmd) {
368  break;
371  break;
374  break;
378  break;
379  default:
380  break;
381  }
382 
383  ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", static_cast<uint8_t>(command.cmd),
384  version, format_hex_pretty(command.payload).c_str(), static_cast<uint8_t>(this->init_state_));
385 
386  this->write_array({0x55, 0xAA, version, (uint8_t) command.cmd, len_hi, len_lo});
387  if (!command.payload.empty())
388  this->write_array(command.payload.data(), command.payload.size());
389 
390  uint8_t checksum = 0x55 + 0xAA + (uint8_t) command.cmd + len_hi + len_lo;
391  for (auto &data : command.payload)
392  checksum += data;
393  this->write_byte(checksum);
394 }
395 
397  uint32_t now = millis();
398  uint32_t delay = now - this->last_command_timestamp_;
399 
400  if (now - this->last_rx_char_timestamp_ > RECEIVE_TIMEOUT) {
401  this->rx_message_.clear();
402  }
403 
404  if (this->expected_response_.has_value() && delay > RECEIVE_TIMEOUT) {
405  this->expected_response_.reset();
407  if (++this->init_retries_ >= MAX_RETRIES) {
408  this->init_failed_ = true;
409  ESP_LOGE(TAG, "Initialization failed at init_state %u", static_cast<uint8_t>(this->init_state_));
410  this->command_queue_.erase(command_queue_.begin());
411  this->init_retries_ = 0;
412  }
413  } else {
414  this->command_queue_.erase(command_queue_.begin());
415  }
416  }
417 
418  // Left check of delay since last command in case there's ever a command sent by calling send_raw_command_ directly
419  if (delay > COMMAND_DELAY && !this->command_queue_.empty() && this->rx_message_.empty() &&
420  !this->expected_response_.has_value()) {
421  this->send_raw_command_(command_queue_.front());
422  if (!this->expected_response_.has_value())
423  this->command_queue_.erase(command_queue_.begin());
424  }
425 }
426 
427 void Tuya::send_command_(const TuyaCommand &command) {
428  command_queue_.push_back(command);
430 }
431 
433  send_command_(TuyaCommand{.cmd = command, .payload = std::vector<uint8_t>{}});
434 }
435 
437  bool is_network_ready = network::is_connected() && remote_is_connected();
438  this->status_pin_.value()->digital_write(is_network_ready);
439 }
440 
442  uint8_t status = 0x02;
443  if (network::is_connected()) {
444  status = 0x03;
445 
446  // Protocol version 3 also supports specifying when connected to "the cloud"
447  if (this->protocol_version_ >= 0x03 && remote_is_connected()) {
448  status = 0x04;
449  }
450  }
451 
452  if (status == this->wifi_status_) {
453  return;
454  }
455 
456  ESP_LOGD(TAG, "Sending WiFi Status");
457  this->wifi_status_ = status;
458  this->send_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_STATE, .payload = std::vector<uint8_t>{status}});
459 }
460 
461 #ifdef USE_TIME
463  std::vector<uint8_t> payload;
464  auto *time_id = *this->time_id_;
465  time::ESPTime now = time_id->now();
466  if (now.is_valid()) {
467  uint8_t year = now.year - 2000;
468  uint8_t month = now.month;
469  uint8_t day_of_month = now.day_of_month;
470  uint8_t hour = now.hour;
471  uint8_t minute = now.minute;
472  uint8_t second = now.second;
473  // Tuya days starts from Monday, esphome uses Sunday as day 1
474  uint8_t day_of_week = now.day_of_week - 1;
475  if (day_of_week == 0) {
476  day_of_week = 7;
477  }
478  ESP_LOGD(TAG, "Sending local time");
479  payload = std::vector<uint8_t>{0x01, year, month, day_of_month, hour, minute, second, day_of_week};
480  } else {
481  // By spec we need to notify MCU that the time was not obtained if this is a response to a query
482  ESP_LOGW(TAG, "Sending missing local time");
483  payload = std::vector<uint8_t>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
484  }
485  this->send_command_(TuyaCommand{.cmd = TuyaCommandType::LOCAL_TIME_QUERY, .payload = payload});
486 }
487 #endif
488 
489 void Tuya::set_raw_datapoint_value(uint8_t datapoint_id, const std::vector<uint8_t> &value) {
490  this->set_raw_datapoint_value_(datapoint_id, value, false);
491 }
492 
493 void Tuya::set_boolean_datapoint_value(uint8_t datapoint_id, bool value) {
494  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BOOLEAN, value, 1, false);
495 }
496 
497 void Tuya::set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value) {
498  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::INTEGER, value, 4, false);
499 }
500 
501 void Tuya::set_string_datapoint_value(uint8_t datapoint_id, const std::string &value) {
502  this->set_string_datapoint_value_(datapoint_id, value, false);
503 }
504 
505 void Tuya::set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value) {
506  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::ENUM, value, 1, false);
507 }
508 
509 void Tuya::set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length) {
510  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BITMASK, value, length, false);
511 }
512 
513 void Tuya::force_set_raw_datapoint_value(uint8_t datapoint_id, const std::vector<uint8_t> &value) {
514  this->set_raw_datapoint_value_(datapoint_id, value, true);
515 }
516 
517 void Tuya::force_set_boolean_datapoint_value(uint8_t datapoint_id, bool value) {
518  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BOOLEAN, value, 1, true);
519 }
520 
521 void Tuya::force_set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value) {
522  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::INTEGER, value, 4, true);
523 }
524 
525 void Tuya::force_set_string_datapoint_value(uint8_t datapoint_id, const std::string &value) {
526  this->set_string_datapoint_value_(datapoint_id, value, true);
527 }
528 
529 void Tuya::force_set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value) {
530  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::ENUM, value, 1, true);
531 }
532 
533 void Tuya::force_set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length) {
534  this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BITMASK, value, length, true);
535 }
536 
538  for (auto &datapoint : this->datapoints_) {
539  if (datapoint.id == datapoint_id)
540  return datapoint;
541  }
542  return {};
543 }
544 
545 void Tuya::set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, const uint32_t value,
546  uint8_t length, bool forced) {
547  ESP_LOGD(TAG, "Setting datapoint %u to %u", datapoint_id, value);
548  optional<TuyaDatapoint> datapoint = this->get_datapoint_(datapoint_id);
549  if (!datapoint.has_value()) {
550  ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id);
551  } else if (datapoint->type != datapoint_type) {
552  ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id);
553  return;
554  } else if (!forced && datapoint->value_uint == value) {
555  ESP_LOGV(TAG, "Not sending unchanged value");
556  return;
557  }
558 
559  std::vector<uint8_t> data;
560  switch (length) {
561  case 4:
562  data.push_back(value >> 24);
563  data.push_back(value >> 16);
564  case 2:
565  data.push_back(value >> 8);
566  case 1:
567  data.push_back(value >> 0);
568  break;
569  default:
570  ESP_LOGE(TAG, "Unexpected datapoint length %u", length);
571  return;
572  }
573  this->send_datapoint_command_(datapoint_id, datapoint_type, data);
574 }
575 
576 void Tuya::set_raw_datapoint_value_(uint8_t datapoint_id, const std::vector<uint8_t> &value, bool forced) {
577  ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, format_hex_pretty(value).c_str());
578  optional<TuyaDatapoint> datapoint = this->get_datapoint_(datapoint_id);
579  if (!datapoint.has_value()) {
580  ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id);
581  } else if (datapoint->type != TuyaDatapointType::RAW) {
582  ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id);
583  return;
584  } else if (!forced && datapoint->value_raw == value) {
585  ESP_LOGV(TAG, "Not sending unchanged value");
586  return;
587  }
588  this->send_datapoint_command_(datapoint_id, TuyaDatapointType::RAW, value);
589 }
590 
591 void Tuya::set_string_datapoint_value_(uint8_t datapoint_id, const std::string &value, bool forced) {
592  ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, value.c_str());
593  optional<TuyaDatapoint> datapoint = this->get_datapoint_(datapoint_id);
594  if (!datapoint.has_value()) {
595  ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id);
596  } else if (datapoint->type != TuyaDatapointType::STRING) {
597  ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id);
598  return;
599  } else if (!forced && datapoint->value_string == value) {
600  ESP_LOGV(TAG, "Not sending unchanged value");
601  return;
602  }
603  std::vector<uint8_t> data;
604  for (char const &c : value) {
605  data.push_back(c);
606  }
607  this->send_datapoint_command_(datapoint_id, TuyaDatapointType::STRING, data);
608 }
609 
610 void Tuya::send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, std::vector<uint8_t> data) {
611  std::vector<uint8_t> buffer;
612  buffer.push_back(datapoint_id);
613  buffer.push_back(static_cast<uint8_t>(datapoint_type));
614  buffer.push_back(data.size() >> 8);
615  buffer.push_back(data.size() >> 0);
616  buffer.insert(buffer.end(), data.begin(), data.end());
617 
618  this->send_command_(TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_DELIVER, .payload = buffer});
619 }
620 
621 void Tuya::register_listener(uint8_t datapoint_id, const std::function<void(TuyaDatapoint)> &func) {
622  auto listener = TuyaDatapointListener{
623  .datapoint_id = datapoint_id,
624  .on_datapoint = func,
625  };
626  this->listeners_.push_back(listener);
627 
628  // Run through existing datapoints
629  for (auto &datapoint : this->datapoints_) {
630  if (datapoint.id == datapoint_id)
631  func(datapoint);
632  }
633 }
634 
636 
637 } // namespace tuya
638 } // namespace esphome
uint8_t month
month; january=1 [1-12]
uint8_t protocol_version_
Definition: tuya.h:129
void set_interval(const std::string &name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition: component.cpp:51
void set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value)
Definition: tuya.cpp:505
void set_raw_datapoint_value(uint8_t datapoint_id, const std::vector< uint8_t > &value)
Definition: tuya.cpp:489
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:237
uint8_t wifi_status_
Definition: tuya.h:142
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
TuyaInitState init_state_
Definition: tuya.h:126
void setup() override
Definition: tuya.cpp:16
CallbackManager< void()> initialized_callback_
Definition: tuya.h:143
bool is_valid() const
Check if this ESPTime is valid (all fields in range and year is greater than 2018) ...
void write_byte(uint8_t data)
Definition: uart.h:19
TuyaCommandType
Definition: tuya.h:44
void handle_char_(uint8_t c)
Definition: tuya.cpp:128
void handle_datapoints_(const uint8_t *buffer, size_t len)
Definition: tuya.cpp:252
void set_raw_datapoint_value_(uint8_t datapoint_id, const std::vector< uint8_t > &value, bool forced)
Definition: tuya.cpp:576
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:68
void force_set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length)
Definition: tuya.cpp:533
uint32_t last_command_timestamp_
Definition: tuya.h:133
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition: helpers.h:175
void force_set_string_datapoint_value(uint8_t datapoint_id, const std::string &value)
Definition: tuya.cpp:525
void force_set_raw_datapoint_value(uint8_t datapoint_id, const std::vector< uint8_t > &value)
Definition: tuya.cpp:513
void send_local_time_()
Definition: tuya.cpp:462
std::vector< uint8_t > ignore_mcu_update_on_datapoints_
Definition: tuya.h:139
TuyaDatapointType
Definition: tuya.h:15
void process_command_queue_()
Definition: tuya.cpp:396
bool has_value() const
Definition: optional.h:87
void register_listener(uint8_t datapoint_id, const std::function< void(TuyaDatapoint)> &func)
Definition: tuya.cpp:621
optional< TuyaCommandType > expected_response_
Definition: tuya.h:141
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition: util.cpp:15
std::vector< TuyaDatapointListener > listeners_
Definition: tuya.h:136
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:26
void force_set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value)
Definition: tuya.cpp:529
std::vector< TuyaDatapoint > datapoints_
Definition: tuya.h:137
uint8_t minute
minutes after the hour [0-59]
void set_string_datapoint_value_(uint8_t datapoint_id, const std::string &value, bool forced)
Definition: tuya.cpp:591
std::vector< uint8_t > rx_message_
Definition: tuya.h:138
TuyaCommandType cmd
Definition: tuya.h:68
optional< TuyaDatapoint > get_datapoint_(uint8_t datapoint_id)
Definition: tuya.cpp:537
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:12
A more user-friendly version of struct tm from time.h.
bool init_failed_
Definition: tuya.h:127
bool read_byte(uint8_t *data)
Definition: uart.h:29
TuyaInitState get_init_state()
Definition: tuya.cpp:635
bool remote_is_connected()
Return whether the node has any form of "remote" connection via the API or to an MQTT broker...
Definition: util.cpp:35
int reset_pin_reported_
Definition: tuya.h:132
std::string product_
Definition: tuya.h:135
uint8_t second
seconds after the minute [0-60]
void send_wifi_status_()
Definition: tuya.cpp:441
uint8_t checksum
Definition: bl0939.h:35
void force_set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value)
Definition: tuya.cpp:521
optional< time::RealTimeClock * > time_id_
Definition: tuya.h:124
std::vector< uint8_t > payload
Definition: tuya.h:69
int status_pin_reported_
Definition: tuya.h:131
uint8_t status
Definition: bl0942.h:23
uint8_t day_of_week
day of the week; sunday=1 [1-7]
void loop() override
Definition: tuya.cpp:23
void set_boolean_datapoint_value(uint8_t datapoint_id, bool value)
Definition: tuya.cpp:493
std::string size_t len
Definition: helpers.h:281
void set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value)
Definition: tuya.cpp:497
void set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length)
Definition: tuya.cpp:509
void send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, std::vector< uint8_t > data)
Definition: tuya.cpp:610
void send_raw_command_(TuyaCommand command)
Definition: tuya.cpp:359
bool validate_message_()
Definition: tuya.cpp:72
void set_string_datapoint_value(uint8_t datapoint_id, const std::string &value)
Definition: tuya.cpp:501
void send_command_(const TuyaCommand &command)
Definition: tuya.cpp:427
Definition: a4988.cpp:4
std::vector< TuyaCommand > command_queue_
Definition: tuya.h:140
void send_empty_command_(TuyaCommandType command)
Definition: tuya.cpp:432
uint8_t day_of_month
day of the month [1-31]
void set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, uint32_t value, uint8_t length, bool forced)
Definition: tuya.cpp:545
optional< InternalGPIOPin * > status_pin_
Definition: tuya.h:130
TuyaInitState
Definition: tuya.h:58
uint32_t last_rx_char_timestamp_
Definition: tuya.h:134
void force_set_boolean_datapoint_value(uint8_t datapoint_id, bool value)
Definition: tuya.cpp:517
void handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len)
Definition: tuya.cpp:137
void dump_config() override
Definition: tuya.cpp:32
uint8_t hour
hours since midnight [0-23]
void set_status_pin_()
Definition: tuya.cpp:436
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27