From bee722c9c4a934761c2bef9c404f582093f84ec0 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 18 Mar 2019 17:33:13 +0100 Subject: [PATCH] Skip re-transmitted Xiaomi messages. #1238 --- lib/extension/deviceReceive.js | 19 +++++++++++++++++++ lib/util/utils.js | 1 + test/deviceReceive.test.js | 16 ++++++++++++++++ test/utils.js | 4 ++-- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/extension/deviceReceive.js b/lib/extension/deviceReceive.js index a28f8144f..0a2a669cb 100644 --- a/lib/extension/deviceReceive.js +++ b/lib/extension/deviceReceive.js @@ -80,6 +80,25 @@ class DeviceReceive { return; } + /** + * Don't handle re-transmitted Xiaomi messages. + * https://github.com/Koenkk/zigbee2mqtt/issues/1238 + * + * Some Xiaomi router devices re-transmit messages from Xiaomi end devices. + * The source address of these message is set to the one of the Xiaomi router. + * Therefore it looks like if the message came from the Xiaomi router, while in + * fact it came from the end device. + * Handling these message would result in false state updates. + * The group ID attribute of these message defines the source address of the end device. + * As the same message is also received directly from the end device, it makes no sense + * to handle these messages. + */ + const hasGroupID = message.hasOwnProperty('groupid') && message.groupid != 0; + if (utils.isXiaomiDevice(device) && utils.isRouter(device) && hasGroupID) { + logger.debug('Skipping re-transmitted Xiaomi message'); + return; + } + // Find a conveter for this message. const cid = message.data.cid; const cmdId = message.data.cmdId; diff --git a/lib/util/utils.js b/lib/util/utils.js index 671b31f0c..6bbf084b4 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -94,6 +94,7 @@ module.exports = { secondsToMilliseconds: (seconds) => seconds * 1000, isXiaomiDevice: (device) => xiaomiManufacturerID.includes(device.manufId), isIkeaTradfriDevice: (device) => ikeaTradfriManufacturerID.includes(device.manufId), + isRouter: (device) => device.type === 'Router', isNumeric: (string) => /^\d+$/.test(string), toLocalISOString: (dDate) => toLocalISOString(dDate), getPostfixes: () => postfixes, diff --git a/test/deviceReceive.test.js b/test/deviceReceive.test.js index ef42f383e..93a8af93a 100644 --- a/test/deviceReceive.test.js +++ b/test/deviceReceive.test.js @@ -10,6 +10,7 @@ const WXKG11LM = devices.find((d) => d.model === 'WXKG11LM'); const WXKG02LM = devices.find((d) => d.model === 'WXKG02LM'); const WSDCGQ11LM = devices.find((d) => d.model === 'WSDCGQ11LM'); const RTCGQ11LM = devices.find((d) => d.model === 'RTCGQ11LM'); +const ZNCZ02LM = devices.find((d) => d.model === 'ZNCZ02LM'); const mqtt = { log: () => {}, @@ -248,5 +249,20 @@ describe('DeviceReceive', () => { expect(publishEntityState).toHaveBeenCalledTimes(1); expect(typeof publishEntityState.mock.calls[0][1].last_seen).toBe('string'); }); + + it('Should not handle messages forwarded Xiaomi messages', () => { + const device = {ieeeAddr: '0x12345678', manufId: 4151, type: 'Router'}; + const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1, 599); + deviceReceive.onZigbeeMessage(message, device, ZNCZ02LM); + expect(publishEntityState).toHaveBeenCalledTimes(0); + }); + + it('Should handle messages from Xiaomi router devices', () => { + const device = {ieeeAddr: '0x12345678', manufId: 4151, type: 'Router'}; + const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}); + deviceReceive.onZigbeeMessage(message, device, ZNCZ02LM); + expect(publishEntityState).toHaveBeenCalledTimes(1); + expect(publishEntityState.mock.calls[0][1]).toStrictEqual({state: 'ON'}); + }); }); }); diff --git a/test/utils.js b/test/utils.js index 756838f94..a25b18863 100644 --- a/test/utils.js +++ b/test/utils.js @@ -7,7 +7,7 @@ module.exports = { jest.spyOn(logger, 'debug').mockReturnValue(undefined); jest.spyOn(logger, 'error').mockReturnValue(undefined); }, - zigbeeMessage: (device, cid, type, data, epId) => { - return {data: {cid, data}, type, endpoints: [{device, epId}]}; + zigbeeMessage: (device, cid, type, data, epId, groupid=0) => { + return {data: {cid, data}, type, groupid, endpoints: [{device, epId}]}; }, };