From ae2ef5d478fa089892063003a0d09debc3a4b27f Mon Sep 17 00:00:00 2001 From: Chris Nussbaum Date: Sun, 25 Jul 2021 09:33:16 -0500 Subject: [PATCH] Update Tuya component to match the eventual state of the ESPHome version (#13) --- components/tuya/tuya.cpp | 99 +++++++++++-------- components/tuya/tuya.h | 13 ++- .../tuya_dimmer_as_fan/tuya_dimmer_as_fan.cpp | 6 +- .../tuya_light_plus/tuya_light_plus.cpp | 18 ++-- devices/office_light.yaml | 10 -- 5 files changed, 80 insertions(+), 66 deletions(-) diff --git a/components/tuya/tuya.cpp b/components/tuya/tuya.cpp index f497ffd..087e756 100644 --- a/components/tuya/tuya.cpp +++ b/components/tuya/tuya.cpp @@ -11,7 +11,7 @@ static const int COMMAND_DELAY = 50; static const int RECEIVE_TIMEOUT = 300; void Tuya::setup() { - this->set_interval("heartbeat", 10000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); + this->set_interval("heartbeat", 15000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); } void Tuya::loop() { @@ -379,7 +379,7 @@ void Tuya::send_command_(TuyaCommand command) { } void Tuya::send_empty_command_(TuyaCommandType command) { - send_command_(TuyaCommand{.cmd = command, .payload = std::vector{0x04}}); + send_command_(TuyaCommand{.cmd = command, .payload = std::vector{}}); } void Tuya::send_wifi_status_() { @@ -432,42 +432,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, 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; } @@ -478,20 +474,12 @@ void Tuya::set_datapoint_value(uint8_t datapoint_id, std::string value) { this->send_datapoint_command_(datapoint->id, datapoint->type, data); } -void Tuya::set_datapoint_value(uint8_t datapoint_id, bool value) { - ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, TRUEFALSE(value)); - optional datapoint = this->get_datapoint_(datapoint_id); - if (!datapoint.has_value()) { - ESP_LOGE(TAG, "Attempt to set unknown datapoint %u", datapoint_id); - } - else if (datapoint->value_bool == value) { - ESP_LOGV(TAG, "Not sending unchanged value"); - return; - } +void Tuya::set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value) { + this->set_numeric_datapoint_value_(datapoint_id, TuyaDatapointType::ENUM, value, 1); +} - std::vector data; - data.push_back(value >> 0); - this->send_datapoint_command_(datapoint_id, TuyaDatapointType::BOOLEAN, data); +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) { @@ -501,6 +489,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/components/tuya/tuya.h b/components/tuya/tuya.h index 08d5e13..183a175 100644 --- a/components/tuya/tuya.h +++ b/components/tuya/tuya.h @@ -75,9 +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, std::string value); - void set_datapoint_value(uint8_t datapoint_id, bool 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 @@ -96,6 +99,8 @@ class Tuya : public Component, public uart::UARTDevice { 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_(); @@ -114,7 +119,7 @@ class Tuya : public Component, public uart::UARTDevice { std::vector rx_message_; std::vector ignore_mcu_update_on_datapoints_{}; std::vector command_queue_; - optional expected_response_{}; + optional expected_response_{}; uint8_t wifi_status_ = -1; }; diff --git a/components/tuya_dimmer_as_fan/tuya_dimmer_as_fan.cpp b/components/tuya_dimmer_as_fan/tuya_dimmer_as_fan.cpp index 5e288f9..4891735 100644 --- a/components/tuya_dimmer_as_fan/tuya_dimmer_as_fan.cpp +++ b/components/tuya_dimmer_as_fan/tuya_dimmer_as_fan.cpp @@ -20,14 +20,14 @@ void TuyaDimmerAsFan::setup() { this->parent_->register_listener(*this->dimmer_id_, [this](const TuyaDatapoint &datapoint) { if (datapoint.value_int != this->dimmer_max_value_) { - this->parent_->set_datapoint_value(*this->dimmer_id_, this->dimmer_max_value_); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, this->dimmer_max_value_); } }); - this->fan_->add_on_state_callback([this]() { this->parent_->set_datapoint_value(*this->switch_id_, this->fan_->state); }); + this->fan_->add_on_state_callback([this]() { this->parent_->set_boolean_datapoint_value(*this->switch_id_, this->fan_->state); }); // Make sure we start at the max value - this->parent_->set_datapoint_value(*this->dimmer_id_, this->dimmer_max_value_); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, this->dimmer_max_value_); } void TuyaDimmerAsFan::dump_config() { diff --git a/components/tuya_light_plus/tuya_light_plus.cpp b/components/tuya_light_plus/tuya_light_plus.cpp index 73acc66..64d4742 100644 --- a/components/tuya_light_plus/tuya_light_plus.cpp +++ b/components/tuya_light_plus/tuya_light_plus.cpp @@ -12,7 +12,7 @@ void TuyaLightPlus::setup() this->parent_->register_listener(*this->switch_id_, [this](tuya::TuyaDatapoint datapoint) { this->handle_tuya_datapoint_(datapoint); }); this->parent_->register_listener(*this->dimmer_id_, [this](tuya::TuyaDatapoint datapoint) { this->handle_tuya_datapoint_(datapoint); }); if (this->min_value_datapoint_id_.has_value()) { - this->parent_->set_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); + this->parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); } this->register_service(&TuyaLightPlus::set_default_brightness, "set_default_brightness", {"brightness"}); @@ -52,13 +52,13 @@ void TuyaLightPlus::write_state(light::LightState *state) if (brightness == 0.0f) { this->tuya_state_is_on_ = false; - this->parent_->set_datapoint_value(*this->switch_id_, false); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, false); } else { this->tuya_state_is_on_ = true; - this->parent_->set_datapoint_value(*this->dimmer_id_, this->brightness_to_tuya_level_(brightness)); - this->parent_->set_datapoint_value(*this->switch_id_, true); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, this->brightness_to_tuya_level_(brightness)); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, true); } } @@ -134,7 +134,7 @@ void TuyaLightPlus::handle_tuya_datapoint_(tuya::TuyaDatapoint datapoint) { ESP_LOGD(TAG, "Switch was double clicked while on"); - this->parent_->set_datapoint_value(*this->switch_id_, false); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, false); this->double_click_while_on_timeout_ = 0; this->double_click_while_on_callback_.call(); @@ -149,7 +149,7 @@ void TuyaLightPlus::handle_tuya_datapoint_(tuya::TuyaDatapoint datapoint) if (this->double_click_while_off_timeout_ == 0) { // Turn the light back off and wait to see if we get a double click - this->parent_->set_datapoint_value(*this->switch_id_, false); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, false); this->double_click_while_off_timeout_ = millis() + DOUBLE_CLICK_TIMEOUT; return; } @@ -164,7 +164,7 @@ void TuyaLightPlus::handle_tuya_datapoint_(tuya::TuyaDatapoint datapoint) // Double click while off can be configured to result in the light being off or on if (this->double_click_while_off_stays_off_) { - this->parent_->set_datapoint_value(*this->switch_id_, false); + this->parent_->set_boolean_datapoint_value(*this->switch_id_, false); return; } } @@ -173,7 +173,7 @@ void TuyaLightPlus::handle_tuya_datapoint_(tuya::TuyaDatapoint datapoint) // When the light is turned on at the switch the level of the Tuya device will stll be 0 so we set it to the current state value float brightness; this->state_->current_values_as_brightness(&brightness); - this->parent_->set_datapoint_value(*this->dimmer_id_, this->brightness_to_tuya_level_(brightness)); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, this->brightness_to_tuya_level_(brightness)); } // Turned off with the physical button @@ -195,7 +195,7 @@ void TuyaLightPlus::handle_tuya_datapoint_(tuya::TuyaDatapoint datapoint) // default brightness set the current brightness value to the default so that if it is turned on remotely it will be at the default value if (!datapoint.value_bool) { - this->parent_->set_datapoint_value(*this->dimmer_id_, static_cast(0)); + this->parent_->set_integer_datapoint_value(*this->dimmer_id_, 0); if (this->default_brightness_.has_value()) { this->state_->current_values.set_brightness(*this->default_brightness_); diff --git a/devices/office_light.yaml b/devices/office_light.yaml index e792a18..479f60b 100644 --- a/devices/office_light.yaml +++ b/devices/office_light.yaml @@ -6,11 +6,6 @@ substitutions: api_pwd: !secret office_light_api_pwd ap_wifi_pwd: !secret office_light_ap_wifi_pwd -script: - - id: double_click - then: - - logger.log: "Double click script has been run!" - packages: feit_dimmer: !include ../packages/feit_dimmer.yaml @@ -31,8 +26,3 @@ light: night_default_brightness: 1 day_auto_off_time: 0 min night_auto_off_time: 15 min - on_double_click_while_off: - - script.execute: double_click - double_click_while_off_stays_off: true - on_double_click_while_on: - - script.execute: double_click