diff --git a/analysis/wordpos/72-wordpos.html b/analysis/wordpos/72-wordpos.html
new file mode 100644
index 00000000..b50156ea
--- /dev/null
+++ b/analysis/wordpos/72-wordpos.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
diff --git a/analysis/wordpos/72-wordpos.js b/analysis/wordpos/72-wordpos.js
new file mode 100644
index 00000000..0725cb1e
--- /dev/null
+++ b/analysis/wordpos/72-wordpos.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var util = require("util");
+var WordPos = require('wordpos');
+
+var wordpos = new WordPos();
+
+function WordPOSNode(n) {
+ RED.nodes.createNode(this,n);
+ this.on("input", function(msg) {
+ var node = this;
+ wordpos.getPOS(msg.payload, function (result) {
+ msg.pos = result;
+ node.send(msg);
+ });
+ });
+}
+
+RED.nodes.registerType("wordpos",WordPOSNode);
diff --git a/hardware/blink/76-blinkstick.html b/hardware/blink/76-blinkstick.html
new file mode 100644
index 00000000..1bef4306
--- /dev/null
+++ b/hardware/blink/76-blinkstick.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
diff --git a/hardware/blink/76-blinkstick.js b/hardware/blink/76-blinkstick.js
new file mode 100644
index 00000000..cae30ca8
--- /dev/null
+++ b/hardware/blink/76-blinkstick.js
@@ -0,0 +1,62 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var blinkstick = require("blinkstick");
+
+Object.size = function(obj) {
+ var size = 0, key;
+ for (key in obj) { if (obj.hasOwnProperty(key)) size++; }
+ return size;
+};
+
+function BlinkStick(n) {
+ RED.nodes.createNode(this,n);
+ var p1 = /^\#[A-Fa-f0-9]{6}$/
+ var p2 = /[0-9]+,[0-9]+,[0-9]+/
+ this.led = blinkstick.findFirst(); // maybe try findAll() (one day)
+ var node = this;
+
+ this.on("input", function(msg) {
+ if (msg != null) {
+ if (Object.size(node.led) !== 0) {
+ try {
+ if (p2.test(msg.payload)) {
+ var rgb = msg.payload.split(",");
+ node.led.setColor(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255);
+ }
+ else {
+ node.led.setColor(msg.payload.toLowerCase().replace(/\s+/g,''));
+ }
+ }
+ catch (err) {
+ node.warn("BlinkStick missing ?");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ else {
+ //node.warn("No BlinkStick found");
+ node.led = blinkstick.findFirst();
+ }
+ }
+ });
+ if (Object.size(node.led) === 0) {
+ node.error("No BlinkStick found");
+ }
+
+}
+
+RED.nodes.registerType("blinkstick",BlinkStick);
diff --git a/hardware/blink/77-blink1.html b/hardware/blink/77-blink1.html
new file mode 100644
index 00000000..00579ce8
--- /dev/null
+++ b/hardware/blink/77-blink1.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
diff --git a/hardware/blink/77-blink1.js b/hardware/blink/77-blink1.js
new file mode 100644
index 00000000..236683ed
--- /dev/null
+++ b/hardware/blink/77-blink1.js
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var Blink1 = require("node-blink1");
+
+function Blink1Node(n) {
+ RED.nodes.createNode(this,n);
+ this.fade = n.fade||0;
+ var node = this;
+
+ try {
+ var p1 = /^\#[A-Fa-f0-9]{6}$/
+ var p2 = /[0-9]+,[0-9]+,[0-9]+/
+ this.on("input", function(msg) {
+ if (blink1) {
+ if (p1.test(msg.payload)) {
+ // if it is a hex colour string
+ var r = parseInt(msg.payload.slice(1,3),16);
+ var g = parseInt(msg.payload.slice(3,5),16);
+ var b = parseInt(msg.payload.slice(5),16);
+ if (node.fade == 0) { blink1.setRGB( r, g, b ); }
+ else { blink1.fadeToRGB(node.fade, r, g, b ); }
+ }
+ else if (p2.test(msg.payload)) {
+ // if it is a r,g,b triple
+ var rgb = msg.payload.split(',');
+ if (node.fade == 0) { blink1.setRGB(parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255); }
+ else { blink1.fadeToRGB(node.fade, parseInt(rgb[0])&255, parseInt(rgb[1])&255, parseInt(rgb[2])&255); }
+ }
+ else {
+ // you can do fancy colours by name here if you want...
+ node.warn("Blink1 : invalid msg : "+msg.payload);
+ }
+ }
+ else {
+ node.warn("No Blink1 found");
+ }
+ });
+ var blink1 = new Blink1.Blink1();
+ }
+ catch(e) {
+ node.error("No Blink1 found");
+ }
+}
+
+RED.nodes.registerType("blink1",Blink1Node);
diff --git a/social/notify/57-notify.html b/social/notify/57-notify.html
new file mode 100644
index 00000000..67c04cbf
--- /dev/null
+++ b/social/notify/57-notify.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
diff --git a/social/notify/57-notify.js b/social/notify/57-notify.js
new file mode 100644
index 00000000..9330966d
--- /dev/null
+++ b/social/notify/57-notify.js
@@ -0,0 +1,39 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var growl = require('growl');
+var imagefile = __dirname+"/../../public/mqtt-node-red.png";
+
+function NotifyNode(n) {
+ RED.nodes.createNode(this,n);
+ this.title = n.title;
+ var node = this;
+ this.on("input",function(msg) {
+ var titl = this.title||msg.topic;
+ if (typeof(msg.payload) == 'object') {
+ msg.payload = JSON.stringify(msg.payload);
+ }
+ if (typeof(titl) != 'undefined') {
+ growl(msg.payload, { title: titl, image: imagefile });
+ }
+ else {
+ growl(msg.payload, { image: imagefile });
+ }
+ });
+}
+
+RED.nodes.registerType("notify",NotifyNode);
diff --git a/social/prowl/57-prowl.html b/social/prowl/57-prowl.html
new file mode 100644
index 00000000..6a897f79
--- /dev/null
+++ b/social/prowl/57-prowl.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
diff --git a/social/prowl/57-prowl.js b/social/prowl/57-prowl.js
new file mode 100644
index 00000000..d59dc2a3
--- /dev/null
+++ b/social/prowl/57-prowl.js
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var Prowl = require('node-prowl');
+var util = require('util');
+
+// Either add a line like this to settings.js
+// prowl: {prowlkey:'My-API-KEY'},
+// or create pushkey.js in dir ABOVE node-red, it just needs to be like
+// module.exports = {prowlkey:'My-API-KEY'}
+
+try {
+ var pushkey = require("../../settings").prowl || require("../../../pushkey.js");
+}
+catch(err) {
+ util.log("[57-prowl.js] Error: Failed to load Prowl credentials");
+}
+
+if (pushkey) {
+ var prowl = new Prowl(pushkey.prowlkey);
+}
+
+function ProwlNode(n) {
+ RED.nodes.createNode(this,n);
+ this.title = n.title;
+ this.priority = parseInt(n.priority);
+ if (this.priority > 2) this.priority = 2;
+ if (this.priority < -2) this.priority = -2;
+ var node = this;
+ this.on("input",function(msg) {
+ var titl = this.title||msg.topic||"Node-RED";
+ var pri = msg.priority||this.priority;
+ if (typeof(msg.payload) == 'object') {
+ msg.payload = JSON.stringify(msg.payload);
+ }
+ if (pushkey) {
+ try {
+ prowl.push(msg.payload, titl, { priority: pri }, function(err, remaining) {
+ if (err) node.error(err);
+ node.log( remaining + ' calls to Prowl api during current hour.' );
+ });
+ }
+ catch (err) {
+ node.error(err);
+ }
+ }
+ else {
+ node.warn("Prowl credentials not set/found. See node info.");
+ }
+ });
+}
+
+RED.nodes.registerType("prowl",ProwlNode);
diff --git a/social/prowl/icons/prowl.png b/social/prowl/icons/prowl.png
new file mode 100644
index 00000000..43f32253
Binary files /dev/null and b/social/prowl/icons/prowl.png differ
diff --git a/social/pushbullet/57-pushbullet.html b/social/pushbullet/57-pushbullet.html
new file mode 100644
index 00000000..32020297
--- /dev/null
+++ b/social/pushbullet/57-pushbullet.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
diff --git a/social/pushbullet/57-pushbullet.js b/social/pushbullet/57-pushbullet.js
new file mode 100644
index 00000000..b839e893
--- /dev/null
+++ b/social/pushbullet/57-pushbullet.js
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var PushBullet = require('pushbullet');
+var util = require('util');
+
+// Either add a line like this to settings.js
+// pushbullet: {pushbullet:'My-API-KEY', deviceid:'12345'},
+// or create pushkey.js in dir ABOVE node-red, it just needs to be like
+// module.exports = {pushbullet:'My-API-KEY', deviceid:'12345'}
+
+try {
+ var pushkey = require("../../settings").pushbullet || require("../../../pushkey.js");
+}
+catch(err) {
+ util.log("[57-pushbullet.js] Error: Failed to load PushBullet credentials");
+}
+
+if (pushkey) {
+ var pusher = new PushBullet(pushkey.pushbullet);
+ var deviceId = pushkey.deviceid;
+}
+
+function PushbulletNode(n) {
+ RED.nodes.createNode(this,n);
+ this.title = n.title;
+ var node = this;
+ this.on("input",function(msg) {
+ var titl = this.title||msg.topic||"Node-RED";
+ if (typeof(msg.payload) == 'object') {
+ msg.payload = JSON.stringify(msg.payload);
+ }
+ if (pushkey) {
+ try {
+ pusher.note(deviceId, titl, msg.payload, function(err, response) {
+ if (err) node.error(err);
+ console.log(response);
+ });
+ }
+ catch (err) {
+ node.error(err);
+ }
+ }
+ else {
+ node.warn("Pushbullet credentials not set/found. See node info.");
+ }
+ });
+}
+
+RED.nodes.registerType("pushbullet",PushbulletNode);
diff --git a/social/pushbullet/icons/bullet.png b/social/pushbullet/icons/bullet.png
new file mode 100644
index 00000000..3cf11b3f
Binary files /dev/null and b/social/pushbullet/icons/bullet.png differ
diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html
new file mode 100644
index 00000000..daaa2715
--- /dev/null
+++ b/social/xmpp/92-xmpp.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
diff --git a/social/xmpp/92-xmpp.js b/social/xmpp/92-xmpp.js
new file mode 100644
index 00000000..15f95795
--- /dev/null
+++ b/social/xmpp/92-xmpp.js
@@ -0,0 +1,118 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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 orig=console.warn;
+console.warn=(function() { // suppress warning from stringprep when not needed)
+ var orig=console.warn;
+ return function() {
+ //orig.apply(console, arguments);
+ };
+})();
+
+var RED = require("../../red/red");
+var xmpp = require('simple-xmpp');
+console.warn = orig;
+
+try {
+ var xmppkey = require("../../settings").xmpp || require("../../../xmppkeys.js");
+} catch(err) {
+ throw new Error("Failed to load XMPP credentials");
+}
+
+function XmppNode(n) {
+ RED.nodes.createNode(this,n);
+ this.server = n.server;
+ this.port = n.port;
+ this.join = n.join || false;
+ this.nick = n.nick || "Node-RED";
+ this.sendAll = n.sendObject;
+ this.to = n.to || "";
+ var node = this;
+
+ setTimeout(function() {
+ xmpp.connect({
+ jid : xmppkey.jid,
+ password : xmppkey.password,
+ host : this.server,
+ port : this.port,
+ skipPresence : true,
+ reconnect : false
+ });
+ }, 5000);
+
+ xmpp.on('online', function() {
+ node.log('connected to '+node.server);
+ xmpp.setPresence('online', node.nick+' online');
+ if (node.join) {
+ xmpp.join(node.to+'/'+node.nick);
+ }
+ });
+
+ xmpp.on('chat', function(from, message) {
+ var msg = { topic:from, payload:message };
+ node.send([msg,null]);
+ });
+
+ xmpp.on('groupchat', function(conference, from, message, stamp) {
+ var msg = { topic:from, payload:message, room:conference };
+ if (from != node.nick) { node.send([msg,null]); }
+ });
+
+ //xmpp.on('chatstate', function(from, state) {
+ //console.log('%s is currently %s', from, state);
+ //var msg = { topic:from, payload:state };
+ //node.send([null,msg]);
+ //});
+
+ xmpp.on('buddy', function(jid, state, statusText) {
+ node.log(jid+" is "+state+" : "+statusText);
+ var msg = { topic:jid, payload: { presence:state, status:statusText} };
+ node.send([null,msg]);
+ });
+
+ xmpp.on('error', function(err) {
+ console.error(err);
+ });
+
+ xmpp.on('close', function(err) {
+ node.log('connection closed');
+ });
+
+ xmpp.on('subscribe', function(from) {
+ xmpp.acceptSubscription(from);
+ });
+
+ this.on("input", function(msg) {
+ var to = msg.topic;
+ if (node.to != "") { to = node.to; }
+ if (node.sendAll) {
+ xmpp.send(to, JSON.stringify(msg), node.join);
+ }
+ else {
+ xmpp.send(to, msg.payload, node.join);
+ }
+ });
+
+ this.on("close", function() {
+ xmpp.setPresence('offline');
+ //xmpp.conn.end();
+ // TODO - DCJ NOTE... this is not good. It leaves the connection up over a restart - which will end up with bad things happening...
+ // (but requires the underlying xmpp lib to be fixed (which does have an open bug request on fixing the close method)).
+ this.warn("Due to an underlying bug in the xmpp library this does not disconnect old sessions. This is bad... A restart would be better.");
+ });
+}
+
+RED.nodes.registerType("xmpp",XmppNode);
diff --git a/social/xmpp/icons/xmpp.png b/social/xmpp/icons/xmpp.png
new file mode 100644
index 00000000..57e43fc4
Binary files /dev/null and b/social/xmpp/icons/xmpp.png differ
diff --git a/storage/leveldb/67-leveldb.html b/storage/leveldb/67-leveldb.html
new file mode 100644
index 00000000..bca7afb0
--- /dev/null
+++ b/storage/leveldb/67-leveldb.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/storage/leveldb/67-leveldb.js b/storage/leveldb/67-leveldb.js
new file mode 100644
index 00000000..050d456b
--- /dev/null
+++ b/storage/leveldb/67-leveldb.js
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * 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("../../red/red");
+var lvldb = require('level');
+
+function LevelNode(n) {
+ RED.nodes.createNode(this,n);
+ this.dbname = n.db;
+ var node = this;
+ lvldb(this.dbname, function(err, db) {
+ if (err) node.error(err);
+ node.db = db;
+ });
+}
+RED.nodes.registerType("leveldbase",LevelNode);
+LevelNode.prototype.close = function() {
+ this.db.close();
+}
+
+function LevelDBNodeIn(n) {
+ RED.nodes.createNode(this,n);
+ this.level = n.level;
+ this.levelConfig = RED.nodes.getNode(this.level);
+
+ if (this.levelConfig) {
+ var node = this;
+ node.on("input", function(msg) {
+ if (typeof msg.topic === 'string') {
+ node.levelConfig.db.get(msg.topic, function(err, value) {
+ if (err) {
+ //node.warn(err);
+ // for some reason they treat nothing found as an error...
+ msg.payload = null; // so we should return null
+ }
+ else { msg.payload = value; }
+ node.send(msg);
+ });
+ }
+ else {
+ if (typeof msg.topic !== 'string') node.error("msg.topic (the key is not defined");
+ }
+ });
+ }
+ else {
+ this.error("LevelDB database name not configured");
+ }
+}
+RED.nodes.registerType("leveldb in",LevelDBNodeIn);
+
+
+function LevelDBNodeOut(n) {
+ RED.nodes.createNode(this,n);
+ this.level = n.level;
+ this.operation = n.operation;
+ this.levelConfig = RED.nodes.getNode(this.level);
+
+ if (this.levelConfig) {
+ var node = this;
+ node.on("input", function(msg) {
+ if (typeof msg.topic === 'string') {
+ if (node.operation === "delete") {
+ node.levelConfig.db.del(msg.topic);
+ }
+ else {
+ node.levelConfig.db.put(msg.topic, msg.payload, function(err) {
+ if (err) node.error(err);
+ });
+ }
+ }
+ else {
+ if (typeof msg.topic !== 'string') node.error("msg.topic (the key is not defined");
+ }
+ });
+ }
+ else {
+ this.error("LevelDB database name not configured");
+ }
+}
+RED.nodes.registerType("leveldb out",LevelDBNodeOut);