diff --git a/README.md b/README.md index 5c150f4c..fa93915e 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ Uses a simple read of the serial port as a file to input data. You **must** set **68-mysql** - Allows basic access to a MySQL database. This node uses the **query** operation against the configured database. This does allow both INSERTS and DELETES. By it's very nature it allows SQL injection... *so be careful out there...* +**69-ddbout** - Support output to Amazon DynamoDB. + ### Time **79-suncalc** - Uses the suncalc module to generate an output at sunrise and sunset based on a specified location. Several choices of definition of sunrise and sunset are available, diff --git a/hardware/BBB/README.md b/hardware/BBB/README.md index a141bbbc..1a94e5af 100644 --- a/hardware/BBB/README.md +++ b/hardware/BBB/README.md @@ -63,7 +63,6 @@ of the pulse, or by both edges. Two outputs are provided: Separate scaling factors are applied to each output. Output messages are generated at regular intervals, controlled by an internal timer. The count can be cleared or set to an arbitrary value by an input message who's topic contains 'load'. - Useful for energy monitoring, e.g. electricty meter pulse outputs. The 'instantaneous' pulse rate is derived from either the time between the last two diff --git a/hardware/sensorTag/79-sensorTag.js b/hardware/sensorTag/79-sensorTag.js index 61dbc24b..91d40b07 100644 --- a/hardware/sensorTag/79-sensorTag.js +++ b/hardware/sensorTag/79-sensorTag.js @@ -51,7 +51,7 @@ function sensorTagNode(n) { sensorTag.enableIrTemperature(function(){}); sensorTag.on('irTemperatureChange', function(objectTemperature, ambientTemperature){ - var msg = {'topic': node.topic + '/tempature'}; + var msg = {'topic': node.topic + '/temperature'}; msg.payload = {'object': objectTemperature.toFixed(1), 'ambient':ambientTemperature.toFixed(1) }; diff --git a/io/emoncms/88-emoncms.html b/io/emoncms/88-emoncms.html new file mode 100644 index 00000000..8f20f07e --- /dev/null +++ b/io/emoncms/88-emoncms.html @@ -0,0 +1,120 @@ + + + + + + + + + + + diff --git a/io/emoncms/88-emoncms.js b/io/emoncms/88-emoncms.js new file mode 100644 index 00000000..c1dde58b --- /dev/null +++ b/io/emoncms/88-emoncms.js @@ -0,0 +1,111 @@ +/** + * Copyright 2013 Henrik Olsson henols@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); +//The Server Definition - this opens (and closes) the connection +function EmoncmsServerNode(n) { + RED.nodes.createNode(this,n); + this.server = n.server; + this.name = n.name; + var credentials = RED.nodes.getCredentials(n.id); + if (credentials) { + this.apikey = credentials.apikey; + } + +} +RED.nodes.registerType("emoncms-server",EmoncmsServerNode); + +var querystring = require('querystring'); + +RED.app.get('/emoncms-server/:id',function(req,res) { + var credentials = RED.nodes.getCredentials(req.params.id); + if (credentials) { + res.send(JSON.stringify({apikey:credentials.apikey})); + } else { + res.send(JSON.stringify({})); + } +}); + +RED.app.delete('/emoncms-server/:id',function(req,res) { + RED.nodes.deleteCredentials(req.params.id); + res.send(200); +}); + +RED.app.post('/emoncms-server/:id',function(req,res) { + + var body = ""; + req.on('data', function(chunk) { + body+=chunk; + }); + req.on('end', function(){ + var newCreds = querystring.parse(body); + var credentials = RED.nodes.getCredentials(req.params.id)||{}; + if (newCreds.apikey == null || newCreds.apikey == "") { + delete credentials.apikey; + } else { + credentials.apikey = newCreds.apikey; + } + RED.nodes.addCredentials(req.params.id,credentials); + res.send(200); + }); +}); + +function Emoncms(n) { + RED.nodes.createNode(this,n); + this.emonServer = n.emonServer; + var sc = RED.nodes.getNode(this.emonServer); + + this.baseurl = sc.server; + this.apikey = sc.apikey; + + this.topic = n.topic ||""; + this.nodegroup = n.nodegroup || ""; + var node = this; + if (this.baseurl.substring(0,5) === "https") { var http = require("https"); } + else { var http = require("http"); } + this.on("input", function(msg) { + + var topic = this.topic || msg.topic; + var nodegroup = this.nodegroup || msg.nodegroup; + this.url = this.baseurl + '/input/post.json?json={' + topic + ':' + msg.payload+'}&apikey='+this.apikey; + if(nodegroup != ""){ + this.url += '&node='+nodegroup; + } + node.log("[emoncms] "+this.url); + http.get(this.url, function(res) { + node.log("Http response: " + res.statusCode); + msg.rc = res.statusCode; + msg.payload = ""; + if ((msg.rc != 200) && (msg.rc != 404)) { + node.send(msg); + } + res.setEncoding('utf8'); + res.on('data', function(chunk) { + msg.payload += chunk; + }); + res.on('end', function() { + node.send(msg); + }); + }).on('error', function(e) { + // node.error(e); + msg.rc = 503; + msg.payload = e; + node.send(msg); + }); + }); +} + +RED.nodes.registerType("emoncms",Emoncms); diff --git a/io/emoncms/icons/emoncms-logo.png b/io/emoncms/icons/emoncms-logo.png new file mode 100644 index 00000000..8cde7871 Binary files /dev/null and b/io/emoncms/icons/emoncms-logo.png differ diff --git a/io/emoncms/icons/emoncms.png b/io/emoncms/icons/emoncms.png new file mode 100644 index 00000000..a3abe7d2 Binary files /dev/null and b/io/emoncms/icons/emoncms.png differ diff --git a/io/ping/88-ping.html b/io/ping/88-ping.html index f171c2c5..06e3028f 100644 --- a/io/ping/88-ping.html +++ b/io/ping/88-ping.html @@ -29,14 +29,12 @@ - - + + + + diff --git a/storage/ddb/69-ddbout.js b/storage/ddb/69-ddbout.js new file mode 100644 index 00000000..939d7d23 --- /dev/null +++ b/storage/ddb/69-ddbout.js @@ -0,0 +1,44 @@ +/** + * Copyright 2013 Wolfgang Nagele + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); +var util = require("util"); +var aws = require("aws-sdk"); +var attrWrapper = require("dynamodb-data-types").AttributeValue; + +function DDBOutNode(n) { + RED.nodes.createNode(this, n); + this.credentials = RED.nodes.getNode(n.credentials); + this.region = n.region || "us-east-1"; + this.table = n.table; + + aws.config.update({ accessKeyId: this.credentials.accessKey, + secretAccessKey: this.credentials.secretAccessKey, + region: this.region }); + + var ddb = new aws.DynamoDB(); + + this.on("input", function(msg) { + if (msg != null) { + ddb.putItem({ "TableName": this.table, + "Item": attrWrapper.wrap(msg.payload) }, + function(err, data) { + err && util.log(err); + }); + } + }); +} +RED.nodes.registerType("ddb out", DDBOutNode); diff --git a/storage/ddb/aws.html b/storage/ddb/aws.html new file mode 100644 index 00000000..5c26c904 --- /dev/null +++ b/storage/ddb/aws.html @@ -0,0 +1,68 @@ + + + + + diff --git a/storage/ddb/aws.js b/storage/ddb/aws.js new file mode 100644 index 00000000..9997b676 --- /dev/null +++ b/storage/ddb/aws.js @@ -0,0 +1,65 @@ +/** + * Copyright 2013 Wolfgang Nagele + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); +var querystring = require('querystring'); + +function AWSCredentialsNode(n) { + RED.nodes.createNode(this, n); + var credentials = RED.nodes.getCredentials(n.id); + if (credentials) { + this.accessKey = credentials.accessKey; + this.secretAccessKey = credentials.secretAccessKey; + } +} +RED.nodes.registerType("aws credentials", AWSCredentialsNode); + +RED.app.get('/aws-credentials/:id', function(req, res) { + var credentials = RED.nodes.getCredentials(req.params.id); + if (credentials) { + res.send(JSON.stringify({ accessKey: credentials.accessKey, secretAccessKey: credentials.secretAccessKey })); + } else { + res.send(JSON.stringify({})); + } +}); + +RED.app.delete('/aws-credentials/:id', function(req, res) { + RED.nodes.deleteCredentials(req.params.id); + res.send(200); +}); + +RED.app.post('/aws-credentials/:id', function(req, res) { + var body = ""; + req.on("data", function(chunk) { + body += chunk; + }); + req.on("end", function() { + var newCreds = querystring.parse(body); + var credentials = RED.nodes.getCredentials(req.params.id) || {}; + if (newCreds.accessKey == null || newCreds.accessKey == "") { + delete credentials.accessKey; + } else { + credentials.accessKey = newCreds.accessKey || credentials.accessKey; + } + if (newCreds.secretAccessKey == null || newCreds.secretAccessKey == "") { + delete credentials.secretAccessKey; + } else { + credentials.secretAccessKey = newCreds.secretAccessKey || credentials.secretAccessKey; + } + RED.nodes.addCredentials(req.params.id, credentials); + res.send(200); + }); +}); diff --git a/storage/postgres/110-postgres.html b/storage/postgres/110-postgres.html new file mode 100644 index 00000000..6fae6ac1 --- /dev/null +++ b/storage/postgres/110-postgres.html @@ -0,0 +1,146 @@ + + + + + + + + + + + diff --git a/storage/postgres/110-postgres.js b/storage/postgres/110-postgres.js new file mode 100644 index 00000000..4e2d41ee --- /dev/null +++ b/storage/postgres/110-postgres.js @@ -0,0 +1,123 @@ +/** + * Copyright 2013 Kris Daniels. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var RED = require(process.env.NODE_RED_HOME+"/red/red"); +var pg=require('pg'); +var named=require('node-postgres-named'); +var querystring = require('querystring'); + +RED.app.get('/postgresdb/:id',function(req,res) { + var credentials = RED.nodes.getCredentials(req.params.id); + if (credentials) { + res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")})); + } else { + res.send(JSON.stringify({})); + } +}); + +RED.app.delete('/postgresdb/:id',function(req,res) { + RED.nodes.deleteCredentials(req.params.id); + res.send(200); +}); + +RED.app.post('/postgresdb/:id',function(req,res) { + var body = ""; + req.on('data', function(chunk) { + body+=chunk; + }); + req.on('end', function(){ + var newCreds = querystring.parse(body); + var credentials = RED.nodes.getCredentials(req.params.id)||{}; + if (newCreds.user == null || newCreds.user == "") { + delete credentials.user; + } else { + credentials.user = newCreds.user; + } + if (newCreds.password == "") { + delete credentials.password; + } else { + credentials.password = newCreds.password||credentials.password; + } + RED.nodes.addCredentials(req.params.id,credentials); + res.send(200); + }); +}); + + +function PostgresDatabaseNode(n) { + RED.nodes.createNode(this,n); + this.hostname = n.hostname; + this.port = n.port; + this.db = n.db; + + var credentials = RED.nodes.getCredentials(n.id); + if (credentials) { + this.user = credentials.user; + this.password = credentials.password; + } +} + +RED.nodes.registerType("postgresdb",PostgresDatabaseNode); + +function PostgresNode(n) { + RED.nodes.createNode(this,n); + + this.topic = n.topic; + this.postgresdb = n.postgresdb; + this.postgresConfig = RED.nodes.getNode(this.postgresdb); + this.sqlquery = n.sqlquery; + this.output = n.output; + + var node = this; + + if(this.postgresConfig) + { + + var conString = 'postgres://'+this.postgresConfig.user +':' + this.postgresConfig.password + '@' + this.postgresConfig.hostname + ':' + this.postgresConfig.port + '/' + this.postgresConfig.db; + node.clientdb = new pg.Client(conString); + named.patch(node.clientdb); + + node.clientdb.connect(function(err){ + if(err) { node.error(err); } + else { + node.on('input', + function(msg){ + if(!msg.queryParameters) msg.queryParameters={}; + node.clientdb.query(msg.payload, + msg.queryParameters, + function (err, results) { + if(err) { node.error(err); } + else { + if(node.output) + { + msg.payload = results.rows; + node.send(msg); + } + } + }); + }); + } + }); + } else { + this.error("missing postgres configuration"); + } + + this.on("close", function() { + if(node.clientdb) node.clientdb.end(); + }); +} + +RED.nodes.registerType("postgres",PostgresNode); diff --git a/storage/postgres/icons/postgres.png b/storage/postgres/icons/postgres.png new file mode 100644 index 00000000..e46c3169 Binary files /dev/null and b/storage/postgres/icons/postgres.png differ