diff --git a/lib/controller.js b/lib/controller.js index 5b79cab0..8623c330 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -126,6 +126,8 @@ class Controller { } start() { + this.state.start(); + this.startupLogVersion(() => { this.zigbee.start(this.onZigbeeMessage, (error) => { if (error) { @@ -142,7 +144,7 @@ class Controller { this.extensions.filter((e) => e.stop).forEach((e) => e.stop()); // Wrap-up - this.state.save(); + this.state.stop(); this.mqtt.disconnect(); this.zigbee.stop(callback); } @@ -237,7 +239,7 @@ class Controller { return { ieeeAddr, - friendlyName: deviceSettings.friendly_name || '', + friendlyName: deviceSettings ? (deviceSettings.friendly_name || '') : '', type, nwkAddr, manufId, diff --git a/lib/state.js b/lib/state.js index abd04d31..03eca684 100644 --- a/lib/state.js +++ b/lib/state.js @@ -8,12 +8,29 @@ class State { constructor() { this.state = {}; this.file = data.joinPath('state.json'); + this.timer = null; + } + + start() { this._load(); // Save the state on every interval + this.clearTimer(); this.timer = setInterval(() => this.save(), saveInterval); } + clearTimer() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + } + + stop() { + this.clearTimer(); + this.save(); + } + _load() { if (fs.existsSync(this.file)) { try { diff --git a/test/controller.test.js b/test/controller.test.js new file mode 100644 index 00000000..5eb41fd6 --- /dev/null +++ b/test/controller.test.js @@ -0,0 +1,58 @@ +const chai = require('chai'); +const sinon = require('sinon'); +const Controller = require('../lib/controller'); +const settings = require('../lib/util/settings'); +const mqtt = require('../lib/mqtt'); +const utils = require('./utils'); +const sandbox = sinon.createSandbox(); + +describe('Controller', () => { + let controller; + let mqttPublish; + + beforeEach(() => { + utils.stubLogger(sandbox); + sandbox.stub(settings, 'getDevice').callsFake((ieeeAddr) => { + return {friendly_name: 'test'}; + }); + mqttPublish = sandbox.stub(mqtt.prototype, 'publish').callsFake(() => {}); + controller = new Controller(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('Handling zigbee messages', () => { + it('Should handle a zigbee message', () => { + const device = {ieeeAddr: '0x12345678', modelId: 'TRADFRI bulb E27 CWS opal 600lm'}; + const message = utils.zigbeeMessage(device, 'genOnOff', 'devChange', {onOff: 1}); + controller.onZigbeeMessage(message); + chai.assert.isTrue(mqttPublish.calledOnce); + chai.assert.strictEqual(mqttPublish.getCall(0).args[1], JSON.stringify({state: 'ON'})); + }); + + it('Should handle a zigbee message when include_device_information is set', () => { + sandbox.stub(settings, 'get').callsFake(() => { + return { + mqtt: { + include_device_information: true, + }, + advanced: { + cache_state: false, + }, + }; + }); + + const device = {ieeeAddr: '0x12345678', modelId: 'TRADFRI bulb E27 CWS opal 600lm'}; + const message = utils.zigbeeMessage(device, 'genOnOff', 'devChange', {onOff: 1}); + controller.onZigbeeMessage(message); + chai.assert.isTrue(mqttPublish.calledOnce); + chai.assert.strictEqual( + mqttPublish.getCall(0).args[1], + `{"state":"ON","device":{"ieeeAddr":"0x12345678","friendlyName":"test",` + + `"modelId":"TRADFRI bulb E27 CWS opal 600lm"}}` + ); + }); + }); +}); diff --git a/test/devicePublish.test.js b/test/devicePublish.test.js index 2cb050e1..ab7585b8 100644 --- a/test/devicePublish.test.js +++ b/test/devicePublish.test.js @@ -2,6 +2,8 @@ const chai = require('chai'); const sinon = require('sinon'); const DevicePublish = require('../lib/extension/devicePublish'); const settings = require('../lib/util/settings'); +const utils = require('./utils'); +const sandbox = sinon.createSandbox(); const mqtt = { subscribe: (topic) => {}, @@ -23,9 +25,14 @@ describe('DevicePublish', () => { let devicePublish; beforeEach(() => { + utils.stubLogger(sandbox); devicePublish = new DevicePublish(zigbee, mqtt, null, null); }); + afterEach(() => { + sandbox.restore(); + }); + describe('Parse topic', () => { it('Should publish messages to zigbee devices', () => { zigbee.publish.resetHistory(); @@ -196,7 +203,7 @@ describe('DevicePublish', () => { }); it('Should parse topic with when base topic has multiple slashes', () => { - const stub = sinon.stub(settings, 'get').callsFake(() => { + sandbox.stub(settings, 'get').callsFake(() => { return { mqtt: { base_topic: 'zigbee2mqtt/at/my/home', @@ -209,7 +216,6 @@ describe('DevicePublish', () => { chai.assert.strictEqual(parsed.type, 'get'); chai.assert.strictEqual(parsed.deviceID, 'my_device_id2'); chai.assert.strictEqual(parsed.postfix, ''); - stub.restore(); }); it('Should parse topic with when deviceID has multiple slashes', () => { @@ -221,7 +227,7 @@ describe('DevicePublish', () => { }); it('Should parse topic with when base and deviceID have multiple slashes', () => { - const stub = sinon.stub(settings, 'get').callsFake(() => { + sandbox.stub(settings, 'get').callsFake(() => { return { mqtt: { base_topic: 'zigbee2mqtt/at/my/basement', @@ -234,7 +240,6 @@ describe('DevicePublish', () => { chai.assert.strictEqual(parsed.type, 'set'); chai.assert.strictEqual(parsed.deviceID, 'floor0/basement/my_device_id2'); chai.assert.strictEqual(parsed.postfix, ''); - stub.restore(); }); it('Should parse set with ieeAddr topic', () => { @@ -278,7 +283,7 @@ describe('DevicePublish', () => { }); it('Should parse set with and slashes in base and deviceID postfix topic', () => { - const stub = sinon.stub(settings, 'get').callsFake(() => { + sandbox.stub(settings, 'get').callsFake(() => { return { mqtt: { base_topic: 'zigbee2mqtt/at/my/home', @@ -291,7 +296,6 @@ describe('DevicePublish', () => { chai.assert.strictEqual(parsed.type, 'get'); chai.assert.strictEqual(parsed.deviceID, 'my/device/in/basement/sensor'); chai.assert.strictEqual(parsed.postfix, 'bottom_left'); - stub.restore(); }); }); }); diff --git a/test/deviceReceive.test.js b/test/deviceReceive.test.js index 63bdc121..ac215d3d 100644 --- a/test/deviceReceive.test.js +++ b/test/deviceReceive.test.js @@ -2,8 +2,9 @@ const chai = require('chai'); const sinon = require('sinon'); const DeviceReceive = require('../lib/extension/deviceReceive'); const settings = require('../lib/util/settings'); -const logger = require('../lib/util/logger'); const devices = require('zigbee-shepherd-converters').devices; +const utils = require('./utils'); +const sandbox = sinon.createSandbox(); // Devices const WXKG11LM = devices.find((d) => d.model === 'WXKG11LM'); @@ -13,29 +14,25 @@ const mqtt = { log: () => {}, }; -const msg = (device, cid, type, data) => { - return {data: {cid: cid, data: data}, type: type, endpoints: [device]}; -}; - describe('DeviceReceive', () => { let deviceReceive; let publishDeviceState; - before(() => { - sinon.stub(settings, 'addDevice').callsFake(() => {}); - sinon.stub(logger, 'info').callsFake(() => {}); - sinon.stub(logger, 'warn').callsFake(() => {}); - }); - beforeEach(() => { + utils.stubLogger(sandbox); + sandbox.stub(settings, 'addDevice').callsFake(() => {}); publishDeviceState = sinon.spy(); deviceReceive = new DeviceReceive(null, mqtt, null, publishDeviceState); }); + afterEach(() => { + sandbox.restore(); + }); + describe('Handling zigbee messages', () => { it('Should handle a zigbee message', () => { const device = {ieeeAddr: '0x12345678'}; - const message = msg(device, 'genOnOff', 'attReport', {onOff: 1}); + const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}); deviceReceive.onZigbeeMessage(message, device, WXKG11LM); chai.assert.isTrue(publishDeviceState.calledOnce); chai.assert.deepEqual(publishDeviceState.getCall(0).args[1], {click: 'single'}); @@ -43,7 +40,7 @@ describe('DeviceReceive', () => { it('Should handle a zigbee message which uses ep (left)', () => { const device = {ieeeAddr: '0x12345678', epId: 1}; - const message = msg(device, 'genOnOff', 'attReport', {onOff: 1}); + const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); chai.assert.isTrue(publishDeviceState.calledOnce); chai.assert.deepEqual(publishDeviceState.getCall(0).args[1], {click: 'left'}); @@ -51,7 +48,7 @@ describe('DeviceReceive', () => { it('Should handle a zigbee message which uses ep (right)', () => { const device = {ieeeAddr: '0x12345678', epId: 2}; - const message = msg(device, 'genOnOff', 'attReport', {onOff: 1}); + const message = utils.zigbeeMessage(device, 'genOnOff', 'attReport', {onOff: 1}); deviceReceive.onZigbeeMessage(message, device, WXKG02LM); chai.assert.isTrue(publishDeviceState.calledOnce); chai.assert.deepEqual(publishDeviceState.getCall(0).args[1], {click: 'right'}); diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 00000000..8994981c --- /dev/null +++ b/test/utils.js @@ -0,0 +1,13 @@ +const logger = require('../lib/util/logger'); + +module.exports = { + stubLogger: (sandbox) => { + sandbox.stub(logger, 'info').callsFake(() => {}); + sandbox.stub(logger, 'warn').callsFake(() => {}); + sandbox.stub(logger, 'debug').callsFake(() => {}); + sandbox.stub(logger, 'error').callsFake(() => {}); + }, + zigbeeMessage: (device, cid, type, data) => { + return {data: {cid: cid, data: data}, type: type, endpoints: [{device: device}]}; + }, +};