From 2da8f3c3934bbd8747545ea3700b8048fc0acef2 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 20 Jul 2020 19:30:32 +0200 Subject: [PATCH] Added `SO101 1` to suffix commands with source endpoint --- tasmota/settings.h | 2 +- tasmota/xdrv_23_zigbee_2_devices.ino | 18 +++++++++ tasmota/xdrv_23_zigbee_5_converters.ino | 9 ++++- tasmota/xdrv_23_zigbee_6_commands.ino | 24 +++++++++-- tasmota/xdrv_23_zigbee_8_parsers.ino | 53 +++++++++++++++++-------- tasmota/xdrv_23_zigbee_A_impl.ino | 16 +++++++- 6 files changed, 99 insertions(+), 23 deletions(-) diff --git a/tasmota/settings.h b/tasmota/settings.h index 9d4466028..57aa35860 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -120,7 +120,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t rotary_uses_rules : 1; // bit 16 (v8.3.1.6) - SetOption98 - Use rules instead of light control uint32_t zerocross_dimmer : 1; // bit 17 (v8.3.1.4) - SetOption99 - Enable zerocross dimmer on PWM DIMMER uint32_t remove_zbreceived : 1; // bit 18 (v8.3.1.7) - SetOption100 - Remove ZbReceived form JSON message - uint32_t spare19 : 1; + uint32_t zb_index_ep : 1; // bit 19 (v8.3.1.7) - SetOption101 - Add the source endpoint as suffix to attributes, ex `Power3` instead of `Power` if sent from endpoint 3 uint32_t spare20 : 1; uint32_t spare21 : 1; uint32_t spare22 : 1; diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index abcf6440c..d87fe4005 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -141,6 +141,7 @@ public: // Add an endpoint to a device void addEndpoint(uint16_t shortaddr, uint8_t endpoint); void clearEndpoints(uint16_t shortaddr); + uint32_t countEndpoints(uint16_t shortaddr) const; // return the number of known endpoints (0 if unknown) void setManufId(uint16_t shortaddr, const char * str); void setModelId(uint16_t shortaddr, const char * str); @@ -533,6 +534,23 @@ void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) { } } +// +// Count the number of known endpoints +// +uint32_t Z_Devices::countEndpoints(uint16_t shortaddr) const { + uint32_t count_ep = 0; + int32_t found = findShortAddr(shortaddr); + if (found < 0) return 0; // avoid creating an entry if the device was never seen + const Z_Device &device = devicesAt(found); + + for (uint32_t i = 0; i < endpoints_max; i++) { + if (0 != device.endpoints[i]) { + count_ep++; + } + } + return count_ep; +} + // Find the first endpoint of the device uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const { // When in router of end-device mode, the coordinator was not probed, in this case always talk to endpoint 1 diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 578e3c3bf..454e9b5cf 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1157,7 +1157,7 @@ void ZCLFrame::parseResponse(void) { // Parse non-normalized attributes void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { - convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _payload); + convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); sendHueUpdate(_srcaddr, _groupaddr, _cluster_id, _cmd_id, _frame_control.b.direction); } @@ -1423,6 +1423,8 @@ int32_t Z_ApplyConverter(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObje } void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { + // source endpoint + uint8_t src_ep = _srcendpoint; // iterate on json elements for (auto kv : json) { String key_string = kv.key; @@ -1490,6 +1492,11 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { String new_name_str = (const __FlashStringHelper*) converter->name; if (suffix > 1) { new_name_str += suffix; } // append suffix number + // else if (Settings.flag4.zb_index_ep) { + // if (zigbee_devices.countEndpoints(shortaddr) > 0) { + // new_name_str += _srcendpoint; + // } + // } // apply the transformation int32_t drop = Z_ApplyConverter(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute, conv_multiplier, conv_cb); if (drop) { diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 40545f737..c2ea81765 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -341,7 +341,7 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin // Parse a cluster specific command, and try to convert into human readable -void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, const SBuffer &payload) { +void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, uint16_t shortaddr, uint8_t srcendpoint, const SBuffer &payload) { size_t hex_char_len = payload.len()*2+2; char *hex_char = (char*) malloc(hex_char_len); if (!hex_char) { return; } @@ -414,11 +414,11 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo if (command_name) { // Now try to transform into a human readable format + String command_name2 = String(command_name); // if (direction & 0x80) then specific transform if (conv_direction & 0x80) { // TODO need to create a specific command // IAS - String command_name2 = String(command_name); if ((cluster == 0x0500) && (cmd == 0x00)) { // "ZoneStatusChange" json[command_name] = xyz.x; @@ -465,11 +465,21 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo String scene_payload = json[attrid_str]; json[F("ScenePayload")] = scene_payload.substring(8); // remove first 8 characters } - } else { + } else { // general case + bool extended_command = false; // do we send command with endpoint suffix + // if SO101 and multiple endpoints, append endpoint number + if (Settings.flag4.zb_index_ep) { + if (zigbee_devices.countEndpoints(shortaddr) > 0) { + command_name2 += srcendpoint; + extended_command = true; + } + } if (0 == xyz.x_type) { json[command_name] = true; // no parameter + if (extended_command) { json[command_name2] = true; } } else if (0 == xyz.y_type) { json[command_name] = xyz.x; // 1 parameter + if (extended_command) { json[command_name2] = xyz.x; } } else { // multiple answers, create an array JsonArray &arr = json.createNestedArray(command_name); @@ -478,6 +488,14 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo if (xyz.z_type) { arr.add(xyz.z); } + if (extended_command) { + JsonArray &arr = json.createNestedArray(command_name2); + arr.add(xyz.x); + arr.add(xyz.y); + if (xyz.z_type) { + arr.add(xyz.z); + } + } } } } diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index e0239f316..c08ff4d28 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -966,23 +966,44 @@ void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload, SBuffer buf(payload_len + 22); uint8_t seq = zigbee_devices.getNextSeqNumber(0x0000); - buf.add16(EZSP_sendUnicast); + if (shortaddr < 0xFFFC) { + // send unicast + buf.add16(EZSP_sendUnicast); - buf.add8(EMBER_OUTGOING_DIRECT); // 00 - buf.add16(shortaddr); // dest addr - // ApsFrame - buf.add16(0x0000); // ZOD profile - buf.add16(cmd); // ZDO cmd in cluster - buf.add8(0); // srcEp - buf.add8(0); // dstEp - buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame - buf.add16(0x0000); // groupId - buf.add8(seq); - // end of ApsFrame - buf.add8(0x01); // tag TODO - buf.add8(payload_len + 1); // insert seq number - buf.add8(seq); - buf.addBuffer(payload, payload_len); + buf.add8(EMBER_OUTGOING_DIRECT); // 00 + buf.add16(shortaddr); // dest addr + // ApsFrame + buf.add16(0x0000); // ZOD profile + buf.add16(cmd); // ZDO cmd in cluster + buf.add8(0); // srcEp + buf.add8(0); // dstEp + buf.add16(EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY); // APS frame + buf.add16(0x0000); // groupId + buf.add8(seq); + // end of ApsFrame + buf.add8(0x01); // tag TODO + buf.add8(payload_len + 1); // insert seq number + buf.add8(seq); + buf.addBuffer(payload, payload_len); + } else { + // send broadcast + buf.add16(EZSP_sendBroadcast); + buf.add16(shortaddr); // dest addr + // ApsFrame + buf.add16(0x0000); // ZOD profile + buf.add16(cmd); // ZDO cmd in cluster + buf.add8(0); // srcEp + buf.add8(0); // dstEp + buf.add16(0x00); // APS frame + buf.add16(0x0000); // groupId + buf.add8(seq); + // end of ApsFrame + buf.add8(0x1E); // radius + buf.add8(0x01); // tag TODO + buf.add8(payload_len + 1); // insert seq number + buf.add8(seq); + buf.addBuffer(payload, payload_len); + } ZigbeeEZSPSendCmd(buf.buf(), buf.len(), true); } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 57ecd3764..5922b3ee3 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -992,12 +992,14 @@ void CmndZbPermitJoin(void) { if (payload <= 0) { duration = 0; - } else if (99 == payload) { - duration = 0xFF; // unlimited time } // ZNP Version #ifdef USE_ZIGBEE_ZNP + if (99 == payload) { + duration = 0xFF; // unlimited time + } + uint16_t dstAddr = 0xFFFC; // default addr SBuffer buf(34); @@ -1014,10 +1016,20 @@ void CmndZbPermitJoin(void) { // EZSP VERSION #ifdef USE_ZIGBEE_EZSP + if (99 == payload) { + ResponseCmndChar_P(PSTR("Unlimited time not supported")); return; + } + SBuffer buf(3); buf.add16(EZSP_permitJoining); buf.add8(duration); ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); + + // send ZDO_Mgmt_Permit_Joining_req to all routers + buf.setLen(0); + buf.add8(duration); + buf.add8(0x01); // TC_Significance - This field shall always have a value of 1, indicating a request to change the Trust Center policy. If a frame is received with a value of 0, it shall be treated as having a value of 1. + // EZ_SendZDO(0xFFFC, ZDO_Mgmt_Permit_Joining_req, buf.buf(), buf.len()); TODO fix NAK/ACK first #endif // USE_ZIGBEE_EZSP ResponseCmndDone();