Merge pull request #8035 from s-hadinger/zigbee_bindstate

Add Zigbee commands ``ZbBindState`` and ``manuf``attribute
This commit is contained in:
Theo Arends 2020-03-30 22:22:11 +02:00 committed by GitHub
commit b982ea8d08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 152 additions and 49 deletions

View File

@ -3,6 +3,7 @@
### 8.2.0.3 20200329
- Add support for longer template names
- Add Zigbee commands ``ZbBindState`` and ``manuf``attribute
### 8.2.0.2 20200328

View File

@ -507,6 +507,8 @@
#define D_JSON_ZIGBEE_BIND "ZbBind"
#define D_CMND_ZIGBEE_UNBIND "Unbind"
#define D_JSON_ZIGBEE_UNBIND "ZbUnbind"
#define D_CMND_ZIGBEE_BIND_STATE "BindState"
#define D_JSON_ZIGBEE_BIND_STATE "ZbBindState"
#define D_CMND_ZIGBEE_PING "Ping"
#define D_JSON_ZIGBEE_PING "ZbPing"
#define D_JSON_ZIGBEE_IEEE "IEEEAddr"

View File

@ -399,14 +399,14 @@ String getZigbeeStatusMessage(uint8_t status) {
"|UNSUP_MANUF_CLUSTER_COMMAND|UNSUP_MANUF_GENERAL_COMMAND|INVALID_FIELD|UNSUPPORTED_ATTRIBUTE|INVALID_VALE|READ_ONLY"
"|INSUFFICIENT_SPACE|DUPLICATE_EXISTS|NOT_FOUND|UNREPORTABLE_ATTRIBUTE|INVALID_DATA_TYPE|INVALID_SELECTOR|WRITE_ONLY"
"|INCONSISTENT_STARTUP_STATE|DEFINED_OUT_OF_BAND|INCONSISTENT|ACTION_DENIED|TIMEOUT|ABORT|INVALID_IMAGE|WAIT_FOR_DATA"
"|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER"
"|NO_IMAGE_AVAILABLE|REQUIRE_MORE_IMAGE|NOTIFICATION_PENDING|HARDWARE_FAILURE|SOFTWARE_FAILURE|CALIBRATION_ERROR|UNSUPPORTED_CLUSTER|NO_ROUTE"
"|CHANNEL_ACCESS_FAILURE|NO_ACK|NO_APP_ACK|NO_ROUTE"
;
static const uint8_t StatusIdx[] PROGMEM = { 0x00, 0x01, 0x7E, 0x7F, 0x80, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3,
0x98, 0x99, 0x9A, 0xC0, 0xC1, 0xC2, 0xC3, 0xCD,
0xE1, 0xE9, 0xA7, 0xD0};
char msg[32];

View File

@ -130,7 +130,7 @@ void ZigbeeHueGroups(String * lights) {
// Send commands
// Power On/Off
void ZigbeeHuePower(uint16_t shortaddr, bool power) {
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0006, power ? 1 : 0, "");
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, "");
zigbee_devices.updateHueState(shortaddr, &power, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
}
@ -139,7 +139,7 @@ void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) {
if (dimmer > 0xFE) { dimmer = 0xFE; }
char param[8];
snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer);
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0008, 0x04, param);
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param);
zigbee_devices.updateHueState(shortaddr, nullptr, nullptr, &dimmer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
}
@ -150,7 +150,7 @@ void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) {
char param[12];
snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8);
uint8_t colormode = 2; // "ct"
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x0A, param);
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param);
zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, &ct, nullptr, nullptr, nullptr, nullptr);
}
@ -161,7 +161,7 @@ void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) {
if (y > 0xFEFF) { y = 0xFEFF; }
snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8);
uint8_t colormode = 1; // "xy"
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x07, param);
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param);
zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, nullptr, nullptr, nullptr, &x, &y, nullptr);
}
@ -172,7 +172,7 @@ void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) {
if (sat > 0xFE) { sat = 0xFE; }
snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat);
uint8_t colormode = 0; // "hs"
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0x0300, 0x06, param);
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param);
zigbee_devices.updateHueState(shortaddr, nullptr, &colormode, nullptr, &sat, nullptr, &hue, nullptr, nullptr, nullptr);
}

View File

@ -202,13 +202,6 @@ uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer
case 0xFF: // unk
break;
case 0x10: // bool
{
uint8_t val_bool = buf.get8(i++);
if (0xFF != val_bool) {
json[attrid_str] = (bool) (val_bool ? true : false);
}
}
break;
case 0x20: // uint8
case 0x30: // enum8
{

View File

@ -169,7 +169,7 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
break;
}
if (attrs) {
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr));
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr));
}
}
@ -306,10 +306,10 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
}
if (z_cat >= 0) {
uint8_t endpoint = 0;
if (!groupaddr) {
if (shortaddr) {
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
}
if ((endpoint) || (groupaddr)) { // send only if we know the endpoint
if ((!shortaddr) || (endpoint)) { // send if group address or endpoint is known
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);

View File

@ -56,7 +56,7 @@ typedef struct Zigbee_Instruction_Type {
} Zigbee_Instruction_Type;
enum Zigbee_StateMachine_Instruction_Set {
// 2 bytes instructions
// 4 bytes instructions
ZGB_INSTR_4_BYTES = 0,
ZGB_INSTR_NOOP = 0, // do nothing
ZGB_INSTR_LABEL, // define a label
@ -67,7 +67,7 @@ enum Zigbee_StateMachine_Instruction_Set {
ZGB_INSTR_WAIT_FOREVER, // wait forever but state machine still active
ZGB_INSTR_STOP, // stop state machine with optional error code
// 6 bytes instructions
// 8 bytes instructions
ZGB_INSTR_8_BYTES = 0x80,
ZGB_INSTR_CALL = 0x80, // call a function
ZGB_INSTR_LOG, // log a message, if more detailed logging required, call a function
@ -77,7 +77,7 @@ enum Zigbee_StateMachine_Instruction_Set {
ZGB_INSTR_WAIT_RECV, // wait for a message according to the filter
ZGB_ON_RECV_UNEXPECTED, // function to handle unexpected messages, or nullptr
// 10 bytes instructions
// 12 bytes instructions
ZGB_INSTR_12_BYTES = 0xF0,
ZGB_INSTR_WAIT_RECV_CALL, // wait for a filtered message and call function upon receive
};

View File

@ -388,6 +388,7 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Handle Unbind Rsp incoming message
//
@ -413,6 +414,72 @@ int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) {
return -1;
}
//
// Handle MgMt Bind Rsp incoming message
//
int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
uint16_t shortaddr = buf.get16(2);
uint8_t status = buf.get8(4);
uint8_t bind_total = buf.get8(5);
uint8_t bind_start = buf.get8(6);
uint8_t bind_len = buf.get8(7);
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND_STATE "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), shortaddr);
if (friendlyName) {
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
}
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
",\"BindingsTotal\":%d"
//",\"BindingsStart\":%d"
",\"Bindings\":["
), status, getZigbeeStatusMessage(status).c_str(), bind_total);
uint32_t idx = 8;
for (uint32_t i = 0; i < bind_len; i++) {
if (idx + 14 > buf.len()) { break; } // overflow, frame size is between 14 and 21
//uint64_t srcaddr = buf.get16(idx); // unused
uint8_t srcep = buf.get8(idx + 8);
uint8_t cluster = buf.get16(idx + 9);
uint8_t addrmode = buf.get8(idx + 11);
uint16_t group = 0x0000;
uint64_t dstaddr = 0;
uint8_t dstep = 0x00;
if (Z_Addr_Group == addrmode) { // Group address mode
group = buf.get16(idx + 12);
idx += 14;
} else if (Z_Addr_IEEEAddress == addrmode) { // IEEE address mode
dstaddr = buf.get64(idx + 12);
dstep = buf.get8(idx + 20);
idx += 21;
} else {
//AddLog_P2(LOG_LEVEL_INFO, PSTR("Z_MgmtBindRsp unknwon address mode %d"), addrmode);
break; // abort for any other value since we don't know the length of the field
}
if (i > 0) {
ResponseAppend_P(PSTR(","));
}
ResponseAppend_P(PSTR("{\"Cluster\":\"0x%04X\",\"Endpoint\":%d,"), cluster, srcep);
if (Z_Addr_Group == addrmode) { // Group address mode
ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group);
} else if (Z_Addr_IEEEAddress == addrmode) { // IEEE address mode
char hex[20];
Uint64toHex(dstaddr, hex, 64);
ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep);
}
}
ResponseAppend_P(PSTR("]}}"));
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_BIND_STATE));
XdrvRulesProcess();
return -1;
}
/*********************************************************************************************\
* Send specific ZNP messages
@ -579,6 +646,7 @@ ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4
ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581
ZBM(AREQ_ZDO_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_BIND_RSP) // 45A1
ZBM(AREQ_ZDO_UNBIND_RSP, Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP) // 45A2
ZBM(AREQ_ZDO_MGMT_BIND_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP) // 45B3
// Dispatcher callbacks table
const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
@ -592,6 +660,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr },
{ AREQ_ZDO_BIND_RSP, &Z_BindRsp },
{ AREQ_ZDO_UNBIND_RSP, &Z_UnbindRsp },
{ AREQ_ZDO_MGMT_BIND_RSP, &Z_MgmtBindRsp },
};
/*********************************************************************************************\
@ -643,6 +712,7 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) {
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
wait_ms += 1000; // wait 1 second between devices
}
}
}

View File

@ -35,7 +35,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE
;
void (* const ZigbeeCommand[])(void) PROGMEM = {
@ -44,7 +44,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
&CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive,
&CmndZbForget, &CmndZbSave, &CmndZbName,
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
&CmndZbLight, CmndZbRestore,
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
};
//
@ -294,12 +294,12 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None
//
void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) {
void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, uint16_t manuf, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) {
SBuffer buf(32+len);
buf.add8(Z_SREQ | Z_AF); // 24
buf.add8(AF_DATA_REQUEST_EXT); // 02
if (groupaddr) {
if (0x0000 == shortaddr) { // if no shortaddr we assume group address
buf.add8(Z_Addr_Group); // 01
buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded
buf.add8(0xFF); // dest endpoint is not used for group addresses
@ -315,8 +315,11 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
buf.add8(0x30); // 30 options
buf.add8(0x1E); // 1E radius
buf.add16(3 + len);
buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field
buf.add16(3 + len + (manuf ? 2 : 0));
buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00) | (manuf ? 0x04 : 0x00)); // Frame Control Field
if (manuf) {
buf.add16(manuf); // add Manuf Id if not null
}
buf.add8(transacId); // Transaction Sequance Number
buf.add8(cmdId);
if (len > 0) {
@ -342,7 +345,7 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
// - param: pointer to HEX string for payload, should not be nullptr
// Returns: None
//
void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific,
void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint, bool clusterSpecific, uint16_t manuf,
uint16_t cluster, uint8_t cmd, const char *param) {
size_t size = param ? strlen(param) : 0;
SBuffer buf((size+2)/2); // actual bytes buffer for data
@ -368,7 +371,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
}
// everything is good, we can send the command
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr));
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, cmd, clusterSpecific, manuf, buf.getBuffer(), buf.len(), true, zigbee_devices.getNextSeqNumber(shortaddr));
// now set the timer, if any, to read back the state later
if (clusterSpecific) {
zigbeeSetCommandTimer(shortaddr, groupaddr, cluster, endpoint);
@ -397,9 +400,10 @@ void CmndZbSend(void) {
// params
static char delim[] = ", "; // delimiters for parameters
uint16_t device = 0x0000; // 0xFFFF is broadcast, so considered valid
uint16_t groupaddr = 0x0000; // ignore group address if 0x0000
uint16_t device = 0x0000; // 0x0000 is local, so considered invalid
uint16_t groupaddr = 0x0000; // group address
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
// Command elements
uint16_t cluster = 0;
uint8_t cmd = 0;
@ -408,19 +412,25 @@ void CmndZbSend(void) {
bool clusterSpecific = true;
// parse JSON
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) { groupaddr = strToUInt(val_group); }
if (0x0000 == groupaddr) { // if no group address, we need a device address
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if ((nullptr == &val_device) || (0x0000 == device)) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (0x0000 == device) { // if not found, check if we have a group
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) {
groupaddr = strToUInt(val_group);
} else { // no device nor group
ResponseCmndChar_P(PSTR("Unknown device"));
return;
}
}
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
if (nullptr != &val_cmd) {
// probe the type of the argument
@ -523,7 +533,7 @@ void CmndZbSend(void) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""),
device, groupaddr, endpoint, cluster, cmd, cmd_s);
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, cluster, cmd, cmd_s);
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
ResponseCmndDone();
} else {
Response_P(PSTR("Missing zigbee 'Send'"));
@ -639,6 +649,26 @@ void CmndZbUnbind(void) {
ZbBindUnbind(true);
}
//
// Command `ZbBindState`
//
void CmndZbBindState(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
SBuffer buf(10);
buf.add8(Z_SREQ | Z_ZDO); // 25
buf.add8(ZDO_MGMT_BIND_REQ); // 33
buf.add16(shortaddr); // shortaddr
buf.add8(0); // StartIndex = 0
ZigbeeZNPSend(buf.getBuffer(), buf.len());
ResponseCmndDone();
}
// Probe a specific device to get its endpoints and supported clusters
void CmndZbProbe(void) {
CmndZbProbeOrPing(true);
@ -865,24 +895,31 @@ void CmndZbRead(void) {
uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress
uint16_t cluster = 0x0000; // default to general cluster
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
size_t attrs_len = 0;
uint8_t* attrs = nullptr; // empty string is valid
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) { groupaddr = strToUInt(val_group); }
if (0x0000 == groupaddr) { // if no group address, we need a device address
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if ((nullptr == &val_device) || (0x0000 == device)) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (0x0000 == device) { // if not found, check if we have a group
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) {
groupaddr = strToUInt(val_group);
} else { // no device nor group
ResponseCmndChar_P(PSTR("Unknown device"));
return;
}
}
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read"));
if (nullptr != &val_attr) {
@ -910,12 +947,12 @@ void CmndZbRead(void) {
endpoint = zigbee_devices.findFirstEndpoint(device);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
}
if (groupaddr) {
if (0x0000 == device) {
endpoint = 0xFF; // endpoint not used for group addresses
}
if ((0 != endpoint) && (attrs_len > 0)) {
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
ZigbeeZCLSend_Raw(device, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, manuf, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device));
ResponseCmndDone();
} else {
ResponseCmndChar_P(PSTR("Missing parameters"));