Merge pull request #7230 from s-hadinger/zigbee_read

Add Zigbee improving Occupancy:false detection for Aqara sensor
This commit is contained in:
Theo Arends 2019-12-15 19:45:03 +01:00 committed by GitHub
commit 765cecd4a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 19 deletions

View File

@ -4,6 +4,7 @@
- Change some more Settings locations freeing up space for future single char allowing variable length text - Change some more Settings locations freeing up space for future single char allowing variable length text
- Add Zigbee send automatic ZigbeeRead after sending a command - Add Zigbee send automatic ZigbeeRead after sending a command
- Add Zigbee improving Occupancy:false detection for Aqara sensor
### 7.1.2.5 20191213 ### 7.1.2.5 20191213

View File

@ -39,20 +39,23 @@ class ZCLFrame {
public: public:
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 = 0, uint16_t groupid = 0): const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0,
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
uint32_t timestamp = 0):
_cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq),
_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), _group_id(groupid) _cluster_id(clusterid), _group_id(groupid),
_srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
_linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber),
_timestamp(timestamp)
{ {
_frame_control.d8 = frame_control; _frame_control.d8 = frame_control;
_payload.addBuffer(buf, buf_len); _payload.addBuffer(buf, buf_len);
}; };
void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, uint16_t srcaddr, void log(void) {
uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast,
uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber,
uint32_t timestamp) {
char hex_char[_payload.len()*2+2]; char hex_char[_payload.len()*2+2];
ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" AddLog_P2(LOG_LEVEL_DEBUG, PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{"
@ -62,15 +65,18 @@ public:
"\"timestamp\":%d," "\"timestamp\":%d,"
"\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d,"
"\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"),
groupid, clusterid, srcaddr, _group_id, _cluster_id, _srcaddr,
srcendpoint, dstendpoint, wasbroadcast, _srcendpoint, _dstendpoint, _wasbroadcast,
linkquality, securityuse, seqnumber, _linkquality, _securityuse, _seqnumber,
timestamp, _timestamp,
_frame_control, _manuf_code, _transact_seq, _cmd_id, _frame_control, _manuf_code, _transact_seq, _cmd_id,
hex_char); hex_char);
} }
static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid,
uint16_t srcaddr = 0, uint8_t srcendpoint = 0, uint8_t dstendpoint = 0, uint8_t wasbroadcast = 0,
uint8_t linkquality = 0, uint8_t securityuse = 0, uint8_t seqnumber = 0,
uint32_t timestamp = 0) { // parse a raw frame and build the ZCL frame object
uint32_t i = offset; uint32_t i = offset;
ZCLHeaderFrameControl_t frame_control; ZCLHeaderFrameControl_t frame_control;
uint16_t manuf_code = 0; uint16_t manuf_code = 0;
@ -115,6 +121,10 @@ public:
return _cluster_id; return _cluster_id;
} }
inline uint16_t getSrcEndpoint(void) const {
return _srcendpoint;
}
const SBuffer &getPayload(void) const { const SBuffer &getPayload(void) const {
return _payload; return _payload;
} }
@ -131,6 +141,15 @@ private:
uint16_t _cluster_id = 0; uint16_t _cluster_id = 0;
uint16_t _group_id = 0; uint16_t _group_id = 0;
SBuffer _payload; SBuffer _payload;
// information from decoded ZCL frame
uint16_t _srcaddr;
uint8_t _srcendpoint;
uint8_t _dstendpoint;
uint8_t _wasbroadcast;
uint8_t _linkquality;
uint8_t _securityuse;
uint8_t _seqnumber;
uint32_t _timestamp;
}; };
// Zigbee ZCL converters // Zigbee ZCL converters
@ -461,6 +480,8 @@ typedef struct Z_AttributeConverter {
Z_AttrConverter func; Z_AttrConverter func;
} Z_AttributeConverter; } Z_AttributeConverter;
#define OCCUPANCY "Occupancy" // global define for Aqara
// list of post-processing directives // list of post-processing directives
const Z_AttributeConverter Z_PostProcess[] PROGMEM = { const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x0000, 0x0000, "ZCLVersion", &Z_Copy }, { 0x0000, 0x0000, "ZCLVersion", &Z_Copy },
@ -729,7 +750,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values { 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
// Occupancy Sensing cluster // Occupancy Sensing cluster
{ 0x0406, 0x0000, "Occupancy", &Z_Copy }, // Occupancy (map8) { 0x0406, 0x0000, OCCUPANCY, &Z_AqaraOccupancy }, // Occupancy (map8)
{ 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType { 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType
{ 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values { 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
@ -796,6 +817,30 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
return 1; // remove original key return 1; // remove original key
} }
// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds.
// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false
const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
// send Occupancy:false message
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":{\"" OCCUPANCY "\":0}}}"), shortaddr);
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
XdrvRulesProcess();
}
int32_t Z_AqaraOccupancy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
json[new_name] = value;
uint32_t occupancy = value;
if (occupancy) {
zigbee_devices.setTimer(shortaddr, OCCUPANCY_TIMEOUT, cluster, zcl->getSrcEndpoint(), 0, &Z_OccupancyCallback);
} else {
zigbee_devices.resetTimer(shortaddr);
}
return 1; // remove original key
}
// Aqara Vibration Sensor - special proprietary attributes // Aqara Vibration Sensor - special proprietary attributes
int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) { int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name, uint16_t cluster, uint16_t attr) {
//json[new_name] = value; //json[new_name] = value;

View File

@ -370,13 +370,12 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
uint8_t seqnumber = buf.get8(17); uint8_t seqnumber = buf.get8(17);
zigbee_devices.updateLastSeen(srcaddr); zigbee_devices.updateLastSeen(srcaddr);
ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid); ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid,
srcaddr,
zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr, srcendpoint, dstendpoint, wasbroadcast,
srcendpoint, dstendpoint, wasbroadcast, linkquality, securityuse, seqnumber,
linkquality, securityuse, seqnumber, timestamp);
timestamp); zcl_received.log();
char shortaddr[8]; char shortaddr[8];
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);