From 55a1709103b449564fd0c90582fb86b6397b6688 Mon Sep 17 00:00:00 2001 From: ptvoinfo Date: Thu, 17 May 2018 10:52:28 +0300 Subject: [PATCH 01/10] Added polling for routers (prevents deep sleep mode of Xiaomi routers) Added more parsers for Xiaomi Power Plug Added a custom router --- lib/controller.js | 91 ++++++++++++++++++++++++++++++++++- lib/converters/zigbee2mqtt.js | 56 +++++++++++++++++++++ lib/devices.js | 8 +++ lib/zigbee.js | 19 ++++++-- 4 files changed, 170 insertions(+), 4 deletions(-) diff --git a/lib/controller.js b/lib/controller.js index a390b0a9b..c4ff95421 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -14,6 +14,11 @@ const mqttDevicePrefixRegex = new RegExp(`${settings.get().mqtt.base_topic}/\\w+ const issueLink = 'https://github.com/Koenkk/zigbee2mqtt/issues'; +function getTimestamp() { + var d = new Date(); + return d.getTime(); +} + class Controller { constructor() { this.zigbee = new Zigbee(); @@ -21,17 +26,26 @@ class Controller { this.stateCache = {}; this.handleZigbeeMessage = this.handleZigbeeMessage.bind(this); this.handleMQTTMessage = this.handleMQTTMessage.bind(this); + + this.checkOnlineTimer = null; + this.lastDeviceActivity = {}; // timestamps of last data/activity + this.lastControllerActivity = 0; } start() { this.zigbee.start(this.handleZigbeeMessage, (error) => { + this.lastDeviceActivity = {}; + if (error) { logger.error('Failed to start'); } else { // Log zigbee clients on startup. const devices = this.zigbee.getAllClients(); logger.info(`Currently ${devices.length} devices are joined:`); - devices.forEach((device) => logger.info(this.getDeviceStartupLogMessage(device))); + devices.forEach((device) => { + logger.info(this.getDeviceStartupLogMessage(device)) + this.setLastDeviceActivity(device.ieeeAddr); + }); // Connect to MQTT broker const subscriptions = [ @@ -62,10 +76,20 @@ class Controller { logger.warn('Set `permit_join` to `false` once you joined all devices.'); this.zigbee.permitJoin(true); } + + // Set timer at interval to check online status of Zigbee routers. + // For example, it prevents Xiaomi routers to go to a deep sleep mode + const interval = 1 * 1000; // seconds * 1000. + this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), interval); + this.lastControllerActivity = getTimestamp(); } stop(callback) { this.mqtt.disconnect(); + if(this.checkOnlineTimer) { + clearTimeout(this.checkOnlineTimer); + this.checkOnlineTimer = null; + } this.zigbee.stop(callback); } @@ -106,6 +130,8 @@ class Controller { return; } + this.setLastDeviceActivity(device.ieeeAddr); + // Check if this is a new device. if (!settings.getDevice(device.ieeeAddr)) { logger.info(`New device with address ${device.ieeeAddr} connected!`); @@ -257,6 +283,69 @@ class Controller { this.mqtt.publish(deviceSettings.friendly_name, JSON.stringify(payload), options); } + + setLastDeviceActivity(ieeeAddr) { + this.lastDeviceActivity[ieeeAddr] = getTimestamp(); + } + + zigbeeCheckOnline() { + var dt = getTimestamp(); + + //TO-DO: may be allow to configure the timeout + if ((dt - this.lastControllerActivity) > 3600000) { + // no data received in 1 hour. + // This problem may occur sometimes with CC2531 (USB devices can be pluged/unpluged by a PnP system) + // try to restart and self-recovery + this.checkOnlineTimer = null; + this.lastControllerActivity = dt; + this.lastDeviceActivity = {}; + logger.warn('Soft restart'); + this.zigbee.shepherd.reset('soft', (err) => { + if(err){ + logger.warn('Soft reset error:', err); + this.zigbee.stop( (err) => { + logger.warn('Stop:', err); + this.zigbee.start(this.zigbee.onMessage, () => {}); + }); + } + else{ + this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), 1000); + } + }); + return; + } + + var device, devInfo, devType, power, dev_desc; + for (device in this.lastDeviceActivity) { + if ((dt - this.lastDeviceActivity[device]) > 60000) { + this.lastDeviceActivity[device] = dt; + + devInfo = this.zigbee.shepherd._findDevByAddr(device); + if (devInfo) { + // battery powered endpoint devices are in the sleep mode most time + if(devInfo.powerSource){ + power = devInfo.powerSource.toLowerCase().split(' ')[0]; + } + else{ + power = 'unknown'; + } + devType = devInfo.type.toLowerCase(); + if ( + ((power !== 'battery') && (power !== 'unknown')) || + (devType === 'router') + ) { + dev_desc = this.getDeviceStartupLogMessage(devInfo); + logger.info('Data timeout for device:', dev_desc, ' Checking online status.'); + // note: checkOnline has the callback argument but does not call callback + this.zigbee.shepherd.controller.checkOnline(devInfo); + } + } + } + } + + this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), 1000); + return; + } } module.exports = Controller; diff --git a/lib/converters/zigbee2mqtt.js b/lib/converters/zigbee2mqtt.js index 96d4dac88..deb1ba3f2 100644 --- a/lib/converters/zigbee2mqtt.js +++ b/lib/converters/zigbee2mqtt.js @@ -309,6 +309,42 @@ const parsers = [ return {power: precisionRound(msg.data.data['presentValue'], 2)}; }, }, + { + devices: ['ZNCZ02LM'], + cid: 'genBasic', + type: 'attReport', + convert: (msg) => { + if(msg.data.data['65281']){ + const data = msg.data.data['65281']; + const result = { + state: data['100'] === 1 ? "ON" : "OFF", + power: precisionRound(data['152'], 2), + consumption: precisionRound(data['149'], 2), + temperature: precisionRound(data['3'], 2), + voltage: precisionRound(data['150'] * 0.1, 1), + }; + return result; + } + } + }, + { + devices: ['ZNCZ02LM'], + cid: 'genBasic', + type: 'devChange', + convert: (msg) => { + if(msg.data.data['65281']){ + const data = msg.data.data['65281']; + const result = { + state: data['100'] === 1 ? "ON" : "OFF", + power: precisionRound(data['152'], 2), + consumption: precisionRound(data['149'], 2), + temperature: precisionRound(data['3'], 2), + voltage: precisionRound(data['150'] * 0.1, 1), + }; + return result; + } + } + }, { devices: ['QBKG04LM'], cid: 'genOnOff', @@ -408,6 +444,26 @@ const parsers = [ type: 'devChange', convert: () => null, }, + { + devices: ['LUMI.ROUTER'], + cid: 'genOnOff', + type: 'attReport', + convert: (msg) => {return {state: msg.data.data['onOff'] === 1 ? "ON" : "OFF"}} + }, + { + devices: ['LUMI.ROUTER'], + cid: 'genBinaryValue', + type: 'attReport', + convert: (msg) => { + const data = msg.data.data; + const result = { + description: data['description'], + type: data['inactiveText'], + rssi: data['presentValue'] + }; + return result; + } + }, ]; module.exports = parsers; diff --git a/lib/devices.js b/lib/devices.js index 289bf04fa..d52d55573 100644 --- a/lib/devices.js +++ b/lib/devices.js @@ -156,6 +156,14 @@ const devices = { description: 'WeMo smart LED bulb', supports: 'on/off, brightness', }, + + // Zigbee router: http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/ + 'lumi.router': { + model: 'LUMI.ROUTER', + vendor: 'TexasInstruments', + description: 'Router', + supports: 'on/off' + }, }; module.exports = devices; diff --git a/lib/zigbee.js b/lib/zigbee.js index c16fe0f6e..a06b6c48a 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -4,8 +4,13 @@ const settings = require('./util/settings'); const data = require('./util/data'); const shepherdSettings = { - net: {panId: settings.get().advanced.pan_id}, - dbPath: data.joinPath('database.db'), + net: { + panId: settings.get().advanced.pan_id, + channelList: [settings.get().advanced.hasOwnProperty('channel') + ? settings.get().advanced.channel + : 11] + }, + dbPath: data.joinPath('database.db') }; class Zigbee { @@ -33,6 +38,9 @@ class Zigbee { this.shepherd.on('ready', this.handleReady); this.shepherd.on('ind', this.handleMessage); + // this event may appear if zigbee lib cannot decode bad packets (Invalid checksum) + this.shepherd.on('error', this.handleError); + this.onMessage = onMessage; } @@ -46,9 +54,10 @@ class Zigbee { handleReady() { // Set all Xiaomi devices (manufId === 4151) to be online, so shepherd won't try // to query info from devices (which would fail because they go tosleep). + // Xiaomi lumi.plug has manufId === 4447 and can be in the sleep mode too const devices = this.getAllClients(); devices.forEach((device) => { - if (device.manufId === 4151) { + if ((device.manufId === 4151) || (device.manufId === 4447)) { this.shepherd.find(device.ieeeAddr, 1).getDevice().update({ status: 'online', joinTime: Math.floor(Date.now() / 1000), @@ -59,6 +68,10 @@ class Zigbee { logger.info('zigbee-shepherd ready'); } + handleError(message) { + logger.error(message); + } + permitJoin(permit) { if (permit) { logger.info('Zigbee: allowing new devices to join.'); From 0fdec7c22583c170a55e3b0fa92787381422cf25 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Thu, 17 May 2018 17:48:41 +0200 Subject: [PATCH 02/10] Refactor zigbee.js --- lib/zigbee.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/zigbee.js b/lib/zigbee.js index a06b6c48a..d3bbaea10 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -6,17 +6,16 @@ const data = require('./util/data'); const shepherdSettings = { net: { panId: settings.get().advanced.pan_id, - channelList: [settings.get().advanced.hasOwnProperty('channel') - ? settings.get().advanced.channel - : 11] + channelList: [settings.get().advanced.hasOwnProperty('channel') ? settings.get().advanced.channel : 11], }, - dbPath: data.joinPath('database.db') + dbPath: data.joinPath('database.db'), }; class Zigbee { constructor() { this.handleReady = this.handleReady.bind(this); this.handleMessage = this.handleMessage.bind(this); + this.handleError = this.handleError.bind(this); } start(onMessage, callback) { @@ -37,8 +36,6 @@ class Zigbee { // Register callbacks. this.shepherd.on('ready', this.handleReady); this.shepherd.on('ind', this.handleMessage); - - // this event may appear if zigbee lib cannot decode bad packets (Invalid checksum) this.shepherd.on('error', this.handleError); this.onMessage = onMessage; @@ -69,6 +66,7 @@ class Zigbee { } handleError(message) { + // This event may appear if zigbee-shepherd cannot decode bad packets (invalid checksum). logger.error(message); } From 7f5865aaf92fbcfd4f52bb48639ea736d8acda5d Mon Sep 17 00:00:00 2001 From: Koenkk Date: Thu, 17 May 2018 18:03:54 +0200 Subject: [PATCH 03/10] Update lumi.router device specification. --- lib/converters/zigbee2mqtt.js | 43 ++++++++++++++++++----------------- lib/devices.js | 10 ++++---- lib/homeassistant.js | 12 ++++++++++ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/converters/zigbee2mqtt.js b/lib/converters/zigbee2mqtt.js index deb1ba3f2..b5d127e30 100644 --- a/lib/converters/zigbee2mqtt.js +++ b/lib/converters/zigbee2mqtt.js @@ -344,7 +344,7 @@ const parsers = [ return result; } } - }, + }, { devices: ['QBKG04LM'], cid: 'genOnOff', @@ -376,6 +376,27 @@ const parsers = [ return {smoke: msg.data.zoneStatus === 1}; }, }, + { + devices: ['CC2530.ROUTER'], + cid: 'genOnOff', + type: 'attReport', + convert: (msg) => { + return {state: msg.data.data['onOff'] === 1}; + }, + }, + { + devices: ['CC2530.ROUTER'], + cid: 'genBinaryValue', + type: 'attReport', + convert: (msg) => { + const data = msg.data.data; + return { + description: data['description'], + type: data['inactiveText'], + rssi: data['presentValue'], + }; + }, + }, // Ignore parsers (these message dont need parsing). { @@ -444,26 +465,6 @@ const parsers = [ type: 'devChange', convert: () => null, }, - { - devices: ['LUMI.ROUTER'], - cid: 'genOnOff', - type: 'attReport', - convert: (msg) => {return {state: msg.data.data['onOff'] === 1 ? "ON" : "OFF"}} - }, - { - devices: ['LUMI.ROUTER'], - cid: 'genBinaryValue', - type: 'attReport', - convert: (msg) => { - const data = msg.data.data; - const result = { - description: data['description'], - type: data['inactiveText'], - rssi: data['presentValue'] - }; - return result; - } - }, ]; module.exports = parsers; diff --git a/lib/devices.js b/lib/devices.js index d52d55573..3f4a9dd2e 100644 --- a/lib/devices.js +++ b/lib/devices.js @@ -157,12 +157,12 @@ const devices = { supports: 'on/off, brightness', }, - // Zigbee router: http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/ + // Texax Instruments 'lumi.router': { - model: 'LUMI.ROUTER', - vendor: 'TexasInstruments', - description: 'Router', - supports: 'on/off' + model: 'CC2530.ROUTER', + vendor: 'Texas Instruments', + description: 'CC2530 router [link](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)', + supports: 'state, description, type, rssi', }, }; diff --git a/lib/homeassistant.js b/lib/homeassistant.js index 81dd65d7e..f6a0f0f20 100644 --- a/lib/homeassistant.js +++ b/lib/homeassistant.js @@ -46,6 +46,17 @@ const configurations = { json_attributes: ['battery'], }, }, + 'binary_sensor_router': { + type: 'binary_sensor', + object_id: 'router', + discovery_payload: { + payload_on: true, + payload_off: false, + value_template: '{{ value_json.state }}', + device_class: 'connectivity', + json_attributes: ['description', 'type', 'rssi'], + }, + }, // Sensor 'sensor_illuminance': { @@ -209,6 +220,7 @@ const mapping = { '7146060PH': [configurations.light_brightness_colortemp_xy], 'F7C033': [configurations.light_brightness], 'JTYJ-GD-01LM/BW': [configurations.binary_sensor_smoke], + 'CC2530.ROUTER': [configurations.binary_sensor_router], }; // A map of all discoverd devices From 8207321c3014d3267c797f931f4ffeb7aca45c9f Mon Sep 17 00:00:00 2001 From: Koenkk Date: Thu, 17 May 2018 18:33:32 +0200 Subject: [PATCH 04/10] Update ZNCZ02LM converters. --- lib/converters/zigbee2mqtt.js | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/lib/converters/zigbee2mqtt.js b/lib/converters/zigbee2mqtt.js index b5d127e30..157e1bc1d 100644 --- a/lib/converters/zigbee2mqtt.js +++ b/lib/converters/zigbee2mqtt.js @@ -314,36 +314,17 @@ const parsers = [ cid: 'genBasic', type: 'attReport', convert: (msg) => { - if(msg.data.data['65281']){ + if (msg.data.data['65281']) { const data = msg.data.data['65281']; - const result = { - state: data['100'] === 1 ? "ON" : "OFF", + return { + state: data['100'] === 1 ? 'ON' : 'OFF', power: precisionRound(data['152'], 2), - consumption: precisionRound(data['149'], 2), - temperature: precisionRound(data['3'], 2), voltage: precisionRound(data['150'] * 0.1, 1), + consumption: precisionRound(data['149'], 2), // ?? + temperature: precisionRound(data['3'], 2), // ?? }; - return result; } - } - }, - { - devices: ['ZNCZ02LM'], - cid: 'genBasic', - type: 'devChange', - convert: (msg) => { - if(msg.data.data['65281']){ - const data = msg.data.data['65281']; - const result = { - state: data['100'] === 1 ? "ON" : "OFF", - power: precisionRound(data['152'], 2), - consumption: precisionRound(data['149'], 2), - temperature: precisionRound(data['3'], 2), - voltage: precisionRound(data['150'] * 0.1, 1), - }; - return result; - } - } + }, }, { devices: ['QBKG04LM'], @@ -411,7 +392,7 @@ const parsers = [ { devices: [ 'WXKG11LM', 'MCCGQ11LM', 'RTCGQ11LM', 'WSDCGQ11LM', 'SJCGQ11LM', 'MCCGQ01LM', 'RTCGQ01LM', 'WXKG01LM', - 'WSDCGQ01LM', 'JTYJ-GD-01LM/BW', + 'WSDCGQ01LM', 'JTYJ-GD-01LM/BW', 'ZNCZ02LM', ], cid: 'genBasic', type: 'devChange', From f58b0fa4f2d7eaa4610418ae60ce20677181cec8 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Thu, 17 May 2018 18:41:03 +0200 Subject: [PATCH 05/10] Add voltage to homeassisant power sensor. --- lib/homeassistant.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/homeassistant.js b/lib/homeassistant.js index f6a0f0f20..98f3039ab 100644 --- a/lib/homeassistant.js +++ b/lib/homeassistant.js @@ -115,6 +115,7 @@ const configurations = { unit_of_measurement: 'Watt', icon: 'mdi:flash', value_template: '{{ value_json.power }}', + json_attributes: ['voltage'], }, }, 'sensor_action': { From f886259f92dedddd35dd5034bd927740cff0c734 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 21 May 2018 11:49:02 +0200 Subject: [PATCH 06/10] Refactor controller.js. --- lib/controller.js | 150 ++++++++++++++++++---------------------------- lib/zigbee.js | 15 +++++ 2 files changed, 74 insertions(+), 91 deletions(-) diff --git a/lib/controller.js b/lib/controller.js index c4ff95421..4231da247 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -6,46 +6,37 @@ const deviceMapping = require('./devices'); const zigbee2mqtt = require('./converters/zigbee2mqtt'); const mqtt2zigbee = require('./converters/mqtt2zigbee'); const homeassistant = require('./homeassistant'); -const debug = require('debug')('zigbee2mqtt'); +const debug = require('debug')('zigbee2mqtt:controller'); const mqttConfigRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/config/\\w+`, 'g'); const mqttDeviceRegex = new RegExp(`${settings.get().mqtt.base_topic}/\\w+/set`, 'g'); const mqttDevicePrefixRegex = new RegExp(`${settings.get().mqtt.base_topic}/\\w+/\\w+/set`, 'g'); const issueLink = 'https://github.com/Koenkk/zigbee2mqtt/issues'; - -function getTimestamp() { - var d = new Date(); - return d.getTime(); -} +const pollInterval = 60 * 1000; // seconds * 1000. +const softResetTimeout = 3600 * 1000; // seconds * 1000. class Controller { constructor() { this.zigbee = new Zigbee(); this.mqtt = new MQTT(); - this.stateCache = {}; + + this.stateCache = {}; // Caches messages from devices. + this.resetTimer = null; // After 1 hour of no message, reset CC2531 timer. + this.handleZigbeeMessage = this.handleZigbeeMessage.bind(this); this.handleMQTTMessage = this.handleMQTTMessage.bind(this); - - this.checkOnlineTimer = null; - this.lastDeviceActivity = {}; // timestamps of last data/activity - this.lastControllerActivity = 0; } start() { this.zigbee.start(this.handleZigbeeMessage, (error) => { - this.lastDeviceActivity = {}; - if (error) { logger.error('Failed to start'); } else { // Log zigbee clients on startup. const devices = this.zigbee.getAllClients(); logger.info(`Currently ${devices.length} devices are joined:`); - devices.forEach((device) => { - logger.info(this.getDeviceStartupLogMessage(device)) - this.setLastDeviceActivity(device.ieeeAddr); - }); + devices.forEach((device) => logger.info(this.getDeviceStartupLogMessage(device))); // Connect to MQTT broker const subscriptions = [ @@ -77,19 +68,58 @@ class Controller { this.zigbee.permitJoin(true); } - // Set timer at interval to check online status of Zigbee routers. - // For example, it prevents Xiaomi routers to go to a deep sleep mode - const interval = 1 * 1000; // seconds * 1000. - this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), interval); - this.lastControllerActivity = getTimestamp(); + // Start poll timer. + this.pollTimer(true); + this.resetSoftResetTimeout(); + } + + resetSoftResetTimeout() { + if (this._softResetTimer) { + clearTimeout(this._softResetTimer); + this._softResetTimer = null; + } + + this._softResetTimer = setTimeout(() => { + this.zigbee.softReset((error) => { + if (error) { + logger.warn('Soft reset error', error); + this.zigbee.stop((error) => { + logger.warn('Zigbee stopped'); + this.zigbee.start(this.handleZigbeeMessage, (error) => { + if (error) { + logger.error('Failed to restart!'); + } + }); + }); + } else { + logger.warn('Soft resetted zigbee'); + } + + this.resetSoftResetTimeout(); + }); + }, softResetTimeout); + } + + pollTimer(start) { + // Some routers need polling to prevent them from sleeping. + if (start && !this._pollTimer) { + this._pollTimer = setInterval(() => { + const devices = this.zigbee.getAllClients().filter((d) => { + const power = d.powerSource ? d.powerSource.toLowerCase().split(' ')[0] : 'unknown'; + return power !== 'battery' && power !== 'unknown' && d.type === 'Router'; + }); + + devices.forEach((d) => this.zigbee.ping(d.ieeeAddr)); + }, pollInterval); + } else if (!start && this._pollTimer) { + clearTimeout(this._pollTimer); + this._pollTimer = null; + } } stop(callback) { this.mqtt.disconnect(); - if(this.checkOnlineTimer) { - clearTimeout(this.checkOnlineTimer); - this.checkOnlineTimer = null; - } + this.pollTimer(false); this.zigbee.stop(callback); } @@ -110,6 +140,9 @@ class Controller { } handleZigbeeMessage(message) { + // Zigbee message receieved, reset soft reset timeout. + this.resetSoftResetTimeout(); + debug('Recieved zigbee message with data', message.data); if (message.type == 'devInterview') { @@ -130,8 +163,6 @@ class Controller { return; } - this.setLastDeviceActivity(device.ieeeAddr); - // Check if this is a new device. if (!settings.getDevice(device.ieeeAddr)) { logger.info(`New device with address ${device.ieeeAddr} connected!`); @@ -283,69 +314,6 @@ class Controller { this.mqtt.publish(deviceSettings.friendly_name, JSON.stringify(payload), options); } - - setLastDeviceActivity(ieeeAddr) { - this.lastDeviceActivity[ieeeAddr] = getTimestamp(); - } - - zigbeeCheckOnline() { - var dt = getTimestamp(); - - //TO-DO: may be allow to configure the timeout - if ((dt - this.lastControllerActivity) > 3600000) { - // no data received in 1 hour. - // This problem may occur sometimes with CC2531 (USB devices can be pluged/unpluged by a PnP system) - // try to restart and self-recovery - this.checkOnlineTimer = null; - this.lastControllerActivity = dt; - this.lastDeviceActivity = {}; - logger.warn('Soft restart'); - this.zigbee.shepherd.reset('soft', (err) => { - if(err){ - logger.warn('Soft reset error:', err); - this.zigbee.stop( (err) => { - logger.warn('Stop:', err); - this.zigbee.start(this.zigbee.onMessage, () => {}); - }); - } - else{ - this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), 1000); - } - }); - return; - } - - var device, devInfo, devType, power, dev_desc; - for (device in this.lastDeviceActivity) { - if ((dt - this.lastDeviceActivity[device]) > 60000) { - this.lastDeviceActivity[device] = dt; - - devInfo = this.zigbee.shepherd._findDevByAddr(device); - if (devInfo) { - // battery powered endpoint devices are in the sleep mode most time - if(devInfo.powerSource){ - power = devInfo.powerSource.toLowerCase().split(' ')[0]; - } - else{ - power = 'unknown'; - } - devType = devInfo.type.toLowerCase(); - if ( - ((power !== 'battery') && (power !== 'unknown')) || - (devType === 'router') - ) { - dev_desc = this.getDeviceStartupLogMessage(devInfo); - logger.info('Data timeout for device:', dev_desc, ' Checking online status.'); - // note: checkOnline has the callback argument but does not call callback - this.zigbee.shepherd.controller.checkOnline(devInfo); - } - } - } - } - - this.checkOnlineTimer = setTimeout(this.zigbeeCheckOnline.bind(this), 1000); - return; - } } module.exports = Controller; diff --git a/lib/zigbee.js b/lib/zigbee.js index d3bbaea10..13dcd811e 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -2,6 +2,7 @@ const ZShepherd = require('zigbee-shepherd'); const logger = require('./util/logger'); const settings = require('./util/settings'); const data = require('./util/data'); +const debug = require('debug')('zigbee2mqtt:zigbee'); const shepherdSettings = { net: { @@ -41,6 +42,10 @@ class Zigbee { this.onMessage = onMessage; } + softReset(callback) { + this.shepherd.reset('soft', callback); + } + stop(callback) { this.shepherd.stop((error) => { logger.info('zigbee-shepherd stopped'); @@ -88,6 +93,16 @@ class Zigbee { return this.shepherd.list().filter((device) => device.type !== 'Coordinator'); } + ping(deviceID) { + const device = this.shepherd._findDevByAddr(deviceID); + + if (device) { + // Note: checkOnline has the callback argument but does not call callback + debug(`Check online ${deviceID}`); + this.shepherd.controller.checkOnline(device); + } + } + handleMessage(message) { if (this.onMessage) { this.onMessage(message); From 26f378237f702b0cc3a66a98bd7db16707ad2280 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 21 May 2018 11:50:14 +0200 Subject: [PATCH 07/10] Update sensor_power. --- lib/converters/zigbee2mqtt.js | 4 ++-- lib/homeassistant.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/converters/zigbee2mqtt.js b/lib/converters/zigbee2mqtt.js index 157e1bc1d..18ebf7f77 100644 --- a/lib/converters/zigbee2mqtt.js +++ b/lib/converters/zigbee2mqtt.js @@ -320,8 +320,8 @@ const parsers = [ state: data['100'] === 1 ? 'ON' : 'OFF', power: precisionRound(data['152'], 2), voltage: precisionRound(data['150'] * 0.1, 1), - consumption: precisionRound(data['149'], 2), // ?? - temperature: precisionRound(data['3'], 2), // ?? + consumption: precisionRound(data['149'], 2), + temperature: precisionRound(data['3'], 2), }; } }, diff --git a/lib/homeassistant.js b/lib/homeassistant.js index 98f3039ab..5ca5ebc93 100644 --- a/lib/homeassistant.js +++ b/lib/homeassistant.js @@ -115,7 +115,7 @@ const configurations = { unit_of_measurement: 'Watt', icon: 'mdi:flash', value_template: '{{ value_json.power }}', - json_attributes: ['voltage'], + json_attributes: ['voltage', 'temperature', 'consumption'], }, }, 'sensor_action': { From 78c4f43496d2ce928372a5feaecf2caa422d6568 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 21 May 2018 11:51:53 +0200 Subject: [PATCH 08/10] Update controller.js --- lib/controller.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/controller.js b/lib/controller.js index 4231da247..d0e07b0f4 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -20,10 +20,7 @@ class Controller { constructor() { this.zigbee = new Zigbee(); this.mqtt = new MQTT(); - - this.stateCache = {}; // Caches messages from devices. - this.resetTimer = null; // After 1 hour of no message, reset CC2531 timer. - + this.stateCache = {}; this.handleZigbeeMessage = this.handleZigbeeMessage.bind(this); this.handleMQTTMessage = this.handleMQTTMessage.bind(this); } From 02818ddaf592120ba142fbd7bfcaedffbf26cfe2 Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 21 May 2018 11:59:01 +0200 Subject: [PATCH 09/10] Refactor softResetTimeout --- lib/controller.js | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lib/controller.js b/lib/controller.js index d0e07b0f4..bf6f2f352 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -67,34 +67,36 @@ class Controller { // Start poll timer. this.pollTimer(true); - this.resetSoftResetTimeout(); + this.softResetTimeout(true); } - resetSoftResetTimeout() { + softResetTimeout(start) { if (this._softResetTimer) { clearTimeout(this._softResetTimer); this._softResetTimer = null; } - this._softResetTimer = setTimeout(() => { - this.zigbee.softReset((error) => { - if (error) { - logger.warn('Soft reset error', error); - this.zigbee.stop((error) => { - logger.warn('Zigbee stopped'); - this.zigbee.start(this.handleZigbeeMessage, (error) => { - if (error) { - logger.error('Failed to restart!'); - } + if (start) { + this._softResetTimer = setTimeout(() => { + this.zigbee.softReset((error) => { + if (error) { + logger.warn('Soft reset error', error); + this.zigbee.stop((error) => { + logger.warn('Zigbee stopped'); + this.zigbee.start(this.handleZigbeeMessage, (error) => { + if (error) { + logger.error('Failed to restart!'); + } + }); }); - }); - } else { - logger.warn('Soft resetted zigbee'); - } + } else { + logger.warn('Soft resetted zigbee'); + } - this.resetSoftResetTimeout(); - }); - }, softResetTimeout); + this.softResetTimeout(true); + }); + }, softResetTimeout); + } } pollTimer(start) { @@ -117,6 +119,7 @@ class Controller { stop(callback) { this.mqtt.disconnect(); this.pollTimer(false); + this.softResetTimeout(false); this.zigbee.stop(callback); } @@ -138,7 +141,7 @@ class Controller { handleZigbeeMessage(message) { // Zigbee message receieved, reset soft reset timeout. - this.resetSoftResetTimeout(); + this.softResetTimeout(true); debug('Recieved zigbee message with data', message.data); From b38eb7627441af5ca1e20d88746b83dd93ff98ba Mon Sep 17 00:00:00 2001 From: Koenkk Date: Mon, 21 May 2018 12:00:35 +0200 Subject: [PATCH 10/10] Change comment in controller.js --- lib/controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller.js b/lib/controller.js index bf6f2f352..5f352f554 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -65,7 +65,7 @@ class Controller { this.zigbee.permitJoin(true); } - // Start poll timer. + // Start timers. this.pollTimer(true); this.softResetTimeout(true); }