diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 0248d4ff7..affa7f008 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -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 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 04433788f..7f225838e 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -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" diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index 1a23c98dd..2aeade28d 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -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]; diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index ffbec109c..0d00ad9e0 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -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); } diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index edd5ab682..7a8ea4806 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -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 { diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 818f8afbe..1893925f3 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -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); diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 1c4f782b4..fc93e8a8d 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -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 }; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index cc95d4ed0..7de0d56f4 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -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 } } } diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 423ac43f1..163580255 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -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()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); 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; } - if ((nullptr == &val_device) || (0x0000 == device)) { 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()); - if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); 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; } - if ((nullptr == &val_device) || (0x0000 == device)) { 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"));