diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 5f214d3bfe..a4ce06476d 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -54,14 +54,14 @@ void TuyaClimate::control(const climate::ClimateCall &call) { if (call.get_mode().has_value()) { const bool switch_state = *call.get_mode() != climate::CLIMATE_MODE_OFF; ESP_LOGV(TAG, "Setting switch: %s", ONOFF(switch_state)); - this->parent_->set_datapoint_value(*this->switch_id_, switch_state); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, switch_state); } if (call.get_target_temperature().has_value()) { const float target_temperature = *call.get_target_temperature(); ESP_LOGV(TAG, "Setting target temperature: %.1f", target_temperature); - this->parent_->set_datapoint_value(*this->target_temperature_id_, - (int) (target_temperature / this->target_temperature_multiplier_)); + this->parent_->set_integer_datapoint_value(*this->target_temperature_id_, + (int) (target_temperature / this->target_temperature_multiplier_)); } } diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index ed92713e52..8738b7f4a0 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -67,20 +67,20 @@ void TuyaFan::dump_config() { void TuyaFan::write_state() { if (this->switch_id_.has_value()) { ESP_LOGV(TAG, "Setting switch: %s", ONOFF(this->fan_->state)); - this->parent_->set_datapoint_value(*this->switch_id_, this->fan_->state); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, this->fan_->state); } if (this->oscillation_id_.has_value()) { ESP_LOGV(TAG, "Setting oscillating: %s", ONOFF(this->fan_->oscillating)); - this->parent_->set_datapoint_value(*this->oscillation_id_, this->fan_->oscillating); + this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, this->fan_->oscillating); } if (this->direction_id_.has_value()) { bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; ESP_LOGV(TAG, "Setting reverse direction: %s", ONOFF(enable)); - this->parent_->set_datapoint_value(*this->direction_id_, enable); + this->parent_->set_boolean_datapoint_value(*this->direction_id_, enable); } if (this->speed_id_.has_value()) { ESP_LOGV(TAG, "Setting speed: %d", this->fan_->speed); - this->parent_->set_datapoint_value(*this->speed_id_, this->fan_->speed - 1); + this->parent_->set_integer_datapoint_value(*this->speed_id_, this->fan_->speed - 1); } } diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index e0ca026248..8ad78aacea 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -31,7 +31,7 @@ void TuyaLight::setup() { }); } if (min_value_datapoint_id_.has_value()) { - parent_->set_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); + parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); } } @@ -66,9 +66,9 @@ void TuyaLight::write_state(light::LightState *state) { if (brightness == 0.0f) { // turning off, first try via switch (if exists), then dimmer if (switch_id_.has_value()) { - parent_->set_datapoint_value(*this->switch_id_, false); + parent_->set_boolean_datapoint_value(*this->switch_id_, false); } else if (dimmer_id_.has_value()) { - parent_->set_datapoint_value(*this->dimmer_id_, 0); + parent_->set_integer_datapoint_value(*this->dimmer_id_, 0); } return; } @@ -78,17 +78,17 @@ void TuyaLight::write_state(light::LightState *state) { static_cast(this->color_temperature_max_value_ * (state->current_values.get_color_temperature() - this->cold_white_temperature_) / (this->warm_white_temperature_ - this->cold_white_temperature_)); - parent_->set_datapoint_value(*this->color_temperature_id_, color_temp_int); + parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); } auto brightness_int = static_cast(brightness * this->max_value_); brightness_int = std::max(brightness_int, this->min_value_); if (this->dimmer_id_.has_value()) { - parent_->set_datapoint_value(*this->dimmer_id_, brightness_int); + parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int); } if (this->switch_id_.has_value()) { - parent_->set_datapoint_value(*this->switch_id_, true); + parent_->set_boolean_datapoint_value(*this->switch_id_, true); } } diff --git a/esphome/components/tuya/switch/tuya_switch.cpp b/esphome/components/tuya/switch/tuya_switch.cpp index 8cd09fb01c..cbd794b001 100644 --- a/esphome/components/tuya/switch/tuya_switch.cpp +++ b/esphome/components/tuya/switch/tuya_switch.cpp @@ -15,7 +15,7 @@ void TuyaSwitch::setup() { void TuyaSwitch::write_state(bool state) { ESP_LOGV(TAG, "Setting switch %u: %s", this->switch_id_, ONOFF(state)); - this->parent_->set_datapoint_value(this->switch_id_, state); + this->parent_->set_boolean_datapoint_value(this->switch_id_, state); this->publish_state(state); } diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index 9e036feda9..bbab4f04ce 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -437,42 +437,38 @@ void Tuya::send_local_time_() { } #endif -void Tuya::set_datapoint_value(uint8_t datapoint_id, uint32_t value) { - ESP_LOGD(TAG, "Setting datapoint %u to %u", datapoint_id, value); +void Tuya::set_raw_datapoint_value(uint8_t datapoint_id, const std::vector &value) { + ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, hexencode(value).c_str()); optional datapoint = this->get_datapoint_(datapoint_id); if (!datapoint.has_value()) { - ESP_LOGE(TAG, "Attempt to set unknown datapoint %u", datapoint_id); + ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id); + } else if (datapoint->type != TuyaDatapointType::RAW) { + ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id); return; - } - if (datapoint->value_uint == value) { + } else if (datapoint->value_raw == value) { ESP_LOGV(TAG, "Not sending unchanged value"); return; } - - std::vector data; - switch (datapoint->len) { - case 4: - data.push_back(value >> 24); - data.push_back(value >> 16); - case 2: - data.push_back(value >> 8); - case 1: - data.push_back(value >> 0); - break; - default: - ESP_LOGE(TAG, "Unexpected datapoint length %zu", datapoint->len); - return; - } - this->send_datapoint_command_(datapoint->id, datapoint->type, data); + this->send_datapoint_command_(datapoint_id, TuyaDatapointType::RAW, value); } -void Tuya::set_datapoint_value(uint8_t datapoint_id, const std::string &value) { +void Tuya::set_boolean_datapoint_value(uint8_t datapoint_id, bool value) { + this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BOOLEAN, value, 1); +} + +void Tuya::set_integer_datapoint_value(uint8_t datapoint_id, uint32_t value) { + this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::INTEGER, value, 4); +} + +void Tuya::set_string_datapoint_value(uint8_t datapoint_id, const std::string &value) { ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, value.c_str()); optional datapoint = this->get_datapoint_(datapoint_id); if (!datapoint.has_value()) { - ESP_LOGE(TAG, "Attempt to set unknown datapoint %u", datapoint_id); - } - if (datapoint->value_string == value) { + ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id); + } else if (datapoint->type != TuyaDatapointType::STRING) { + ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id); + return; + } else if (datapoint->value_string == value) { ESP_LOGV(TAG, "Not sending unchanged value"); return; } @@ -483,6 +479,14 @@ void Tuya::set_datapoint_value(uint8_t datapoint_id, const std::string &value) { this->send_datapoint_command_(datapoint->id, datapoint->type, data); } +void Tuya::set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value) { + this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::ENUM, value, 1); +} + +void Tuya::set_bitmask_datapoint_value(uint8_t datapoint_id, uint32_t value, uint8_t length) { + this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::BITMASK, value, length); +} + optional Tuya::get_datapoint_(uint8_t datapoint_id) { for (auto &datapoint : this->datapoints_) if (datapoint.id == datapoint_id) @@ -490,6 +494,37 @@ optional Tuya::get_datapoint_(uint8_t datapoint_id) { return {}; } +void Tuya::set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, const uint32_t value, + uint8_t length) { + ESP_LOGD(TAG, "Setting datapoint %u to %u", datapoint_id, value); + optional datapoint = this->get_datapoint_(datapoint_id); + if (!datapoint.has_value()) { + ESP_LOGW(TAG, "Setting unknown datapoint %u", datapoint_id); + } else if (datapoint->type != datapoint_type) { + ESP_LOGE(TAG, "Attempt to set datapoint %u with incorrect type", datapoint_id); + return; + } else if (datapoint->value_uint == value) { + ESP_LOGV(TAG, "Not sending unchanged value"); + return; + } + + std::vector data; + switch (length) { + case 4: + data.push_back(value >> 24); + data.push_back(value >> 16); + case 2: + data.push_back(value >> 8); + case 1: + data.push_back(value >> 0); + break; + default: + ESP_LOGE(TAG, "Unexpected datapoint length %u", length); + return; + } + this->send_datapoint_command_(datapoint_id, datapoint_type, data); +} + void Tuya::send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, std::vector data) { std::vector buffer; buffer.push_back(datapoint_id); diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index 7ce4be4315..785399502b 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -17,7 +17,7 @@ enum class TuyaDatapointType : uint8_t { INTEGER = 0x02, // 4 byte STRING = 0x03, // variable length ENUM = 0x04, // 1 byte - BITMASK = 0x05, // 2 bytes + BITMASK = 0x05, // 1/2/4 bytes }; struct TuyaDatapoint { @@ -75,8 +75,12 @@ class Tuya : public Component, public uart::UARTDevice { void loop() override; void dump_config() override; void register_listener(uint8_t datapoint_id, const std::function &func); - void set_datapoint_value(uint8_t datapoint_id, uint32_t value); - void set_datapoint_value(uint8_t datapoint_id, const std::string &value); + 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 @@ -95,6 +99,8 @@ class Tuya : public Component, public uart::UARTDevice { void process_command_queue_(); void send_command_(const 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_();