#pragma once #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/components/uart/uart.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" #endif namespace esphome { namespace tuya { enum class TuyaDatapointType : uint8_t { RAW = 0x00, // variable length BOOLEAN = 0x01, // 1 byte (0/1) INTEGER = 0x02, // 4 byte STRING = 0x03, // variable length ENUM = 0x04, // 1 byte BITMASK = 0x05, // 2 bytes }; struct TuyaDatapoint { uint8_t id; TuyaDatapointType type; size_t len; union { bool value_bool; int value_int; uint32_t value_uint; uint8_t value_enum; uint32_t value_bitmask; }; std::string value_string; std::vector value_raw; }; struct TuyaDatapointListener { uint8_t datapoint_id; std::function on_datapoint; }; enum class TuyaCommandType : uint8_t { HEARTBEAT = 0x00, PRODUCT_QUERY = 0x01, CONF_QUERY = 0x02, WIFI_STATE = 0x03, WIFI_RESET = 0x04, WIFI_SELECT = 0x05, DATAPOINT_DELIVER = 0x06, DATAPOINT_REPORT = 0x07, DATAPOINT_QUERY = 0x08, WIFI_TEST = 0x0E, LOCAL_TIME_QUERY = 0x1C, }; enum class TuyaInitState : uint8_t { INIT_HEARTBEAT = 0x00, INIT_PRODUCT, INIT_CONF, INIT_WIFI, INIT_DATAPOINT, INIT_DONE, }; struct TuyaCommand { TuyaCommandType cmd; std::vector payload; }; class Tuya : public Component, public uart::UARTDevice { public: float get_setup_priority() const override { return setup_priority::LATE; } void setup() override; void loop() override; void dump_config() override; void register_listener(uint8_t datapoint_id, const std::function &func); void set_raw_datapoint_value(uint8_t datapoint_id, const std::vector &value); void set_boolean_datapoint_value(uint8_t datapoint_id, bool value); void set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value); void set_string_datapoint_value(uint8_t datapoint_id, const std::string &value); void set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value); void set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length); #ifdef USE_TIME void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; } #endif void add_ignore_mcu_update_on_datapoints(uint8_t ignore_mcu_update_on_datapoints) { this->ignore_mcu_update_on_datapoints_.push_back(ignore_mcu_update_on_datapoints); } protected: void handle_char_(uint8_t c); void handle_datapoint_(const uint8_t *buffer, size_t len); optional get_datapoint_(uint8_t datapoint_id); bool validate_message_(); void handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len); void send_raw_command_(TuyaCommand command); void process_command_queue_(); void send_command_(TuyaCommand command); void send_empty_command_(TuyaCommandType command); void set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, uint32_t value, uint8_t length); void send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, std::vector data); void send_wifi_status_(); #ifdef USE_TIME void send_local_time_(); optional time_id_{}; #endif TuyaInitState init_state_ = TuyaInitState::INIT_HEARTBEAT; uint8_t protocol_version_ = -1; int gpio_status_ = -1; int gpio_reset_ = -1; uint32_t last_command_timestamp_ = 0; std::string product_ = ""; std::vector listeners_; std::vector datapoints_; std::vector rx_message_; std::vector ignore_mcu_update_on_datapoints_{}; std::vector command_queue_; optional expected_response_{}; uint8_t wifi_status_ = -1; }; } // namespace tuya } // namespace esphome