Skip to content

Commit

Permalink
Skip re-transmitted Xiaomi messages. #1238
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenkk committed Mar 18, 2019
1 parent 36bba01 commit d75c431
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 2 deletions.
16 changes: 16 additions & 0 deletions lib/extension/deviceReceive.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ 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.
*/
if (utils.isXiaomiDevice(device) && utils.isRouter(device) && message.hasOwnProperty('groupid') && message.groupid != 0) {
logger.debug("Skipping re-transmitted Xiaomi message");
return;
}

// Find a conveter for this message.
const cid = message.data.cid;
const cmdId = message.data.cmdId;
Expand Down
1 change: 1 addition & 0 deletions lib/util/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions test/deviceReceive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => {},
Expand Down Expand Up @@ -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'});
});
});
});
4 changes: 2 additions & 2 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}]};
},
};

0 comments on commit d75c431

Please sign in to comment.