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/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