Zigbee tuya phase 2

This commit is contained in:
Stephan Hadinger 2020-10-13 22:45:11 +02:00
parent 95c0274d59
commit 80d7922977
6 changed files with 179 additions and 108 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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];

View File

@ -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;

View File

@ -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
}

View File

@ -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) {