diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/xdrv_23_zigbee_0_constants.ino index 467f70e35853..92af3a046c21 100644 --- a/tasmota/xdrv_23_zigbee_0_constants.ino +++ b/tasmota/xdrv_23_zigbee_0_constants.ino @@ -57,7 +57,53 @@ enum ZnpSubsystem { #ifdef USE_ZIGBEE_EZSP -enum EZSPCondigId { +enum EZSPNodeType { + EMBER_UNKNOWN_DEVICE = 0x00, + EMBER_COORDINATOR = 0x01, + EMBER_ROUTER = 0x02, + EMBER_END_DEVICE = 0x03, + EMBER_SLEEPY_END_DEVICE = 0x04 +}; + +enum EZSPDeviceUpdate { + EMBER_STANDARD_SECURITY_SECURED_REJOIN = 0x00, + EMBER_STANDARD_SECURITY_UNSECURED_JOIN = 0x01, + EMBER_DEVICE_LEFT = 0x02, + EMBER_STANDARD_SECURITY_UNSECURED_REJOIN = 0x03, +}; + +enum EZSPJoinDecision { + EMBER_USE_PRECONFIGURED_KEY = 0x00, + EMBER_SEND_KEY_IN_THE_CLEAR = 0x01, + EMBER_DENY_JOIN = 0x02, + EMBER_NO_ACTION = 0x03 +}; + +enum EZSPCurrentSecurytBitMask { + EMBER_STANDARD_SECURITY_MODE = 0x0000, + EMBER_DISTRIBUTED_TRUST_CENTER_MODE = 0x0002, + EMBER_GLOBAL_LINK_KEY = 0x0004, + EMBER_TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004, + EMBER_PRECONFIGURED_NETWORK_KEY_MODE = 0x0008, + EMBER_HAVE_TRUST_CENTER_LINK_KEY = 0x0010, + EMBER_TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084, + EMBER_HAVE_PRECONFIGURED_KEY = 0x0100, + EMBER_HAVE_NETWORK_KEY = 0x0200, + EMBER_GET_LINK_KEY_WHEN_JOINING = 0x0400, + EMBER_REQUIRE_ENCRYPTED_KEY = 0x0800, + EMBER_NO_FRAME_COUNTER_RESET = 0x1000, + EMBER_GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000, + EMBER_HAVE_TRUST_CENTER_EUI64 = 0x0040 +}; + +enum EZSPJoinMethod { + EMBER_USE_MAC_ASSOCIATION = 0x0, + EMBER_USE_NWK_REJOIN = 0x1, + EMBER_USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2, + EMBER_USE_CONFIGURED_NWK_STATE = 0x3 +}; + +enum EZSPConfigId { EZSP_CONFIG_PACKET_BUFFER_COUNT = 0x01, EZSP_CONFIG_NEIGHBOR_TABLE_SIZE = 0x02, EZSP_CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03, @@ -301,6 +347,159 @@ enum EZSPStatusId { EZSP_NO_ERROR = 0xFF }; +enum EZSPPolicyId { + EZSP_TRUST_CENTER_POLICY = 0x00, + EZSP_BINDING_MODIFICATION_POLICY = 0x01, + EZSP_UNICAST_REPLIES_POLICY = 0x02, + EZSP_POLL_HANDLER_POLICY = 0x03, + EZSP_MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04, + EZSP_TC_KEY_REQUEST_POLICY = 0x05, + EZSP_APP_KEY_REQUEST_POLICY = 0x06, + EZSP_PACKET_VALIDATE_LIBRARY_POLICY = 0x07, + EZSP_ZLL_POLICY = 0x08, + EZSP_TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 +}; + +enum EZSPDecisionBitmask { + EZSP_DECISION_BITMASK_DEFAULT_CONFIGURATION = 0x0000, + EZSP_DECISION_ALLOW_JOINS = 0x0001, + EZSP_DECISION_ALLOW_UNSECURED_REJOINS = 0x0002, + EZSP_DECISION_SEND_KEY_IN_CLEAR = 0x0004, + EZSP_DECISION_IGNORE_UNSECURED_REJOINS = 0x0008, + EZSP_DECISION_JOINS_USE_INSTALL_CODE_KEY = 0x0010, + EZSP_DECISION_DEFER_JOINS = 0x0020 +}; + +enum EZSPDecisionId { + EZSP_DEFER_JOINS_REJOINS_HAVE_LINK_KEY = 0x07, + EZSP_DISALLOW_BINDING_MODIFICATION = 0x10, + EZSP_ALLOW_BINDING_MODIFICATION = 0x11, + EZSP_CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS = 0x12, + EZSP_HOST_WILL_NOT_SUPPLY_REPLY = 0x20, + EZSP_HOST_WILL_SUPPLY_REPLY = 0x21, + EZSP_POLL_HANDLER_IGNORE = 0x30, + EZSP_POLL_HANDLER_CALLBACK = 0x31, + EZSP_MESSAGE_TAG_ONLY_IN_CALLBACK = 0x40, + EZSP_MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK = 0x41, + EZSP_DENY_TC_KEY_REQUESTS = 0x50, + EZSP_ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY = 0x51, + EZSP_ALLOW_TC_KEY_REQUEST_AND_GENERATE_NEW_KEY = 0x52, + EZSP_DENY_APP_KEY_REQUESTS = 0x60, + EZSP_ALLOW_APP_KEY_REQUESTS = 0x61, + EZSP_PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62, + EZSP_PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 +}; + +enum EZSP_ZdoConfigurationFlags { + EMBER_APP_RECEIVES_SUPPORTED_ZDO_REQUESTS = 0x01, + EMBER_APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS = 0x02, + EMBER_APP_HANDLES_ZDO_ENDPOINT_REQUESTS = 0x04, + EMBER_APP_HANDLES_ZDO_BINDING_REQUESTS = 0x08 +}; + +enum EZSP_EmberIncomingMessageType { + EMBER_INCOMING_UNICAST = 0x00, + EMBER_INCOMING_UNICAST_REPLY = 0x01, + EMBER_INCOMING_MULTICAST = 0x02, + EMBER_INCOMING_MULTICAST_LOOPBACK = 0x03, + EMBER_INCOMING_BROADCAST = 0x04, + EMBER_INCOMING_BROADCAST_LOOPBACK = 0x05, + EMBER_INCOMING_MANY_TO_ONE_ROUTE_REQUEST = 0x06 +}; + +enum EZSP_EmberApsOption { + EMBER_APS_OPTION_NONE = 0x0000, + EMBER_APS_OPTION_ENCRYPTION = 0x0020, + EMBER_APS_OPTION_RETRY = 0x0040, + EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY = 0x0100, + EMBER_APS_OPTION_FORCE_ROUTE_DISCOVERY = 0x0200, + EMBER_APS_OPTION_SOURCE_EUI64 = 0x0400, + EMBER_APS_OPTION_DESTINATION_EUI64 = 0x0800, + EMBER_APS_OPTION_ENABLE_ADDRESS_DISCOVERY = 0x1000, + EMBER_APS_OPTION_POLL_RESPONSE = 0x2000, + EMBER_APS_OPTION_ZDO_RESPONSE_REQUIRED = 0x4000, + EMBER_APS_OPTION_FRAGMENT = 0x8000 +}; + +enum EZSP_EmberOutgoingMessageType { + EMBER_OUTGOING_DIRECT = 0x00, + EMBER_OUTGOING_VIA_ADDRESS_TABLE = 0x01, + EMBER_OUTGOING_VIA_BINDING = 0x02, + EMBER_OUTGOING_MULTICAST = 0x03, + EMBER_OUTGOING_BROADCAST = 0x04 +}; + +// inspired from https://github.com/zigpy/zigpy/blob/dev/zigpy/zdo/types.py +enum EZSP_ZDO { + ZDO_NWK_addr_req = 0x0000, + ZDO_IEEE_addr_req = 0x0001, + ZDO_Node_Desc_req = 0x0002, + ZDO_Power_Desc_req = 0x0003, + ZDO_Simple_Desc_req = 0x0004, + ZDO_Active_EP_req = 0x0005, + ZDO_Match_Desc_req = 0x0006, + ZDO_Complex_Desc_req = 0x0010, + ZDO_User_Desc_req = 0x0011, + ZDO_Discovery_Cache_req = 0x0012, + ZDO_Device_annce = 0x0013, + ZDO_User_Desc_set = 0x0014, + ZDO_System_Server_Discovery_req = 0x0015, + ZDO_Discovery_store_req = 0x0016, + ZDO_Node_Desc_store_req = 0x0017, + ZDO_Active_EP_store_req = 0x0019, + ZDO_Simple_Desc_store_req = 0x001A, + ZDO_Remove_node_cache_req = 0x001B, + ZDO_Find_node_cache_req = 0x001C, + ZDO_Extended_Simple_Desc_req = 0x001D, + ZDO_Extended_Active_EP_req = 0x001E, + ZDO_Parent_annce = 0x001F, + // Bind Management Server Services Responses + ZDO_End_Device_Bind_req = 0x0020, + ZDO_Bind_req = 0x0021, + ZDO_Unbind_req = 0x0022, + // Network Management Server Services Requests + ZDO_Mgmt_Lqi_req = 0x0031, + ZDO_Mgmt_Rtg_req = 0x0032, + ZDO_Mgmt_Leave_req = 0x0034, + ZDO_Mgmt_Permit_Joining_req = 0x0036, + ZDO_Mgmt_NWK_Update_req = 0x0038, + + // Responses + // Device and Service Discovery Server Responses + ZDO_NWK_addr_rsp = 0x8000, + ZDO_IEEE_addr_rsp = 0x8001, + ZDO_Node_Desc_rsp = 0x8002, + ZDO_Power_Desc_rsp = 0x8003, + ZDO_Simple_Desc_rsp = 0x8004, + ZDO_Active_EP_rsp = 0x8005, + ZDO_Match_Desc_rsp = 0x8006, + ZDO_Complex_Desc_rsp = 0x8010, + ZDO_User_Desc_rsp = 0x8011, + ZDO_Discovery_Cache_rsp = 0x8012, + ZDO_User_Desc_conf = 0x8014, + ZDO_System_Server_Discovery_rsp = 0x8015, + ZDO_Discovery_Store_rsp = 0x8016, + ZDO_Node_Desc_store_rsp = 0x8017, + ZDO_Power_Desc_store_rsp = 0x8018, + ZDO_Active_EP_store_rsp = 0x8019, + ZDO_Simple_Desc_store_rsp = 0x801A, + ZDO_Remove_node_cache_rsp = 0x801B, + ZDO_Find_node_cache_rsp = 0x801C, + ZDO_Extended_Simple_Desc_rsp = 0x801D, + ZDO_Extended_Active_EP_rsp = 0x801E, + ZDO_Parent_annce_rsp = 0x801F, + // Bind Management Server Services Responses + ZDO_End_Device_Bind_rsp = 0x8020, + ZDO_Bind_rsp = 0x8021, + ZDO_Unbind_rsp = 0x8022, + // Network Management Server Services Responses + ZDO_Mgmt_Lqi_rsp = 0x8031, + ZDO_Mgmt_Rtg_rsp = 0x8032, + ZDO_Mgmt_Leave_rsp = 0x8034, + ZDO_Mgmt_Permit_Joining_rsp = 0x8036, + ZDO_Mgmt_NWK_Update_rsp = 0x8038, +}; + enum EZSP_Commands { EZSP_version = 0x0000, EZSP_getLibraryStatus = 0x0001, diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 9a71075c07a2..d570c45600bb 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -244,6 +244,7 @@ Z_Devices zigbee_devices = Z_Devices(); // Local coordinator information uint64_t localIEEEAddr = 0; +uint16_t localShortAddr = 0; /*********************************************************************************************\ * Implementation diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 497f867e881d..4cba005005b1 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -595,14 +595,12 @@ public: 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, uint16_t groupaddr, uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp): + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber): _manuf_code(manuf_code), _transact_seq(transact_seq), _cmd_id(cmd_id), _payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough _cluster_id(clusterid), _groupaddr(groupaddr), _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), - _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), - _timestamp(timestamp) + _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber) { _frame_control.d8 = frame_control; _payload.addBuffer(buf, buf_len); @@ -616,13 +614,11 @@ public: "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," - "\"timestamp\":%d," "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), _groupaddr, _cluster_id, _srcaddr, _srcendpoint, _dstendpoint, _wasbroadcast, _linkquality, _securityuse, _seqnumber, - _timestamp, _frame_control, _manuf_code, _transact_seq, _cmd_id, hex_char); if (Settings.flag3.tuya_serial_mqtt_publish) { @@ -635,8 +631,7 @@ public: static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp) { // parse a raw frame and build the ZCL frame object + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber) { // parse a raw frame and build the ZCL frame object uint32_t i = offset; ZCLHeaderFrameControl_t frame_control; uint16_t manuf_code = 0; @@ -654,8 +649,7 @@ public: (const char *)(buf.buf() + i), len + offset - i, clusterid, groupid, srcaddr, srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); + linkquality, securityuse, seqnumber); return zcl_frame; } @@ -679,17 +673,12 @@ public: _cluster_id = clusterid; } - inline uint8_t getCmdId(void) const { - return _cmd_id; - } - - inline uint16_t getClusterId(void) const { - return _cluster_id; - } - - inline uint16_t getSrcEndpoint(void) const { - return _srcendpoint; - } + inline uint16_t getSrcAddr(void) const { return _srcaddr; } + inline uint16_t getGroupAddr(void) const { return _groupaddr; } + inline uint16_t getClusterId(void) const { return _cluster_id; } + inline uint8_t getLinkQuality(void) const { return _linkquality; } + inline uint8_t getCmdId(void) const { return _cmd_id; } + inline uint16_t getSrcEndpoint(void) const { return _srcendpoint; } const SBuffer &getPayload(void) const { return _payload; @@ -699,6 +688,7 @@ public: return _manuf_code; } + private: ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; uint16_t _manuf_code = 0; // optional @@ -715,7 +705,6 @@ private: uint8_t _linkquality; uint8_t _securityuse; uint8_t _seqnumber; - uint32_t _timestamp; }; // Zigbee ZCL converters diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 949f4acb8b5c..1f6c4e3a1c37 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -50,12 +50,15 @@ ZF(HueSat) ZF(Color) ZF(ShutterOpen) ZF(ShutterClose) ZF(ShutterStop) ZF(ShutterLift) ZF(ShutterTilt) ZF(Shutter) //ZF(Occupancy) ZF(DimmerMove) ZF(DimmerStep) ZF(DimmerStepUp) ZF(DimmerStepDown) -ZF(HueMove) ZF(HueStep) ZF(HueStepUp) ZF(HueStepDown) ZF(SatMove) ZF(SatStep) ZF(ColorMove) ZF(ColorStep) ZF(ColorTempStep) ZF(ColorTempStepUp) ZF(ColorTempStepDown) +ZF(HueMove) ZF(HueStep) ZF(HueStepUp) ZF(HueStepDown) ZF(SatMove) ZF(SatStep) ZF(ColorMove) ZF(ColorStep) +ZF(ColorTempMoveUp) ZF(ColorTempMoveDown) ZF(ColorTempMoveStop) ZF(ColorTempMove) +ZF(ColorTempStep) ZF(ColorTempStepUp) ZF(ColorTempStepDown) ZF(ArrowClick) ZF(ArrowHold) ZF(ArrowRelease) ZF(ZoneStatusChange) ZF(xxxx00) ZF(xxxx) ZF(01xxxx) ZF(03xxxx) ZF(00) ZF(01) ZF() ZF(xxxxyy) ZF(00190200) ZF(01190200) ZF(xxyyyy) ZF(xx) ZF(xx000A00) ZF(xx0A00) ZF(xxyy0A00) ZF(xxxxyyyy0A00) ZF(xxxx0A00) ZF(xx0A) ZF(xx190A00) ZF(xx19) ZF(xx190A) ZF(xxxxyyyy) ZF(xxxxyyzz) ZF(xxyyzzzz) ZF(xxyyyyzz) +ZF(01xxxx000000000000) ZF(03xxxx000000000000) ZF(00xxxx000000000000) ZF(xxyyyy000000000000) ZF(00xx0A00) ZF(01xx0A00) ZF(03xx0A00) ZF(01xxxx0A0000000000) ZF(03xxxx0A0000000000) ZF(xxyyyy0A0000000000) // Cluster specific commands @@ -119,8 +122,12 @@ const Z_CommandConverter Z_Commands[] PROGMEM = { { Z(SatStep), 0x0300, 0x05, 0x01, Z(xx190A) }, { Z(ColorMove), 0x0300, 0x08, 0x01, Z(xxxxyyyy) }, { Z(ColorStep), 0x0300, 0x09, 0x01, Z(xxxxyyyy0A00) }, - { Z(ColorTempStepUp), 0x0300, 0x4C, 0x01, Z(01xxxx0A0000000000) }, //xxxx = step - { Z(ColorTempStepDown),0x0300, 0x4C, 0x01, Z(03xxxx0A0000000000) }, //xxxx = step + { Z(ColorTempMoveUp), 0x0300, 0x4B, 0x01, Z(01xxxx000000000000) }, + { Z(ColorTempMoveDown),0x0300, 0x4B, 0x01, Z(03xxxx000000000000) }, + { Z(ColorTempMoveStop),0x0300, 0x4B, 0x01, Z(00xxxx000000000000) }, + { Z(ColorTempMove), 0x0300, 0x4B, 0x01, Z(xxyyyy000000000000) }, + { Z(ColorTempStepUp), 0x0300, 0x4C, 0x01, Z(01xxxx0A0000000000) }, + { Z(ColorTempStepDown),0x0300, 0x4C, 0x01, Z(03xxxx0A0000000000) }, { Z(ColorTempStep), 0x0300, 0x4C, 0x01, Z(xxyyyy0A0000000000) }, //xx = 0x01 up, 0x03 down, yyyy = step // Tradfri { Z(ArrowClick), 0x0005, 0x07, 0x01, Z(xx) }, // xx == 0x01 = left, 0x00 = right diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 912a493ca888..dd09cd18b5e4 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -36,6 +36,8 @@ const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its const uint8_t ZIGBEE_STATUS_SCANNING = 40; // State change const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration +const uint8_t ZIGBEE_STATUS_EZ_VERSION = 55; // Status: EFR32 EZ Version +const uint8_t ZIGBEE_STATUS_EZ_INFO = 56; // Status: EFR32 EZ Device Configuration const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version const uint8_t ZIGBEE_STATUS_ABORT = 99; // Fatal error, Zigbee not working @@ -170,6 +172,19 @@ SBuffer *zigbee_buffer = nullptr; #define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) +const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration"; +const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator"; +const char kConfiguredRouter[] PROGMEM = "Configured, starting router"; +const char kConfiguredDevice[] PROGMEM = "Configured, starting device"; +const char kStarted[] PROGMEM = "Started"; +const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started"; +const char kResetting[] PROGMEM = "Resetting configuration"; +const char kResettingDevice[] PROGMEM = D_LOG_ZIGBEE "Resetting EZSP device"; +const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported"; +const char kEZ8[] PROGMEM = "Only EZSP protocol v8 is currently supported"; +const char kAbort[] PROGMEM = "Abort"; +const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; + #ifdef USE_ZIGBEE_ZNP // ZBS_* Zigbee Send @@ -402,17 +417,6 @@ void Z_UpdateConfig(uint8_t zb_channel, uint16_t zb_pan_id, uint64_t zb_ext_pani ) // 2605621001030507090B0D0F00020406080A0C0D } -const char kCheckingDeviceConfiguration[] PROGMEM = D_LOG_ZIGBEE "checking device configuration"; -const char kConfiguredCoord[] PROGMEM = "Configured, starting coordinator"; -const char kConfiguredRouter[] PROGMEM = "Configured, starting router"; -const char kConfiguredDevice[] PROGMEM = "Configured, starting device"; -const char kStarted[] PROGMEM = "Started"; -const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started"; -const char kResetting[] PROGMEM = "Resetting configuration"; -const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported"; -const char kAbort[] PROGMEM = "Abort"; -const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; - static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_LABEL(0) ZI_NOOP() @@ -543,7 +547,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { // ====================================================================== // Start as Zigbee Router // ====================================================================== - ZI_LABEL(ZIGBEE_LABEL_INIT_ROUTER) // Init as a router + ZI_LABEL(ZIGBEE_LABEL_INIT_ROUTER) // Init as a router // Check the configuration as Router ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER) ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured @@ -654,8 +658,7 @@ ZBM(ZBS_SET_TC_CACHE, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG ZBM(ZBS_SET_ROUTE_TBL, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_SOURCE_ROUTE_TABLE_SIZE, 0x10, 0x00) // 53001A1000 ZBM(ZBS_SET_KEY_TBL, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_KEY_TABLE_SIZE, 0x04, 0x00) // 53001E0400 ZBM(ZBS_SET_PANID_CNFLCT, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 0x02, 0x00)// 5300220200 -// TODO APP_RECEIVES_SUPPORTED_ZDO_REQUESTS -ZBM(ZBS_SET_ZDO_REQ, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_APPLICATION_ZDO_FLAGS, 0x03, 0x00) // 53002A0300 +ZBM(ZBS_SET_ZDO_REQ, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_APPLICATION_ZDO_FLAGS, EMBER_APP_RECEIVES_SUPPORTED_ZDO_REQUESTS | EMBER_APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS, 0x00) // 53002A0300 ZBM(ZBS_SET_NETWORKS, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_SUPPORTED_NETWORKS, 0x01, 0x00) // 53002D0100 ZBM(ZBS_SET_PACKET_BUF, EZSP_setConfigurationValue, 0x00 /*high*/, EZSP_CONFIG_PACKET_BUFFER_COUNT, 0xFF, 0x00) // 530001FF00 @@ -667,23 +670,24 @@ ZBM(ZBS_GET_APS_UNI, EZSP_getConfigurationValue, 0x00 /*high*/, EZSP_CONFIG ZBM(ZBR_GET_OK, EZSP_getConfigurationValue, 0x00 /*high*/, 0x00 /*ok*/) // 5200 - followed by the value // Add Endpoints -// ZBM(ZBS_ADD_ENDPOINT1, EZSP_addEndpoint, 0x00 /*high*/, 0x01 /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), -// 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, -// 0x0E /* inputClusterCount */, // actually all clusters will be received -// 0X00 /* outputClusterCount */, -// 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, // 0x0000, 0x0004, 0x0005, 0x0006 -// 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 -// ) ZBM(ZBS_ADD_ENDPOINT1, EZSP_addEndpoint, 0x00 /*high*/, 0x01 /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, - 0x00 /* inputClusterCount */, // actually all clusters will be received - 0X00 /* outputClusterCount */ ) // 02000104010500000000 + 0x0E /* inputClusterCount */, // actually all clusters will be received + 0X00 /* outputClusterCount */, // 02000104010500000000 + 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, // 0x0000, 0x0004, 0x0005, 0x0006 + 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 + ) ZBM(ZBS_ADD_ENDPOINTB, EZSP_addEndpoint, 0x00 /*high*/, 0x0B /*ep*/, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, - 0x00 /* inputClusterCount */, // actually all clusters will be received - 0X00 /* outputClusterCount */ ) // 02000B04010500000000 + 0x0E /* inputClusterCount */, // actually all clusters will be received + 0X00 /* outputClusterCount */, // 02000B04010500000000 + 0x00,0x00, 0x04,0x00, 0x05,0x00, 0x06,0x00, // 0x0000, 0x0004, 0x0005, 0x0006 + 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 + ) ZBM(ZBR_ADD_ENDPOINT, EZSP_addEndpoint, 0x00 /*high*/, 0x00 /*ok*/) // 020000 // set concentrator false @@ -691,11 +695,58 @@ ZBM(ZBS_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*false*/, 0 0x58,0x02 /*minTime*/, 0x08,0x07 /*maxTime*/, 0x02 /*errThr*/, 0x05 /*failThr*/, 0x00 /*maxHops*/) // 100000F9FF58020807020500 ZBM(ZBR_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*ok*/) // 100000 -//False, , 600, 1800, 2, 5, 0) +// setInitialSecurityState +#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY +ZBM(ZBS_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 /*high*/, + Z_B0(EZ_SECURITY_MODE), Z_B1(EZ_SECURITY_MODE), + // preConfiguredKey + 0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0x30, 0x39, // well known key "ZigBeeAlliance09" + // networkKey + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + 0x00 /*sequence*/, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*trustcenter*/ + ) +ZBM(ZBR_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 /*high*/, 0x00 /*status*/) + +// setIndividual policies +ZBM(ZBS_SET_POLICY_00, EZSP_setPolicy, 0x00 /*high*/, EZSP_TRUST_CENTER_POLICY, + EZSP_DECISION_ALLOW_JOINS | EZSP_DECISION_ALLOW_UNSECURED_REJOINS) // 55000003 +ZBM(ZBS_SET_POLICY_02, EZSP_setPolicy, 0x00 /*high*/, EZSP_UNICAST_REPLIES_POLICY, + EZSP_HOST_WILL_NOT_SUPPLY_REPLY) // 550002210 +ZBM(ZBS_SET_POLICY_03, EZSP_setPolicy, 0x00 /*high*/, EZSP_POLL_HANDLER_POLICY, + EZSP_POLL_HANDLER_IGNORE) // 55000330 +ZBM(ZBS_SET_POLICY_05, EZSP_setPolicy, 0x00 /*high*/, EZSP_TC_KEY_REQUEST_POLICY, + EZSP_ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY) // 55000551 +ZBM(ZBS_SET_POLICY_06, EZSP_setPolicy, 0x00 /*high*/, EZSP_APP_KEY_REQUEST_POLICY, + EZSP_DENY_APP_KEY_REQUESTS) // 55000660 +ZBM(ZBR_SET_POLICY_XX, EZSP_setPolicy, 0x00 /*high*/, 0x00 /*status*/) + +// formNetwork - i.e. start zigbee network as coordinator +ZBM(ZBS_FORM_NETWORK, EZSP_formNetwork, 0x00 /*high*/, + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), + Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID), + 20 /*radioTxPower*/, + USE_ZIGBEE_CHANNEL /*channel*/, + EMBER_USE_MAC_ASSOCIATION, + 0xFF,0xFF, /*nwkManagerId, unused*/ + 0x00, /*nwkUpdateId, unused*/ + 0x00,0x00,0x00,0x00, /*NWK channel mask, unused*/ + ) // 1E00... +ZBM(ZBR_FORM_NETWORK, EZSP_formNetwork, 0x00 /*high*/, 0x00 /*status*/) // 1E0000 +ZBM(ZBR_NETWORK_UP, EZSP_stackStatusHandler, 0x00 /*high*/, EMBER_NETWORK_UP) // 190090 + +// read configuration details +ZBM(ZBS_GET_NETW_PARM, EZSP_getNetworkParameters, 0x00 /*high*/) // 2800 +ZBM(ZBR_GET_NETW_PARM, EZSP_getNetworkParameters, 0x00 /*high*/, 0x00 /*ok*/) // 2800 +ZBM(ZBS_GET_EUI64, EZSP_getEui64, 0x00 /*high*/) // 2600 +ZBM(ZBR_GET_EUI64, EZSP_getEui64, 0x00 /*high*/) // 2600 +ZBM(ZBS_GET_NODEID, EZSP_getNodeId, 0x00 /*high*/) // 2700 +ZBM(ZBR_GET_NODEID, EZSP_getNodeId, 0x00 /*high*/) // 2700 -const char kResetingDevice[] PROGMEM = D_LOG_ZIGBEE "resetting EZSP device"; -const char kAbort[] PROGMEM = "Abort"; -const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort"; static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_LABEL(0) @@ -706,7 +757,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize // Hardware reset - ZI_LOG(LOG_LEVEL_INFO, kResetingDevice) // Log Debug: resetting EZSP device + ZI_LOG(LOG_LEVEL_INFO, kResettingDevice) // Log Debug: resetting EZSP device ZI_CALL(&Z_Reset_Device, 0) // LOW = reset ZI_WAIT(100) // wait for .1 second ZI_CALL(&Z_Reset_Device, 1) // HIGH = release reset @@ -715,9 +766,10 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_WAIT_UNTIL(5000, ZBR_RSTACK) // wait for RSTACK message // Init device and probe version - ZI_SEND(ZBS_VERSION) ZI_WAIT_RECV(1000, ZBR_VERSION) // check EXT PAN ID + ZI_SEND(ZBS_VERSION) ZI_WAIT_RECV_FUNC(1000, ZBR_VERSION, &Z_ReceiveCheckVersion) // check EXT PAN ID // configure EFR32 + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord) ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) // Address table size ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK) @@ -733,7 +785,8 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2) // read configuration - ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &Z_ReadAPSUnicastMessage) + // TODO - not sure it's useful + //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &Z_ReadAPSUnicastMessage) // add endpoint 0x01 and 0x0B ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) @@ -742,9 +795,38 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { // set Concentrator ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR) + // setInitialSecurityState + ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(500, ZBR_SET_SECURITY) + ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + + // formNetwork + ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(500, ZBR_FORM_NETWORK) + ZI_WAIT_RECV(5000, ZBR_NETWORK_UP) // wait for network to start + + // Query device information + ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &Z_EZSPGetEUI64) + ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NODEID, &Z_EZSPGetNodeId) + ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NETW_PARM, &Z_EZSPNetworkParameters) + + ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) + ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) + ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages + ZI_CALL(&Z_Load_Devices, 0) + ZI_CALL(&Z_Query_Bulbs, 0) + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) ZI_WAIT_FOREVER() - ZI_GOTO(ZIGBEE_LABEL_READY) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + // Error: version of Z-Stack is not supported + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kEZ8) + ZI_GOTO(ZIGBEE_LABEL_ABORT) // Abort state machine, general error ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 548c09a258ac..870caaa7de30 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -64,26 +64,6 @@ int32_t Z_EZSP_ERROR(uint8_t error_code) { XdrvRulesProcess(); } -/*********************************************************************************************\ - * Default resolver -\*********************************************************************************************/ - -int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { - // Default message handler for new messages - if (zigbee.init_phase) { - // if still during initialization phase, ignore any unexpected message - return -1; // ignore message - } else { - // TODO - // for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { - // if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { - // (*Z_DispatchTable[i].func)(res, buf); - // } - // } - return -1; - } -} - int32_t Z_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) { // Called when receiving a response from getConfigurationValue // Value is in bytes 2+3 @@ -94,6 +74,53 @@ int32_t Z_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) { #endif // USE_ZIGBEE_EZSP +/*********************************************************************************************\ + * Parsers for incoming EZSP messages +\*********************************************************************************************/ + +// +// Handle a "getEui64" incoming message +// +int32_t Z_EZSPGetEUI64(int32_t res, class SBuffer &buf) { + localIEEEAddr = buf.get64(2); + return res; +} + +// +// Handle a "getEui64" incoming message +// +int32_t Z_EZSPGetNodeId(int32_t res, class SBuffer &buf) { + localShortAddr = buf.get8(2); + return res; +} + +// +// Handle a "getNetworkParameters" incoming message +// +int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf) { + uint8_t node_type = buf.get8(3); + // ext panid: 4->11 + // panid: 12->13 + // radioTxPower: 14 + // radioChannel: 15 + + // Local short and long addresses are supposed to be already retrieved + // localIEEEAddr = long_adr; + // localShortAddr = short_adr; + + char hex[20]; + Uint64toHex(localIEEEAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d}}"), + ZIGBEE_STATUS_EZ_INFO, hex, localShortAddr, node_type); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + return res; +} + /*********************************************************************************************\ * Parsers for incoming ZNP messages \*********************************************************************************************/ @@ -117,6 +144,7 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { // keep track of the local IEEE address localIEEEAddr = long_adr; + localShortAddr = short_adr; char hex[20]; Uint64toHex(long_adr, hex, 64); @@ -194,6 +222,7 @@ int32_t Z_Reboot(int32_t res, class SBuffer &buf) { } int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP // check that the version is supported // typical version for ZNP 1.2 // 61020200-02.06.03.D9143401.0200000000 @@ -222,6 +251,34 @@ int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { } else { return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort } +#endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP + uint8_t protocol_version = buf.get8(2); + uint8_t stack_type = buf.get8(3); + uint16_t stack_version = buf.get16(4); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Version\":\"%d.%d.%d.%d\",\"Protocol\":%d" + ",\"Stack\":%d}}"), + ZIGBEE_STATUS_EZ_VERSION, + (stack_version & 0xF000) >> 12, + (stack_version & 0x0F00) >> 8, + (stack_version & 0x00F0) >> 4, + stack_version & 0x000F, + protocol_version, + stack_type + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + if (0x08 == protocol_version) { + return 0; // protocol v8 is ok + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort + } +#endif // USE_ZIGBEE_EZSP } // checks the device type (coordinator, router, end-device) @@ -466,10 +523,18 @@ int32_t Z_ReceiveStateChange(int32_t res, const class SBuffer &buf) { // Send back Active Ep Req message // int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { - Z_ShortAddress srcAddr = buf.get16(2); +#ifdef USE_ZIGBEE_ZNP + // Z_ShortAddress srcAddr = buf.get16(2); Z_ShortAddress nwkAddr = buf.get16(4); Z_IEEEAddress ieeeAddr = buf.get64(6); uint8_t capabilities = buf.get8(14); +#endif +#ifdef USE_ZIGBEE_EZSP + // uint8_t seq = buf.get8(0); + Z_ShortAddress nwkAddr = buf.get16(1); + Z_IEEEAddress ieeeAddr = buf.get64(3); + uint8_t capabilities = buf.get8(11); +#endif zigbee_devices.updateDevice(nwkAddr, ieeeAddr); @@ -627,7 +692,6 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { return -1; } -#ifdef USE_ZIGBEE_ZNP /*********************************************************************************************\ * Send specific ZNP messages \*********************************************************************************************/ @@ -636,24 +700,32 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { // Send ZDO_IEEE_ADDR_REQ request to get IEEE long address // void Z_SendIEEEAddrReq(uint16_t shortaddr) { +#ifdef USE_ZIGBEE_ZNP uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); +#endif } // // Send ACTIVE_EP_REQ to collect active endpoints for this address // void Z_SendActiveEpReq(uint16_t shortaddr) { +#ifdef USE_ZIGBEE_ZNP uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); +#endif +#ifdef USE_ZIGBEE_EZSP + uint8_t ActiveEpReq[] = { Z_B0(shortaddr), Z_B1(shortaddr) }; + EZ_SendZDO(shortaddr, ZDO_Active_EP_req, ActiveEpReq, sizeof(ActiveEpReq)); +#endif } // // Send AF Info Request // void Z_SendAFInfoRequest(uint16_t shortaddr) { +#ifdef USE_ZIGBEE_ZNP uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01 uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr); @@ -663,106 +735,63 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) { 0x00, transacid, ZCL_READ_ATTRIBUTES, 0x04, 0x00, 0x05, 0x00 }; ZigbeeZNPSend(AFInfoReq, sizeof(AFInfoReq)); +#endif } -#endif // USE_ZIGBEE_ZNP - -#ifdef USE_ZIGBEE_EZSP -/*********************************************************************************************\ - * Send specific EZS¨ messages -\*********************************************************************************************/ - -// -// Callback for loading Zigbee configuration from Flash, called by the state machine -// -int32_t Z_Reset_Device(uint8_t value) { - // TODO - GPIO is hardwired to GPIO4 - digitalWrite(4, value ? HIGH : LOW); - return 0; // continue -} - -// -// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address -// -void Z_SendIEEEAddrReq(uint16_t shortaddr) { -} - -// -// Send ACTIVE_EP_REQ to collect active endpoints for this address -// -void Z_SendActiveEpReq(uint16_t shortaddr) { -} // -// Send AF Info Request +// Handle trustCenterJoinHandler +// 2400 // -void Z_SendAFInfoRequest(uint16_t shortaddr) { -} - -#endif // USE_ZIGBEE_EZSP - -/*********************************************************************************************\ - * Callbacks -\*********************************************************************************************/ +#ifdef USE_ZIGBEE_EZSP +int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf) { + uint16_t srcAddr = buf.get16(2); + uint64_t ieeeAddr = buf.get64(4); + uint8_t status = buf.get8(12); + uint8_t decision = buf.get8(13); + uint16_t parentNw = buf.get16(14); + if (EMBER_DEVICE_LEFT != status) { // ignore message if the device is leaving + zigbee_devices.updateDevice(srcAddr, ieeeAddr); -// 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 -void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) { - static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s - // Read OCCUPANCY value if any - const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(OCCUPANCY)); - if (nullptr != &val_endpoint) { - uint32_t occupancy = strToUInt(val_endpoint); + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\"" + ",\"ParentNetwork\":\"0x%04X\"" + ",\"Status\":%d,\"Decision\":%d" + "}}"), + ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw, + status, decision + ); - if (occupancy) { - zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, OCCUPANCY_TIMEOUT, cluster, endpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); - } else { - zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY); - } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); } + return -1; } +#endif // USE_ZIGBEE_EZSP - -// Publish the received values once they have been coalesced -int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { - const JsonObject *json = zigbee_devices.jsonGet(shortaddr); - if (json == nullptr) { return 0; } // don't crash if not found - - zigbee_devices.jsonPublishFlush(shortaddr); - return 1; -} - -/*********************************************************************************************\ - * Global dispatcher for incoming messages -\*********************************************************************************************/ - -int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { - uint16_t groupid = buf.get16(2); - uint16_t clusterid = buf.get16(4); - uint16_t srcaddr = buf.get16(6); - uint8_t srcendpoint = buf.get8(8); - uint8_t dstendpoint = buf.get8(9); - uint8_t wasbroadcast = buf.get8(10); - uint8_t linkquality = buf.get8(11); - uint8_t securityuse = buf.get8(12); - uint32_t timestamp = buf.get32(13); - uint8_t seqnumber = buf.get8(17); +// +// Parse incoming ZCL message. This code is common to ZNP and EZSP +// +void Z_IncomingMessage(ZCLFrame &zcl_received) { + uint16_t srcaddr = zcl_received.getSrcAddr(); + uint16_t groupid = zcl_received.getGroupAddr(); + uint16_t clusterid = zcl_received.getClusterId(); + uint8_t linkquality = zcl_received.getLinkQuality(); + uint8_t srcendpoint = zcl_received.getSrcEndpoint(); bool defer_attributes = false; // do we defer attributes reporting to coalesce - ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, - srcaddr, - srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); + // log the packet details zcl_received.log(); - zigbee_devices.setLQI(srcaddr, linkquality); + zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI char shortaddr[8]; snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); - + DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); @@ -823,17 +852,180 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { Z_AutoResponder(srcaddr, clusterid, srcendpoint, json[F("ReadNames")]); } } +} + + +#ifdef USE_ZIGBEE_EZSP + +/*********************************************************************************************\ + * Send ZDO Message +\*********************************************************************************************/ + +void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload, size_t payload_len) { + SBuffer buf(payload_len + 20); + uint8_t seq = zigbee_devices.getNextSeqNumber(0x0000); + + 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); +} + +/*********************************************************************************************\ + * Send specific EZSP messages +\*********************************************************************************************/ + +int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { + uint8_t msgtype = buf.get8(2); // see EZSP_EmberIncomingMessageType + bool wasbroadcast = (msgtype >= EMBER_INCOMING_MULTICAST) && (msgtype <= EMBER_INCOMING_BROADCAST_LOOPBACK); + uint16_t profileid = buf.get16(3); // HA = 0x0104, ZDO = 0x0000 + uint16_t clusterid = buf.get16(5); + uint8_t srcendpoint = buf.get8(7); + uint8_t dstendpoint = buf.get8(8); + uint16_t apsoptions = buf.get16(9); // see EZSP_EmberApsOption, usually EMBER_APS_OPTION_ENABLE_ADDRESS_DISCOVERY + bool securityuse = (apsoptions & EMBER_APS_OPTION_ENCRYPTION) ? true : false; + uint16_t groupid = buf.get16(11); + uint8_t seqnumber = buf.get8(13); + uint8_t linkquality = buf.get8(14); + // uint8_t linkrsssi = buf.get8(15); // probably not used as there is no equivalent in Z-Stack + uint16_t srcaddr = buf.get16(16); + uint8_t bindingindex = buf.get8(18); // TODO not sure we need this one as a coordinator + uint8_t addressindex = buf.get8(19); // TODO not sure how to handle this one + // offset 20 is len, and buffer starts at offset 21 + + + if ((0x0000 == profileid) && (0x00 == srcendpoint)) { + // ZDO request + SBuffer zdo_buf = buf.subBuffer(21, buf.get8(20)); + switch (clusterid) { + case ZDO_Device_annce: + Z_ReceiveEndDeviceAnnonce(res, zdo_buf); + break; + } + } else { + bool defer_attributes = false; // do we defer attributes reporting to coalesce + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 21, buf.get8(20), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber); + // + Z_IncomingMessage(zcl_received); + } return -1; } +// +// Callback for loading Zigbee configuration from Flash, called by the state machine +// +int32_t Z_Reset_Device(uint8_t value) { + // TODO - GPIO is hardwired to GPIO4 + digitalWrite(4, value ? HIGH : LOW); + return 0; // continue +} + +/*********************************************************************************************\ + * Default resolver +\*********************************************************************************************/ + +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { + // Default message handler for new messages + if (zigbee.init_phase) { + // if still during initialization phase, ignore any unexpected message + return -1; // ignore message + } else { + switch (buf.get8(0)) { + case EZSP_incomingMessageHandler: + return EZ_IncomingMessage(res, buf); + break; + case EZSP_trustCenterJoinHandler: + return EZ_ReceiveTCJoinHandler(res, buf); + break; + } + return -1; + } +} + +#endif // USE_ZIGBEE_EZSP + +/*********************************************************************************************\ + * Callbacks +\*********************************************************************************************/ + + +// 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 +void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) { + static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s + // Read OCCUPANCY value if any + const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(OCCUPANCY)); + if (nullptr != &val_endpoint) { + uint32_t occupancy = strToUInt(val_endpoint); + + if (occupancy) { + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, OCCUPANCY_TIMEOUT, cluster, endpoint, Z_CAT_VIRTUAL_OCCUPANCY, 0, &Z_OccupancyCallback); + } else { + zigbee_devices.resetTimersForDevice(shortaddr, 0 /* groupaddr */, Z_CAT_VIRTUAL_OCCUPANCY); + } + } +} + + +// Publish the received values once they have been coalesced +int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + const JsonObject *json = zigbee_devices.jsonGet(shortaddr); + if (json == nullptr) { return 0; } // don't crash if not found + + zigbee_devices.jsonPublishFlush(shortaddr); + return 1; +} + +/*********************************************************************************************\ + * Global dispatcher for incoming messages +\*********************************************************************************************/ + +int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + uint16_t srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + // uint32_t timestamp = buf.get32(13); + uint8_t seqnumber = buf.get8(17); + + bool defer_attributes = false; // do we defer attributes reporting to coalesce + + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber); + // + Z_IncomingMessage(zcl_received); + + return -1; +} + +#ifdef USE_ZIGBEE_ZNP + // Structure for the Dispatcher callbacks table typedef struct Z_Dispatcher { const uint8_t* match; ZB_RecvMsgFunc func; } Z_Dispatcher; -#ifdef USE_ZIGBEE_ZNP - // Ffilters based on ZNP frames ZBM(AREQ_AF_DATA_CONFIRM, Z_AREQ | Z_AF, AF_DATA_CONFIRM) // 4480 ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481 @@ -874,7 +1066,7 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { // if still during initialization phase, ignore any unexpected message return -1; // ignore message } else { - for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(Z_DispatchTable); i++) { if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { (*Z_DispatchTable[i].func)(res, buf); } diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index 5c492e007433..5fb60de56813 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -495,7 +495,7 @@ int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) { bool overflow = frame_control & 0x01; bool callbackPending = frame_control & 0x04; bool security_enabled = frame_control & 0x8000; - if (frame_control != 0x0180) { + if (truncated || overflow || security_enabled) { AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: specific frame_control 0x%04X"), frame_control); } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index c59daf1147ee..1370db3cc189 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -954,8 +954,8 @@ void CmndZbRestore(void) { // void CmndZbPermitJoin(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + uint32_t payload = XdrvMailbox.payload; - uint16_t dstAddr = 0xFFFC; // default addr uint8_t duration = 60; // default 60s if (payload <= 0) { @@ -964,7 +964,10 @@ void CmndZbPermitJoin(void) { duration = 0xFF; // unlimited time } +// ZNP Version #ifdef USE_ZIGBEE_ZNP + uint16_t dstAddr = 0xFFFC; // default addr + SBuffer buf(34); buf.add8(Z_SREQ | Z_ZDO); // 25 buf.add8(ZDO_MGMT_PERMIT_JOIN_REQ); // 36 @@ -974,8 +977,17 @@ void CmndZbPermitJoin(void) { buf.add8(0x00); // TCSignificance ZigbeeZNPSend(buf.getBuffer(), buf.len()); + #endif // USE_ZIGBEE_ZNP +// EZSP VERSION +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(3); + buf.add16(EZSP_permitJoining); + buf.add8(duration); + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len(), true); +#endif // USE_ZIGBEE_EZSP + ResponseCmndDone(); }