From 6a6454d8ab12a88cd1fbcaa3ba1aa01473b89442 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sat, 9 Jan 2021 17:58:57 +0100 Subject: [PATCH] Zigbee debounce duplicate commands (#10477) * Zigbee debounce duplicate commands received from the same device within ``USE_ZIGBEE_DEBOUNCE_COMMANDS`` milliseconds * Zigbee debounce duplicate commands received from the same device within ``USE_ZIGBEE_DEBOUNCE_COMMANDS`` milliseconds Co-authored-by: Stephan Hadinger --- CHANGELOG.md | 1 + tasmota/my_user_config.h | 1 + tasmota/xdrv_23_zigbee_2_devices.ino | 10 +++++++- tasmota/xdrv_23_zigbee_5_converters.ino | 32 ++++++++++++++++++++----- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80ae57d59..1c896080b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control (#10412) - Support rotary encoder on Shelly Dimmer (#10407) - Command ``SetOption43 1..100`` to control Rotary step (#10407) +- Zigbee debounce duplicate commands received from the same device within ``USE_ZIGBEE_DEBOUNCE_COMMANDS`` milliseconds ### Breaking Changed - ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 44415794f..8c0c7cac3 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -746,6 +746,7 @@ #define USE_ZIGBEE_TXRADIO_DBM 20 // Tx Radio power in dBm (only for EZSP, EFR32 can go up to 20 dBm) #define USE_ZIGBEE_COALESCE_ATTR_TIMER 350 // timer to coalesce attribute values (in ms) + #define USE_ZIGBEE_DEBOUNCE_COMMANDS 200 // if commands are received from the same device/endpoint with same ZCL transaction number, discard packet in this time window (ms) #define USE_ZIGBEE_MODELID "Tasmota Z2T" // reported "ModelId" (cluster 0000 / attribute 0005) #define USE_ZIGBEE_MANUFACTURER "Tasmota" // reported "Manufacturer" (cluster 0000 / attribute 0004) #define USE_ZBBRIDGE_TLS // TLS support for zbbridge diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index e03e27d3d..d2515cd14 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -730,6 +730,11 @@ public: uint8_t lqi; // lqi from last message, 0xFF means unknown uint8_t batterypercent; // battery percentage (0..100), 0xFF means unknwon uint16_t reserved_for_alignment; + // Debounce informmation when receiving commands + // If we receive the same ZCL transaction number from the same device and the same endpoint within 300ms + // then discard the second packet + uint8_t debounce_endpoint; // endpoint of the last received packet, or 0 if not active (expired) + uint8_t debounce_transact; // ZCL transaction number of the last packet // END OF DEVICE WIDE DATA // Constructor with all defaults @@ -750,7 +755,9 @@ public: last_seen(0), lqi(0xFF), batterypercent(0xFF), - reserved_for_alignment(0xFFFF) + reserved_for_alignment(0xFFFF), + debounce_endpoint(0), + debounce_transact(0) { }; inline bool valid(void) const { return BAD_SHORTADDR != shortaddr; } // is the device known, valid and found? @@ -847,6 +854,7 @@ typedef enum Z_Def_Category { Z_CAT_BIND, // send auto-binding to coordinator Z_CAT_CONFIG_ATTR, // send a config attribute reporting request Z_CAT_READ_ATTRIBUTE, // read a single attribute + Z_CAT_DEBOUNCE_CMD, // debounce incoming commands Z_CAT_CIE_ATTRIBUTE, // write CIE address Z_CAT_CIE_ENROLL, // enroll CIE zone } Z_Def_Category; diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 9d2db36e5..cbd03603f 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1606,14 +1606,34 @@ void ZCLFrame::parseResponse(void) { parseResponse_inner(cmd, true, status); } +/*********************************************************************************************\ + * Callbacks +\*********************************************************************************************/ +// Reset the debounce marker +void Z_ResetDebounce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + zigbee_devices.getShortAddr(shortaddr).debounce_endpoint = 0; +} + // Parse non-normalized attributes void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { - convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); - if (!Settings.flag5.zb_disable_autoquery) { - // read attributes unless disabled - if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator) - if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target - sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); + // Check if debounce is active and if the packet is a duplicate + Z_Device & device = zigbee_devices.getShortAddr(_srcaddr); + if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == _transact_seq)) { + // this is a duplicate, drop the packet + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), _srcaddr, _srcendpoint); + } else { + // reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint) + device.debounce_endpoint = _srcendpoint; + device.debounce_transact = _transact_seq; + zigbee_devices.setTimer(_srcaddr, 0 /* groupaddr */, USE_ZIGBEE_DEBOUNCE_COMMANDS, 0 /*clusterid*/, _srcendpoint, Z_CAT_DEBOUNCE_CMD, 0, &Z_ResetDebounce); + + convertClusterSpecific(attr_list, _cluster_id, _cmd_id, _frame_control.b.direction, _srcaddr, _srcendpoint, _payload); + if (!Settings.flag5.zb_disable_autoquery) { + // read attributes unless disabled + if (!_frame_control.b.direction) { // only handle server->client (i.e. device->coordinator) + if (_wasbroadcast) { // only update for broadcast messages since we don't see unicast from device to device and we wouldn't know the target + sendHueUpdate(BAD_SHORTADDR, _groupaddr, _cluster_id); + } } } }