diff --git a/lib/extension/deviceReceive.js b/lib/extension/deviceReceive.js index 58ee23bb..25598dcd 100644 --- a/lib/extension/deviceReceive.js +++ b/lib/extension/deviceReceive.js @@ -1,7 +1,7 @@ const settings = require('../util/settings'); const logger = require('../util/logger'); -const dontCacheProperties = ['action', 'button', 'button_left', 'button_right', 'click', 'forgotten', 'keyerror']; +const statelessProperties = ['action', 'button', 'button_left', 'button_right', 'click', 'forgotten', 'keyerror']; /** * This extensions handles messages received from devices. @@ -100,11 +100,18 @@ class DeviceReceive { // - If NO payload is returned do nothing. This is for non-standard behaviour // for e.g. click switches where we need to count number of clicks and detect long presses. const publish = (payload) => { - // Don't cache messages with following properties: + // Don't cache messages with stateless properties. + // After a stateless property is send we need to clear it. + // Otherwise the state of the device is incorrect. + // E.g. when a button is pressed a payload like {"click": "single"} + // is produced, afterwards we want to clear this with {"click": "idle"} + // https://github.com/Koenkk/zigbee2mqtt/issues/959 let cache = true; - dontCacheProperties.forEach((property) => { + const clearStatePayload = {}; + statelessProperties.forEach((property) => { if (payload.hasOwnProperty(property)) { cache = false; + clearStatePayload[property] = 'idle'; } }); @@ -133,6 +140,9 @@ class DeviceReceive { } this.publishEntityState(device.ieeeAddr, payload, cache); + if (Object.keys(clearStatePayload).length > 0) { + this.publishEntityState(device.ieeeAddr, clearStatePayload, false); + } }; let payload = {}; diff --git a/test/deviceReceive.test.js b/test/deviceReceive.test.js index 1d2e9ef8..b90cee79 100644 --- a/test/deviceReceive.test.js +++ b/test/deviceReceive.test.js @@ -36,24 +36,27 @@ describe('DeviceReceive', () => { const device = {ieeeAddr: '0x12345678'}; const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1); deviceReceive.onZigbeeMessage(message, device, WXKG11LM); - chai.assert.isTrue(publishEntityState.calledOnce); + chai.assert.isTrue(publishEntityState.calledTwice); chai.assert.deepEqual(publishEntityState.getCall(0).args[1], {click: 'single'}); + chai.assert.deepEqual(publishEntityState.getCall(1).args[1], {click: 'idle'}); }); it('Should handle a zigbee message which uses ep (left)', () => { const device = {ieeeAddr: '0x12345678'}; const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 1); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); - chai.assert.isTrue(publishEntityState.calledOnce); + chai.assert.isTrue(publishEntityState.calledTwice); chai.assert.deepEqual(publishEntityState.getCall(0).args[1], {click: 'left'}); + chai.assert.deepEqual(publishEntityState.getCall(1).args[1], {click: 'idle'}); }); it('Should handle a zigbee message which uses ep (right)', () => { const device = {ieeeAddr: '0x12345678'}; const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}, 2); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); - chai.assert.isTrue(publishEntityState.calledOnce); + chai.assert.isTrue(publishEntityState.calledTwice); chai.assert.deepEqual(publishEntityState.getCall(0).args[1], {click: 'right'}); + chai.assert.deepEqual(publishEntityState.getCall(1).args[1], {click: 'idle'}); }); it('Should handle a zigbee message with default precision', () => { @@ -207,7 +210,7 @@ describe('DeviceReceive', () => { }; }); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); - chai.assert.isTrue(publishEntityState.calledOnce); + chai.assert.isTrue(publishEntityState.calledTwice); chai.assert.equal(typeof publishEntityState.getCall(0).args[1].last_seen, 'number'); }); @@ -222,7 +225,7 @@ describe('DeviceReceive', () => { }; }); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); - chai.assert.isTrue(publishEntityState.calledOnce); + chai.assert.isTrue(publishEntityState.calledTwice); chai.assert.equal(typeof publishEntityState.getCall(0).args[1].last_seen, 'string'); }); });