Merge pull request #15642 from s-hadinger/zigbee_merge_zcl

Zigbee merge ZCLMessage and ZCLFrame
This commit is contained in:
s-hadinger 2022-05-16 19:28:30 +02:00 committed by GitHub
commit 1903f09b97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 244 additions and 249 deletions

View File

@ -30,43 +30,6 @@
// contains some definitions for functions used before their declarations // contains some definitions for functions used before their declarations
//
// structure containing all needed information to send a ZCL packet
//
class ZCLMessage {
public:
ZCLMessage(void); // allocate 16 bytes vy default
ZCLMessage(size_t size);
inline bool validShortaddr(void) const { return BAD_SHORTADDR != shortaddr; }
inline bool validGroupaddr(void) const { return 0 != groupaddr; }
inline bool validCluster(void) const { return 0xFFFF != cluster; }
inline bool validEndpoint(void) const { return 0x00 != endpoint; }
inline bool validCmd(void) const { return 0xFF != cmd; }
inline void setTransac(uint8_t _transac) { transac = _transac; transacSet = true; }
uint16_t shortaddr = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
uint16_t groupaddr = 0x0000; // group address valid only if device == BAD_SHORTADDR
uint16_t cluster = 0xFFFF; // no default
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint8_t cmd = 0xFF; // 0xFF is invalid command number
uint16_t manuf = 0x0000; // default manuf id
bool clusterSpecific = false;
bool needResponse = true;
bool direct = false; // true if direct, false if discover router
bool transacSet = false; // is transac already set
uint8_t transac = 0; // ZCL transaction number
SBuffer buf;
// const uint8_t *msg = nullptr;
// size_t len = 0;
};
// define constructor seperately to avoid inlining and reduce Flash size
ZCLMessage::ZCLMessage(void) : buf(12) {};
ZCLMessage::ZCLMessage(size_t size) : buf(size) {};
typedef int32_t (*ZB_Func)(uint8_t value); typedef int32_t (*ZB_Func)(uint8_t value);
typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const SBuffer &buf); typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const SBuffer &buf);
@ -150,8 +113,8 @@ public:
struct ZigbeeStatus zigbee; struct ZigbeeStatus zigbee;
SBuffer *zigbee_buffer = nullptr; SBuffer *zigbee_buffer = nullptr;
void zigbeeZCLSendCmd(ZCLMessage &msg); void zigbeeZCLSendCmd(ZCLFrame &msg);
void ZigbeeZCLSend_Raw(const ZCLMessage &zcl); void ZigbeeZCLSend_Raw(const ZCLFrame &zcl);
bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false); bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false);
// parse Hex formatted attribute names like '0301/0001" // parse Hex formatted attribute names like '0301/0001"

View File

@ -975,7 +975,7 @@ public:
void clean(void); // avoid writing to flash the last changes void clean(void); // avoid writing to flash the last changes
// Find device by name, can be short_addr, long_addr, number_in_array or name // Find device by name, can be short_addr, long_addr, number_in_array or name
Z_Device & parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr = nullptr); Z_Device & parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr = nullptr, int32_t mailbox_payload = 0);
bool isTuyaProtocol(uint16_t shortaddr, uint8_t ep = 0) const; bool isTuyaProtocol(uint16_t shortaddr, uint8_t ep = 0) const;
@ -983,7 +983,7 @@ private:
LList<Z_Device> _devices; // list of devices LList<Z_Device> _devices; // list of devices
LList<Z_Deferred> _deferred; // list of deferred calls LList<Z_Deferred> _deferred; // list of deferred calls
uint32_t _saveTimer = 0; uint32_t _saveTimer = 0;
uint8_t _seqNumber = 0; // global seqNumber if device is unknown uint8_t _seqnumber = 0; // global seqNumber if device is unknown
//int32_t findShortAddrIdx(uint16_t shortaddr) const; //int32_t findShortAddrIdx(uint16_t shortaddr) const;
// Create a new entry in the devices list - must be called if it is sure it does not already exist // Create a new entry in the devices list - must be called if it is sure it does not already exist
@ -991,6 +991,13 @@ private:
void freeDeviceEntry(Z_Device *device); void freeDeviceEntry(Z_Device *device);
}; };
/*********************************************************************************************\
* Berry support
\*********************************************************************************************/
#ifdef USE_BERRY
extern "C" void callBerryZigbeeDispatcher(const char* type, ZCLFrame* zcl_received);
#endif // USE_BERRY
/*********************************************************************************************\ /*********************************************************************************************\
* Singleton variable * Singleton variable
\*********************************************************************************************/ \*********************************************************************************************/

View File

@ -345,8 +345,8 @@ uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
device.seqNumber += 1; device.seqNumber += 1;
return device.seqNumber; return device.seqNumber;
} else { } else {
_seqNumber += 1; _seqnumber += 1;
return _seqNumber; return _seqnumber;
} }
} }
@ -622,7 +622,7 @@ void Z_Devices::clean(void) {
// - a friendly name, between quotes, example: "Room_Temp" // - a friendly name, between quotes, example: "Room_Temp"
// //
// In case the device is not found, the parsed 0x.... short address is passed to *parsed_shortaddr // In case the device is not found, the parsed 0x.... short address is passed to *parsed_shortaddr
Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr) { Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_shortaddr, int32_t mailbox_payload) {
if (nullptr == param) { return device_unk; } if (nullptr == param) { return device_unk; }
size_t param_len = strlen(param); size_t param_len = strlen(param);
char dataBuf[param_len + 1]; char dataBuf[param_len + 1];
@ -632,8 +632,8 @@ Z_Device & Z_Devices::parseDeviceFromName(const char * param, uint16_t * parsed_
if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) { if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) {
// simple number 0..99 // simple number 0..99
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { if ((mailbox_payload > 0) && (mailbox_payload <= 99)) {
return isKnownIndexDevice(XdrvMailbox.payload - 1); return isKnownIndexDevice(mailbox_payload - 1);
} else { } else {
return device_unk; return device_unk;
} }

View File

@ -722,18 +722,25 @@ const __FlashStringHelper* zigbeeFindAttributeById(uint16_t cluster, uint16_t at
class ZCLFrame { class ZCLFrame {
public: public:
// constructor used when creating a message from scratch to send it later
ZCLFrame(void); // allocate 16 bytes by default
ZCLFrame(size_t size);
// constructore used when receiving a Zigbee frame and populating the class
ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id,
const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr, const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupaddr,
uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber): uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber):
_manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), manuf(manuf_code), transactseq(transact_seq), cmd(cmd_id),
_payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough
_cluster_id(clusterid), _groupaddr(groupaddr), cluster(clusterid), groupaddr(groupaddr),
_srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), shortaddr(srcaddr), _srcendpoint(srcendpoint), dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
_linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber) _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber)
{ {
_frame_control.d8 = frame_control; _frame_control.d8 = frame_control;
_payload.addBuffer(buf, buf_len); clusterSpecific = (_frame_control.b.frame_type != 0);
needResponse = !_frame_control.b.disable_def_resp;
payload.addBuffer(buf, buf_len);
}; };
@ -746,13 +753,13 @@ public:
"\"frametype\":%d,\"direction\":%d,\"disableresp\":%d," "\"frametype\":%d,\"direction\":%d,\"disableresp\":%d,"
"\"manuf\":\"0x%04X\",\"transact\":%d," "\"manuf\":\"0x%04X\",\"transact\":%d,"
"\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"), "\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"),
_groupaddr, _cluster_id, _srcaddr, groupaddr, cluster, shortaddr,
_srcendpoint, _dstendpoint, _wasbroadcast, _srcendpoint, dstendpoint, _wasbroadcast,
_linkquality, _securityuse, _seqnumber, _linkquality, _securityuse, _seqnumber,
_frame_control, _frame_control,
_frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp, _frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp,
_manuf_code, _transact_seq, _cmd_id, manuf, transactseq, cmd,
&_payload); &payload);
if (Settings->flag3.tuya_serial_mqtt_publish) { if (Settings->flag3.tuya_serial_mqtt_publish) {
MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR)); MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR));
} else { } else {
@ -788,6 +795,7 @@ public:
return _frame_control.b.frame_type & 1; return _frame_control.b.frame_type & 1;
} }
// parsers for received messages
void parseReportAttributes(Z_attribute_list& attr_list); void parseReportAttributes(Z_attribute_list& attr_list);
void generateSyntheticAttributes(Z_attribute_list& attr_list); void generateSyntheticAttributes(Z_attribute_list& attr_list);
void removeInvalidAttributes(Z_attribute_list& attr_list); void removeInvalidAttributes(Z_attribute_list& attr_list);
@ -813,47 +821,59 @@ public:
void autoResponder(const uint16_t *attr_list_ids, size_t attr_len); void autoResponder(const uint16_t *attr_list_ids, size_t attr_len);
inline void setGroupId(uint16_t groupid) { inline void setGroupId(uint16_t groupid) {
_groupaddr = groupid; groupaddr = groupid;
} }
inline void setClusterId(uint16_t clusterid) { inline void setClusterId(uint16_t clusterid) {
_cluster_id = clusterid; cluster = clusterid;
} }
inline uint16_t getSrcAddr(void) const { return _srcaddr; } inline uint16_t getSrcAddr(void) const { return shortaddr; }
inline uint16_t getGroupAddr(void) const { return _groupaddr; } inline uint16_t getGroupAddr(void) const { return groupaddr; }
inline uint16_t getClusterId(void) const { return _cluster_id; } inline uint16_t getClusterId(void) const { return cluster; }
inline uint8_t getLinkQuality(void) const { return _linkquality; } inline uint8_t getLinkQuality(void) const { return _linkquality; }
inline uint8_t getCmdId(void) const { return _cmd_id; } inline uint8_t getCmdId(void) const { return cmd; }
inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; } inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; }
const SBuffer &getPayload(void) const { return payload; }
uint16_t getManufCode(void) const { return manuf; }
const SBuffer &getPayload(void) const { inline void setTransac(uint8_t _transac) { transactseq = _transac; transacSet = true; }
return _payload;
}
uint16_t getManufCode(void) const { inline bool validShortaddr(void) const { return BAD_SHORTADDR != shortaddr; }
return _manuf_code; inline bool validCluster(void) const { return 0xFFFF != cluster; }
} inline bool validEndpoint(void) const { return 0x00 != dstendpoint; }
inline bool validCmd(void) const { return 0xFF != cmd; }
public:
private: uint16_t manuf = 0; // optional
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; uint8_t transactseq = 0; // transaction sequence number
uint16_t _manuf_code = 0; // optional uint8_t cmd = 0;
uint8_t _transact_seq = 0; // transaction sequence number SBuffer payload;
uint8_t _cmd_id = 0; uint16_t cluster = 0;
SBuffer _payload; uint16_t groupaddr = 0;
uint16_t _cluster_id = 0;
uint16_t _groupaddr = 0;
// information from decoded ZCL frame // information from decoded ZCL frame
uint16_t _srcaddr; uint16_t shortaddr = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
uint8_t _srcendpoint; uint8_t dstendpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint8_t _dstendpoint; // attributes used in send-only mode
uint8_t _wasbroadcast; bool clusterSpecific = false;
uint8_t _linkquality; bool needResponse = true;
uint8_t _securityuse; bool direct = false; // true if direct, false if discover router
uint8_t _seqnumber; bool transacSet = false; // is transac already set
// below private attributes are not used when sending a message
private:
uint8_t _srcendpoint = 0x00; // 0x00 is invalid for the src endpoint
ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 };
bool _wasbroadcast = false;
uint8_t _linkquality = 0x00;
uint8_t _securityuse = 0; // not used by Z2T, logging only
uint8_t _seqnumber = 0; // not used by Z2T, logging only
}; };
// define constructor seperately to avoid inlining and reduce Flash size
ZCLFrame::ZCLFrame(void) : payload(12) {};
ZCLFrame::ZCLFrame(size_t size) : payload(size) {};
// Zigbee ZCL converters // Zigbee ZCL converters
// from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55 // from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55
@ -1197,45 +1217,45 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
// First pass, parse all attributes in their native format // First pass, parse all attributes in their native format
void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
if (ZCL_WRITE_ATTRIBUTES == getCmdId()) { if (ZCL_WRITE_ATTRIBUTES == getCmdId()) {
attr_list.addAttribute(PSTR("Command"), true).setStr(PSTR("Write")); attr_list.addAttribute(PSTR("Command"), true).setStr(PSTR("Write"));
} }
while (len >= i + 3) { while (len >= i + 3) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
i += 2; i += 2;
// exception for Xiaomi lumi.weather - specific field to be treated as octet and not char // exception for Xiaomi lumi.weather - specific field to be treated as octet and not char
if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { if ((0x0000 == cluster) && (0xFF01 == attrid)) {
if (0x42 == _payload.get8(i)) { if (0x42 == payload.get8(i)) {
_payload.set8(i, 0x41); // change type from 0x42 to 0x41 payload.set8(i, 0x41); // change type from 0x42 to 0x41
} }
} }
// TODO look for suffix // TODO look for suffix
Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); Z_attribute & attr = attr_list.addAttribute(cluster, attrid);
i += parseSingleAttribute(attr, _payload, i); i += parseSingleAttribute(attr, payload, i);
} }
// Issue Philips outdoor motion sensor SML002, see https://github.com/Koenkk/zigbee2mqtt/issues/897 // Issue Philips outdoor motion sensor SML002, see https://github.com/Koenkk/zigbee2mqtt/issues/897
// The sensor expects the coordinator to send a Default Response to acknowledge the attribute reporting // The sensor expects the coordinator to send a Default Response to acknowledge the attribute reporting
if (0 == _frame_control.b.disable_def_resp) { if (0 == _frame_control.b.disable_def_resp) {
// the device expects a default response // the device expects a default response
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE; zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = _manuf_code; zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct no retry */ zcl.direct = true; /* direct no retry */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.add8(_cmd_id); zcl.payload.add8(cmd);
zcl.buf.add8(0); // Status = OK zcl.payload.add8(0); // Status = OK
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1295,8 +1315,8 @@ void ZCLFrame::removeInvalidAttributes(Z_attribute_list& attr_list) {
// Note: both function are now split to compute on extracted attributes // Note: both function are now split to compute on extracted attributes
// //
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
const Z_Device & device = zigbee_devices.findShortAddr(_srcaddr); const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * model_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) model_c); String modelId((char*) model_c);
// scan through attributes and apply specific converters // scan through attributes and apply specific converters
for (auto &attr : attr_list) { for (auto &attr : attr_list) {
@ -1324,7 +1344,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
case 0x02010008: // Pi Heating Demand - solve Eutotronic bug case 0x02010008: // Pi Heating Demand - solve Eutotronic bug
case 0x02014008: // Eurotronic Host Flags decoding case 0x02014008: // Eurotronic Host Flags decoding
{ {
const char * manufacturer_c = zigbee_devices.getManufacturerId(_srcaddr); // null if unknown const char * manufacturer_c = zigbee_devices.getManufacturerId(shortaddr); // null if unknown
String manufacturerId((char*) manufacturer_c); String manufacturerId((char*) manufacturer_c);
if (manufacturerId.equals(F("Eurotronic"))) { if (manufacturerId.equals(F("Eurotronic"))) {
if (ccccaaaa == 0x02010008) { if (ccccaaaa == 0x02010008) {
@ -1382,7 +1402,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
} }
break; break;
case 0x05000002: // ZoneStatus case 0x05000002: // ZoneStatus
const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(_srcaddr).data.find(Z_Data_Type::Z_Alarm, _srcendpoint); const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_Alarm, _srcendpoint);
if (&alarm != nullptr) { if (&alarm != nullptr) {
alarm.convertZoneStatus(attr_list, attr.getUInt()); alarm.convertZoneStatus(attr_list, attr.getUInt());
} }
@ -1405,15 +1425,15 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
uint32_t occupancy = attr.getUInt(); uint32_t occupancy = attr.getUInt();
if (occupancy) { if (occupancy) {
uint32_t pir_timer = OCCUPANCY_TIMEOUT; uint32_t pir_timer = OCCUPANCY_TIMEOUT;
const Z_Data_PIR & pir_found = (const Z_Data_PIR&) zigbee_devices.getShortAddr(_srcaddr).data.find(Z_Data_Type::Z_PIR, _srcendpoint); const Z_Data_PIR & pir_found = (const Z_Data_PIR&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_PIR, _srcendpoint);
if (&pir_found != nullptr) { if (&pir_found != nullptr) {
pir_timer = pir_found.getTimeoutSeconds() * 1000; pir_timer = pir_found.getTimeoutSeconds() * 1000;
} }
if (pir_timer > 0) { if (pir_timer > 0) {
zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, pir_timer, _cluster_id, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, pir_timer, cluster, _srcendpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback);
} }
} else { } else {
zigbee_devices.resetTimersForDevice(_srcaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY); zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY);
} }
break; break;
} }
@ -1460,16 +1480,16 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
// ZCL_READ_ATTRIBUTES // ZCL_READ_ATTRIBUTES
void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
uint16_t read_attr_ids[len/2]; uint16_t read_attr_ids[len/2];
attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id); attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_CLUSTER)).setUInt(cluster);
JsonGeneratorArray attr_numbers; JsonGeneratorArray attr_numbers;
Z_attribute_list attr_names; Z_attribute_list attr_names;
while (len >= 2 + i) { while (len >= 2 + i) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
attr_numbers.add(attrid); attr_numbers.add(attrid);
read_attr_ids[i/2] = attrid; read_attr_ids[i/2] = attrid;
@ -1479,7 +1499,7 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
uint16_t conv_attribute = pgm_read_word(&converter->attribute); uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { if ((conv_cluster == cluster) && (conv_attribute == attrid)) {
attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true); attr_names.addAttribute(Z_strings + pgm_read_word(&converter->name_offset), true).setBool(true);
break; break;
} }
@ -1490,29 +1510,29 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
attr_list.addAttributePMEM(PSTR("ReadNames")).setStrRaw(attr_names.toString(true).c_str()); attr_list.addAttributePMEM(PSTR("ReadNames")).setStrRaw(attr_names.toString(true).c_str());
// call auto-responder only if src address if different from ourselves and it was a broadcast // call auto-responder only if src address if different from ourselves and it was a broadcast
if (_srcaddr != localShortAddr || !_wasbroadcast) { if (shortaddr != localShortAddr || !_wasbroadcast) {
autoResponder(read_attr_ids, len/2); autoResponder(read_attr_ids, len/2);
} }
} }
// ZCL_CONFIGURE_REPORTING_RESPONSE // ZCL_CONFIGURE_REPORTING_RESPONSE
void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
uint32_t len = _payload.len(); uint32_t len = payload.len();
Z_attribute_list attr_config_list; Z_attribute_list attr_config_list;
for (uint32_t i=0; len >= i+4; i+=4) { for (uint32_t i=0; len >= i+4; i+=4) {
uint8_t status = _payload.get8(i); uint8_t status = payload.get8(i);
uint16_t attr_id = _payload.get8(i+2); uint16_t attr_id = payload.get8(i+2);
Z_attribute_list attr_config_response; Z_attribute_list attr_config_response;
attr_config_response.addAttributePMEM(PSTR("Status")).setUInt(status); attr_config_response.addAttributePMEM(PSTR("Status")).setUInt(status);
attr_config_response.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); attr_config_response.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr); const __FlashStringHelper* attr_name = zigbeeFindAttributeById(cluster, attr_id, nullptr, nullptr);
if (attr_name) { if (attr_name) {
attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str()); attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str());
} else { } else {
attr_config_list.addAttribute(_cluster_id, attr_id).setStrRaw(attr_config_response.toString(true).c_str()); attr_config_list.addAttribute(cluster, attr_id).setStrRaw(attr_config_response.toString(true).c_str());
} }
} }
@ -1522,21 +1542,21 @@ void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
// ZCL_WRITE_ATTRIBUTES_RESPONSE // ZCL_WRITE_ATTRIBUTES_RESPONSE
void ZCLFrame::parseWriteAttributesResponse(Z_attribute_list& attr_list) { void ZCLFrame::parseWriteAttributesResponse(Z_attribute_list& attr_list) {
parseResponse_inner(ZCL_WRITE_ATTRIBUTES_RESPONSE, false, _payload.get8(0)); parseResponse_inner(ZCL_WRITE_ATTRIBUTES_RESPONSE, false, payload.get8(0));
} }
// ZCL_READ_REPORTING_CONFIGURATION_RESPONSE // ZCL_READ_REPORTING_CONFIGURATION_RESPONSE
void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
Z_attribute &attr_root = attr_list.addAttributePMEM(PSTR("ReadConfig")); Z_attribute &attr_root = attr_list.addAttributePMEM(PSTR("ReadConfig"));
Z_attribute_list attr_1; Z_attribute_list attr_1;
while (len >= i + 4) { while (len >= i + 4) {
uint8_t status = _payload.get8(i); uint8_t status = payload.get8(i);
uint8_t direction = _payload.get8(i+1); uint8_t direction = payload.get8(i+1);
uint16_t attrid = _payload.get16(i+2); uint16_t attrid = payload.get16(i+2);
Z_attribute_list attr_2; Z_attribute_list attr_2;
if (direction) { if (direction) {
@ -1550,7 +1570,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
uint16_t conv_attribute = pgm_read_word(&converter->attribute); uint16_t conv_attribute = pgm_read_word(&converter->attribute);
if ((conv_cluster == _cluster_id) && (conv_attribute == attrid)) { if ((conv_cluster == cluster) && (conv_attribute == attrid)) {
const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset); const char * attr_name = Z_strings + pgm_read_word(&converter->name_offset);
attr_2.addAttribute(attr_name, true).setBool(true); attr_2.addAttribute(attr_name, true).setBool(true);
multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx));
@ -1565,22 +1585,22 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
// no error, decode data // no error, decode data
if (direction) { if (direction) {
// only Timeout period is present // only Timeout period is present
uint16_t attr_timeout = _payload.get16(i); uint16_t attr_timeout = payload.get16(i);
i += 2; i += 2;
attr_2.addAttributePMEM(PSTR("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout); attr_2.addAttributePMEM(PSTR("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout);
} else { } else {
// direction == 0, we have a data type // direction == 0, we have a data type
uint8_t attr_type = _payload.get8(i); uint8_t attr_type = payload.get8(i);
bool attr_discrete = Z_isDiscreteDataType(attr_type); bool attr_discrete = Z_isDiscreteDataType(attr_type);
uint16_t attr_min_interval = _payload.get16(i+1); uint16_t attr_min_interval = payload.get16(i+1);
uint16_t attr_max_interval = _payload.get16(i+3); uint16_t attr_max_interval = payload.get16(i+3);
i += 5; i += 5;
attr_2.addAttributePMEM(PSTR("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval); attr_2.addAttributePMEM(PSTR("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval);
attr_2.addAttributePMEM(PSTR("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval); attr_2.addAttributePMEM(PSTR("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval);
if (!attr_discrete) { if (!attr_discrete) {
// decode Reportable Change // decode Reportable Change
Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange")); Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange"));
i += parseSingleAttribute(attr_change, _payload, i, attr_type); i += parseSingleAttribute(attr_change, payload, i, attr_type);
if ((1 != multiplier) && (0 != multiplier)) { if ((1 != multiplier) && (0 != multiplier)) {
float fval = attr_change.getFloat(); float fval = attr_change.getFloat();
if (multiplier > 0) { fval = fval * multiplier; } if (multiplier > 0) { fval = fval * multiplier; }
@ -1590,7 +1610,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
} }
} }
} }
attr_1.addAttribute(_cluster_id, attrid).setStrRaw(attr_2.toString(true).c_str()); attr_1.addAttribute(cluster, attrid).setStrRaw(attr_2.toString(true).c_str());
} }
attr_root.setStrRaw(attr_1.toString(true).c_str()); attr_root.setStrRaw(attr_1.toString(true).c_str());
} }
@ -1598,16 +1618,16 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) {
// ZCL_READ_ATTRIBUTES_RESPONSE // ZCL_READ_ATTRIBUTES_RESPONSE
void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) { void ZCLFrame::parseReadAttributesResponse(Z_attribute_list& attr_list) {
uint32_t i = 0; uint32_t i = 0;
uint32_t len = _payload.len(); uint32_t len = payload.len();
while (len >= i + 4) { while (len >= i + 4) {
uint16_t attrid = _payload.get16(i); uint16_t attrid = payload.get16(i);
i += 2; i += 2;
uint8_t status = _payload.get8(i++); uint8_t status = payload.get8(i++);
if (0 == status) { if (0 == status) {
Z_attribute & attr = attr_list.addAttribute(_cluster_id, attrid); Z_attribute & attr = attr_list.addAttribute(cluster, attrid);
i += parseSingleAttribute(attr, _payload, i); i += parseSingleAttribute(attr, payload, i);
} }
} }
} }
@ -1618,15 +1638,15 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// "Device" // "Device"
char s[12]; char s[12];
snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); snprintf_P(s, sizeof(s), PSTR("0x%04X"), shortaddr);
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(s); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(s);
// "Name" // "Name"
const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
if (friendlyName) { if (friendlyName) {
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(friendlyName); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(friendlyName);
} }
// "Command" // "Command"
snprintf_P(s, sizeof(s), PSTR("%04X%c%02X"), _cluster_id, cluster_specific ? '!' : '_', cmd); snprintf_P(s, sizeof(s), PSTR("%04X%c%02X"), cluster, cluster_specific ? '!' : '_', cmd);
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_CMD)).setStr(s); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_CMD)).setStr(s);
// "Status" // "Status"
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS)).setUInt(status); attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS)).setUInt(status);
@ -1635,8 +1655,8 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// Add Endpoint // Add Endpoint
attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint); attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint);
// Add Group if non-zero // Add Group if non-zero
if (_groupaddr) { // TODO what about group zero if (groupaddr) { // TODO what about group zero
attr_list.group_id = _groupaddr; attr_list.group_id = groupaddr;
} }
// Add linkquality // Add linkquality
attr_list.lqi = _linkquality; attr_list.lqi = _linkquality;
@ -1647,9 +1667,9 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// ZCL_DEFAULT_RESPONSE // ZCL_DEFAULT_RESPONSE
void ZCLFrame::parseResponse(void) { void ZCLFrame::parseResponse(void) {
if (_payload.len() < 2) { return; } // wrong format if (payload.len() < 2) { return; } // wrong format
uint8_t cmd = _payload.get8(0); uint8_t cmd = payload.get8(0);
uint8_t status = _payload.get8(1); uint8_t status = payload.get8(1);
parseResponse_inner(cmd, true, status); parseResponse_inner(cmd, true, status);
} }
@ -1665,22 +1685,22 @@ void Z_ResetDebounce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, u
// Parse non-normalized attributes // Parse non-normalized attributes
void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// Check if debounce is active and if the packet is a duplicate // Check if debounce is active and if the packet is a duplicate
Z_Device & device = zigbee_devices.getShortAddr(_srcaddr); Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == _transact_seq)) { if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == transactseq)) {
// this is a duplicate, drop the packet // this is a duplicate, drop the packet
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), _srcaddr, _srcendpoint); AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), shortaddr, _srcendpoint);
} else { } else {
// reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint) // reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint)
device.debounce_endpoint = _srcendpoint; device.debounce_endpoint = _srcendpoint;
device.debounce_transact = _transact_seq; device.debounce_transact = transactseq;
zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, USE_ZIGBEE_DEBOUNCE_COMMANDS, 0 /*clusterid*/, _srcendpoint, Z_CAT_DEBOUNCE_CMD, 0, &Z_ResetDebounce); zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, USE_ZIGBEE_DEBOUNCE_COMMANDS, 0 /*clusterid*/, _srcendpoint, Z_CAT_DEBOUNCE_CMD, 0, &Z_ResetDebounce);
convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); convertClusterSpecific(attr_list, cluster, cmd, _frame_control.b.direction, shortaddr, _srcendpoint, payload);
if (!Settings->flag5.zb_disable_autoquery) { if (!Settings->flag5.zb_disable_autoquery) {
// read attributes unless disabled // read attributes unless disabled
if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator) if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator)
if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target
sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); sendHueUpdate(BAD_SHORTADDR, groupaddr, cluster);
} }
} }
} }
@ -1688,18 +1708,18 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// Send Default Response to acknowledge the attribute reporting // Send Default Response to acknowledge the attribute reporting
if (0 == _frame_control.b.disable_def_resp) { if (0 == _frame_control.b.disable_def_resp) {
// the device expects a default response // the device expects a default response
ZCLMessage zcl(2); // message is 4 bytes ZCLFrame zcl(2); // message is 4 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE; zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = _manuf_code; zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct no retry */ zcl.direct = true; /* direct no retry */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.add8(_cmd_id); zcl.payload.add8(cmd);
zcl.buf.add8(0x00); // Status = OK zcl.payload.add8(0x00); // Status = OK
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1707,7 +1727,7 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// ====================================================================== // ======================================================================
// Convert AnalogValue according to the device type // Convert AnalogValue according to the device type
void ZCLFrame::syntheticAnalogValue(Z_attribute_list &attr_list, class Z_attribute &attr) { void ZCLFrame::syntheticAnalogValue(Z_attribute_list &attr_list, class Z_attribute &attr) {
const char * modelId_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
if (modelId.startsWith(F("lumi.sensor_cube"))) { if (modelId.startsWith(F("lumi.sensor_cube"))) {
@ -1731,7 +1751,7 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu
uint32_t i = 0; uint32_t i = 0;
uint32_t len = buf2.len(); uint32_t len = buf2.len();
const char * modelId_c = zigbee_devices.getModelId(_srcaddr); // null if unknown const char * modelId_c = zigbee_devices.getModelId(shortaddr); // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
while (len >= 2 + i) { while (len >= 2 + i) {
@ -1830,7 +1850,7 @@ void ZCLFrame::syntheticAqaraSensor2(class Z_attribute_list &attr_list, class Z_
// Aqara Cube and Button // Aqara Cube and Button
void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) { void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, class Z_attribute &attr) {
const char * modelId_c = zigbee_devices.findShortAddr(_srcaddr).modelId; // null if unknown const char * modelId_c = zigbee_devices.findShortAddr(shortaddr).modelId; // null if unknown
String modelId((char*) modelId_c); String modelId((char*) modelId_c);
if (modelId.startsWith(F("lumi.sensor_cube"))) { // only for Aqara cube if (modelId.startsWith(F("lumi.sensor_cube"))) { // only for Aqara cube

View File

@ -152,14 +152,14 @@ void ZigbeeHueGroups(String * lights) {
} }
void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) { void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) {
ZCLMessage zcl(s.len()); ZCLFrame zcl(s.len());
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.cmd = cmd; zcl.cmd = cmd;
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.replace(s); zcl.payload.replace(s);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }

View File

@ -183,16 +183,16 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster
shortaddr = BAD_SHORTADDR; // if group address, don't send to device shortaddr = BAD_SHORTADDR; // if group address, don't send to device
} }
ZCLMessage zcl(attrs_len); // message is `attrs_len` bytes ZCLFrame zcl(attrs_len); // message is `attrs_len` bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.groupaddr = groupaddr; zcl.groupaddr = groupaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.addBuffer(attrs, attrs_len); zcl.payload.addBuffer(attrs, attrs_len);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }

View File

@ -1380,15 +1380,15 @@ void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluste
// Iterate among // Iterate among
// //
void Z_SendDeviceInfoRequest(uint16_t shortaddr) { void Z_SendDeviceInfoRequest(uint16_t shortaddr) {
ZCLMessage zcl(4); // message is 4 bytes ZCLFrame zcl(4); // message is 4 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0; zcl.cluster = 0;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(0x0005); zcl.payload.add16(0x0005);
zcl.buf.add16(0x0004); zcl.payload.add16(0x0004);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1396,15 +1396,15 @@ void Z_SendDeviceInfoRequest(uint16_t shortaddr) {
// Send single attribute read request in Timer // Send single attribute read request in Timer
// //
void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES; zcl.cmd = ZCL_READ_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(value); // 04000500 zcl.payload.add16(value); // 04000500
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1413,17 +1413,17 @@ void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t
// //
void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending CIE Address for Cluster %d in Endpoint %d of Device 0x%04X"), cluster, endpoint, shortaddr); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending CIE Address for Cluster %d in Endpoint %d of Device 0x%04X"), cluster, endpoint, shortaddr);
ZCLMessage zcl(12); // message is 12 bytes ZCLFrame zcl(12); // message is 12 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0x0500; zcl.cluster = 0x0500;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_WRITE_ATTRIBUTES; zcl.cmd = ZCL_WRITE_ATTRIBUTES;
zcl.clusterSpecific = false; zcl.clusterSpecific = false;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add16(0x0010); // attribute 0x0010 zcl.payload.add16(0x0010); // attribute 0x0010
zcl.buf.add8(ZEUI64); zcl.payload.add8(ZEUI64);
zcl.buf.add64(localIEEEAddr); zcl.payload.add64(localIEEEAddr);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1433,16 +1433,16 @@ void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster,
// //
void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d for Cluster %d in Endpoint %d of Device 0x%04X"), Z_B0(value), cluster, endpoint, shortaddr); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d for Cluster %d in Endpoint %d of Device 0x%04X"), Z_B0(value), cluster, endpoint, shortaddr);
ZCLMessage zcl(2); // message is 2 bytes ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = 0x0500; zcl.cluster = 0x0500;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = 0x00; // Zone Enroll Response zcl.cmd = 0x00; // Zone Enroll Response
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
zcl.needResponse = true; zcl.needResponse = true;
zcl.direct = false; // discover route zcl.direct = false; // discover route
zcl.buf.add8(0x00); // success zcl.payload.add8(0x00); // success
zcl.buf.add8(Z_B0(value)); // ZoneID zcl.payload.add8(Z_B0(value)); // ZoneID
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
@ -1579,15 +1579,15 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
if (buf.len() > 0) { if (buf.len() > 0) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), ResponseData()); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), ResponseData());
ZCLMessage zcl(buf.len()); // message is 4 bytes ZCLFrame zcl(buf.len()); // message is 4 bytes
zcl.shortaddr = shortaddr; zcl.shortaddr = shortaddr;
zcl.cluster = cluster; zcl.cluster = cluster;
zcl.endpoint = endpoint; zcl.dstendpoint = endpoint;
zcl.cmd = ZCL_CONFIGURE_REPORTING; zcl.cmd = ZCL_CONFIGURE_REPORTING;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = false; /* discover route */ zcl.direct = false; /* discover route */
zcl.buf.addBuffer(buf); zcl.payload.addBuffer(buf);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }
@ -1639,6 +1639,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// log the packet details // log the packet details
zcl_received.log(); zcl_received.log();
#ifdef USE_BERRY
// Berry pre-process messages
// callBerryZigbeeDispatcher("pre", &zcl_received);
#endif // USE_BERRY
// create the device entry if it does not exist and if it's not the local device // create the device entry if it does not exist and if it's not the local device
Z_Device & device = (srcaddr != localShortAddr) ? zigbee_devices.getShortAddr(srcaddr) : Z_Device & device = (srcaddr != localShortAddr) ? zigbee_devices.getShortAddr(srcaddr) :
device_unk; device_unk;
@ -2097,9 +2102,9 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
for (uint32_t i=0; i<attr_len; i++) { for (uint32_t i=0; i<attr_len; i++) {
uint16_t attr_id = attr_list_ids[i]; uint16_t attr_id = attr_list_ids[i];
uint32_t ccccaaaa = (_cluster_id << 16) | attr_id; uint32_t ccccaaaa = (cluster << 16) | attr_id;
Z_attribute attr; Z_attribute attr;
attr.setKeyId(_cluster_id, attr_id); attr.setKeyId(cluster, attr_id);
switch (ccccaaaa) { switch (ccccaaaa) {
case 0x00000004: attr.setStr(PSTR(USE_ZIGBEE_MANUFACTURER)); break; // Manufacturer case 0x00000004: attr.setStr(PSTR(USE_ZIGBEE_MANUFACTURER)); break; // Manufacturer
@ -2149,8 +2154,8 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
break; break;
} }
if (!attr.isNone()) { if (!attr.isNone()) {
Z_parseAttributeKey(attr, _cluster_id); Z_parseAttributeKey(attr, cluster);
attr_list.addAttribute(_cluster_id, attr_id) = attr; attr_list.addAttribute(cluster, attr_id) = attr;
} }
} }
@ -2170,21 +2175,21 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
",\"Endpoint\":%d" ",\"Endpoint\":%d"
",\"Response\":%s}" ",\"Response\":%s}"
), ),
_srcaddr, _cluster_id, _srcendpoint, shortaddr, cluster, _srcendpoint,
attr_list.toString().c_str()); attr_list.toString().c_str());
// send // send
// all good, send the packet // all good, send the packet
ZCLMessage zcl(buf.len()); // message is 4 bytes ZCLFrame zcl(buf.len()); // message is 4 bytes
zcl.shortaddr = _srcaddr; zcl.shortaddr = shortaddr;
zcl.cluster = _cluster_id; zcl.cluster = cluster;
zcl.endpoint = _srcendpoint; zcl.dstendpoint = _srcendpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES_RESPONSE; zcl.cmd = ZCL_READ_ATTRIBUTES_RESPONSE;
zcl.clusterSpecific = false; /* not cluster specific */ zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */ zcl.needResponse = false; /* noresponse */
zcl.direct = true; /* direct response */ zcl.direct = true; /* direct response */
zcl.setTransac(_transact_seq); zcl.setTransac(transactseq);
zcl.buf.addBuffer(buf); zcl.payload.addBuffer(buf);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} }
} }

View File

@ -753,12 +753,12 @@ void CmndZbEZSPSend(void)
// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr // - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr
// - len: length of the 'msg' payload // - len: length of the 'msg' payload
// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond // - needResponse: boolean, true = we ask the target to respond, false = the target should not respond
// - transac: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number // - transactseq: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None // Returns: None
// //
void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) { void ZigbeeZCLSend_Raw(const ZCLFrame &zcl) {
SBuffer buf(32+zcl.buf.len()); SBuffer buf(32+zcl.payload.len());
#ifdef USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_ZNP
buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(Z_SREQ | Z_AF); // 24
@ -770,23 +770,23 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
} else { } else {
buf.add8(Z_Addr_ShortAddress); // 02 buf.add8(Z_Addr_ShortAddress); // 02
buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded buf.add64(zcl.shortaddr); // dest address, only 2 LSB, upper 6 MSB are discarded
buf.add8(zcl.endpoint); // dest endpoint buf.add8(zcl.dstendpoint); // dest endpoint
} }
buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan
buf.add8(0x01); // source endpoint buf.add8(0x01); // source endpoint
buf.add16(zcl.cluster); buf.add16(zcl.cluster);
buf.add8(zcl.transac); // transac buf.add8(zcl.transactseq); // transactseq
buf.add8(0x30); // 30 options buf.add8(0x30); // 30 options
buf.add8(0x1E); // 1E radius buf.add8(0x1E); // 1E radius
buf.add16(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add16(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequence Number buf.add8(zcl.transactseq); // Transaction Sequence Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
ZigbeeZNPSend(buf.getBuffer(), buf.len()); ZigbeeZNPSend(buf.getBuffer(), buf.len());
#endif // USE_ZIGBEE_ZNP #endif // USE_ZIGBEE_ZNP
@ -801,25 +801,25 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
buf.add16(Z_PROF_HA); // Home Automation profile buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(zcl.cluster); // cluster buf.add16(zcl.cluster); // cluster
buf.add8(0x01); // srcEp buf.add8(0x01); // srcEp
buf.add8(zcl.endpoint); // dstEp buf.add8(zcl.dstendpoint); // dstEp
if (zcl.direct) { if (zcl.direct) {
buf.add16(0x0000); // APS frame buf.add16(0x0000); // APS frame
} else { } else {
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
} }
buf.add16(zcl.groupaddr); // groupId buf.add16(zcl.groupaddr); // groupId
buf.add8(zcl.transac); buf.add8(zcl.transactseq);
// end of ApsFrame // end of ApsFrame
buf.add8(0x01); // tag TODO buf.add8(0x01); // tag TODO
buf.add8(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add8(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequance Number buf.add8(zcl.transactseq); // Transaction Sequance Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
} else { } else {
// send broadcast group address, aka groupcast // send broadcast group address, aka groupcast
buf.add16(EZSP_sendMulticast); // 3800 buf.add16(EZSP_sendMulticast); // 3800
@ -827,27 +827,27 @@ void ZigbeeZCLSend_Raw(const ZCLMessage &zcl) {
buf.add16(Z_PROF_HA); // Home Automation profile buf.add16(Z_PROF_HA); // Home Automation profile
buf.add16(zcl.cluster); // cluster buf.add16(zcl.cluster); // cluster
buf.add8(0x01); // srcEp buf.add8(0x01); // srcEp
buf.add8(zcl.endpoint); // broadcast endpoint for groupcast buf.add8(zcl.dstendpoint); // broadcast endpoint for groupcast
if (zcl.direct) { if (zcl.direct) {
buf.add16(0x0000); // APS frame buf.add16(0x0000); // APS frame
} else { } else {
buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame
} }
buf.add16(zcl.groupaddr); // groupId buf.add16(zcl.groupaddr); // groupId
buf.add8(zcl.transac); buf.add8(zcl.transactseq);
// end of ApsFrame // end of ApsFrame
buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS buf.add8(0); // hops, 0x00 = EMBER_MAX_HOPS
buf.add8(7); // nonMemberRadius, 7 = infinite buf.add8(7); // nonMemberRadius, 7 = infinite
buf.add8(0x01); // tag TODO buf.add8(0x01); // tag TODO
buf.add8(3 + zcl.buf.len() + (zcl.manuf ? 2 : 0)); buf.add8(3 + zcl.payload.len() + (zcl.manuf ? 2 : 0));
buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field buf.add8((zcl.needResponse ? 0x00 : 0x10) | (zcl.clusterSpecific ? 0x01 : 0x00) | (zcl.manuf ? 0x04 : 0x00)); // Frame Control Field
if (zcl.manuf) { if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null buf.add16(zcl.manuf); // add Manuf Id if not null
} }
buf.add8(zcl.transac); // Transaction Sequance Number buf.add8(zcl.transactseq); // Transaction Sequance Number
buf.add8(zcl.cmd); buf.add8(zcl.cmd);
buf.addBuffer(zcl.buf); buf.addBuffer(zcl.payload);
} }
ZigbeeEZSPSendCmd(buf.buf(), buf.len()); ZigbeeEZSPSendCmd(buf.buf(), buf.len());

View File

@ -178,18 +178,18 @@ void CmndZbReset(void) {
// - param: pointer to HEX string for payload, should not be nullptr // - param: pointer to HEX string for payload, should not be nullptr
// Returns: None // Returns: None
// //
void zigbeeZCLSendCmd(class ZCLMessage &zcl) { void zigbeeZCLSendCmd(class ZCLFrame &zcl) {
if ((0 == zcl.endpoint) && (zcl.validShortaddr())) { if ((0 == zcl.dstendpoint) && (zcl.validShortaddr())) {
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
zcl.endpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr); zcl.dstendpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr);
if (0x00 == zcl.endpoint) { zcl.endpoint = 0x01; } // if we don't know the endpoint, try 0x01 if (0x00 == zcl.dstendpoint) { zcl.dstendpoint = 0x01; } // if we don't know the endpoint, try 0x01
//AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); //AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
} }
// AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %_B"), // AddLog(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %_B"),
// zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.endpoint, zcl.cmd, &zcl.buf); // zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.dstendpoint, zcl.cmd, &zcl.payload);
if ((0 == zcl.endpoint) && (zcl.validShortaddr())) { // endpoint null is ok for group address if ((0 == zcl.dstendpoint) && (zcl.validShortaddr())) { // endpoint null is ok for group address
AddLog(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); AddLog(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
return; return;
} }
@ -197,12 +197,12 @@ void zigbeeZCLSendCmd(class ZCLMessage &zcl) {
// everything is good, we can send the command // everything is good, we can send the command
if (!zcl.transacSet) { if (!zcl.transacSet) {
zcl.transac = zigbee_devices.getNextSeqNumber(zcl.shortaddr); zcl.transactseq = zigbee_devices.getNextSeqNumber(zcl.shortaddr);
zcl.transacSet = true; zcl.transacSet = true;
} }
AddLog(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend %s: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%_B\""), AddLog(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend %s: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%_B\""),
zcl.validShortaddr() ? "device":"group", zcl.validShortaddr() ? zcl.shortaddr : zcl.groupaddr, zcl.endpoint, zcl.cluster, zcl.cmd, &zcl.buf); zcl.validShortaddr() ? "device":"group", zcl.validShortaddr() ? zcl.shortaddr : zcl.groupaddr, zcl.dstendpoint, zcl.cluster, zcl.cmd, &zcl.payload);
ZigbeeZCLSend_Raw(zcl); ZigbeeZCLSend_Raw(zcl);
@ -210,7 +210,7 @@ void zigbeeZCLSendCmd(class ZCLMessage &zcl) {
if (zcl.clusterSpecific) { if (zcl.clusterSpecific) {
if (!Settings->flag5.zb_disable_autoquery) { if (!Settings->flag5.zb_disable_autoquery) {
// read back attribute value unless it is disabled // read back attribute value unless it is disabled
sendHueUpdate(zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.endpoint); sendHueUpdate(zcl.shortaddr, zcl.groupaddr, zcl.cluster, zcl.dstendpoint);
} }
} }
} }
@ -321,15 +321,15 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat
// Parse "Report", "Write", "Response" or "Config" 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) // Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01)
// //
void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLMessage & zcl) { void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl) {
zcl.buf.reserve(200); // buffer to store the binary output of attibutes zcl.payload.reserve(200); // buffer to store the binary output of attibutes
SBuffer & buf = zcl.buf; // synonym SBuffer & buf = zcl.payload; // synonym
if (nullptr == XdrvMailbox.command) { if (nullptr == XdrvMailbox.command) {
XdrvMailbox.command = (char*) ""; // prevent a crash when calling ReponseCmndChar and there was no previous command XdrvMailbox.command = (char*) ""; // prevent a crash when calling ReponseCmndChar and there was no previous command
} }
bool tuya_protocol = zigbee_devices.isTuyaProtocol(zcl.shortaddr, zcl.endpoint); bool tuya_protocol = zigbee_devices.isTuyaProtocol(zcl.shortaddr, zcl.dstendpoint);
// iterate on keys // iterate on keys
for (auto key : val_pubwrite.getObject()) { for (auto key : val_pubwrite.getObject()) {
@ -449,7 +449,7 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLMessage & zc
} }
// Parse the "Send" attribute and send the command // Parse the "Send" attribute and send the command
void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) { void ZbSendSend(class JsonParserToken val_cmd, ZCLFrame & zcl) {
zcl.clusterSpecific = true; zcl.clusterSpecific = true;
static const char delim[] = ", "; // delimiters for parameters static const char delim[] = ", "; // delimiters for parameters
@ -525,7 +525,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
} else { } else {
zcl.cmd = cmd_var; // or simply copy the cmd number zcl.cmd = cmd_var; // or simply copy the cmd number
} }
zigbeeCmdAddParams(zcl.buf, tasmota_cmd, x, y, z); // fill in parameters zigbeeCmdAddParams(zcl.payload, tasmota_cmd, x, y, z); // fill in parameters
} else { } else {
// we have zero command, pass through until last error for missing command // we have zero command, pass through until last error for missing command
return; return;
@ -563,7 +563,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
// delimiter is optional // delimiter is optional
if ('/' == *data) { data++; } // skip delimiter if ('/' == *data) { data++; } // skip delimiter
zcl.buf.replace(SBuffer::SBufferFromHex(data, strlen(data))); zcl.payload.replace(SBuffer::SBufferFromHex(data, strlen(data)));
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
} else { } else {
// we have an unsupported command type // we have an unsupported command type
@ -574,7 +574,7 @@ void ZbSendSend(class JsonParserToken val_cmd, ZCLMessage & zcl) {
} }
// Parse the "Send" attribute and send the command // Parse the "Send" attribute and send the command
void ZbSendRead(JsonParserToken val_attr, ZCLMessage & zcl) { void ZbSendRead(JsonParserToken val_attr, ZCLFrame & zcl) {
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":5}
// ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"} // ZbSend {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Read":"0x0005"}
// ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]} // ZbSend {"Device":"0xF289","Cluster":0,"Endpoint":3,"Read":[5,6,7,4]}
@ -670,9 +670,9 @@ void ZbSendRead(JsonParserToken val_attr, ZCLMessage & zcl) {
if (attrs_len > 0) { if (attrs_len > 0) {
// all good, send the packet // all good, send the packet
zcl.buf.reserve(attrs_len); zcl.payload.reserve(attrs_len);
zcl.buf.setLen(0); // clear any previous buffer zcl.payload.setLen(0); // clear any previous buffer
zcl.buf.addBuffer(attrs, attrs_len); zcl.payload.addBuffer(attrs, attrs_len);
zigbeeZCLSendCmd(zcl); zigbeeZCLSendCmd(zcl);
ResponseCmndDone(); ResponseCmndDone();
} else { } else {
@ -712,7 +712,7 @@ void CmndZbSend(void) {
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params // params
ZCLMessage zcl; // prepare the ZCL structure ZCLFrame zcl; // prepare the ZCL structure
// parse "Device" and "Group" // parse "Device" and "Group"
JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)]; JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)];
@ -733,15 +733,15 @@ void CmndZbSend(void) {
// read other parameters // read other parameters
zcl.cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), zcl.cluster); zcl.cluster = root.getUInt(PSTR(D_CMND_ZIGBEE_CLUSTER), zcl.cluster);
zcl.endpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), zcl.endpoint); zcl.dstendpoint = root.getUInt(PSTR(D_CMND_ZIGBEE_ENDPOINT), zcl.dstendpoint);
zcl.manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), zcl.manuf); zcl.manuf = root.getUInt(PSTR(D_CMND_ZIGBEE_MANUF), zcl.manuf);
// infer endpoint // infer endpoint
if (!zcl.validShortaddr()) { if (!zcl.validShortaddr()) {
zcl.endpoint = 0xFF; // endpoint not used for group addresses, so use a dummy broadcast endpoint zcl.dstendpoint = 0xFF; // endpoint not used for group addresses, so use a dummy broadcast endpoint
} else if (!zcl.validEndpoint()) { // if it was not already specified, try to guess it } else if (!zcl.validEndpoint()) { // if it was not already specified, try to guess it
zcl.endpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr); zcl.dstendpoint = zigbee_devices.findFirstEndpoint(zcl.shortaddr);
AddLog(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), zcl.endpoint); AddLog(LOG_LEVEL_DEBUG, PSTR("ZIG: guessing endpoint %d"), zcl.dstendpoint);
} }
if (!zcl.validEndpoint()) { // after this, if it is still zero, then it's an error if (!zcl.validEndpoint()) { // after this, if it is still zero, then it's an error
ResponseCmndChar_P(PSTR("Missing endpoint")); ResponseCmndChar_P(PSTR("Missing endpoint"));
@ -848,7 +848,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// Information about source device: "Device", "Endpoint", "Cluster" // Information about source device: "Device", "Endpoint", "Cluster"
// - the source endpoint must have a known IEEE address // - the source endpoint must have a known IEEE address
const Z_Device & src_device = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)); const Z_Device & src_device = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE)));
if (!src_device.valid()) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } if (!src_device.valid()) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
// check if IEEE address is known // check if IEEE address is known
uint64_t srcLongAddr = src_device.longaddr; uint64_t srcLongAddr = src_device.longaddr;
@ -961,7 +961,7 @@ void CmndZbUnbind(void) {
// //
void CmndZbLeave(void) { void CmndZbLeave(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
#ifdef USE_ZIGBEE_ZNP #ifdef USE_ZIGBEE_ZNP
@ -993,7 +993,7 @@ void CmndZbLeave(void) {
void CmndZbBindState_or_Map(bool map) { void CmndZbBindState_or_Map(bool map) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t parsed_shortaddr;; uint16_t parsed_shortaddr;;
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, &parsed_shortaddr).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, &parsed_shortaddr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { if (BAD_SHORTADDR == shortaddr) {
if ((map) && (parsed_shortaddr != shortaddr)) { if ((map) && (parsed_shortaddr != shortaddr)) {
shortaddr = parsed_shortaddr; // allow a non-existent address when ZbMap shortaddr = parsed_shortaddr; // allow a non-existent address when ZbMap
@ -1069,7 +1069,7 @@ void CmndZbProbe(void) {
// //
void CmndZbProbeOrPing(boolean probe) { void CmndZbProbeOrPing(boolean probe) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr; uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// set a timer for Reachable - 2s default value // set a timer for Reachable - 2s default value
@ -1107,7 +1107,7 @@ void CmndZbName(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // it's the only case where we create a new device Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // it's the only case where we create a new device
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p == nullptr) { if (p == nullptr) {
@ -1139,7 +1139,7 @@ void CmndZbModelId(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p != nullptr) { if (p != nullptr) {
@ -1166,7 +1166,7 @@ void CmndZbLight(void) {
strtok_r(XdrvMailbox.data, ", ", &p); strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p) { if (p) {
@ -1210,7 +1210,7 @@ void CmndZbOccupancy(void) {
strtok_r(XdrvMailbox.data, ", ", &p); strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
int8_t occupancy_time = -1; int8_t occupancy_time = -1;
@ -1237,7 +1237,7 @@ void CmndZbOccupancy(void) {
// //
void CmndZbForget(void) { void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// everything is good, we can send the command // everything is good, we can send the command
@ -1267,7 +1267,7 @@ void CmndZbInfo(void) {
CmndZbInfo_inner(device); CmndZbInfo_inner(device);
} }
} else { // try JSON } else { // try JSON
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// everything is good, we can send the command // everything is good, we can send the command
@ -1337,7 +1337,7 @@ void CmndZbenroll(void) {
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
char argument[XdrvMailbox.data_len]; char argument[XdrvMailbox.data_len];
Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1)); Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1), nullptr, XdrvMailbox.payload);
int enrollEndpoint = atoi(ArgV(argument, 2)); int enrollEndpoint = atoi(ArgV(argument, 2));
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
@ -1355,7 +1355,7 @@ void CmndZbcie(void) {
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
char argument[XdrvMailbox.data_len]; char argument[XdrvMailbox.data_len];
Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1)); Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1), nullptr, XdrvMailbox.payload);
int enrollEndpoint = atoi(ArgV(argument, 2)); int enrollEndpoint = atoi(ArgV(argument, 2));
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
@ -1576,7 +1576,7 @@ void CmndZbStatus(void) {
if (0 == XdrvMailbox.index) { if (0 == XdrvMailbox.index) {
dump = zigbee_devices.dumpCoordinator(); dump = zigbee_devices.dumpCoordinator();
} else { } else {
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload);
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device); dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
@ -1608,7 +1608,7 @@ void CmndZbData(void) {
strtok_r(XdrvMailbox.data, ",", &p); strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p) { if (p) {