diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fbad5390..a29e60f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - TLS in binary tasmota-zbbridge (#9635) - Support for EZO O2 sensors by Christopher Tremblay (#9619) - Zigbee reduce battery drain (#9642) +- Zigbee added ``ZbMap`` command to describe Zigbee topology ### Changed - PlatformIO library structure redesigned for compilation speed by Jason2866 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index ac4107daa..50287a783 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -565,6 +565,8 @@ #define D_JSON_ZIGBEE_UNBIND "ZbUnbind" #define D_CMND_ZIGBEE_BIND_STATE "BindState" #define D_JSON_ZIGBEE_BIND_STATE "ZbBindState" +#define D_CMND_ZIGBEE_MAP "Map" + #define D_JSON_ZIGBEE_MAP "ZbMap" #define D_JSON_ZIGBEE_PARENT "ZbParent" #define D_CMND_ZIGBEE_PING "Ping" #define D_JSON_ZIGBEE_PING "ZbPing" diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index cc45d8a5e..3baad68c8 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -925,6 +925,7 @@ int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) { return -1; } + // // Handle MgMt Bind Rsp incoming message // @@ -1002,6 +1003,116 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) { return -1; } +// Return false, true or null (if unknown) +const char * TrueFalseNull(uint32_t value) { + if (value == 0) { + return PSTR("false"); + } else if (value == 1) { + return PSTR("true"); + } else { + return PSTR("null"); + } +} + +const char * Z_DeviceRelationship(uint32_t value) { + switch (value) { + case 0: return PSTR("Parent"); + case 1: return PSTR("Child"); + case 2: return PSTR("Sibling"); + case 4: return PSTR("Previous"); + case 3: + default: return PSTR("None"); + } +} + +const char * Z_DeviceType(uint32_t value) { + switch (value) { + case 0: return PSTR("Coordinator"); + case 1: return PSTR("Router"); + case 2: return PSTR("Device"); + default: return PSTR("Unknown"); + } +} + +// +// Handle MgMt Bind Rsp incoming message +// +int32_t Z_MgmtLqiRsp(int32_t res, const class SBuffer &buf) { +#ifdef USE_ZIGBEE_ZNP + uint16_t shortaddr = buf.get16(2); + uint8_t status = buf.get8(4); + uint8_t lqi_total = buf.get8(5); + uint8_t lqi_start = buf.get8(6); + uint8_t lqi_len = buf.get8(7); + const size_t prefix_len = 8; +#endif // USE_ZIGBEE_ZNP +#ifdef USE_ZIGBEE_EZSP + uint16_t shortaddr = buf.get16(buf.len()-2); + uint8_t status = buf.get8(0); + uint8_t lqi_total = buf.get8(1); + uint8_t lqi_start = buf.get8(2); + uint8_t lqi_len = buf.get8(3); + const size_t prefix_len = 4; +#endif // USE_ZIGBEE_EZSP + + const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_MAP "\":{\"" 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\"" + ",\"MapTotal\":%d" + ",\"MapStart\":%d" + ",\"Map\":[" + ), status, getZigbeeStatusMessage(status).c_str(), lqi_total, lqi_start + 1); + + uint32_t idx = prefix_len; + for (uint32_t i = 0; i < lqi_len; i++) { + if (idx + 22 > buf.len()) { break; } // size 22 for EZSP + + //uint64_t extpanid = buf.get16(idx); // unused + // uint64_t m_longaddr = buf.get64(idx + 8); + uint16_t m_shortaddr = buf.get16(idx + 16); + uint8_t m_dev_type = buf.get8(idx + 18); + uint8_t m_permitjoin = buf.get8(idx + 19); + uint8_t m_depth = buf.get8(idx + 20); + uint8_t m_lqi = buf.get8(idx + 21); + idx += 22; + + if (i > 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"Device\":\"0x%04X\","), m_shortaddr); + + const char * friendlyName = zigbee_devices.getFriendlyName(m_shortaddr); + if (friendlyName) { + ResponseAppend_P(PSTR("\"Name\":\"%s\","), friendlyName); + } + ResponseAppend_P(PSTR("\"DeviceType\":\"%s\"," + "\"RxOnWhenIdle\":%s," + "\"Relationship\":\"%s\"," + "\"PermitJoin\":%s," + "\"Depth\":%d," + "\"LinkQuality\":%d" + "}" + ), + Z_DeviceType(m_dev_type & 0x03), + TrueFalseNull((m_dev_type & 0x0C) >> 2), + Z_DeviceRelationship((m_dev_type & 0x70) >> 4), + TrueFalseNull(m_permitjoin & 0x02), + m_depth, + m_lqi); + } + + ResponseAppend_P(PSTR("]}}")); + + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_MAP)); + + return -1; +} + #ifdef USE_ZIGBEE_EZSP // // Handle Parent Annonce Rsp incoming message @@ -1524,6 +1635,8 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { return Z_BindRsp(res, zdo_buf); case ZDO_Unbind_rsp: return Z_UnbindRsp(res, zdo_buf); + case ZDO_Mgmt_Lqi_rsp: + return Z_MgmtLqiRsp(res, zdo_buf); case ZDO_Mgmt_Bind_rsp: return Z_MgmtBindRsp(res, zdo_buf); case ZDO_Parent_annce: @@ -1697,6 +1810,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { { Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP }, &Z_ReceiveIEEEAddr }, // 4581 { { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &Z_BindRsp }, // 45A1 { { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &Z_UnbindRsp }, // 45A2 + { { Z_AREQ | Z_ZDO, ZDO_MGMT_LQI_RSP }, &Z_MgmtLqiRsp }, // 45B1 { { Z_AREQ | Z_ZDO, ZDO_MGMT_BIND_RSP }, &Z_MgmtBindRsp }, // 45B3 }; diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 5dc2a4af1..c455e187e 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -32,7 +32,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|" 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_BIND_STATE "|" + D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" D_CMND_ZIGBEE_MAP "|" D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA ; @@ -47,7 +47,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe, &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, - &CmndZbLight, &CmndZbRestore, &CmndZbBindState, + &CmndZbLight, &CmndZbRestore, &CmndZbBindState, &CmndZbMap, &CmndZbConfig, CmndZbData, }; @@ -937,11 +937,7 @@ void CmndZbUnbind(void) { ZbBindUnbind(true); } -// -// Command `ZbBindState` -// `ZbBindState` as index if it does not fit. If default, `1` starts at the beginning -// -void CmndZbBindState(void) { +void CmndZbBindState_or_Map(uint16_t zdo_cmd) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } @@ -950,7 +946,7 @@ void CmndZbBindState(void) { #ifdef USE_ZIGBEE_ZNP SBuffer buf(10); buf.add8(Z_SREQ | Z_ZDO); // 25 - buf.add8(ZDO_MGMT_BIND_REQ); // 33 + buf.add8(zdo_cmd); // 33 buf.add16(shortaddr); // shortaddr buf.add8(index); // StartIndex = 0 @@ -962,12 +958,28 @@ void CmndZbBindState(void) { // ZDO message payload (see Zigbee spec 2.4.3.3.4) uint8_t buf[] = { index }; // index = 0 - EZ_SendZDO(shortaddr, ZDO_Mgmt_Bind_req, buf, sizeof(buf)); + EZ_SendZDO(shortaddr, zdo_cmd, buf, sizeof(buf)); #endif // USE_ZIGBEE_EZSP ResponseCmndDone(); } +// +// Command `ZbBindState` +// `ZbBindState` as index if it does not fit. If default, `1` starts at the beginning +// +void CmndZbBindState(void) { + CmndZbBindState_or_Map(ZDO_Mgmt_Bind_req); +} + +// +// Command `ZbMap` +// `ZbMap` as index if it does not fit. If default, `1` starts at the beginning +// +void CmndZbMap(void) { + CmndZbBindState_or_Map(ZDO_Mgmt_Lqi_req); +} + // Probe a specific device to get its endpoints and supported clusters void CmndZbProbe(void) { CmndZbProbeOrPing(true);