From 64611b723d0f67ce8ba92632c504a85c3b17a831 Mon Sep 17 00:00:00 2001 From: atrovato <1839717+atrovato@users.noreply.github.com> Date: Sat, 4 Dec 2021 09:21:46 +0100 Subject: [PATCH] Fix NaN device state --- server/lib/device/device.saveState.js | 5 ++++ .../20211204080815-clean-nan-device-states.js | 12 +++++++++ .../zigbee2mqtt/utils/convertValue.js | 3 +-- .../test/lib/device/device.saveState.test.js | 26 +++++++++++++++++++ .../zigbee2mqtt/utils/convertValue.test.js | 9 ++++--- 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 server/migrations/20211204080815-clean-nan-device-states.js diff --git a/server/lib/device/device.saveState.js b/server/lib/device/device.saveState.js index 430263d1eb..845dad8a46 100644 --- a/server/lib/device/device.saveState.js +++ b/server/lib/device/device.saveState.js @@ -1,6 +1,7 @@ const db = require('../../models'); const logger = require('../../utils/logger'); const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../utils/constants'); +const { BadParameters } = require('../../utils/coreErrors'); /** * @description Save new device feature state in DB. @@ -13,6 +14,10 @@ const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../utils/constants'); * }, 12); */ async function saveState(deviceFeature, newValue) { + if (Number.isNaN(newValue)) { + throw new BadParameters(`device.saveState of NaN value on ${deviceFeature.selector}`); + } + logger.debug(`device.saveState of deviceFeature ${deviceFeature.selector}`); const now = new Date(); const previousDeviceFeature = this.stateManager.get('deviceFeature', deviceFeature.selector); diff --git a/server/migrations/20211204080815-clean-nan-device-states.js b/server/migrations/20211204080815-clean-nan-device-states.js new file mode 100644 index 0000000000..7bb9a79be0 --- /dev/null +++ b/server/migrations/20211204080815-clean-nan-device-states.js @@ -0,0 +1,12 @@ +const db = require('../models'); + +module.exports = { + up: async () => { + await db.DeviceFeatureState.destroy({ + where: { + value: NaN, + }, + }); + }, + down: async () => {}, +}; diff --git a/server/services/zigbee2mqtt/utils/convertValue.js b/server/services/zigbee2mqtt/utils/convertValue.js index 7abaec269e..39cd11794a 100644 --- a/server/services/zigbee2mqtt/utils/convertValue.js +++ b/server/services/zigbee2mqtt/utils/convertValue.js @@ -47,8 +47,7 @@ function convertValue(feature, value) { result = value ? 1 : 0; break; } - case 'number': - case 'string': { + case 'number': { result = value; break; } diff --git a/server/test/lib/device/device.saveState.test.js b/server/test/lib/device/device.saveState.test.js index 71a6218de9..b50413106d 100644 --- a/server/test/lib/device/device.saveState.test.js +++ b/server/test/lib/device/device.saveState.test.js @@ -1,6 +1,8 @@ +const { expect } = require('chai'); const { assert, stub, useFakeTimers } = require('sinon'); const Device = require('../../../lib/device'); const StateManager = require('../../../lib/state'); +const { BadParameters } = require('../../../utils/coreErrors'); describe('Device.saveState', () => { it('should saveState and keep history', async () => { @@ -116,4 +118,28 @@ describe('Device.saveState', () => { } await Promise.all(promises); }); + it('should not save NaN as state', async () => { + const event = { + on: stub().returns(null), + }; + const stateManager = new StateManager(event); + const device = new Device(event, {}, stateManager); + + const nanValue = parseInt('NaN value', 10); + + try { + await device.saveState( + { + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + selector: 'test-device-feature', + has_feedback: false, + keep_history: false, + }, + nanValue, + ); + assert.fail('NaN device state should fail'); + } catch (e) { + expect(e).instanceOf(BadParameters); + } + }); }); diff --git a/server/test/services/zigbee2mqtt/utils/convertValue.test.js b/server/test/services/zigbee2mqtt/utils/convertValue.test.js index f4c14b7361..36f132e135 100644 --- a/server/test/services/zigbee2mqtt/utils/convertValue.test.js +++ b/server/test/services/zigbee2mqtt/utils/convertValue.test.js @@ -43,9 +43,12 @@ describe('zigbee2mqtt convertValue', () => { const result = convertValue('unknown feature', 4); return assert.deepEqual(result, 4); }); - it('should return unknown feature string', () => { - const result = convertValue('unknown feature', 'closed'); - return assert.equal(result, 'closed'); + it('should throw exception on string value', () => { + assert.throw( + () => convertValue('unknown feature', 'closed'), + Error, + `Zigbee2mqqt don't handle value "closed" for feature "unknown feature".`, + ); }); it('should throw Exception', () => { assert.throw(