From c83abd12c811f605d50e41e2aef8a5b6861f8df6 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 21 Sep 2022 22:33:53 +0200 Subject: [PATCH] Zigbee Tuya wildcard support --- .../xdrv_23_zigbee_5_2_converters.ino | 11 +++-- .../xdrv_23_zigbee_6_0_commands.ino | 41 +++++++++++-------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino index 47acc0fe5..dfb8e962a 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino @@ -1472,15 +1472,20 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib uint16_t cluster = attr.cluster; uint16_t attribute = attr.attr_id; uint32_t ccccaaaa = (attr.cluster << 16) | attr.attr_id; - // Look for an entry in the converter table bool found = false; // first search in device plug-ins - const Z_attribute_match matched_attr = Z_findAttributeMatcherById(shortaddr, cluster, attribute, true); + Z_attribute_match matched_attr = Z_findAttributeMatcherById(shortaddr, cluster, attribute, true); found = matched_attr.found(); + // special case for Tuya attributes, also search for type `FF` if not found + if (!found && cluster == 0xEF00) { + // search for attribute `FFxx` for wildcard types + matched_attr = Z_findAttributeMatcherById(shortaddr, cluster, 0xFF00 | (attribute & 0x00FF), true); + found = matched_attr.found(); + } - float fval = attr.getFloat(); + float fval = attr.getFloat(); if (found && (matched_attr.map_type != Z_Data_Type::Z_Unknown)) { // We apply an automatic mapping to Z_Data_XXX object // First we find or instantiate the correct Z_Data_XXX according to the endpoint diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_0_commands.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_0_commands.ino index 95063a755..4aaad582b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_0_commands.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_0_commands.ino @@ -338,11 +338,30 @@ void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) } // Parse a cluster specific command, and try to convert into human readable +// Includes specific handling for Tuya attributes void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) { const char * command_name = nullptr; uint8_t conv_direction; Z_XYZ_Var xyz; + // always report attribute in raw format + // Format: "0001!06": "00" = "!": "" for commands to devices + // Format: "0004<00": "00" = "<": "" for commands to devices + // char attrid_str[12]; + // snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '?' : '!', cmd); + // Z_attribute & attr_raw = attr_list.addAttribute(attrid_str); + Z_attribute & attr_raw = attr_list.addAttributeCmd(cluster, cmd, direction, false /* cluster specific */); + attr_raw.setBuf(payload, 0, payload.len()); + + // Take a shotcut in case of Tuya attribute which follow a different scheme + if (cluster == 0xEF00) { + // Tuya Cmd + if (convertTuyaSpecificCluster(attr_list, cluster, cmd, direction, shortaddr, srcendpoint, payload)) { + attr_list.removeAttribute(&attr_raw); // remove raw command + } + return; // abort, normal processing doesn't apply here + } + //AddLog(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2)); for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { const Z_CommandConverter *conv = &Z_Commands[i]; @@ -396,15 +415,6 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, } } - // always report attribute in raw format - // Format: "0001!06": "00" = "!": "" for commands to devices - // Format: "0004<00": "00" = "<": "" for commands to devices - // char attrid_str[12]; - // snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '?' : '!', cmd); - // Z_attribute & attr_raw = attr_list.addAttribute(attrid_str); - Z_attribute & attr_raw = attr_list.addAttributeCmd(cluster, cmd, direction, false /* cluster specific */); - attr_raw.setBuf(payload, 0, payload.len()); - // TODO Berry encode command if (command_name) { @@ -570,22 +580,21 @@ void parseSingleTuyaAttribute(Z_attribute & attr, const SBuffer &buf, // // Tuya - MOES specifc cluster 0xEF00 -// See https://medium.com/@dzegarra/zigbee2mqtt-how-to-add-support-for-a-new-tuya-based-device-part-2-5492707e882d -// and https://github.com/Koenkk/zigbee-herdsman-converters/blob/9f503d47d3df6a99d133b78d2b52aa5c701ddddf/converters/fromZigbee.js#L339 +// https://developer.tuya.com/en/docs/iot-device-dev/tuya-zigbee-universal-docking-access-standard?id=K9ik6zvofpzql#subtitle-6-Private%20cluster // bool convertTuyaSpecificCluster(class Z_attribute_list &attr_list, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &buf) { - // uint8_t status = buf.get8(0); - // uint8_t transid = buf.get8(1); - uint8_t dp = buf.get8(2); // dpid from Tuya documentation + // uint16_t seq_number = buf.get16BigEndian(0) + uint8_t dpid = buf.get8(2); // dpid from Tuya documentation uint8_t attr_type = buf.get8(3); // data type from Tuya documentation uint16_t len = buf.get16BigEndian(4); if ((1 == cmd) || (2 == cmd)) { // attribute report or attribute response - // create a synthetic attribute with id 'dp' - Z_attribute & attr = attr_list.addAttribute(cluster, (attr_type << 8) | dp); + // create a synthetic attribute with id 'dpid' + Z_attribute & attr = attr_list.addAttribute(cluster, (attr_type << 8) | dpid); parseSingleTuyaAttribute(attr, buf, 6, len, attr_type); return true; // true = remove the original Tuya attribute } + // TODO Cmd 0x24 to sync clock with coordinator time return false; }