diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a591a2c6..a03f301f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - Command ``L1MusicSync <0|Off>|<1|On>|<2|Toggle>, 1..10, 1..100>`` to control Sonoff L1 Music Sync mode sensitivity and speed (#10722) - Command ``Speed2`` to control a once off fade (#10741) - Zigbee command ``SetOption120 1`` or ``ZbEndpointTopic 1`` to add the endpoint as suffix in topic when using ``SetOption89 1`` +- Zigbee command ``ZbScan`` to do an energy scan on each radio channel ### Changed - Maximum chars in ``AddLog_P`` logging restored from 128 to 700 (MAX_LOGSZ) to solve broken error messages diff --git a/tasmota/i18n.h b/tasmota/i18n.h index daf55043e..710061e51 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -615,6 +615,8 @@ #define D_CMND_ZIGBEE_CONFIG "Config" #define D_JSON_ZIGBEE_CONFIG "Config" #define D_CMND_ZIGBEE_DATA "Data" +#define D_CMND_ZIGBEE_SCAN "Scan" + #define D_JSON_ZIGBEE_SCAN "ZbScan" // Commands xdrv_25_A4988_Stepper.ino #define D_CMND_MOTOR "MOTOR" diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index de7bbff39..0ec1a7af3 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -23,6 +23,11 @@ #include "Eeprom24C512.h" #endif // USE_ZIGBEE_EZSP +// channels numbers for Zigbee radio energy scan +#define USE_ZIGBEE_CHANNEL_MIN 11 +#define USE_ZIGBEE_CHANNEL_MAX 26 +#define USE_ZIGBEE_CHANNEL_COUNT (USE_ZIGBEE_CHANNEL_MAX - USE_ZIGBEE_CHANNEL_MIN + 1) + // contains some definitions for functions used before their declarations // @@ -123,6 +128,9 @@ public: ZB_RecvMsgFunc recv_func = nullptr; // function to call when message is expected ZB_RecvMsgFunc recv_unexpected = nullptr; // function called when unexpected message is received + // Energy scan + int8_t energy[USE_ZIGBEE_CHANNEL_COUNT]; + #ifdef USE_ZIGBEE_EZSP uint32_t permit_end_time = 0; // timestamp when permit join ends uint16_t ezsp_version = 0; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index b10129f72..8232cc502 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -160,6 +160,60 @@ int32_t EZ_RouteError(int32_t res, const SBuffer &buf) { return -1; } +// +// Handle EZSP Energy Scan result +// +int32_t EZSP_EnergyScanResult(int32_t res, const SBuffer &buf) { + uint8_t channel = buf.get8(2); + int8_t energy = buf.get8(3); + + if ((channel >= USE_ZIGBEE_CHANNEL_MIN) && (channel <= USE_ZIGBEE_CHANNEL_MAX)) { + zigbee.energy[channel - USE_ZIGBEE_CHANNEL_MIN] = energy; + } + return -1; +} +// +// Handle EZSP Energy Scan complete +// +int32_t EZSP_EnergyScanComplete(int32_t res, const SBuffer &buf) { + // uint8_t channel = buf.get8(2); + uint8_t status = buf.get8(3); + + if (0 == status) { + EnergyScanResults(); + } + + return -1; +} + +// +// Dump energu scan results +// +void EnergyScanResults(void) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_SCAN "\":[")); + for (uint32_t i = 0; i < USE_ZIGBEE_CHANNEL_COUNT; i++) { + int8_t energy = zigbee.energy[i]; + + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%d\":%d"), i + USE_ZIGBEE_CHANNEL_MIN, energy); + + // display as log + static const int32_t bar_min = -87; + static const int32_t bar_max = 10; + static const uint32_t bar_count = 60; + char bar_str[bar_count + 1]; + uint32_t energy_unsigned = energy + 0x80; + + uint32_t bars = changeUIntScale(energy_unsigned, bar_min + 0x80, bar_max + 0x80, 0, bar_count); + for (uint32_t j = 0; j < bars; j++) { bar_str[j] = '#'; } + bar_str[bars] = 0; + + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Channel %2d: %s"), i + USE_ZIGBEE_CHANNEL_MIN, bar_str); + } + ResponseAppend_P(PSTR("]}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); +} + // // Handle a "permitJoining" incoming message // @@ -1817,19 +1871,18 @@ int32_t EZ_Recv_Default(int32_t res, const SBuffer &buf) { switch (ezsp_command_index) { case EZSP_incomingMessageHandler: return EZ_IncomingMessage(res, buf); - break; case EZSP_trustCenterJoinHandler: return EZ_ReceiveTCJoinHandler(res, buf); - break; case EZSP_incomingRouteErrorHandler: return EZ_RouteError(res, buf); - break; case EZSP_permitJoining: return EZ_PermitJoinRsp(res, buf); - break; case EZSP_messageSentHandler: return EZ_MessageSent(res, buf); - break; + case EZSP_energyScanResultHandler: + return EZSP_EnergyScanResult(res, buf); + case EZSP_scanCompleteHandler: + return EZSP_EnergyScanComplete(res, buf); } return -1; } diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino index 463782ad7..7f3a21d9b 100644 --- a/tasmota/xdrv_23_zigbee_9_serial.ino +++ b/tasmota/xdrv_23_zigbee_9_serial.ino @@ -570,6 +570,8 @@ void ZigbeeProcessInputEZSP(SBuffer &buf) { case EZSP_setConcentrator: // 1000 case EZSP_networkInit: // 1700 case EZSP_stackStatusHandler: // 1900 + case EZSP_startScan: // 1A00 + case EZSP_scanCompleteHandler: // 1C00 case EZSP_formNetwork: // 1E00 case EZSP_permitJoining: // 2200 case EZSP_getEui64: // 2600 @@ -580,6 +582,7 @@ void ZigbeeProcessInputEZSP(SBuffer &buf) { case EZSP_sendMulticast: // 3800 case EZSP_messageSentHandler: // 3F00 case EZSP_incomingMessageHandler: // 4500 + case EZSP_energyScanResultHandler: // 4800 case EZSP_setConfigurationValue: // 5300 case EZSP_setPolicy: // 5500 case 0x0059: // 5900 - supposedly removed by still happening diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 540b32278..925a35cd1 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -39,7 +39,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|" D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_OCCUPANCY "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" D_CMND_ZIGBEE_MAP "|" D_CMND_ZIGBEE_LEAVE "|" - D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA + D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA "|" D_CMND_ZIGBEE_SCAN ; SO_SYNONYMS(kZbSynonyms, @@ -60,7 +60,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId, &CmndZbLight, &CmndZbOccupancy, &CmndZbRestore, &CmndZbBindState, &CmndZbMap, CmndZbLeave, - &CmndZbConfig, CmndZbData, + &CmndZbConfig, &CmndZbData, &CmndZbScan, }; /********************************************************************************************/ @@ -1311,6 +1311,33 @@ void CmndZbSave(void) { ResponseCmndDone(); } +// +// Command `ZbScan` +// Run an energy scan +// +void CmndZbScan(void) { + if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } + + for (uint32_t i = 0; i < USE_ZIGBEE_CHANNEL_COUNT; i++) { + zigbee.energy[i] = -0x80; + } + +#ifdef USE_ZIGBEE_ZNP + ResponseCmndChar_P(PSTR("Unsupported command on ZNP")); + return; + #endif // USE_ZIGBEE_ZNP + +#ifdef USE_ZIGBEE_EZSP + SBuffer buf(8); + buf.add16(EZSP_startScan); + buf.add8(0x00); // EZSP_ENERGY_SCAN + buf.add32(0x07FFF800); // standard channels 11-26 + buf.add8(0x04); // duration 2 ^ 4 + ZigbeeEZSPSendCmd(buf.getBuffer(), buf.len()); + +#endif // USE_ZIGBEE_EZSP + ResponseCmndDone(); +} // Restore a device configuration previously exported via `ZbStatus2`` // Format: