mirror of https://github.com/arendst/Tasmota.git
Zigbee tuya phase 2
This commit is contained in:
parent
95c0274d59
commit
80d7922977
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue