Zigbee prepare for Green Power support

This commit is contained in:
Stephan Hadinger 2022-09-01 08:14:37 +02:00
parent db87f8e8d5
commit 7fb00daa52
9 changed files with 666 additions and 547 deletions

View File

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Support for Modbus writing using ModbusBridge by JeroenSt (#16351)
- Support for Ethernet in ESP32 safeboot firmware (#16388)
- Flowrate meter flow amount/duration, show values in table format (#16385)
- Zigbee prepare for Green Power support
### Changed
- TasmotaModbus library from v3.5.0 to v3.6.0 (#16351)

View File

@ -923,6 +923,8 @@ enum Z_App_Profiles {
Z_PROF_TA = 0x0107, // Telecom Applications
Z_PROF_PHHC = 0x0108, // Personal Home & Hospital Care
Z_PROF_AMI = 0x0109, // Advanced Metering Initiative
// Green Power
Z_PROF_GP = 0xa1e0, // Green Power profile
};
enum Z_Device_Ids {

File diff suppressed because it is too large Load Diff

View File

@ -187,8 +187,8 @@ const uint8_t Z_EXPORT_DATA = 0x80;
enum Cx_cluster_short {
Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007,
Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F,
Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100,
Cx0101, Cx0102, Cx0201, Cx0202, Cx0203, Cx0204,
Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0021,
Cx0100, Cx0101, Cx0102, Cx0201, Cx0202, Cx0203, Cx0204,
Cx0300, Cx0301, Cx0400, Cx0401, Cx0402, Cx0403,
Cx0404, Cx0405, Cx0406, Cx0500, Cx0702, Cx0B01, Cx0B04, Cx0B05,
CxEF00, CxFC01, CxFC40, CxFCC0, CxFCCC,
@ -197,8 +197,8 @@ enum Cx_cluster_short {
const uint16_t Cx_cluster[] PROGMEM = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100,
0x0101, 0x0102, 0x0201, 0x0202, 0x0203, 0x0204,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0021,
0x0100, 0x0101, 0x0102, 0x0201, 0x0202, 0x0203, 0x0204,
0x0300, 0x0301, 0x0400, 0x0401, 0x0402, 0x0403,
0x0404, 0x0405, 0x0406, 0x0500, 0x0702, 0x0B01, 0x0B04, 0x0B05,
0xEF00, 0xFC01, 0xFC40, 0xFCC0, 0xFCCC,
@ -746,6 +746,27 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), Cm1, 0 },
{ Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), Cm1, 0 },
// Green Power
{ Zuint8, Cx0021, 0x0000, Z_(MaxSinkTableEntries), Cm1, 0 },
{ Zoctstr16,Cx0021, 0x0001, Z_(SinkTable), Cm1, 0 },
{ Zmap8, Cx0021, 0x0002, Z_(CommunicationMode), Cm1, 0 },
{ Zmap8, Cx0021, 0x0003, Z_(CcommissioningExitMode),Cm1, 0 },
{ Zuint16, Cx0021, 0x0004, Z_(CommissioningWindow), Cm1, 0 },
{ Zmap8, Cx0021, 0x0005, Z_(SecurityLevel), Cm1, 0 },
{ Zmap24, Cx0021, 0x0006, Z_(ServerFunctionality), Cm1, 0 },
{ Zmap24, Cx0021, 0x0007, Z_(ServerActiveFunctionality), Cm1, 0 },
{ Zuint8, Cx0021, 0x0010, Z_(MaxProxyTableEntries), Cm1, 0 },
{ Zoctstr16,Cx0021, 0x0011, Z_(ProxyTable), Cm1, 0 },
{ Zuint8, Cx0021, 0x0012, Z_(NotificationRetryNumber),Cm1, 0 },
{ Zuint8, Cx0021, 0x0013, Z_(NotificationRetryTimer),Cm1, 0 },
{ Zuint8, Cx0021, 0x0014, Z_(MaxSearchCounter), Cm1, 0 },
{ Zoctstr16,Cx0021, 0x0015, Z_(BlockedGPDID), Cm1, 0 },
{ Zmap24, Cx0021, 0x0016, Z_(ClientFunctionality), Cm1, 0 },
{ Zmap24, Cx0021, 0x0017, Z_(ClientActiveFunctionality), Cm1, 0 },
{ Zmap8, Cx0021, 0x0020, Z_(SharedSecurityKeyType),Cm1, 0 },
{ Zkey128, Cx0021, 0x0021, Z_(SharedSecurityKey), Cm1, 0 },
{ Zkey128, Cx0021, 0x0022, Z_(LinkKey), Cm1, 0 },
// Shade Configuration cluster
{ Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), Cm1, 0 },
{ Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), Cm1, 0 },

View File

@ -108,7 +108,7 @@ public:
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
cluster(clusterid), groupaddr(groupaddr),
shortaddr(srcaddr), _srcendpoint(srcendpoint), dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
shortaddr(srcaddr), srcendpoint(srcendpoint), dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast),
_linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber)
{
_frame_control.d8 = frame_control;
@ -128,7 +128,7 @@ public:
"\"manuf\":\"0x%04X\",\"transact\":%d,"
"\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"),
groupaddr, cluster, shortaddr,
_srcendpoint, dstendpoint, _wasbroadcast,
srcendpoint, dstendpoint, _wasbroadcast,
_linkquality, _securityuse, _seqnumber,
_frame_control,
_frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp,
@ -207,7 +207,7 @@ public:
inline uint16_t getClusterId(void) const { return cluster; }
inline uint8_t getLinkQuality(void) const { return _linkquality; }
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; }
@ -234,9 +234,11 @@ public:
bool direct = false; // true if direct, false if discover router
bool transacSet = false; // is transac already set
uint8_t srcendpoint = 0x00; // 0x00 is invalid for the src endpoint
bool direction = false; // false = client to server (default), true = server to client (rare)
// 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;
@ -531,6 +533,16 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
attr.setUInt(uint16_val);
}
break;
case Zdata24: // data16
case Zmap24: // map16
{
uint32_t uint32_val = buf.get16(i);
uint8_t high = buf.get8(i+2);
uint32_val = uint32_val | (high << 16);
// i += 3;
attr.setUInt(uint32_val);
}
break;
case Zdata32: // data32
case Zmap32: // map32
{
@ -549,7 +561,7 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
}
break;
// TODO
// All other fixed size, convert to a HEX dump
case ZToD: // ToD
case Zdate: // date
case ZclusterId: // clusterId
@ -558,17 +570,19 @@ uint32_t parseSingleAttribute(Z_attribute & attr, const SBuffer &buf,
case ZEUI64: // EUI64
case Zkey128: // key128
case Zsemi: // semi (float on 2 bytes)
{
attr.setBuf(buf, i, len);
}
// i += 16;
break;
// Other un-implemented data types
case Zdata24: // data24
case Zdata40: // data40
case Zdata48: // data48
case Zdata56: // data56
case Zdata64: // data64
break;
// map<x>
case Zmap24: // map24
case Zmap40: // map40
case Zmap48: // map48
case Zmap56: // map56
@ -621,7 +635,7 @@ void ZCLFrame::parseReportAttributes(Z_attribute_list& attr_list) {
ZCLFrame zcl(2); // message is 2 bytes
zcl.shortaddr = shortaddr;
zcl.cluster = cluster;
zcl.dstendpoint = _srcendpoint;
zcl.dstendpoint = srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */
@ -768,7 +782,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
if (attr_rgb == nullptr) { // make sure we didn't already computed it
uint8_t brightness = 255;
if (device.valid()) {
const Z_Data_Light & light = device.data.find<Z_Data_Light>(_srcendpoint);
const Z_Data_Light & light = device.data.find<Z_Data_Light>(srcendpoint);
if ((&light != &z_data_unk) && (light.validDimmer())) {
// Dimmer has a valid value
brightness = changeUIntScale(light.getDimmer(), 0, 254, 0, 255); // range is 0..255
@ -798,7 +812,7 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
}
break;
case 0x05000002: // ZoneStatus
const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).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) {
alarm.convertZoneStatus(attr_list, attr.getUInt());
}
@ -821,12 +835,12 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
uint32_t occupancy = attr.getUInt();
if (occupancy) {
uint32_t pir_timer = OCCUPANCY_TIMEOUT;
const Z_Data_PIR & pir_found = (const Z_Data_PIR&) zigbee_devices.getShortAddr(shortaddr).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) {
pir_timer = pir_found.getTimeoutSeconds() * 1000;
}
if (pir_timer > 0) {
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, pir_timer, cluster, _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 {
zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY);
@ -1049,7 +1063,7 @@ void ZCLFrame::parseResponse_inner(uint8_t cmd, bool cluster_specific, uint8_t s
// "StatusMessage"
attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS_MSG)).setStr(getZigbeeStatusMessage(status).c_str());
// 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
if (groupaddr) { // TODO what about group zero
attr_list.group_id = groupaddr;
@ -1082,16 +1096,16 @@ void Z_ResetDebounce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, u
void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
// Check if debounce is active and if the packet is a duplicate
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == transactseq)) {
if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == srcendpoint) && (device.debounce_transact == transactseq)) {
// this is a duplicate, drop the packet
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), shortaddr, _srcendpoint);
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), shortaddr, srcendpoint);
} 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)
device.debounce_endpoint = _srcendpoint;
device.debounce_endpoint = srcendpoint;
device.debounce_transact = transactseq;
zigbee_devices.setTimer(shortaddr, 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, cmd, _frame_control.b.direction, shortaddr, _srcendpoint, payload);
convertClusterSpecific(attr_list, cluster, cmd, _frame_control.b.direction, shortaddr, srcendpoint, payload);
if (!Settings->flag5.zb_disable_autoquery) {
// read attributes unless disabled
if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator)
@ -1107,7 +1121,7 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
ZCLFrame zcl(2); // message is 4 bytes
zcl.shortaddr = shortaddr;
zcl.cluster = cluster;
zcl.dstendpoint = _srcendpoint;
zcl.dstendpoint = srcendpoint;
zcl.cmd = ZCL_DEFAULT_RESPONSE;
zcl.manuf = manuf;
zcl.clusterSpecific = false; /* not cluster specific */

View File

@ -315,16 +315,22 @@ ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_SUCCESS) // 65050
// Change #14819 - we now allow some EP to be alreaady declared
ZBM(ZBR_ZDO_ACTIVEEPRSP_SUCESS, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 /* srcAddr */, Z_SUCCESS) // 45050000xxxx - no Ep running
ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 /* srcAddr */, Z_SUCCESS,
0x00, 0x00 /* nwkaddr */, 0x02 /* activeepcount */, 0x0B, 0x01 /* the actual endpoints */) // 25050000 - no Ep running
0x00, 0x00 /* nwkaddr */, 0x03 /* activeepcount */, 0xF2, 0x0B, 0x01 /* the actual endpoints */) // 4585000000000003F20B01
// Z_AF:register profile:104, ep:01
ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 24000401050000000000
0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
0x00 /* AppNumInClusters */, 0x00 /* AppNumInClusters */)
0x00 /* AppNumInClusters */, 0x00 /* AppNumOutClusters */)
ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_SUCCESS) // 640000
ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 2400040B050000000000
0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
0x00 /* AppNumInClusters */, 0x00 /* AppNumInClusters */)
0x00 /* AppNumInClusters */, 0x00 /* AppNumOutClusters */)
// Green Power endpoint 242 0xF2
ZBM(ZBS_AF_REGISTERF2, Z_SREQ | Z_AF, AF_REGISTER, 0xF2 /* endpoint */, Z_B0(Z_PROF_GP), Z_B1(Z_PROF_GP), //
0x05, 0x61 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
0x00 /* AppNumInClusters */,
0x01 /* AppNumOutClusters */,
0x21,0x00) // 0x0021
// Z_AF:register profile:104, ep:01 - main clusters for router or device
ZBM(ZBS_AF_REGISTER_ALL, Z_SREQ | Z_AF, AF_REGISTER, 0x01 /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 24000401050000000000
0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */,
@ -333,7 +339,7 @@ ZBM(ZBS_AF_REGISTER_ALL, Z_SREQ | Z_AF, AF_REGISTER, 0x01 /* endpoint */, Z_B0(Z
0x07,0x00, 0x08,0x00, 0x0A,0x00, 0x02,0x01, // 0x0007, 0x0008, 0x000A, 0X0102
0x00,0x03, 0x00,0x04, 0x02,0x04, 0x03,0x04, // 0x0300, 0x0400, 0x0402, 0x0403
0x05,0x04, 0x06,0x04, // 0x0405, 0x0406
0x00 /* AppNumInClusters */)
0x00 /* AppNumOutClusters */)
// Z_ZDO:mgmtPermitJoinReq
ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 /* AddrMode */, // 25360200000000
@ -506,6 +512,8 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
ZI_SEND(ZBS_AF_REGISTERF2) // Z_AF register for endpoint F2, profile 0xa1e0 Green Power
ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
// Write again channels, see https://github.com/Koenkk/zigbee-herdsman/blob/37bea20ba04ee5d4938abc21a7569b43f831de32/src/adapter/z-stack/adapter/startZnp.ts#L244-L245
ZI_SEND(ZBS_W_CHANN) // write CHANNEL
ZI_WAIT_RECV(1000, ZBR_WNV_OK)

View File

@ -2232,7 +2232,7 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
",\"Endpoint\":%d"
",\"Response\":%s}"
),
shortaddr, cluster, _srcendpoint,
shortaddr, cluster, srcendpoint,
attr_list.toString().c_str());
// send
@ -2240,7 +2240,7 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
ZCLFrame zcl(buf.len()); // message is 4 bytes
zcl.shortaddr = shortaddr;
zcl.cluster = cluster;
zcl.dstendpoint = _srcendpoint;
zcl.dstendpoint = srcendpoint;
zcl.cmd = ZCL_READ_ATTRIBUTES_RESPONSE;
zcl.clusterSpecific = false; /* not cluster specific */
zcl.needResponse = false; /* noresponse */

View File

@ -775,14 +775,19 @@ void ZigbeeZCLSend_Raw(const ZCLFrame &zcl) {
buf.add8(zcl.dstendpoint); // dest endpoint
}
buf.add16(0x0000); // dest Pan ID, 0x0000 = intra-pan
buf.add8(0x01); // source endpoint
if (zcl.srcendpoint) {
buf.add8(zcl.srcendpoint); // source endpoint
} else {
buf.add8(1); // set endpoint to 1 if not specified
}
buf.add16(zcl.cluster);
buf.add8(zcl.transactseq); // transactseq
buf.add8(0x30); // 30 options
buf.add8(zcl.direct ? 0x00 : 0x30); // 30 options
buf.add8(0x1E); // 1E radius
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) | (zcl.direction ? 0x08 : 0x00)); // Frame Control Field
if (zcl.manuf) {
buf.add16(zcl.manuf); // add Manuf Id if not null
}

View File

@ -730,8 +730,17 @@ void CmndZbSend(void) {
// parse "Device" and "Group"
JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)];
if (val_device) {
zcl.shortaddr = zigbee_devices.parseDeviceFromName(val_device.getStr()).shortaddr;
if (!zcl.validShortaddr()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_INVALID_PARAM)); return; }
uint16_t parsed_shortaddr = BAD_SHORTADDR;
zcl.shortaddr = zigbee_devices.parseDeviceFromName(val_device.getStr(), &parsed_shortaddr).shortaddr;
if (!zcl.validShortaddr()) {
if (parsed_shortaddr != BAD_SHORTADDR) {
// we still got a short address
zcl.shortaddr = parsed_shortaddr;
} else {
ResponseCmndChar_P(PSTR(D_ZIGBEE_INVALID_PARAM));
return;
}
}
}
if (!zcl.validShortaddr()) { // if not found, check if we have a group
JsonParserToken val_group = root[PSTR(D_CMND_ZIGBEE_GROUP)];
@ -760,6 +769,10 @@ void CmndZbSend(void) {
ResponseCmndChar_P(PSTR("Missing endpoint"));
return;
}
// Special case for Green Power, if dstendpoint is 0xF2, then source endpoint should also be 0xF2
if (zcl.dstendpoint == 0xF2) {
zcl.srcendpoint = 0xF2;
}
// from here endpoint is valid and non-zero
// cluster may be already specified or 0xFFFF
@ -1517,6 +1530,7 @@ void CmndZbPermitJoin(void) {
// ZNP Version
#ifdef USE_ZIGBEE_ZNP
// put all routers in pairing mode
SBuffer buf(34);
buf.add8(Z_SREQ | Z_ZDO); // 25
buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); // 36
@ -1527,6 +1541,22 @@ void CmndZbPermitJoin(void) {
ZigbeeZNPSend(buf.getBuffer(), buf.len());
// send Green Power pairing mode
ZCLFrame zcl(4); // message is 4 bytes max
zcl.cmd = 0x02; // GP Proxy Commissioning Mode
zcl.cluster = 0x0021; // GP cluster
zcl.shortaddr = 0xFFFC; // Broadcast to all routers
zcl.srcendpoint = 0xF2; // GP endpoint
zcl.dstendpoint = 0xF2; // GP endpoint
zcl.needResponse = false; // as per GP spec The Disable default response sub-field of the Frame Control Field of the ZCL header shall be set to 0b1."
zcl.clusterSpecific = true; // command
zcl.direct = true; // broadcast so no need to discover routes
zcl.direction = true; // server to client
zcl.payload.add8(0x0B); // Action=1, gpsCommissioningExitMode=0b101 (window expiration + GP Proxy Commissioning Mode)
zcl.payload.add16(duration);
zigbeeZCLSendCmd(zcl);
#endif // USE_ZIGBEE_ZNP
// EZSP VERSION