From 54840f4f161904d3611fd62347d5ea4897ff43b2 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Sat, 17 Aug 2013 17:03:44 -0600 Subject: [PATCH 01/12] trying to wrap my head around how this all works, dirty hacks --- index.js | 30 ++++++++- package.json | 3 +- src/functions/definitions.js | 25 ++++++-- src/functions/functions.js | 120 ++++++++++++++++++++++++++++++++++- src/interface.js | 46 +++++++++----- src/node-zwave.js | 4 ++ 6 files changed, 199 insertions(+), 29 deletions(-) diff --git a/index.js b/index.js index a30de20..1a6c70b 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,36 @@ - var zwave = require('./src/node-zwave'); var promise = zwave.connect(); promise.then(function(connection) { console.log("I connected! Sweet!"); - zwave.getNodeAbilities(1); + //console.log(zwave); + + /* + var nodes = zwave.getNodes(); + nodes.then(function(data){ + console.log('woot! '+data); + }); + */ + + zwave.getNodes(function(data){ + //console.log('Woot!! '+data); + //console.log(data); + //console.log(data.length); + for(var i=0;i 7){ + for(var i=7; i<36; i++){ + var base = ((i-7)*8)+1; + + for (var j = 0; j < 8; j++) { + if((data[i] >> j) & 1) { + nodesFound.push(base+j); + } + } + } + } + + cb(nodesFound); + }); + +} + +function addNodeToNetwork(nodeId, cb) { + console.log('Adding node to network ' + nodeId); + + var command = [ + 0x01, + 0x04, // Length, including checksum which is added after + 0x00, + defs.ADD_NODE_TO_NETWORK, + nodeId + ]; + + var promise = iface.sendMessage(command, listener); + if(typeof callback === 'function') { + promise.then(callback); + } + return promise; } function getNodeAbilities(nodeId, cb) { console.log('Getting node abilities for node ' + nodeId); + var command = [ + 0x01, + 0x04, // Length, including checksum which is added after + 0x00, + defs.GET_NODE_ABILITIES, + nodeId + ]; + + var promise = iface.sendMessage(command, listener); + if(typeof callback === 'function') { + promise.then(callback); + } + return promise; + +} + +function getNodeInfo(nodeId, cb) { + console.log('Getting node info for node ' + nodeId); + var command = [ 0x01, 0x04, // Length, including checksum which is added after @@ -17,13 +83,58 @@ function getNodeAbilities(nodeId, cb) { ]; var promise = iface.sendMessage(command, listener); - if(typeof callback === 'Function') { + if(typeof callback === 'function') { promise.then(callback); } return promise; } +function getNodeProtocol(nodeId, cb) { + console.log('Getting node protocol for node ' + nodeId); + + var command = [ + 0x01, + 0x04, // Length, including checksum which is added after + 0x00, + defs.GET_NODE_PROTOCOL, + nodeId + ]; + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + + var returnInfo = defs.DEVICE_TYPE[data[8]]; + returnInfo.id = nodeId; + + console.log(returnInfo); + if(typeof cb === 'function') { + cb(returnInfo); + } + }).catch(function (error) { + console.log(error); + }); + +} + +function getNodeSupportedClasses(nodeId, cb) { + console.log('Getting node supported classes for node ' + nodeId); + + var command = [ + 0x01, + 0x04, // Length, including checksum which is added after + 0x00, + defs.GET_NODE_SUPPORTED_CLASSES, + nodeId + ]; + + var promise = iface.sendMessage(command, listener); + if(typeof callback === 'function') { + promise.then(callback); + } + return promise; + +} function listener(data) { console.log('Function response...'); @@ -33,5 +144,8 @@ function listener(data) { module.exports = { getNodes: getNodes, - getNodeAbilities: getNodeAbilities + getNodeAbilities: getNodeAbilities, + getNodeInfo: getNodeInfo, + getNodeProtocol: getNodeProtocol, + getNodeSupportedClasses: getNodeSupportedClasses } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index e1389e4..0d473ce 100644 --- a/src/interface.js +++ b/src/interface.js @@ -34,11 +34,11 @@ function generateChecksum(data) { } function runPendingRequest() { - console.log('Running pending request...'); - console.log(pendingRequests); + //console.log('Running pending request...'); + //console.log(pendingRequests); if(pendingRequests.length) { var request = pendingRequests.shift(); - messageHandler.sendMessage(request.message, request.responseType, request.listener); + messageHandler.sendMessage(request.message, request.responseType, request.listener, request.deferred); } } @@ -69,39 +69,49 @@ messageHandler.connect = function(serialPortAddress, callback) { return deferred.promise; } -messageHandler.sendMessage = function(messageArray, responseType, listener, useCallback) { +messageHandler.sendMessage = function(messageArray, responseType, listener, isPending, useCallback) { + var deferred = Q.defer(); + if(typeof responseType === 'function') { listener = responseType; responseType = 'response'; } if(currentState !== 'ready') { - console.log('Adding request to pending requests...'); + //console.log('Adding request to pending requests...'); pendingRequests.push({ message: messageArray, responseType: responseType, - listener: listener + listener: listener, + deferred: deferred // will need this when you call back later }); - return; + return deferred.promise; // have to return the promise so we know which request to call back } currentState = 'pendingAck'; - - var deferred = Q.defer(); if(useCallback) { messageArray.push(createCallbackId()); } messageArray.push(generateChecksum(messageArray)); messageArray[1] = messageArray.length - 2; - - console.log('Sending message to zwave controller'); - console.log(messageArray); - + + var logmsg = 'Sending message to zwave controller'; + + // pending requests have a different promise to worry about + if(isPending){ + deferred = isPending; + logmsg = 'Sending queued messages to zwave controller'; + } + currentRequest = { - responseType: responseType, - defer: deferred, - time: moment(), - listener: listener + responseType: responseType, + defer: deferred, + time: moment(), + listener: listener } + + //console.log(logmsg); + //console.log(messageArray); + var buffer = new Buffer(messageArray); serialPort.write(buffer); return deferred.promise; @@ -131,6 +141,7 @@ function listener(data) { console.log('Received response for request'); messageHandler.sendAck(); currentRequest.listener(data); + currentRequest.defer.resolve(data); currentState = 'ready'; currentRequest = null; runPendingRequest(); @@ -138,6 +149,7 @@ function listener(data) { } else { messageHandler.sendAck(); + console.log('Catch broadcasted events here…'); // Catch broadcasted events here... } } diff --git a/src/node-zwave.js b/src/node-zwave.js index 287e5e9..d28412c 100644 --- a/src/node-zwave.js +++ b/src/node-zwave.js @@ -7,7 +7,11 @@ var functions = require('./functions/functions'); var zwave = {}; // Basic functions +zwave.getNodes = functions.getNodes; zwave.getNodeAbilities = functions.getNodeAbilities; +zwave.getNodeInfo = functions.getNodeInfo; +zwave.getNodeProtocol = functions.getNodeProtocol; +zwave.getNodeSupportedClasses = functions.getNodeSupportedClasses; zwave.connect = iface.connect; zwave.thermostat = cc.thermostat; From 9885292b307628b499e5f3794d31c3b54a029c41 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Sat, 17 Aug 2013 17:34:07 -0600 Subject: [PATCH 02/12] more tweaks --- index.js | 5 ++-- src/functions/functions.js | 48 ++++++++++++++++++++++---------------- src/interface.js | 7 ++++-- src/node-zwave.js | 2 +- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 1a6c70b..aab117e 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ promise.then(function(connection) { nodes.then(function(data){ console.log('woot! '+data); }); - */ + zwave.getNodes(function(data){ //console.log('Woot!! '+data); @@ -22,8 +22,9 @@ promise.then(function(connection) { zwave.getNodeProtocol(data[i],function(node){console.log(node);}); } }); + */ - //zwave.getNodeProtocol(1,function(node){console.log(node);}); + zwave.addNodeToNetwork(3,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); //zwave.getNodeProtocol(3,function(node){console.log(node);}); //zwave.getNodeAbilities(2); diff --git a/src/functions/functions.js b/src/functions/functions.js index 18b8ab0..675e395 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -33,25 +33,6 @@ function getNodes(cb) { } -function addNodeToNetwork(nodeId, cb) { - console.log('Adding node to network ' + nodeId); - - var command = [ - 0x01, - 0x04, // Length, including checksum which is added after - 0x00, - defs.ADD_NODE_TO_NETWORK, - nodeId - ]; - - var promise = iface.sendMessage(command, listener); - if(typeof callback === 'function') { - promise.then(callback); - } - return promise; - -} - function getNodeAbilities(nodeId, cb) { console.log('Getting node abilities for node ' + nodeId); @@ -117,6 +98,32 @@ function getNodeProtocol(nodeId, cb) { } +function addNodeToNetwork(nodeId, cb) { + console.log('Adding node to network ' + nodeId); + + var command = [ + 0x01, + 0x04, // Length, including checksum which is added after + 0x00, + defs.ADD_NODE_TO_NETWORK, + nodeId + ]; + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + + var returnInfo = data; + + console.log(returnInfo); + if(typeof cb === 'function') { + cb(returnInfo); + } + }).catch(function (error) { + console.log(error); + }); + +} + function getNodeSupportedClasses(nodeId, cb) { console.log('Getting node supported classes for node ' + nodeId); @@ -147,5 +154,6 @@ module.exports = { getNodeAbilities: getNodeAbilities, getNodeInfo: getNodeInfo, getNodeProtocol: getNodeProtocol, - getNodeSupportedClasses: getNodeSupportedClasses + getNodeSupportedClasses: getNodeSupportedClasses, + addNodeToNetwork: addNodeToNetwork } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index 0d473ce..a7c969b 100644 --- a/src/interface.js +++ b/src/interface.js @@ -109,8 +109,8 @@ messageHandler.sendMessage = function(messageArray, responseType, listener, isPe listener: listener } - //console.log(logmsg); - //console.log(messageArray); + console.log(logmsg); + console.log(messageArray); var buffer = new Buffer(messageArray); serialPort.write(buffer); @@ -124,6 +124,9 @@ messageHandler.sendAck = function() { function listener(data) { console.log('Receiving data from zwave controller'); console.log(data); + + // sometimes the data will come bundled with a leading ack, if so we need to strip it + if(data[0] == globals.ACK) { console.log('Received ACK for request'); if(currentRequest.responseType == 'none') { diff --git a/src/node-zwave.js b/src/node-zwave.js index d28412c..7684acd 100644 --- a/src/node-zwave.js +++ b/src/node-zwave.js @@ -1,4 +1,3 @@ - var cc = require('./command_classes/'); // var funcs = require('./functions/'); var iface = require('./interface'); @@ -12,6 +11,7 @@ zwave.getNodeAbilities = functions.getNodeAbilities; zwave.getNodeInfo = functions.getNodeInfo; zwave.getNodeProtocol = functions.getNodeProtocol; zwave.getNodeSupportedClasses = functions.getNodeSupportedClasses; +zwave.addNodeToNetwork = functions.addNodeToNetwork; zwave.connect = iface.connect; zwave.thermostat = cc.thermostat; From 82a07c001cb39fed720ae1b701ba4883ad6aec66 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Sun, 18 Aug 2013 17:37:20 -0600 Subject: [PATCH 03/12] Associating a device --- src/functions/functions.js | 13 ++++++++++--- src/globals.js | 1 + src/interface.js | 17 ++++++++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/functions/functions.js b/src/functions/functions.js index 675e395..36d1b6c 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -103,10 +103,17 @@ function addNodeToNetwork(nodeId, cb) { var command = [ 0x01, - 0x04, // Length, including checksum which is added after + 0x0B, // Length, including checksum which is added after 0x00, - defs.ADD_NODE_TO_NETWORK, - nodeId + defs.DATA, + nodeId, + 0x04, + 0x85, + 0x01, + 0x01, + 0x01, + 0x05, + 0x03 ]; var promise = iface.sendMessage(command, 'request', listener); diff --git a/src/globals.js b/src/globals.js index 92537ea..520404e 100644 --- a/src/globals.js +++ b/src/globals.js @@ -2,6 +2,7 @@ var g = {}; g.ACK = 0x06; +g.NAK = 0x15; g.AUTO_ACK = 0x05; g.OPTION_ACK = 0x01; diff --git a/src/interface.js b/src/interface.js index a7c969b..f8d7680 100644 --- a/src/interface.js +++ b/src/interface.js @@ -126,8 +126,13 @@ function listener(data) { console.log(data); // sometimes the data will come bundled with a leading ack, if so we need to strip it - - if(data[0] == globals.ACK) { + if(data[0] == globals.NAK) { + currentRequest.defer.resolve(false); + currentState = 'ready'; + currentRequest = null; + runPendingRequest() + } + else if(data[0] == globals.ACK) { console.log('Received ACK for request'); if(currentRequest.responseType == 'none') { currentRequest.defer.resolve(true); @@ -151,8 +156,14 @@ function listener(data) { return; } else { - messageHandler.sendAck(); console.log('Catch broadcasted events here…'); + messageHandler.sendAck(); + if(currentRequest){ + currentRequest.defer.resolve(false); + } + currentState = 'ready'; + currentRequest = null; + runPendingRequest() // Catch broadcasted events here... } } From 284fc97774ed28f74885a9b8a611b252ff492ea6 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Sun, 18 Aug 2013 22:10:42 -0600 Subject: [PATCH 04/12] more stuff --- index.js | 9 ++++-- src/functions/functions.js | 57 ++++++++++++++++++++++++++++---------- src/interface.js | 12 +++++++- src/node-zwave.js | 3 +- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index aab117e..b3fdb5b 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ promise.then(function(connection) { console.log('woot! '+data); }); - + */ zwave.getNodes(function(data){ //console.log('Woot!! '+data); //console.log(data); @@ -22,9 +22,12 @@ promise.then(function(connection) { zwave.getNodeProtocol(data[i],function(node){console.log(node);}); } }); - */ + + + //zwave.sendData(2,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically + //zwave.sendData(2,[0x70,0x04,0x6F,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval - zwave.addNodeToNetwork(3,function(node){console.log(node);}); + //zwave.associateNode(2,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); //zwave.getNodeProtocol(3,function(node){console.log(node);}); //zwave.getNodeAbilities(2); diff --git a/src/functions/functions.js b/src/functions/functions.js index 36d1b6c..8ad5a2f 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -1,6 +1,32 @@ var iface = require('../interface'); var defs = require('./definitions'); +function sendData(nodeId,cmd,cb) { + console.log('Sending command data'); + + var command = [ + 0x01, + 0x00, // calculated after we get a command + 0x00, + defs.DATA, + nodeId // nodeid + ]; + + command = command.concat(cmd); + command.push(0x05); + command.push(0x03); + + command[1] = parseInt(command.length-1); + + console.log(command); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + cb(data); + }); + +} + function getNodes(cb) { console.log('Getting list of nodes'); @@ -86,7 +112,7 @@ function getNodeProtocol(nodeId, cb) { promise.then(function(data){ var returnInfo = defs.DEVICE_TYPE[data[8]]; - returnInfo.id = nodeId; + returnInfo.nodeid = nodeId; console.log(returnInfo); if(typeof cb === 'function') { @@ -98,22 +124,22 @@ function getNodeProtocol(nodeId, cb) { } -function addNodeToNetwork(nodeId, cb) { +function associateNode(nodeId, cb) { console.log('Adding node to network ' + nodeId); var command = [ - 0x01, - 0x0B, // Length, including checksum which is added after - 0x00, - defs.DATA, - nodeId, - 0x04, - 0x85, - 0x01, - 0x01, - 0x01, - 0x05, - 0x03 + 0x01, // SOC + 0x0B, // length (11), including the checksum which is added later + 0x00, // request + defs.DATA, // sending data + nodeId, // nodeid + 0x04, // ?? + 0x85, // COMMAND_CLASS_ASSOCIATION + 0x01, // ASSOCIATION_SET + 0x01, // _groupIdx + 0x01, // _targetNodeId + 0x05, // ?? + 0x03 // ?? ]; var promise = iface.sendMessage(command, 'request', listener); @@ -162,5 +188,6 @@ module.exports = { getNodeInfo: getNodeInfo, getNodeProtocol: getNodeProtocol, getNodeSupportedClasses: getNodeSupportedClasses, - addNodeToNetwork: addNodeToNetwork + associateNode: associateNode, + sendData: sendData } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index f8d7680..1faa516 100644 --- a/src/interface.js +++ b/src/interface.js @@ -156,8 +156,18 @@ function listener(data) { return; } else { - console.log('Catch broadcasted events here…'); + console.log('Catch broadcasted events here: '+moment()); messageHandler.sendAck(); + + // lets parse out this packet and try and figure out what it is: + + // these are the door open/close for nodeid 3: / + if(data[3]==0x04 && data.length==11){ + var currentVal = (data[10] == 211 ? 0 : 255); // 0x00 == 211, which is closed, else the door is open + var emitVal = {nodeid:parseInt(data[5],16),value:currentVal}; + console.log(emitVal); + } + if(currentRequest){ currentRequest.defer.resolve(false); } diff --git a/src/node-zwave.js b/src/node-zwave.js index 7684acd..3de6021 100644 --- a/src/node-zwave.js +++ b/src/node-zwave.js @@ -11,7 +11,8 @@ zwave.getNodeAbilities = functions.getNodeAbilities; zwave.getNodeInfo = functions.getNodeInfo; zwave.getNodeProtocol = functions.getNodeProtocol; zwave.getNodeSupportedClasses = functions.getNodeSupportedClasses; -zwave.addNodeToNetwork = functions.addNodeToNetwork; +zwave.associateNode = functions.associateNode; +zwave.sendData = functions.sendData; zwave.connect = iface.connect; zwave.thermostat = cc.thermostat; From 517096a27f044d569e785a6b55a39c4fd3b38ffa Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Mon, 19 Aug 2013 02:55:18 -0600 Subject: [PATCH 05/12] configure HEM, read data from HEM --- index.js | 14 ++++++++++---- src/functions/functions.js | 33 +++++++++++++++++++++++++++++++-- src/interface.js | 12 +++--------- src/node-zwave.js | 1 + 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index b3fdb5b..ac57878 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ promise.then(function(connection) { console.log('woot! '+data); }); - */ + zwave.getNodes(function(data){ //console.log('Woot!! '+data); //console.log(data); @@ -22,10 +22,16 @@ promise.then(function(connection) { zwave.getNodeProtocol(data[i],function(node){console.log(node);}); } }); - + */ + + // CONFIGURE THE HEM TO REPORT EVERY 30 SECONDS AS PER THE TECH PDF + //zwave.sendConfigData(2,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically + //zwave.sendConfigData(2,[0x70,0x04,0x6f,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval + //zwave.sendConfigData(2,[0x70,0x04,0xFF,0x01,0x00],function(reply){console.log(reply);}); //reset to default values - //zwave.sendData(2,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically - //zwave.sendData(2,[0x70,0x04,0x6F,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval + // PULL POWER DATA + //zwave.sendData(2,[0x31,0x04],function(reply){console.log(reply);}); // SENSOR_MULTILEVEL_REPORT for the entire unit // + zwave.sendData(2,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit // //zwave.associateNode(2,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); diff --git a/src/functions/functions.js b/src/functions/functions.js index 8ad5a2f..7460e30 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -9,7 +9,35 @@ function sendData(nodeId,cmd,cb) { 0x00, // calculated after we get a command 0x00, defs.DATA, - nodeId // nodeid + nodeId, // nodeid + 2 // This was required to get the HEM get value call working, probably breaks other stuff + ]; + + command = command.concat(cmd); + command.push(0x05); + command.push(0x03); + + command[1] = parseInt(command.length-1); + + console.log(command); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + cb(data); + }); + +} + +function sendConfigData(nodeId,cmd,cb) { + console.log('Sending command data'); + + var command = [ + 0x01, + 0x00, // calculated after we get a command + 0x00, + defs.DATA, + nodeId, // nodeid + 4+cmd[3] // This was required to get the HEM configuration working, probably breaks other stuff ]; command = command.concat(cmd); @@ -189,5 +217,6 @@ module.exports = { getNodeProtocol: getNodeProtocol, getNodeSupportedClasses: getNodeSupportedClasses, associateNode: associateNode, - sendData: sendData + sendData: sendData, + sendConfigData: sendConfigData } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index 1faa516..3c76af6 100644 --- a/src/interface.js +++ b/src/interface.js @@ -125,7 +125,7 @@ function listener(data) { console.log('Receiving data from zwave controller'); console.log(data); - // sometimes the data will come bundled with a leading ack, if so we need to strip it + // TODO: sometimes the data will come bundled with a leading ack, if so we need to strip it if(data[0] == globals.NAK) { currentRequest.defer.resolve(false); currentState = 'ready'; @@ -156,7 +156,7 @@ function listener(data) { return; } else { - console.log('Catch broadcasted events here: '+moment()); + console.log('Catch broadcasted events here: '+moment().format('MMMM Do YYYY, h:mm:ss a')); messageHandler.sendAck(); // lets parse out this packet and try and figure out what it is: @@ -167,13 +167,7 @@ function listener(data) { var emitVal = {nodeid:parseInt(data[5],16),value:currentVal}; console.log(emitVal); } - - if(currentRequest){ - currentRequest.defer.resolve(false); - } - currentState = 'ready'; - currentRequest = null; - runPendingRequest() + // Catch broadcasted events here... } } diff --git a/src/node-zwave.js b/src/node-zwave.js index 3de6021..acd8420 100644 --- a/src/node-zwave.js +++ b/src/node-zwave.js @@ -13,6 +13,7 @@ zwave.getNodeProtocol = functions.getNodeProtocol; zwave.getNodeSupportedClasses = functions.getNodeSupportedClasses; zwave.associateNode = functions.associateNode; zwave.sendData = functions.sendData; +zwave.sendConfigData = functions.sendConfigData; zwave.connect = iface.connect; zwave.thermostat = cc.thermostat; From 9f3f24b742f85586b3ebde37c0b5514bfc802cee Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Mon, 19 Aug 2013 03:08:18 -0600 Subject: [PATCH 06/12] recording what comes in over the wire for the HEM --- src/interface.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/interface.js b/src/interface.js index 3c76af6..fd7544d 100644 --- a/src/interface.js +++ b/src/interface.js @@ -161,13 +161,20 @@ function listener(data) { // lets parse out this packet and try and figure out what it is: - // these are the door open/close for nodeid 3: / + // these are the door open/close for nodeid 3: + // + // if(data[3]==0x04 && data.length==11){ var currentVal = (data[10] == 211 ? 0 : 255); // 0x00 == 211, which is closed, else the door is open var emitVal = {nodeid:parseInt(data[5],16),value:currentVal}; console.log(emitVal); } - + + // these are energy reports for the HEM nodeid 3: / + // + // + + // Catch broadcasted events here... } } From e4274366b30d9ced9d773783de58902c81dec651 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Mon, 19 Aug 2013 09:07:34 -0600 Subject: [PATCH 07/12] A few more dumps to learn how to digest --- src/interface.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interface.js b/src/interface.js index fd7544d..a1364ed 100644 --- a/src/interface.js +++ b/src/interface.js @@ -173,6 +173,8 @@ function listener(data) { // these are energy reports for the HEM nodeid 3: / // // + // + // // Catch broadcasted events here... From 23b48535ad68f05bf74004df450d399b3f107b27 Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Tue, 20 Aug 2013 00:37:10 -0600 Subject: [PATCH 08/12] parse meter readings, ability to set and get configuration params --- index.js | 19 +++++++++----- src/functions/functions.js | 30 ++++++++++++++++++++- src/interface.js | 54 ++++++++++++++++++++++++++++++++++++++ src/node-zwave.js | 1 + 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index ac57878..724a801 100644 --- a/index.js +++ b/index.js @@ -25,15 +25,20 @@ promise.then(function(connection) { */ // CONFIGURE THE HEM TO REPORT EVERY 30 SECONDS AS PER THE TECH PDF - //zwave.sendConfigData(2,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically - //zwave.sendConfigData(2,[0x70,0x04,0x6f,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval - //zwave.sendConfigData(2,[0x70,0x04,0xFF,0x01,0x00],function(reply){console.log(reply);}); //reset to default values + //zwave.sendConfigData(4,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically + //zwave.sendConfigData(4,[0x70,0x04,0x6f,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval + //zwave.sendConfigData(4,[0x70,0x04,0xFF,0x01,0x00],function(reply){console.log(reply);}); //reset to default values + //zwave.sendConfigData(4,[0x70,0x04,0x01,0x02,0x00,0x78],function(reply){console.log(reply);}); //set unit to 120v (note the size is 2) + + //zwave.sendRequestData(4,[0x70,0x05,0x01],function(reply){console.log(reply);}); //get param 1 (voltage) + // // 110 volts (6e) + // // Aftter setting to 120 // PULL POWER DATA - //zwave.sendData(2,[0x31,0x04],function(reply){console.log(reply);}); // SENSOR_MULTILEVEL_REPORT for the entire unit // - zwave.sendData(2,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit // + //zwave.sendData(4,[0x31,0x04],function(reply){console.log(reply);}); // SENSOR_MULTILEVEL_REPORT for the entire unit // + //zwave.sendData(4,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit // - //zwave.associateNode(2,function(node){console.log(node);}); + //zwave.associateNode(4,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); //zwave.getNodeProtocol(3,function(node){console.log(node);}); //zwave.getNodeAbilities(2); @@ -43,4 +48,6 @@ promise.then(function(connection) { //zwave.thermostat.setMode(5,'heat'); //zwave.thermostat.setSetpoint(5, 62, 'heating'); + //console.log(0x78 & 0xff); + }).catch(function (error) {console.log(error)}); \ No newline at end of file diff --git a/src/functions/functions.js b/src/functions/functions.js index 7460e30..9596108 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -28,6 +28,33 @@ function sendData(nodeId,cmd,cb) { } +function sendRequestData(nodeId,cmd,cb) { + console.log('Sending command data'); + + var command = [ + 0x01, + 0x00, // calculated after we get a command + 0x00, + defs.DATA, + nodeId, // nodeid + 3 // This was required to get the HEM get value call working + ]; + + command = command.concat(cmd); + command.push(0x05); + command.push(0x03); + + command[1] = parseInt(command.length-1); + + console.log(command); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + cb(data); + }); + +} + function sendConfigData(nodeId,cmd,cb) { console.log('Sending command data'); @@ -218,5 +245,6 @@ module.exports = { getNodeSupportedClasses: getNodeSupportedClasses, associateNode: associateNode, sendData: sendData, - sendConfigData: sendConfigData + sendConfigData: sendConfigData, + sendRequestData: sendRequestData } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index a1364ed..f54b7b8 100644 --- a/src/interface.js +++ b/src/interface.js @@ -175,10 +175,64 @@ function listener(data) { // // // + // + // + // + if(data[7]==0x32 && data.length==22){ + console.log('looks like a meter reading'); + // data[10] has the precision(3bits)/scale(2bits)/size(3bits) + var x = pad(toBinary(parseInt(data[10], 10)),8); + //console.log(parseInt(data[10], 10)+' = '+x); + var precision = Bin2Dec(x.substring(0, 3)); + var scale = Bin2Dec(x.substring(3, 5)); + var size = Bin2Dec(x.substring(5)); + console.log('precision:'+precision+' scale:'+scale+' size:'+size); + + // grab the next x hex to concat and find our reading + var reading = ''; + for(var i=11; i < 11+parseInt(size); i++){ + var thisReading = pad(toBinary(parseInt(data[i], 10)),8); + //console.log(parseInt(data[i], 10)+' = '+x); + reading = reading + thisReading; + } + //console.log(reading); + var emitVal = Bin2Dec(reading); + emitVal /= Math.pow(10, precision); // move the decimal over + console.log(emitVal); + + } // Catch broadcasted events here... } } +function toBinary(Decimal){ + var bnum = 0, bexp = 1, digit = 0, bsum = 0; + while(Decimal > 0){ + digit = Decimal % 2; + Decimal = Math.floor(Decimal / 2); + bsum = bsum + digit * bexp; + bexp = bexp * 10; + } + return(bsum); +} +function checkBin(n){return/^[01]{1,64}$/.test(n)} +function checkDec(n){return/^[0-9]{1,64}$/.test(n)} +function checkHex(n){return/^[0-9A-Fa-f]{1,64}$/.test(n)} + +function pad(s,z){s=""+s;return s.length Date: Tue, 20 Aug 2013 23:05:38 -0600 Subject: [PATCH 09/12] some on demand power calcs --- src/interface.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/interface.js b/src/interface.js index f54b7b8..7bea2e0 100644 --- a/src/interface.js +++ b/src/interface.js @@ -5,6 +5,9 @@ var globals = require('./globals'); var Q = require('q'); var moment = require('moment'); +var powerUsed = 0; +var lastPowerRead = moment(); + var currentCallbackId = 1; var currentState = 'ready'; // ready || pendingAck || pendingResponse var pendingRequests = []; // If current state is not ready, store pending requests here. First in first out. @@ -201,8 +204,17 @@ function listener(data) { emitVal /= Math.pow(10, precision); // move the decimal over console.log(emitVal); + // add to the amount of power used + var thisPowerRead = moment(); + var powerSinceLast = ((thisPowerRead.diff(lastPowerRead)/3600000)*emitVal)/1000; + powerUsed = powerUsed+powerSinceLast*1; + lastPowerRead = thisPowerRead; + console.log('THIS power used: '+powerSinceLast); + console.log('TOTAL power used: '+powerUsed.toFixed(2)); + } + runPendingRequest(); // Catch broadcasted events here... } } From 640f00ba1030e73bb65b1e87ee270b337ff5f9be Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Tue, 3 Sep 2013 17:41:00 -0600 Subject: [PATCH 10/12] commit that makes this pretty unusable for anyone but me, sorry, need to refactor at some point. --- .gitignore | 1 + README.md | 2 +- index.js | 30 +++++--- src/functions/functions.js | 2 +- src/interface.js | 146 +++++++++++++++++++++++++++++++++---- 5 files changed, 157 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 75ddea5..4e4c7c7 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ sftp-config*.json d.ts +database diff --git a/README.md b/README.md index e04cd16..5770577 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ node-zwave Control your z-wave devices on node.js -THIS IS NOT READY! +THIS IS NOT READY! IT IS IN LARGE TORN APART, HARD CODED, AND NOT USEFUL FOR ANYTHING BUT MY STUFF. This code is essentially ported from a C library that does the same thing. The C lib was written by a genius and also a close friend whose blog resides here: http://www.squarepenguin.com/wordpress/ diff --git a/index.js b/index.js index 724a801..f5bc778 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ var zwave = require('./src/node-zwave'); -var promise = zwave.connect(); +var promise = zwave.connect('/dev/ttyUSB0'); +//var promise = zwave.connect(); promise.then(function(connection) { console.log("I connected! Sweet!"); @@ -11,34 +12,45 @@ promise.then(function(connection) { nodes.then(function(data){ console.log('woot! '+data); }); - + zwave.getNodes(function(data){ //console.log('Woot!! '+data); //console.log(data); //console.log(data.length); for(var i=0;i + //zwave.sendConfigData(5,[0x70,0x04,0x70,0x04,0x00,0x00,0x00,0x3c],function(reply){console.log(reply);}); //set the reporting interval + //zwave.sendConfigData(4,[0x70,0x04,0xFF,0x01,0x00],function(reply){console.log(reply);}); //reset to default values //zwave.sendConfigData(4,[0x70,0x04,0x01,0x02,0x00,0x78],function(reply){console.log(reply);}); //set unit to 120v (note the size is 2) - //zwave.sendRequestData(4,[0x70,0x05,0x01],function(reply){console.log(reply);}); //get param 1 (voltage) + //zwave.sendRequestData(4,[0x70,0x05,0x01],function(reply){ + // console.log('BBBBBBBOOOOOOOOOOOOOOOOOOOMMMMMMMMM!!!!!!!!!!!'); + // console.log(reply); + // + //}); //get param 1 (voltage) // // 110 volts (6e) // // Aftter setting to 120 + //zwave.sendRequestData(4,[0x70,0x04,0x03,0x00,0x00],function(reply){console.log(reply);}); // Turn on Delta function of the whole HEM + // PULL POWER DATA //zwave.sendData(4,[0x31,0x04],function(reply){console.log(reply);}); // SENSOR_MULTILEVEL_REPORT for the entire unit // - //zwave.sendData(4,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit // + //zwave.sendData(4,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit (for some reason I can only get watts this way // - //zwave.associateNode(4,function(node){console.log(node);}); + //zwave.associateNode(5,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); //zwave.getNodeProtocol(3,function(node){console.log(node);}); //zwave.getNodeAbilities(2); diff --git a/src/functions/functions.js b/src/functions/functions.js index 9596108..4e368b1 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -29,7 +29,7 @@ function sendData(nodeId,cmd,cb) { } function sendRequestData(nodeId,cmd,cb) { - console.log('Sending command data'); + console.log('Sending request data'); var command = [ 0x01, diff --git a/src/interface.js b/src/interface.js index 7bea2e0..c3dd92d 100644 --- a/src/interface.js +++ b/src/interface.js @@ -4,9 +4,10 @@ var globals = require('./globals'); var Q = require('q'); var moment = require('moment'); +var nedb = require('nedb'); -var powerUsed = 0; -var lastPowerRead = moment(); +// load up the database +var db = new nedb({ filename: './database', autoload: true }); var currentCallbackId = 1; var currentState = 'ready'; // ready || pendingAck || pendingResponse @@ -15,7 +16,54 @@ var currentRequest = null; // The current request var serialPort = null; var messageHandler = {}; // Object to export -var SerialPort = require("serialport").SerialPort; +var serialport = require("serialport"); +var SerialPort = serialport.SerialPort; + +var lcd = new SerialPort("/dev/ttyO1", {parser: serialport.parsers.readline("\n") }); +lcd.on("open", function () { + console.log("I am connected to the lcd"); + //sp.write("73,5,0,0,0,0,0,0,s"); + //sp.write("06,0,15,0,0,0,s"); +}); + +// Add the loop to insert load averages into the DB every minute +var powerUsed = 0; +var wattcounter = 0; +var watttotal = 0; +var lastkwh = 0; +var prevmeter = 0; +var last_hour = 0; +var tzoffset = -6; +setInterval(function(){ + console.log('writing avg watts using: '+watttotal+'/'+wattcounter) + var avgwatts = watttotal/wattcounter; + + var thisusage = 0; + if(prevmeter){ + thisusage = lastkwh-prevmeter; + } + prevmeter = lastkwh + + var insData = {timestamp: new Date(),watts:avgwatts.toFixed(3),usage:thisusage.toFixed(3),meter:lastkwh.toFixed(3)}; + wattcounter = 0; + watttotal = 0; + db.insert(insData, function (err, newDoc) { + if(err){ + console.log(err); + } + //console.log('wrote record '+newDoc._id); + }); + + // reset the power today total if we just rolled over the clock + var this_hour = moment().add('h',tzoffset).format('H'); // hack the clock back to our time + console.log(this_hour+' this / last '+last_hour); + if(last_hour == 23 && this_hour == 0){ + console.log('Rolling over the clock') + powerUsed = 0; + } + last_hour = this_hour; + +},60000) function createCallbackId() { currentCallbackId++; @@ -173,14 +221,44 @@ function listener(data) { console.log(emitVal); } + // these are replies to a SENSOR_MULTILEVEL_REPORT + // + if(data[7]==0x31 && data[8]==0x05 && data.length==16){ + console.log('looks like a multilevel report'); + // data[9] is the sensor type 0x04 is power + // data[10] has the precision(3bits)/scale(2bits)/size(3bits) + var x = pad(toBinary(parseInt(data[10], 10)),8); + //console.log(parseInt(data[10], 10)+' = '+x); + var precision = Bin2Dec(x.substring(0, 3)); + var scale = Bin2Dec(x.substring(3, 5)); + var size = Bin2Dec(x.substring(5)); + console.log('precision:'+precision+' scale:'+scale+' size:'+size); + + // in this report scale is WATTS, only + + // grab the next x hex to concat and find our reading + var reading = ''; + for(var i=11; i < 11+parseInt(size); i++){ + var thisReading = pad(toBinary(parseInt(data[i], 10)),8); + //console.log(parseInt(data[i], 10)+' = '+x); + reading = reading + thisReading; + } + //console.log(reading); + var emitVal = Bin2Dec(reading); + emitVal /= Math.pow(10, precision); // move the decimal over + console.log(emitVal); + console.log('done'); + } + // these are energy reports for the HEM nodeid 3: / // // // // - // - // - // + // // kWh (see page 183 command class spec) + // // watts + // // watts + // // watts if(data[7]==0x32 && data.length==22){ console.log('looks like a meter reading'); @@ -192,6 +270,8 @@ function listener(data) { var size = Bin2Dec(x.substring(5)); console.log('precision:'+precision+' scale:'+scale+' size:'+size); + //if scale is 0 then this is kWh, if scale is 2 then it is watts + // grab the next x hex to concat and find our reading var reading = ''; for(var i=11; i < 11+parseInt(size); i++){ @@ -204,13 +284,53 @@ function listener(data) { emitVal /= Math.pow(10, precision); // move the decimal over console.log(emitVal); - // add to the amount of power used - var thisPowerRead = moment(); - var powerSinceLast = ((thisPowerRead.diff(lastPowerRead)/3600000)*emitVal)/1000; - powerUsed = powerUsed+powerSinceLast*1; - lastPowerRead = thisPowerRead; - console.log('THIS power used: '+powerSinceLast); - console.log('TOTAL power used: '+powerUsed.toFixed(2)); + // next 2 bits are the delta time + reading = ''; + var newindex = 11+parseInt(size); + for(var i=newindex; i < newindex+2; i++){ + var thisReading = pad(toBinary(parseInt(data[i], 10)),8); + //console.log(parseInt(data[i], 10)+' = '+x); + reading = reading + thisReading; + } + var deltaval = Bin2Dec(reading); + console.log(deltaval); + + // next X are the previous meter value + reading = ''; + newindex = newindex+2; + for(var i=newindex; i < newindex+parseInt(size); i++){ + var thisReading = pad(toBinary(parseInt(data[i], 10)),8); + //console.log(parseInt(data[i], 10)+' = '+x); + reading = reading + thisReading; + } + var prevval = Bin2Dec(reading); + prevval /= Math.pow(10, precision); // move the decimal over + console.log(prevval); + + + var data = {timestamp: new Date()}; + if(scale==2){ + data.watts = emitVal; + + // convert this into a format the LCD can handle + var tmpPowerUsed = Math.floor(powerUsed*1000); + var writestr = parseInt(data.watts)%256+","+Math.floor(parseInt(data.watts)/256)+",0,0,0,0,"+parseInt(tmpPowerUsed)%256+","+Math.floor(parseInt(tmpPowerUsed)/256)+","+moment().add('h',tzoffset).format('H')+",0,"+moment().add('h',tzoffset).format('m')+",0,"+moment().add('h',tzoffset).format('s')+",0"; + lcd.write(writestr+",s"); + console.log(writestr); + wattcounter += 1; + watttotal += emitVal; + + } + else if(scale==0){ + var usage = emitVal-prevval; + powerUsed = powerUsed+usage*1; + data.usage = usage.toFixed(3); + data.delta = deltaval; + console.log('TOTAL power used today: '+powerUsed.toFixed(3)); + // send the power today total to the LCD? + + lastkwh = emitVal; + } } From f99deac7577ff609c5516ef75584f6c6e65f1d6f Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Wed, 4 Sep 2013 01:30:15 -0600 Subject: [PATCH 11/12] Starting to refactor stuff into their proper homes --- index.js | 28 +-- src/command_classes/energyMonitor.js | 279 +++++++++++++++++++++++++++ src/command_classes/index.js | 1 + src/functions/functions.js | 86 +-------- src/interface.js | 4 +- src/node-zwave.js | 5 +- 6 files changed, 284 insertions(+), 119 deletions(-) create mode 100644 src/command_classes/energyMonitor.js diff --git a/index.js b/index.js index f5bc778..328452b 100644 --- a/index.js +++ b/index.js @@ -24,32 +24,7 @@ promise.then(function(connection) { } }); */ - - - // CONFIGURE THE HEM TO REPORT EVERY 30 SECONDS AS PER THE TECH PDF - //zwave.sendConfigData(4,[0x70,0x04,0x65,0x04,0x00,0x00,0x00,0x04],function(reply){console.log(reply);}); //have group 1 report watts automatically - //zwave.sendConfigData(4,[0x70,0x04,0x6f,0x04,0x00,0x00,0x00,0x05],function(reply){console.log(reply);}); //set the reporting interval - //-and, on another channel- - //zwave.sendConfigData(5,[0x70,0x04,0x66,0x04,0x00,0x00,0x00,0x08],function(reply){console.log(reply);}); //have group 2 report kWh automatically // these look like: - //zwave.sendConfigData(5,[0x70,0x04,0x70,0x04,0x00,0x00,0x00,0x3c],function(reply){console.log(reply);}); //set the reporting interval - - //zwave.sendConfigData(4,[0x70,0x04,0xFF,0x01,0x00],function(reply){console.log(reply);}); //reset to default values - //zwave.sendConfigData(4,[0x70,0x04,0x01,0x02,0x00,0x78],function(reply){console.log(reply);}); //set unit to 120v (note the size is 2) - - //zwave.sendRequestData(4,[0x70,0x05,0x01],function(reply){ - // console.log('BBBBBBBOOOOOOOOOOOOOOOOOOOMMMMMMMMM!!!!!!!!!!!'); - // console.log(reply); - // - //}); //get param 1 (voltage) - // // 110 volts (6e) - // // Aftter setting to 120 - - //zwave.sendRequestData(4,[0x70,0x04,0x03,0x00,0x00],function(reply){console.log(reply);}); // Turn on Delta function of the whole HEM - - // PULL POWER DATA - //zwave.sendData(4,[0x31,0x04],function(reply){console.log(reply);}); // SENSOR_MULTILEVEL_REPORT for the entire unit // - //zwave.sendData(4,[0x32,0x01],function(reply){console.log(reply);}); // METER_REPORT for the entire unit (for some reason I can only get watts this way // - + //zwave.associateNode(5,function(node){console.log(node);}); //zwave.getNodeProtocol(2,function(node){console.log(node);}); //zwave.getNodeProtocol(3,function(node){console.log(node);}); @@ -60,6 +35,5 @@ promise.then(function(connection) { //zwave.thermostat.setMode(5,'heat'); //zwave.thermostat.setSetpoint(5, 62, 'heating'); - //console.log(0x78 & 0xff); }).catch(function (error) {console.log(error)}); \ No newline at end of file diff --git a/src/command_classes/energyMonitor.js b/src/command_classes/energyMonitor.js new file mode 100644 index 0000000..fa3620c --- /dev/null +++ b/src/command_classes/energyMonitor.js @@ -0,0 +1,279 @@ + +var fdefs = require('../functions/definitions'); +var globals = require('../globals'); +var iface = require('../interface'); + +var energyMonitor = {}; + +// Define constants + +// HEM command classes +var COMMAND_CLASS_CONFIGURATION = 0x70; +var COMMAND_CLASS_SENSOR_MULTILEVEL = 0x31; +var COMMAND_CLASS_METER = 0x32; +var CONFIGURATION_SET = 0x04; +var CONFIGURATION_GET = 0x05; + +var INTERVAL_GROUPS = [ + 110, + 111, + 112, + 113 +]; + +var REPORT_GROUPS = [ + 100, + 101, + 102, + 103 +]; + +var REPORT_TYPES = { + watt: 0x04, + kwh: 0x08 +}; + +/******************************************************************************* + Getters +*******************************************************************************/ + +/** + * Get the multilevel report of the entire HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.getMultilevelReport = function(nodeId, callback) { + var command = [ + COMMAND_CLASS_SENSOR_MULTILEVEL, + 0x04 //SENSOR_MULTILEVEL_GET + ]; + + command = prepData(nodeId,command,'send'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +/** + * Get the meter report for the entire HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.getMeterReport = function(nodeId, callback) { + var command = [ + COMMAND_CLASS_METER, + 0x01 //METER_GET + ]; + + command = prepData(nodeId,command,'send'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +/** + * Get the voltage the HEM is currently set to + * @param {Number} nodeId The node ID of the HEM + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.getVoltage = function(nodeId, callback) { + //zwave.sendRequestData(4,[0x70,0x05,0x01],function(reply){ + // console.log('BBBBBBBOOOOOOOOOOOOOOOOOOOMMMMMMMMM!!!!!!!!!!!'); + // console.log(reply); + // + //}); //get param 1 (voltage) + + + var paramId = 1; // voltage param + + var command = [ + COMMAND_CLASS_CONFIGURATION, + CONFIGURATION_GET, + paramId + ]; + + command = prepData(nodeId,command,'request'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + // // 110 volts (6e) + // // Aftter setting to 120 + callback(data); + }); + return promise; // not sure if this promise works? +} + + +/******************************************************************************* + Setters +*******************************************************************************/ + +/** + * Set the voltage of the HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Number} voltage The voltage to set it to, note, this is usually 120 for USA, or 240 outside the USA + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.setVoltage = function(nodeId, voltage, callback) { + var paramId = 1; // voltage param + var paramLength = 2; + + var command = [ + COMMAND_CLASS_CONFIGURATION, + CONFIGURATION_SET, + paramId, + paramLength, + 0x00, // MSB + voltage // LSB + ]; + + command = prepData(nodeId,command,'config'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +/** + * Resets the configuration of the HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.resetConfiguration = function(nodeId) { + var paramId = 255; // reset param + var paramLength = 1; + + var command = [ + COMMAND_CLASS_CONFIGURATION, + CONFIGURATION_SET + paramId, + paramLength, + 0x00 // ?? + ]; + + command = prepData(nodeId,command,'config'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +/** + * Resets the configuration of the HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Number} group The group you would like to set the interval for (1,2, or 3) + * @param {Number} seconds The number of seconds between auto reports, default is 720, ma you can set till I fix this up is 255 + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.setReportingInterval = function(nodeId, group, seconds) { + var paramId = INTERVAL_GROUPS[group]; + var paramLength = 4; + + var command = [ + COMMAND_CLASS_CONFIGURATION, + CONFIGURATION_SET + paramId, + paramLength, + 0x00, // MSB + 0x00, + 0x00, + seconds // LSB TODO: fix this so you can use the full 4 bytes ansd go above 255 seconds + ]; + + command = prepData(nodeId,command,'config'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +/** + * Resets the configuration of the HEM + * @param {Number} nodeId The node ID of the HEM + * @param {Number} group The group you would like to set the interval for (1,2, or 3) + * @param {Number} report Which report to send to that group (watt or kwh) + * @param {Function} [callback] Callback is optional. A promise is returned + */ +energyMonitor.addReportToGroup = function(nodeId, group, report) { + var paramId = REPORT_GROUPS[group]; + var paramLength = 4; + + var command = [ + COMMAND_CLASS_CONFIGURATION, + CONFIGURATION_SET + paramId, + paramLength, + 0x00, // MSB reserved + 0x00, // reserved + 0x00, // see page 6 of the engineering spec from aeon labs, this byte bit 0-5 allows for automatic individual clamp readings + REPORT_TYPES[report] // see page 6 of the engineering spec from aeon labs, this byte bit 0-3 allows for automatic overall readings + ]; + + command = prepData(nodeId,command,'config'); + + var promise = iface.sendMessage(command, 'request', listener); + promise.then(function(data){ + callback(data); + }); + return promise; // not sure if this promise works? +} + +energyMonitor.enableDeltaReporting = function(nodeId) { + // TODO: zwave.sendRequestData(4,[0x70,0x04,0x03,0x00,0x00],function(reply){console.log(reply);}); // Turn on Delta function of the whole HEM + +} + +// helper method to prepare the command packets to send +function prepData(nodeId,cmd,type) { + var mysteryVal = 0; + if(type=='config'){ + mysteryVal = 4+cmd[3]; + } + else if(type=='send'){ + mysteryVal = 2; + } + else if(type=request){ + mysteryVal = 3; + } + + var command = [ + 0x01, + 0x00, // length, calculated after we get a command + 0x00, // I believe this signifies it is a request? + defs.DATA, + nodeId, // nodeid + mysteryVal // This was required to get the HEM configuration working, probably breaks other stuff + ]; + + command = command.concat(cmd); + command.push(0x05); + command.push(0x03); + + command[1] = parseInt(command.length-1); + + return command; +} + +/******************************************************************************* + Listener - handles responses from zwave controller +*******************************************************************************/ + +function listener(data) { + console.log('Energy monitor response received...'); + console.log(data); +} + +module.exports = energyMonitor; + diff --git a/src/command_classes/index.js b/src/command_classes/index.js index e6a7f76..88594d1 100644 --- a/src/command_classes/index.js +++ b/src/command_classes/index.js @@ -3,5 +3,6 @@ var funcs = {}; funcs.thermostat = require('./thermostat'); funcs.binarySwitch = require('./binarySwitch'); +funcs.energyMonitor = require('./energyMonitor'); module.exports = funcs; \ No newline at end of file diff --git a/src/functions/functions.js b/src/functions/functions.js index 4e368b1..49cd9d6 100644 --- a/src/functions/functions.js +++ b/src/functions/functions.js @@ -1,87 +1,6 @@ var iface = require('../interface'); var defs = require('./definitions'); -function sendData(nodeId,cmd,cb) { - console.log('Sending command data'); - - var command = [ - 0x01, - 0x00, // calculated after we get a command - 0x00, - defs.DATA, - nodeId, // nodeid - 2 // This was required to get the HEM get value call working, probably breaks other stuff - ]; - - command = command.concat(cmd); - command.push(0x05); - command.push(0x03); - - command[1] = parseInt(command.length-1); - - console.log(command); - - var promise = iface.sendMessage(command, 'request', listener); - promise.then(function(data){ - cb(data); - }); - -} - -function sendRequestData(nodeId,cmd,cb) { - console.log('Sending request data'); - - var command = [ - 0x01, - 0x00, // calculated after we get a command - 0x00, - defs.DATA, - nodeId, // nodeid - 3 // This was required to get the HEM get value call working - ]; - - command = command.concat(cmd); - command.push(0x05); - command.push(0x03); - - command[1] = parseInt(command.length-1); - - console.log(command); - - var promise = iface.sendMessage(command, 'request', listener); - promise.then(function(data){ - cb(data); - }); - -} - -function sendConfigData(nodeId,cmd,cb) { - console.log('Sending command data'); - - var command = [ - 0x01, - 0x00, // calculated after we get a command - 0x00, - defs.DATA, - nodeId, // nodeid - 4+cmd[3] // This was required to get the HEM configuration working, probably breaks other stuff - ]; - - command = command.concat(cmd); - command.push(0x05); - command.push(0x03); - - command[1] = parseInt(command.length-1); - - console.log(command); - - var promise = iface.sendMessage(command, 'request', listener); - promise.then(function(data){ - cb(data); - }); - -} - function getNodes(cb) { console.log('Getting list of nodes'); @@ -243,8 +162,5 @@ module.exports = { getNodeInfo: getNodeInfo, getNodeProtocol: getNodeProtocol, getNodeSupportedClasses: getNodeSupportedClasses, - associateNode: associateNode, - sendData: sendData, - sendConfigData: sendConfigData, - sendRequestData: sendRequestData + associateNode: associateNode } \ No newline at end of file diff --git a/src/interface.js b/src/interface.js index c3dd92d..8dc7028 100644 --- a/src/interface.js +++ b/src/interface.js @@ -22,8 +22,6 @@ var SerialPort = serialport.SerialPort; var lcd = new SerialPort("/dev/ttyO1", {parser: serialport.parsers.readline("\n") }); lcd.on("open", function () { console.log("I am connected to the lcd"); - //sp.write("73,5,0,0,0,0,0,0,s"); - //sp.write("06,0,15,0,0,0,s"); }); // Add the loop to insert load averages into the DB every minute @@ -216,7 +214,7 @@ function listener(data) { // // if(data[3]==0x04 && data.length==11){ - var currentVal = (data[10] == 211 ? 0 : 255); // 0x00 == 211, which is closed, else the door is open + var currentVal = data[9]; var emitVal = {nodeid:parseInt(data[5],16),value:currentVal}; console.log(emitVal); } diff --git a/src/node-zwave.js b/src/node-zwave.js index 08e1b03..d4223ec 100644 --- a/src/node-zwave.js +++ b/src/node-zwave.js @@ -1,5 +1,4 @@ var cc = require('./command_classes/'); -// var funcs = require('./functions/'); var iface = require('./interface'); var functions = require('./functions/functions'); @@ -12,12 +11,10 @@ zwave.getNodeInfo = functions.getNodeInfo; zwave.getNodeProtocol = functions.getNodeProtocol; zwave.getNodeSupportedClasses = functions.getNodeSupportedClasses; zwave.associateNode = functions.associateNode; -zwave.sendData = functions.sendData; -zwave.sendConfigData = functions.sendConfigData; -zwave.sendRequestData = functions.sendRequestData; zwave.connect = iface.connect; zwave.thermostat = cc.thermostat; +zwave.energyMonitor = cc.energyMonitor; module.exports = zwave; From ffa0d92cfc4fb426a59b3b87fcf9c92b2411edad Mon Sep 17 00:00:00 2001 From: Anhony Webb Date: Wed, 4 Sep 2013 23:29:07 -0600 Subject: [PATCH 12/12] more refactor and cleaning stuff up --- src/interface.js | 204 +--------------------------------------------- src/node-zwave.js | 3 + 2 files changed, 7 insertions(+), 200 deletions(-) diff --git a/src/interface.js b/src/interface.js index 8dc7028..5f39d9f 100644 --- a/src/interface.js +++ b/src/interface.js @@ -1,13 +1,10 @@ /** * Interface for communicating with zwave controller */ +var util = require('util'); var globals = require('./globals'); var Q = require('q'); var moment = require('moment'); -var nedb = require('nedb'); - -// load up the database -var db = new nedb({ filename: './database', autoload: true }); var currentCallbackId = 1; var currentState = 'ready'; // ready || pendingAck || pendingResponse @@ -19,50 +16,6 @@ var messageHandler = {}; // Object to export var serialport = require("serialport"); var SerialPort = serialport.SerialPort; -var lcd = new SerialPort("/dev/ttyO1", {parser: serialport.parsers.readline("\n") }); -lcd.on("open", function () { - console.log("I am connected to the lcd"); -}); - -// Add the loop to insert load averages into the DB every minute -var powerUsed = 0; -var wattcounter = 0; -var watttotal = 0; -var lastkwh = 0; -var prevmeter = 0; -var last_hour = 0; -var tzoffset = -6; -setInterval(function(){ - console.log('writing avg watts using: '+watttotal+'/'+wattcounter) - var avgwatts = watttotal/wattcounter; - - var thisusage = 0; - if(prevmeter){ - thisusage = lastkwh-prevmeter; - } - prevmeter = lastkwh - - var insData = {timestamp: new Date(),watts:avgwatts.toFixed(3),usage:thisusage.toFixed(3),meter:lastkwh.toFixed(3)}; - wattcounter = 0; - watttotal = 0; - db.insert(insData, function (err, newDoc) { - if(err){ - console.log(err); - } - //console.log('wrote record '+newDoc._id); - }); - - // reset the power today total if we just rolled over the clock - var this_hour = moment().add('h',tzoffset).format('H'); // hack the clock back to our time - console.log(this_hour+' this / last '+last_hour); - if(last_hour == 23 && this_hour == 0){ - console.log('Rolling over the clock') - powerUsed = 0; - } - last_hour = this_hour; - -},60000) - function createCallbackId() { currentCallbackId++; if(currentCallbackId > 255) { @@ -208,161 +161,12 @@ function listener(data) { console.log('Catch broadcasted events here: '+moment().format('MMMM Do YYYY, h:mm:ss a')); messageHandler.sendAck(); - // lets parse out this packet and try and figure out what it is: - - // these are the door open/close for nodeid 3: - // - // - if(data[3]==0x04 && data.length==11){ - var currentVal = data[9]; - var emitVal = {nodeid:parseInt(data[5],16),value:currentVal}; - console.log(emitVal); - } - - // these are replies to a SENSOR_MULTILEVEL_REPORT - // - if(data[7]==0x31 && data[8]==0x05 && data.length==16){ - console.log('looks like a multilevel report'); - // data[9] is the sensor type 0x04 is power - // data[10] has the precision(3bits)/scale(2bits)/size(3bits) - var x = pad(toBinary(parseInt(data[10], 10)),8); - //console.log(parseInt(data[10], 10)+' = '+x); - var precision = Bin2Dec(x.substring(0, 3)); - var scale = Bin2Dec(x.substring(3, 5)); - var size = Bin2Dec(x.substring(5)); - console.log('precision:'+precision+' scale:'+scale+' size:'+size); - - // in this report scale is WATTS, only - - // grab the next x hex to concat and find our reading - var reading = ''; - for(var i=11; i < 11+parseInt(size); i++){ - var thisReading = pad(toBinary(parseInt(data[i], 10)),8); - //console.log(parseInt(data[i], 10)+' = '+x); - reading = reading + thisReading; - } - //console.log(reading); - var emitVal = Bin2Dec(reading); - emitVal /= Math.pow(10, precision); // move the decimal over - console.log(emitVal); - console.log('done'); - } - - // these are energy reports for the HEM nodeid 3: / - // - // - // - // - // // kWh (see page 183 command class spec) - // // watts - // // watts - // // watts - - if(data[7]==0x32 && data.length==22){ - console.log('looks like a meter reading'); - // data[10] has the precision(3bits)/scale(2bits)/size(3bits) - var x = pad(toBinary(parseInt(data[10], 10)),8); - //console.log(parseInt(data[10], 10)+' = '+x); - var precision = Bin2Dec(x.substring(0, 3)); - var scale = Bin2Dec(x.substring(3, 5)); - var size = Bin2Dec(x.substring(5)); - console.log('precision:'+precision+' scale:'+scale+' size:'+size); - - //if scale is 0 then this is kWh, if scale is 2 then it is watts - - // grab the next x hex to concat and find our reading - var reading = ''; - for(var i=11; i < 11+parseInt(size); i++){ - var thisReading = pad(toBinary(parseInt(data[i], 10)),8); - //console.log(parseInt(data[i], 10)+' = '+x); - reading = reading + thisReading; - } - //console.log(reading); - var emitVal = Bin2Dec(reading); - emitVal /= Math.pow(10, precision); // move the decimal over - console.log(emitVal); - - // next 2 bits are the delta time - reading = ''; - var newindex = 11+parseInt(size); - for(var i=newindex; i < newindex+2; i++){ - var thisReading = pad(toBinary(parseInt(data[i], 10)),8); - //console.log(parseInt(data[i], 10)+' = '+x); - reading = reading + thisReading; - } - var deltaval = Bin2Dec(reading); - console.log(deltaval); - - // next X are the previous meter value - reading = ''; - newindex = newindex+2; - for(var i=newindex; i < newindex+parseInt(size); i++){ - var thisReading = pad(toBinary(parseInt(data[i], 10)),8); - //console.log(parseInt(data[i], 10)+' = '+x); - reading = reading + thisReading; - } - var prevval = Bin2Dec(reading); - prevval /= Math.pow(10, precision); // move the decimal over - console.log(prevval); - - - var data = {timestamp: new Date()}; - if(scale==2){ - data.watts = emitVal; - - // convert this into a format the LCD can handle - var tmpPowerUsed = Math.floor(powerUsed*1000); - var writestr = parseInt(data.watts)%256+","+Math.floor(parseInt(data.watts)/256)+",0,0,0,0,"+parseInt(tmpPowerUsed)%256+","+Math.floor(parseInt(tmpPowerUsed)/256)+","+moment().add('h',tzoffset).format('H')+",0,"+moment().add('h',tzoffset).format('m')+",0,"+moment().add('h',tzoffset).format('s')+",0"; - lcd.write(writestr+",s"); - console.log(writestr); - wattcounter += 1; - watttotal += emitVal; - - } - else if(scale==0){ - var usage = emitVal-prevval; - powerUsed = powerUsed+usage*1; - data.usage = usage.toFixed(3); - data.delta = deltaval; - console.log('TOTAL power used today: '+powerUsed.toFixed(3)); - // send the power today total to the LCD? - - lastkwh = emitVal; - } - - } + // Emit broadcasted events here... + EVENTS.emit('broadcast',data); runPendingRequest(); - // Catch broadcasted events here... + } } -function toBinary(Decimal){ - var bnum = 0, bexp = 1, digit = 0, bsum = 0; - while(Decimal > 0){ - digit = Decimal % 2; - Decimal = Math.floor(Decimal / 2); - bsum = bsum + digit * bexp; - bexp = bexp * 10; - } - return(bsum); -} -function checkBin(n){return/^[01]{1,64}$/.test(n)} -function checkDec(n){return/^[0-9]{1,64}$/.test(n)} -function checkHex(n){return/^[0-9A-Fa-f]{1,64}$/.test(n)} - -function pad(s,z){s=""+s;return s.length