diff --git a/tasmota/support_static_buffer.ino b/tasmota/support_static_buffer.ino index 55d1a8097..9f4a8456b 100644 --- a/tasmota/support_static_buffer.ino +++ b/tasmota/support_static_buffer.ino @@ -79,6 +79,13 @@ public: } return _buf->len; } + size_t add16BigEndian(const uint16_t data) { // append 16 bits value + if (_buf->len < _buf->size - 1) { // do we have room for 2 bytes + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } size_t add32(const uint32_t data) { // append 32 bits value if (_buf->len < _buf->size - 3) { // do we have room for 4 bytes _buf->buf[_buf->len++] = data; @@ -88,6 +95,15 @@ public: } return _buf->len; } + size_t add32BigEndian(const uint32_t data) { // append 32 bits value + if (_buf->len < _buf->size - 3) { // do we have room for 4 bytes + _buf->buf[_buf->len++] = data >> 24; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } size_t add64(const uint64_t data) { // append 64 bits value if (_buf->len < _buf->size - 7) { // do we have room for 8 bytes _buf->buf[_buf->len++] = data; diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 3ccb54672..e4a71ef4a 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -25,9 +25,9 @@ class ZigbeeZCLSendMessage { public: uint16_t shortaddr; uint16_t groupaddr; - uint16_t clusterId; + uint16_t cluster; uint8_t endpoint; - uint8_t cmdId; + uint8_t cmd; uint16_t manuf; bool clusterSpecific; bool needResponse; diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index b57185e80..055ca1a88 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -40,7 +40,11 @@ enum Z_DataTypes { ZToD = 0xE0, Zdate = 0xE1, ZUTC = 0xE2, ZclusterId = 0xE8, ZattribId = 0xE9, ZbacOID = 0xEA, ZEUI64 = 0xF0, Zkey128 = 0xF1, - Zunk = 0xFF + Zunk = 0xFF, + // adding fake type for Tuya specific encodings + Ztuya1 = 0x80, // 1 byte unsigned int, Big Endian when output (input is taken care of) + Ztuya2 = 0x81, // 2 bytes unsigned, Big Endian when output (input is taken care of) + Ztuya4 = 0x82, // 4 bytes signed, Big Endian when output (input is taken care of) }; // @@ -61,13 +65,18 @@ uint8_t Z_getDatatypeLen(uint8_t t) { case Zsemi: case ZclusterId: case ZattribId: + case Ztuya1: return 2; + case Ztuya2: + return 3; case Zsingle: case ZToD: case Zdate: case ZUTC: case ZbacOID: return 4; + case Ztuya4: + return 5; case Zdouble: case ZEUI64: return 8; @@ -583,25 +592,25 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // Tuya Moes specific - 0xEF00 { Zoctstr, CxEF00, 0x0070, Z_(TuyaScheduleWorkdays), Cm1, 0 }, { Zoctstr, CxEF00, 0x0071, Z_(TuyaScheduleHolidays), Cm1, 0 }, - { Zuint8, CxEF00, 0x0107, Z_(TuyaChildLock), Cm1, 0 }, - { Zuint8, CxEF00, 0x0112, Z_(TuyaWindowDetection), Cm1, 0 }, - { Zuint8, CxEF00, 0x0114, Z_(TuyaValveDetection), Cm1, 0 }, - { Zuint8, CxEF00, 0x0174, Z_(TuyaAutoLock), Cm1, 0 }, - { Zint16, CxEF00, 0x0202, Z_(TuyaTempTarget), Cm_10, 0 }, - { Zint16, CxEF00, 0x0203, Z_(LocalTemperature), Cm_10, 0 }, // will be overwritten by actual LocalTemperature - { Zuint8, CxEF00, 0x0215, Z_(TuyaBattery), Cm1, 0 }, // TODO check equivalent? - { Zint32, CxEF00, 0x0266, Z_(TuyaMinTemp), Cm1, 0 }, - { Zint32, CxEF00, 0x0267, Z_(TuyaMaxTemp), Cm1, 0 }, - { Zint32, CxEF00, 0x0269, Z_(TuyaBoostTime), Cm1, 0 }, - { Zint32, CxEF00, 0x026B, Z_(TuyaComfortTemp), Cm1, 0 }, - { Zint32, CxEF00, 0x026C, Z_(TuyaEcoTemp), Cm1, 0 }, - { Zuint8, CxEF00, 0x026D, Z_(TuyaValvePosition), Cm1, 0 }, - { Zint32, CxEF00, 0x0272, Z_(TuyaAwayTemp), Cm1, 0 }, - { Zint32, CxEF00, 0x0275, Z_(TuyaAwayDays), Cm1, 0 }, - { Zuint8, CxEF00, 0x0404, Z_(TuyaPreset), Cm1, 0 }, - { Zuint8, CxEF00, 0x0405, Z_(TuyaFanMode), Cm1, 0 }, - { Zuint8, CxEF00, 0x046A, Z_(TuyaForceMode), Cm1, 0 }, - { Zuint8, CxEF00, 0x046F, Z_(TuyaWeekSelect), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0107, Z_(TuyaChildLock), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0112, Z_(TuyaWindowDetection), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0114, Z_(TuyaValveDetection), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0174, Z_(TuyaAutoLock), Cm1, 0 }, + { Ztuya4, CxEF00, 0x0202, Z_(TuyaTempTarget), Cm_10, 0 }, + { Ztuya4, CxEF00, 0x0203, Z_(LocalTemperature), Cm_10, 0 }, // will be overwritten by actual LocalTemperature + { Ztuya1, CxEF00, 0x0215, Z_(TuyaBattery), Cm1, 0 }, // TODO check equivalent? + { Ztuya4, CxEF00, 0x0266, Z_(TuyaMinTemp), Cm1, 0 }, + { Ztuya4, CxEF00, 0x0267, Z_(TuyaMaxTemp), Cm1, 0 }, + { Ztuya4, CxEF00, 0x0269, Z_(TuyaBoostTime), Cm1, 0 }, + { Ztuya4, CxEF00, 0x026B, Z_(TuyaComfortTemp), Cm1, 0 }, + { Ztuya4, CxEF00, 0x026C, Z_(TuyaEcoTemp), Cm1, 0 }, + { Ztuya1, CxEF00, 0x026D, Z_(TuyaValvePosition), Cm1, 0 }, + { Ztuya4, CxEF00, 0x0272, Z_(TuyaAwayTemp), Cm1, 0 }, + { Ztuya4, CxEF00, 0x0275, Z_(TuyaAwayDays), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0404, Z_(TuyaPreset), Cm1, 0 }, + { Ztuya1, CxEF00, 0x0405, Z_(TuyaFanMode), Cm1, 0 }, + { Ztuya1, CxEF00, 0x046A, Z_(TuyaForceMode), Cm1, 0 }, + { Ztuya1, CxEF00, 0x046F, Z_(TuyaWeekSelect), Cm1, 0 }, }; #pragma GCC diagnostic pop @@ -816,7 +825,7 @@ uint8_t toPercentageCR2032(uint32_t voltage) { // - n bytes: value (typically between 1 and 4 bytes, or bigger for strings) // returns number of bytes of attribute, or <0 if error int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype) { - uint32_t len = Z_getDatatypeLen(attrtype); // pre-compute lenght, overloaded for variable length attributes + uint32_t len = Z_getDatatypeLen(attrtype); // pre-compute length, overloaded for variable length attributes uint32_t u32 = val_d; int32_t i32 = val_d; float f32 = val_d; @@ -830,6 +839,10 @@ int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_ case Zmap8: // map8 buf.add8(u32); break; + case Ztuya1: // tuya specific 1 byte + buf.add8(1); // len + buf.add8(u32); + break; // unsigned 16 case Zuint16: // uint16 case Zenum16: // enum16 @@ -837,6 +850,9 @@ int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_ case Zmap16: // map16 buf.add16(u32); break; + case Ztuya2: + buf.add8(2); // len + buf.add16BigEndian(u32); // unisgned 32 case Zuint32: // uint32 case Zdata32: // data32 @@ -855,6 +871,10 @@ int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_ case Zint32: // int32 buf.add32(i32); break; + case Ztuya4: + buf.add8(4); // len + buf.add32BigEndian(i32); + break; case Zsingle: // float uint32_t *f_ptr; @@ -944,6 +964,15 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf, } } break; + case Ztuya1: // uint8 Big Endian + attr.setUInt(buf.get8(i+1)); + break; + case Ztuya2: // uint16 Big Endian + attr.setUInt(buf.get16BigEndian(i+1)); + break; + case Ztuya4: + attr.setInt(buf.get32IBigEndian(i+1)); + break; // Note: uint40, uint48, uint56, uint64 are displayed as Hex // Note: int40, int48, int56, int64 are displayed as Hex case Zuint40: // uint40 @@ -1001,14 +1030,14 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf, // For strings, default is to try to do a real string, but reverts to octet stream if null char is present or on some exceptions { bool parse_as_string = true; - len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits - i += (attrtype <= 0x42) ? 1 : 2; // increment pointer + len = (attrtype <= Zstring) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits + i += (attrtype <= Zstring) ? 1 : 2; // increment pointer if (i + len > buf.len()) { // make sure we don't get past the buffer len = buf.len() - i; } // check if we can safely use a string - if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } + if ((Zoctstr == attrtype) || (Zoctstr16 == attrtype)) { parse_as_string = false; } if (parse_as_string) { char str[len+1]; diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index a859c507c..aa85a8def 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -461,34 +461,14 @@ bool convertTuyaSpecificCluster(class Z_attribute_list &attr_list, uint16_t clus if ((1 == cmd) || (2 == cmd)) { // attribute report or attribute response // create a synthetic attribute with id 'dp' Z_attribute & attr = attr_list.addAttribute(cluster, dp); - int32_t ival32 = -0x80000000; - if (1 == len) { - // 1 byte, convert as uint8_t - ival32 = buf.get8(6); - } else if (2 == len) { - ival32 = buf.get16BigEndian(6); - } else if (4 == len) { - // 4 bytes, convert as int32_t - ival32 = buf.get32IBigEndian(6); - } - if (ival32 != -0x80000000) { - // fix temperature coefficient - switch (dp) { - case 0x0202: - attr_list.addAttribute(0x0201, 0x0012).setInt(ival32 * 10); // OccupiedHeatingSetpoint - break; - case 0x0203: - attr_list.addAttribute(0x0201, 0x0000).setInt(ival32 * 10); // LocalTemperature - break; - case 0x026D: - attr_list.addAttribute(0x0201, 0x0008).setUInt(ival32); // PIHeatingDemand - break; - } - attr.setInt(ival32); - } else { - // add as raw buffer - attr.setBuf(buf, 6, len); + uint8_t attr_type; + switch (len) { + case 1: attr_type = Ztuya1; break; + case 2: attr_type = Ztuya2; break; + case 4: attr_type = Ztuya4; break; + default: attr_type = Zoctstr; break; } + parseSingleAttribute(attr, buf, 5, attr_type); return true; // true = remove the original Tuya attribute } return false; diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index d30b760ac..9dd683b9a 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -761,7 +761,7 @@ void CmndZbEZSPSend(void) // - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr // - clusterIf: 16-bits cluster number // - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address. -// - cmdId: 8-bits ZCL command number +// - cmd: 8-bits ZCL command number // - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL // - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr // - len: length of the 'msg' payload @@ -786,7 +786,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { } buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan buf.add8(0x01); // source endpoint - buf.add16(zcl.clusterId); + buf.add16(zcl.cluster); buf.add8(zcl.transacId); // transacId buf.add8(0x30); // 30 options buf.add8(0x1E); // 1E radius @@ -797,7 +797,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { buf.add16(zcl.manuf); // add Manuf Id if not null } buf.add8(zcl.transacId); // Transaction Sequence Number - buf.add8(zcl.cmdId); + buf.add8(zcl.cmd); if (zcl.len > 0) { buf.addBuffer(zcl.msg, zcl.len); // add the payload } @@ -815,7 +815,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { buf.add16(zcl.shortaddr); // dest addr // ApsFrame buf.add16(Z_PROF_HA); // Home Automation profile - buf.add16(zcl.clusterId); // cluster + buf.add16(zcl.cluster); // cluster buf.add8(0x01); // srcEp buf.add8(zcl.endpoint); // dstEp buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame @@ -830,7 +830,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { buf.add16(zcl.manuf); // add Manuf Id if not null } buf.add8(zcl.transacId); // Transaction Sequance Number - buf.add8(zcl.cmdId); + buf.add8(zcl.cmd); if (zcl.len > 0) { buf.addBuffer(zcl.msg, zcl.len); // add the payload } @@ -839,7 +839,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { buf.add16(EZSP_sendMulticast); // 3800 // ApsFrame buf.add16(Z_PROF_HA); // Home Automation profile - buf.add16(zcl.clusterId); // cluster + buf.add16(zcl.cluster); // cluster buf.add8(0x01); // srcEp buf.add8(zcl.endpoint); // broadcast endpoint for groupcast buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame @@ -856,7 +856,7 @@ void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl) { buf.add16(zcl.manuf); // add Manuf Id if not null } buf.add8(zcl.transacId); // Transaction Sequance Number - buf.add8(zcl.cmdId); + buf.add8(zcl.cmd); if (zcl.len > 0) { buf.addBuffer(zcl.msg, zcl.len); // add the payload } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 7fa9707da..cd1b911a2 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -218,6 +218,29 @@ void ZbApplyMultiplier(double &val_d, int8_t multiplier) { } } +// +// Write Tuya-Moes attribute +// +bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr, uint8_t transid) { + double val_d = attr.getFloat(); + const char * val_str = attr.getStr(); + + if (attr.key_is_str) { return false; } // couldn't find attr if so skip + if (attr.isNum() && (1 != attr.attr_multiplier)) { + ZbApplyMultiplier(val_d, attr.attr_multiplier); + } + buf.add8(0); // status + buf.add8(transid); // transid + buf.add16(attr.key.id.attr_id); // dp + buf.add8(0); // fn + int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type); + if (res < 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Error for Tuya attribute type %04X/%04X '0x%02X'"), attr.key.id.cluster, attr.key.id.attr_id, attr.attr_type); + return false; + } + return true; +} + // // Send Attribute Write, apply mutlipliers before // @@ -225,7 +248,7 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat double val_d = attr.getFloat(); const char * val_str = attr.getStr(); - if (attr.key_is_str) { return false; } + if (attr.key_is_str) { return false; } // couldn't find attr if so skip if (attr.isNum() && (1 != attr.attr_multiplier)) { ZbApplyMultiplier(val_d, attr.attr_multiplier); } @@ -246,9 +269,11 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat return true; } -// Parse "Report", "Write", "Response" or "Condig" attribute +// +// Parse "Report", "Write", "Response" or "Config" attribute // Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01) -void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { +// +void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZigbeeZCLSendMessage & packet) { SBuffer buf(200); // buffer to store the binary output of attibutes if (nullptr == XdrvMailbox.command) { @@ -263,9 +288,11 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint attr.setKeyName(key.getStr()); if (Z_parseAttributeKey(attr)) { // Buffer ready, do some sanity checks - if (0xFFFF == cluster) { - cluster = attr.key.id.cluster; // set the cluster for this packet - } else if (cluster != attr.key.id.cluster) { + + // all attributes must use the same cluster + if (0xFFFF == packet.cluster) { + packet.cluster = attr.key.id.cluster; // set the cluster for this packet + } else if (packet.cluster != attr.key.id.cluster) { ResponseCmndChar_P(PSTR("No more than one cluster id per command")); return; } @@ -281,6 +308,7 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint } } + // copy value from input to attribute, in numerical or string format if (value.isStr()) { attr.setStr(value.getStr()); } else if (value.isNum()) { @@ -291,8 +319,19 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint const char* val_str = ""; // variant as string //////////////////////////////////////////////////////////////////////////////// // Split encoding depending on message - if (operation != ZCL_CONFIGURE_REPORTING) { - if (!ZbAppendWriteBuf(buf, attr, operation == ZCL_READ_ATTRIBUTES_RESPONSE)) { + if (packet.cmd != ZCL_CONFIGURE_REPORTING) { + if ((packet.cluster == 0XEF00) && (packet.cmd == ZCL_WRITE_ATTRIBUTES)) { + // special case of Tuya / Moes attributes + if (buf.len() > 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Only 1 attribute allowed for Tuya")); + return; + } + packet.clusterSpecific = true; + packet.cmd = 0x00; + if (!ZbTuyaWrite(buf, attr, zigbee_devices.getNextSeqNumber(packet.shortaddr))) { + return; // error + } + } else if (!ZbAppendWriteBuf(buf, attr, packet.cmd == ZCL_READ_ATTRIBUTES_RESPONSE)) { // general case return; // error } } else { @@ -353,19 +392,10 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, uint16_t device, uint } // all good, send the packet - uint8_t seq = zigbee_devices.getNextSeqNumber(device); - ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ - device, - groupaddr, - cluster /*cluster*/, - endpoint, - operation, - manuf, /* manuf */ - false /* not cluster specific */, - false /* no response */, - seq, /* zcl transaction id */ - buf.getBuffer(), buf.len() - })); + packet.transacId = zigbee_devices.getNextSeqNumber(packet.shortaddr); + packet.msg = buf.getBuffer(); + packet.len = buf.len(); + ZigbeeZCLSend_Raw(packet); ResponseCmndDone(); } @@ -501,7 +531,7 @@ void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupad // Parse the "Send" attribute and send the command -void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) { +void ZbSendRead(JsonParserToken val_attr, ZigbeeZCLSendMessage & packet) { // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5} // ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]} @@ -516,7 +546,7 @@ void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, u uint8_t* attrs = nullptr; // empty string is valid size_t attr_item_len = 2; // how many bytes per attribute, standard for "Read" size_t attr_item_offset = 0; // how many bytes do we offset to store attribute - if (ZCL_READ_REPORTING_CONFIGURATION == operation) { + if (ZCL_READ_REPORTING_CONFIGURATION == packet.cmd) { attr_item_len = 3; attr_item_offset = 1; } @@ -569,9 +599,9 @@ void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, u actual_attr_len += attr_item_len - 2 - attr_item_offset; // normally 0 found = true; // check cluster - if (0xFFFF == cluster) { - cluster = local_cluster_id; - } else if (cluster != local_cluster_id) { + if (0xFFFF == packet.cluster) { + packet.cluster = local_cluster_id; + } else if (packet.cluster != local_cluster_id) { ResponseCmndChar_P(PSTR("No more than one cluster id per command")); if (attrs) { free(attrs); } return; @@ -587,7 +617,7 @@ void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, u attrs_len = actual_attr_len; } else { // value is a literal - if (0xFFFF != cluster) { + if (0xFFFF != packet.cluster) { uint16_t val = val_attr.getUInt(); attrs_len = attr_item_len; attrs = (uint8_t*) calloc(attrs_len, 1); @@ -597,19 +627,11 @@ void ZbSendRead(JsonParserToken val_attr, uint16_t device, uint16_t groupaddr, u } if (attrs_len > 0) { - uint8_t seq = zigbee_devices.getNextSeqNumber(device); - ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ - device, - groupaddr, - cluster /*cluster*/, - endpoint, - operation, - manuf, /* manuf */ - false /* not cluster specific */, - true /* response */, - seq, /* zcl transaction id */ - attrs, attrs_len - })); + // all good, send the packet + packet.transacId = zigbee_devices.getNextSeqNumber(packet.shortaddr); + packet.msg = attrs; + packet.len = attrs_len; + ZigbeeZCLSend_Raw(packet); ResponseCmndDone(); } else { ResponseCmndChar_P(PSTR("Missing parameters")); @@ -707,6 +729,20 @@ void CmndZbSend(void) { } // from here we have one and only one command + // collate information in a ready to send packet + ZigbeeZCLSendMessage packet({ + device, + groupaddr, + cluster /*cluster*/, + endpoint, + ZCL_READ_ATTRIBUTES, + manuf, /* manuf */ + false /* not cluster specific */, + false /* no response */, + 0, /* zcl transaction id */ + nullptr, 0 + }); + if (val_cmd) { // "Send":{...commands...} // we accept either a string or a JSON object @@ -714,7 +750,8 @@ void CmndZbSend(void) { } else if (val_read) { // "Read":{...attributes...}, "Read":attribute or "Read":[...attributes...] // we accept eitehr a number, a string, an array of numbers/strings, or a JSON object - ZbSendRead(val_read, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_ATTRIBUTES); + packet.cmd = ZCL_READ_ATTRIBUTES; + ZbSendRead(val_read, packet); } else if (val_write) { // only KSON object if (!val_write.isObject()) { @@ -722,7 +759,8 @@ void CmndZbSend(void) { return; } // "Write":{...attributes...} - ZbSendReportWrite(val_write, device, groupaddr, cluster, endpoint, manuf, ZCL_WRITE_ATTRIBUTES); + packet.cmd = ZCL_WRITE_ATTRIBUTES; + ZbSendReportWrite(val_write, packet); } else if (val_publish) { // "Publish":{...attributes...} // only KSON object @@ -730,7 +768,8 @@ void CmndZbSend(void) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } - ZbSendReportWrite(val_publish, device, groupaddr, cluster, endpoint, manuf, ZCL_REPORT_ATTRIBUTES); + packet.cmd = ZCL_REPORT_ATTRIBUTES; + ZbSendReportWrite(val_publish, packet); } else if (val_response) { // "Report":{...attributes...} // only KSON object @@ -738,11 +777,13 @@ void CmndZbSend(void) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } - ZbSendReportWrite(val_response, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_ATTRIBUTES_RESPONSE); + packet.cmd = ZCL_READ_ATTRIBUTES_RESPONSE; + ZbSendReportWrite(val_response, packet); } else if (val_read_config) { // "ReadConfg":{...attributes...}, "ReadConfg":attribute or "ReadConfg":[...attributes...] // we accept eitehr a number, a string, an array of numbers/strings, or a JSON object - ZbSendRead(val_read_config, device, groupaddr, cluster, endpoint, manuf, ZCL_READ_REPORTING_CONFIGURATION); + packet.cmd = ZCL_READ_REPORTING_CONFIGURATION; + ZbSendRead(val_read_config, packet); } else if (val_config) { // "Config":{...attributes...} // only JSON object @@ -750,7 +791,8 @@ void CmndZbSend(void) { ResponseCmndChar_P(PSTR("Missing parameters")); return; } - ZbSendReportWrite(val_config, device, groupaddr, cluster, endpoint, manuf, ZCL_CONFIGURE_REPORTING); + packet.cmd = ZCL_CONFIGURE_REPORTING; + ZbSendReportWrite(val_config, packet); } else { Response_P(PSTR("Missing zigbee 'Send', 'Write', 'Report' or 'Response'")); return; @@ -1388,9 +1430,13 @@ void CmndZbData(void) { Z_attribute_list inner_attr; char key[4]; snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint()); - // The key is in the form "L-01", where 'L' is the type and '01' the endpoint in hex format - // 'L' = Light + // The key is in the form "L01", where 'L' is the type and '01' the endpoint in hex format // 'P' = Power + // 'L' = Light + // 'O' = OnOff + // 'T' = Thermo & sensors + // 'A' = Alarm + // '?' = Device wide // Z_Data_Type data_type = data_elt.getType(); switch (data_type) {