From ae5330d09026e8737722ebff58337bdee991688c Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Fri, 17 Jan 2020 22:51:37 +0100 Subject: [PATCH] Update converters. --- lib/extension/deviceReceive.js | 3 ++- lib/extension/homeassistant.js | 1 + npm-shrinkwrap.json | 24 ++++++++++++------------ package.json | 4 ++-- test/bridgeConfig.test.js | 2 +- test/controller.test.js | 4 ++-- test/deviceReceive.test.js | 26 +++++++++++++++++--------- test/entityPublish.test.js | 16 ++++++++++++++++ test/stub/data.js | 6 ++++++ test/stub/zigbeeHerdsman.js | 13 +++++++++++-- 10 files changed, 70 insertions(+), 29 deletions(-) diff --git a/lib/extension/deviceReceive.js b/lib/extension/deviceReceive.js index 06c5e44d1..371721e7a 100755 --- a/lib/extension/deviceReceive.js +++ b/lib/extension/deviceReceive.js @@ -164,10 +164,11 @@ class DeviceReceive extends BaseExtension { } }; + const meta = {device: data.device}; let payload = {}; converters.forEach((converter) => { const options = {...settings.get().device_options, ...settings.getDevice(data.device.ieeeAddr)}; - const converted = converter.convert(mappedDevice, data, publish, options); + const converted = converter.convert(mappedDevice, data, publish, options, meta); if (converted) { payload = {...payload, ...converted}; } diff --git a/lib/extension/homeassistant.js b/lib/extension/homeassistant.js index 76af73145..eb0aff22c 100644 --- a/lib/extension/homeassistant.js +++ b/lib/extension/homeassistant.js @@ -1156,6 +1156,7 @@ const mapping = { '9290018215': [cfg.light_brightness], '1743230P7': [cfg.light_brightness_colortemp_colorxy], '100.110.51': [cfg.light_brightness_colortemp], + 'U86KCJ-ZP': [cfg.sensor_action], }; Object.keys(mapping).forEach((key) => { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index d68c0282f..f36c825e0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -6036,9 +6036,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6726,9 +6726,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6894,9 +6894,9 @@ } }, "zigbee-herdsman": { - "version": "0.12.31", - "resolved": "https://registry.npmjs.org/zigbee-herdsman/-/zigbee-herdsman-0.12.31.tgz", - "integrity": "sha512-kFEpDlq1WjRIn7GgLm52xC2NGoyBbpF8tPc3BPoIGJ1F2imCvhyCXJr29h77u2i/Wb9he9VY1DMdFctsPwE0OQ==", + "version": "0.12.32", + "resolved": "https://registry.npmjs.org/zigbee-herdsman/-/zigbee-herdsman-0.12.32.tgz", + "integrity": "sha512-rgXP/OzrrBgfKoabELNVsBisfLelj+9EEPvi6ziCcf7/cmCyLC/3ItcX+bgHAwbOzx7SfT9mrxtBWrtmP0eeVg==", "requires": { "debug": "^4.1.1", "fast-deep-equal": "^2.0.1", @@ -13465,9 +13465,9 @@ } }, "zigbee-herdsman-converters": { - "version": "11.2.8", - "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-11.2.8.tgz", - "integrity": "sha512-94hdspJdHtamOB7tciVeIZDtfX2Z+FTu5QfzyoQvQ9yUbdExPbPPb+9dCSKDXU0E/whwXJMQbd6gc/Lq8kyIyw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/zigbee-herdsman-converters/-/zigbee-herdsman-converters-12.0.0.tgz", + "integrity": "sha512-BtW0xQEIq3g9ExGYjLmU5Nxz4LMphSwOaTTaZK/ZKnzs/gWsWEGYIkSMBZ3Lt0ijUCFV6Vl4V3lu+7bciyw6xA==", "dependencies": { "@babel/code-frame": { "version": "7.5.5", diff --git a/package.json b/package.json index ea098f165..f1763b949 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,8 @@ "rimraf": "*", "semver": "*", "winston": "*", - "zigbee-herdsman": "0.12.31", - "zigbee-herdsman-converters": "11.2.8" + "zigbee-herdsman": "0.12.32", + "zigbee-herdsman-converters": "12.0.0" }, "devDependencies": { "eslint": "*", diff --git a/test/bridgeConfig.test.js b/test/bridgeConfig.test.js index d2e2bd988..6cf63c5c5 100644 --- a/test/bridgeConfig.test.js +++ b/test/bridgeConfig.test.js @@ -162,7 +162,7 @@ describe('Bridge config', () => { const payload = JSON.parse(MQTT.publish.mock.calls[0][1]); expect(payload.length).toStrictEqual(Object.values(zigbeeHerdsman.devices).length); expect(payload[0]).toStrictEqual({"ieeeAddr": "0x00124b00120144ae", "type": "Coordinator", "dateCode": "20190425", "friendly_name": "Coordinator", networkAddress: 0, softwareBuildID: "z-Stack", lastSeen: 100}); - expect(payload[1]).toStrictEqual({"friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "type": "Router"}); + expect(payload[1]).toStrictEqual({"dateCode": null, "friendly_name": "bulb", "ieeeAddr": "0x000b57fffec6a5b2", "lastSeen": 1000, "manufacturerID": 4476, "model": "LED1545G12", "modelID": "TRADFRI bulb E27 WS opal 980lm", "networkAddress": 40369, "powerSource": "Mains (single phase)", "type": "Router"}); Date.now = now; }); diff --git a/test/controller.test.js b/test/controller.test.js index d393e5551..f72954c54 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -419,12 +419,12 @@ describe('Controller', () => { MQTT.publish.mockClear(); await controller.publishEntityState('bulb', {state: 'ON'}); await flushPromises(); - expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"device":{"friendlyName":"bulb","model":"LED1545G12","ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369,"type":"Router","manufacturerID":4476,"powerSource":"Mains (single phase)"}}', {"qos": 0, "retain": true}, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"device":{"friendlyName":"bulb","model":"LED1545G12","ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369,"type":"Router","manufacturerID":4476,"powerSource":"Mains (single phase)","dateCode":null}}', {"qos": 0, "retain": true}, expect.any(Function)); // Unsupported device should have model "unknown" await controller.publishEntityState('unsupported2', {state: 'ON'}); await flushPromises(); - expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', '{"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery"}}', {"qos": 0, "retain": false}, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', '{"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null}}', {"qos": 0, "retain": false}, expect.any(Function)); }); it('Publish entity state no empty messages', async () => { diff --git a/test/deviceReceive.test.js b/test/deviceReceive.test.js index e57bde430..ce2bb8dfb 100755 --- a/test/deviceReceive.test.js +++ b/test/deviceReceive.test.js @@ -388,16 +388,24 @@ describe('Device receive', () => { expect(logger.debug).toHaveBeenCalledWith('No converter available for \'ZNCZ02LM\' with cluster \'genBinaryOutput\' and type \'attributeReport\' and data \'{"inactiveText":"hello"}\''); }); - it('Should allow to calibrate temperature values', async () => { - const device = zigbeeHerdsman.devices.WSDCGQ11LM; - settings.set(['devices', device.ieeeAddr, 'temperature_calibration'], 2); - const data = {measuredValue: -85} - const payload = {data, cluster: 'msTemperatureMeasurement', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10}; - await zigbeeHerdsman.events.message(payload); + it('Should report correct energy and power values for different versions of SP600', async () => { + // https://github.com/Koenkk/zigbee-herdsman-converters/issues/915, OLD and NEW use different date code + // divisor of OLD is not correct and therefore underreports by factor 10. + const data = {instantaneousDemand:496,currentSummDelivered:[0,6648]} + + const SP600_NEW = zigbeeHerdsman.devices.SP600_NEW; + await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_NEW, endpoint: SP600_NEW.getEndpoint(1), type: 'attributeReport', linkquality: 10}); await flushPromises(); expect(MQTT.publish).toHaveBeenCalledTimes(1); - expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor'); - expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({temperature: 1.15, linkquality: 10}); - expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 1, "retain": false}); + expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_NEW'); + expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({energy: 0.66, power: 49.6, linkquality: 10}); + + MQTT.publish.mockClear(); + const SP600_OLD = zigbeeHerdsman.devices.SP600_OLD; + await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_OLD, endpoint: SP600_OLD.getEndpoint(1), type: 'attributeReport', linkquality: 10}); + await flushPromises(); + expect(MQTT.publish).toHaveBeenCalledTimes(1); + expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD'); + expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({energy: 6.648, power: 496, linkquality: 10}); }); }); diff --git a/test/entityPublish.test.js b/test/entityPublish.test.js index 62d63fa24..837d912e0 100644 --- a/test/entityPublish.test.js +++ b/test/entityPublish.test.js @@ -848,6 +848,22 @@ describe('Entity publish', () => { expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false}); }); + it('Should publish brightness_move up to zigbee devices', async () => { + const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1); + await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_move: -40})); + await flushPromises(); + expect(endpoint.command).toHaveBeenCalledTimes(1); + expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveWithOnOff", {"movemode": 1, "rate": 40}, {}); + }); + + it('Should publish brightness_move down to zigbee devices', async () => { + const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1); + await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_move: 30})); + await flushPromises(); + expect(endpoint.command).toHaveBeenCalledTimes(1); + expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveWithOnOff", {"movemode": 0, "rate": 30}, {}); + }); + it('HS2WD-E burglar warning', async () => { const endpoint = zigbeeHerdsman.devices.HS2WD.getEndpoint(1); const payload = {warning: {duration: 100, mode: 'burglar', strobe: true, level: 'high'}}; diff --git a/test/stub/data.js b/test/stub/data.js index 7e3e1269a..dd6d392dc 100644 --- a/test/stub/data.js +++ b/test/stub/data.js @@ -136,6 +136,12 @@ function writeDefaultConfiguration() { '0x90fd9ffffe4b64ax': { friendly_name: 'ZNLDP12LM', }, + '0x90fd9ffffe4b64aa': { + friendly_name: 'SP600_OLD', + }, + '0x90fd9ffffe4b64ab': { + friendly_name: 'SP600_NEW', + }, }, groups: { '1': { diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index c59049181..91e8dd62a 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -20,8 +20,9 @@ const clusters = { } class Endpoint { - constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[]) { + constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[], clusterValues={}) { this.deviceIeeeAddress = deviceIeeeAddress; + this.clusterValues = clusterValues; this.ID = ID; this.inputClusters = inputClusters; this.outputClusters = outputClusters; @@ -60,13 +61,18 @@ class Endpoint { this.removeFromAllGroups = () => { Object.values(groups).forEach((g) => this.removeFromGroup(g)) } + + this.getClusterAttributeValue = (cluster, value) => { + return this.clusterValues[cluster][value]; + } } } class Device { - constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName) { + constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName, dateCode= null) { this.type = type; this.ieeeAddr = ieeeAddr; + this.dateCode = dateCode; this.networkAddress = networkAddress; this.manufacturerID = manufacturerID; this.endpoints = endpoints; @@ -130,6 +136,9 @@ const devices = { 'tradfri_remote': new Device('EndDevice', '0x90fd9ffffe4b64ae', 33906, 4476, [new Endpoint(1, [0], [0,3,4,6,8,5], '0x90fd9ffffe4b64ae')], true, "Battery", "TRADFRI remote control"), 'roller_shutter': new Device('EndDevice', '0x90fd9ffffe4b64af', 33906, 4476, [new Endpoint(1, [0], [0,3,4,6,8,5], '0x90fd9ffffe4b64af')], true, "Battery", "SCM-R_00.00.03.15TC"), 'ZNLDP12LM': new Device('Router', '0x90fd9ffffe4b64ax', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64ax')], true, "Mains (single phase)", "lumi.light.aqcn02"), + 'SP600_OLD': new Device('Router', '0x90fd9ffffe4b64aa', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64aa', [], {seMetering: {"multiplier":1,"divisor":10000}})], true, "Mains (single phase)", "SP600", false, 'Salus', '20160120'), + 'SP600_NEW': new Device('Router', '0x90fd9ffffe4b64ab', 33901, 4476, [new Endpoint(1, [0,4,3,5,10,258,13,19,6,1,1030,8,768,1027,1029,1026], [0,3,4,6,8,5], '0x90fd9ffffe4b64aa', [], {seMetering: {"multiplier":1,"divisor":10000}})], true, "Mains (single phase)", "SP600", false, 'Salus', '20170220'), + } const groups = {