Enable support for low power protocol v0 of Tuya MCU

This commit is contained in:
Mateusz Wójcik 2024-10-06 22:25:32 +02:00
parent e87169805c
commit 645c8eb7c6
2 changed files with 26 additions and 137 deletions

View file

@ -176,129 +176,29 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff
this->product_ = R"({"p":"INVALID"})"; this->product_ = R"({"p":"INVALID"})";
} }
if (this->init_state_ == TuyaInitState::INIT_PRODUCT) { if (this->init_state_ == TuyaInitState::INIT_PRODUCT) {
this->init_state_ = TuyaInitState::INIT_CONF; this->init_state_ = TuyaInitState::INIT_WIFI;
this->send_empty_command_(TuyaCommandType::CONF_QUERY); this->send_raw_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_STATE, .payload = std::vector<uint8_t>{0x03}});
this->send_raw_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_STATE, .payload = std::vector<uint8_t>{0x04}});
} }
break; break;
} }
case TuyaCommandType::CONF_QUERY: { case TuyaCommandType::LOCAL_TIME_QUERY:
if (len >= 2) { this->send_local_time_();
this->status_pin_reported_ = buffer[0];
this->reset_pin_reported_ = buffer[1];
}
if (this->init_state_ == TuyaInitState::INIT_CONF) {
// If mcu returned status gpio, then we can omit sending wifi state
if (this->status_pin_reported_ != -1) {
this->init_state_ = TuyaInitState::INIT_DATAPOINT;
this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY);
bool is_pin_equals =
this->status_pin_ != nullptr && this->status_pin_->get_pin() == this->status_pin_reported_;
// Configure status pin toggling (if reported and configured) or WIFI_STATE periodic send
if (is_pin_equals) {
ESP_LOGV(TAG, "Configured status pin %i", this->status_pin_reported_);
this->set_interval("wifi", 1000, [this] { this->set_status_pin_(); });
} else {
ESP_LOGW(TAG, "Supplied status_pin does not equals the reported pin %i. TuyaMcu will work in limited mode.",
this->status_pin_reported_);
}
} else {
this->init_state_ = TuyaInitState::INIT_WIFI;
ESP_LOGV(TAG, "Configured WIFI_STATE periodic send");
this->set_interval("wifi", 1000, [this] { this->send_wifi_status_(); });
}
}
break; break;
}
case TuyaCommandType::WIFI_STATE: case TuyaCommandType::WIFI_STATE:
if (this->init_state_ == TuyaInitState::INIT_WIFI) { if (this->init_state_ == TuyaInitState::INIT_WIFI) {
this->init_state_ = TuyaInitState::INIT_DATAPOINT;
this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY);
}
break;
case TuyaCommandType::WIFI_RESET:
ESP_LOGE(TAG, "WIFI_RESET is not handled");
break;
case TuyaCommandType::WIFI_SELECT:
ESP_LOGE(TAG, "WIFI_SELECT is not handled");
break;
case TuyaCommandType::DATAPOINT_DELIVER:
break;
case TuyaCommandType::DATAPOINT_REPORT_ASYNC:
case TuyaCommandType::DATAPOINT_REPORT_SYNC:
if (this->init_state_ == TuyaInitState::INIT_DATAPOINT) {
this->init_state_ = TuyaInitState::INIT_DONE; this->init_state_ = TuyaInitState::INIT_DONE;
this->set_timeout("datapoint_dump", 1000, [this] { this->dump_config(); });
this->initialized_callback_.call();
} }
break;
case TuyaCommandType::DATAPOINT_REPORT:
this->handle_datapoints_(buffer, len); this->handle_datapoints_(buffer, len);
if (command_type == TuyaCommandType::DATAPOINT_REPORT_SYNC) {
this->send_command_(
TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_REPORT_ACK, .payload = std::vector<uint8_t>{0x01}});
}
break;
case TuyaCommandType::DATAPOINT_QUERY:
break;
case TuyaCommandType::WIFI_TEST:
this->send_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_TEST, .payload = std::vector<uint8_t>{0x00, 0x00}});
break;
case TuyaCommandType::WIFI_RSSI:
this->send_command_( this->send_command_(
TuyaCommand{.cmd = TuyaCommandType::WIFI_RSSI, .payload = std::vector<uint8_t>{get_wifi_rssi_()}}); TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_REPORT, .payload = std::vector<uint8_t>{0x01}});
break; break;
case TuyaCommandType::LOCAL_TIME_QUERY: case TuyaCommandType::DP_CACHE:
#ifdef USE_TIME this->send_command_(TuyaCommand{.cmd = TuyaCommandType::DP_CACHE, .payload = std::vector<uint8_t>{0x01}});
if (this->time_id_ != nullptr) {
this->send_local_time_();
if (!this->time_sync_callback_registered_) {
// tuya mcu supports time, so we let them know when our time changed
this->time_id_->add_on_time_sync_callback([this] { this->send_local_time_(); });
this->time_sync_callback_registered_ = true;
}
} else
#endif
{
ESP_LOGW(TAG, "LOCAL_TIME_QUERY is not handled because time is not configured");
}
break; break;
case TuyaCommandType::VACUUM_MAP_UPLOAD:
this->send_command_(
TuyaCommand{.cmd = TuyaCommandType::VACUUM_MAP_UPLOAD, .payload = std::vector<uint8_t>{0x01}});
ESP_LOGW(TAG, "Vacuum map upload requested, responding that it is not enabled.");
break;
case TuyaCommandType::GET_NETWORK_STATUS: {
uint8_t wifi_status = this->get_wifi_status_code_();
this->send_command_(
TuyaCommand{.cmd = TuyaCommandType::GET_NETWORK_STATUS, .payload = std::vector<uint8_t>{wifi_status}});
ESP_LOGV(TAG, "Network status requested, reported as %i", wifi_status);
break;
}
case TuyaCommandType::EXTENDED_SERVICES: {
uint8_t subcommand = buffer[0];
switch ((TuyaExtendedServicesCommandType) subcommand) {
case TuyaExtendedServicesCommandType::RESET_NOTIFICATION: {
this->send_command_(
TuyaCommand{.cmd = TuyaCommandType::EXTENDED_SERVICES,
.payload = std::vector<uint8_t>{
static_cast<uint8_t>(TuyaExtendedServicesCommandType::RESET_NOTIFICATION), 0x00}});
ESP_LOGV(TAG, "Reset status notification enabled");
break;
}
case TuyaExtendedServicesCommandType::MODULE_RESET: {
ESP_LOGE(TAG, "EXTENDED_SERVICES::MODULE_RESET is not handled");
break;
}
case TuyaExtendedServicesCommandType::UPDATE_IN_PROGRESS: {
ESP_LOGE(TAG, "EXTENDED_SERVICES::UPDATE_IN_PROGRESS is not handled");
break;
}
default:
ESP_LOGE(TAG, "Invalid extended services subcommand (0x%02X) received", subcommand);
}
break;
}
default: default:
ESP_LOGE(TAG, "Invalid command (0x%02X) received", command); ESP_LOGE(TAG, "Invalid command (0x%02X) received", command);
} }
@ -424,13 +324,6 @@ void Tuya::send_raw_command_(TuyaCommand command) {
case TuyaCommandType::PRODUCT_QUERY: case TuyaCommandType::PRODUCT_QUERY:
this->expected_response_ = TuyaCommandType::PRODUCT_QUERY; this->expected_response_ = TuyaCommandType::PRODUCT_QUERY;
break; break;
case TuyaCommandType::CONF_QUERY:
this->expected_response_ = TuyaCommandType::CONF_QUERY;
break;
case TuyaCommandType::DATAPOINT_DELIVER:
case TuyaCommandType::DATAPOINT_QUERY:
this->expected_response_ = TuyaCommandType::DATAPOINT_REPORT_ASYNC;
break;
default: default:
break; break;
} }
@ -497,10 +390,11 @@ uint8_t Tuya::get_wifi_status_code_() {
uint8_t status = 0x02; uint8_t status = 0x02;
if (network::is_connected()) { if (network::is_connected()) {
status = 0x03; status = 0x04;
// Protocol version 3 also supports specifying when connected to "the cloud" // Protocol version 3 also supports specifying when connected to "the cloud"
if (this->protocol_version_ >= 0x03 && remote_is_connected()) { // Protocol version 0 only supports it working in low power mode
if ((this->protocol_version_ >= 0x03 && remote_is_connected()) or this->low_power_mode_) {
status = 0x04; status = 0x04;
} }
} else { } else {
@ -691,7 +585,7 @@ void Tuya::send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datap
buffer.push_back(data.size() >> 0); buffer.push_back(data.size() >> 0);
buffer.insert(buffer.end(), data.begin(), data.end()); buffer.insert(buffer.end(), data.begin(), data.end());
this->send_command_(TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_DELIVER, .payload = buffer}); this->send_command_(TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_REPORT, .payload = buffer});
} }
void Tuya::register_listener(uint8_t datapoint_id, const std::function<void(TuyaDatapoint)> &func) { void Tuya::register_listener(uint8_t datapoint_id, const std::function<void(TuyaDatapoint)> &func) {

View file

@ -48,21 +48,17 @@ struct TuyaDatapointListener {
enum class TuyaCommandType : uint8_t { enum class TuyaCommandType : uint8_t {
HEARTBEAT = 0x00, HEARTBEAT = 0x00,
PRODUCT_QUERY = 0x01, PRODUCT_QUERY = 0x01,
CONF_QUERY = 0x02, WIFI_STATE = 0x02,
WIFI_STATE = 0x03, DATAPOINT_REPORT = 0x05,
WIFI_RESET = 0x04, LOCAL_TIME_QUERY = 0x06,
WIFI_SELECT = 0x05, DP_CACHE = 0x10
DATAPOINT_DELIVER = 0x06, };
DATAPOINT_REPORT_ASYNC = 0x07,
DATAPOINT_QUERY = 0x08, enum class TuyaLECommandType : uint8_t {
WIFI_TEST = 0x0E, HEARTBEAT = 0x00,
LOCAL_TIME_QUERY = 0x1C, PRODUCT_QUERY = 0x01,
DATAPOINT_REPORT_SYNC = 0x22, WIFI_STATE = 0x02,
DATAPOINT_REPORT_ACK = 0x23, DATAPOINT_REPORT = 0x05
WIFI_RSSI = 0x24,
VACUUM_MAP_UPLOAD = 0x28,
GET_NETWORK_STATUS = 0x2B,
EXTENDED_SERVICES = 0x34,
}; };
enum class TuyaExtendedServicesCommandType : uint8_t { enum class TuyaExtendedServicesCommandType : uint8_t {
@ -74,9 +70,7 @@ enum class TuyaExtendedServicesCommandType : uint8_t {
enum class TuyaInitState : uint8_t { enum class TuyaInitState : uint8_t {
INIT_HEARTBEAT = 0x00, INIT_HEARTBEAT = 0x00,
INIT_PRODUCT, INIT_PRODUCT,
INIT_CONF,
INIT_WIFI, INIT_WIFI,
INIT_DATAPOINT,
INIT_DONE, INIT_DONE,
}; };
@ -146,6 +140,7 @@ class Tuya : public Component, public uart::UARTDevice {
bool init_failed_{false}; bool init_failed_{false};
int init_retries_{0}; int init_retries_{0};
uint8_t protocol_version_ = -1; uint8_t protocol_version_ = -1;
bool low_power_mode_ = true;
InternalGPIOPin *status_pin_{nullptr}; InternalGPIOPin *status_pin_{nullptr};
int status_pin_reported_ = -1; int status_pin_reported_ = -1;
int reset_pin_reported_ = -1; int reset_pin_reported_ = -1;