diff --git a/hardware/wemo/60-wemo.html b/hardware/wemo/60-wemo.html
deleted file mode 100644
index cec3bd61..00000000
--- a/hardware/wemo/60-wemo.html
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/hardware/wemo/60-wemo.js b/hardware/wemo/60-wemo.js
deleted file mode 100644
index f11383e0..00000000
--- a/hardware/wemo/60-wemo.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * 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.
- **/
-
-module.exports = function(RED) {
- "use strict";
- var Wemo = require('wemo');
-
- function WemoOut(n) {
- RED.nodes.createNode(this,n);
- this.ipaddr = n.ipaddr;
- this.wemoSwitch = new Wemo(n.ipaddr);
- var node = this;
-
- this.on("input", function(msg) {
- var state = 0;
- if ( msg.payload == 1 || msg.payload === true || msg.payload == "on" ) { state = 1; }
- node.wemoSwitch.setBinaryState(state, function(err, result) {
- if (err) { node.warn(err); }
- //else { node.log(result); }
- });
- });
- }
- RED.nodes.registerType("wemo out",WemoOut);
-
- function WemoIn(n) {
- RED.nodes.createNode(this,n);
- this.ipaddr = n.ipaddr;
- this.wemoSwitch = new Wemo(n.ipaddr);
- this.wemoSwitch.state = 0;
- var node = this;
-
- var tick = setInterval(function() {
- node.wemoSwitch.getBinaryState(function(err, result) {
- if (err) { node.warn(err); }
- if (parseInt(result) != node.wemoSwitch.state) {
- node.wemoSwitch.state = parseInt(result);
- node.send({payload:node.wemoSwitch.state,topic:"wemo/"+node.ipaddr});
- }
- });
- }, 2000);
-
- this.on("close", function() {
- clearInterval(tick);
- });
- }
- RED.nodes.registerType("wemo in",WemoIn);
-}
diff --git a/hardware/wemo/README.md b/hardware/wemo/README.md
index 8b9b4ab2..57e4a44f 100644
--- a/hardware/wemo/README.md
+++ b/hardware/wemo/README.md
@@ -1,7 +1,8 @@
-node-red-node-wemo
-==================
+# node-red-contrib-nodes-wemo
-A pair of Node-RED nodes to control a Belkin Wemo set of devices.
+A set of Node-RED nodes for working with Belkin WeMo devices.
+
+These nodes use the uPnP discovery so may not discover your devices if you have a firewall enabled
Install
-------
@@ -11,16 +12,71 @@ Run the following command in your Node-RED user directory - typically `~/.node-r
npm install node-red-node-wemo
-Usage
------
+## Output node
-It doesn't yet do any ip address discovery of the wemo devices.
+The output node switches a socket, a light or group of lights on or off
-### Wemo output node.
+This should be backward compatible with the pervious version of this node but will benefit
+from opening the config dialog and selecting the node you want.
-Expects a `msg.payload` with either 1/0, on/off or true/false.
+The node accecpts the following inputs
+ * Strings on/off
+ * integers 1/0
+ * boolean true/false
+ * an Object like this (lights only, coming soon)
+ ```
+ {
+ state: 1,
+ dim: 255,
+ color: '255,255,255',
+ temperature: 25000
+ }
+ ```
-### Wemo input node.
+## Input Node
-Creates a `msg.payload` with either 1, 0, nc (no change), or na (not available).
+The new input node is now based on uPnP notifications instead of polling. This means messages
+will only be set when an actual change occurs in on the device. This means the node will not
+send regular no-change messages.
+
+The output varies depending on the type of device but examples for sockets look like this:
+
+```
+ {
+ "raw": "\n\n1\n\n\n\n\r",
+ "state": "1",
+ "sid": "uuid:e2c4586c-1dd1-11b2-8f61-b535035ae35d",
+ "type": "socket",
+ "name": "Bedroom Switch",
+ "id": "221448K1100085"
+ }
+```
+
+And a lightblub can look like this:
+
+```
+ {
+ "raw": "\n\n<?xml version="1.0" encoding="utf-8"?><StateEvent><DeviceID\navailable="YES">94103EA2B27803ED</DeviceID><CapabilityId>10006</CapabilityId><Value>1</Value></StateEvent>\n\n\n\n\n\r",
+ "id": "94103EA2B27803ED",
+ "capability": "10006",
+ "value": "1",
+ "sid": "uuid:e2e5739e-1dd1-11b2-943d-c238ce2bad17",
+ "type": "light",
+ "name": "Bedroom"
+ }
+```
+
+Insight
+
+```
+ {
+ "raw": "\n\n8|1454271649|301|834|56717|1209600|8|1010|638602|12104165\n\n\n\n\r",
+ "state": "8",
+ "power": 1.01,
+ "sid": "uuid:ea808ecc-1dd1-11b2-9579-8e5c117d479e",
+ "type": "socket",
+ "name": "WeMo Insight",
+ "id": "221450K1200F5C"
+ }
+```
diff --git a/hardware/wemo/WeMoNG.html b/hardware/wemo/WeMoNG.html
new file mode 100644
index 00000000..71c82e34
--- /dev/null
+++ b/hardware/wemo/WeMoNG.html
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hardware/wemo/WeMoNG.js b/hardware/wemo/WeMoNG.js
new file mode 100644
index 00000000..f3292810
--- /dev/null
+++ b/hardware/wemo/WeMoNG.js
@@ -0,0 +1,386 @@
+/**
+ * Copyright 2016 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 WeMoNG = require('./lib/wemo.js');
+
+var wemo = new WeMoNG();
+
+//this won't work as there is no way to stop it...
+//but is that a problem?
+var interval = setInterval(wemo.start.bind(wemo), 60000);
+wemo.start();
+
+module.exports = function(RED) {
+ "use strict";
+ var util = require('util');
+ var ip = require('ip');
+ var bodyParser = require('body-parser');
+ var http = require('http');
+ var os = require('os');
+
+ var settings = RED.settings;
+
+ var subscriptions = {};
+ var sub2dev = {};
+
+ var resubscribe = function() {
+
+ var subs = Object.keys(subscriptions);
+ for (var s in subs) {
+ if (subs.hasOwnProperty(s)) {
+ var sub = subscriptions[subs[s]];
+ var dev = wemo.get(subs[s]);
+ var reSubOptions = {
+ host: dev.ip,
+ port: dev.port,
+ path: dev.device.UDN.indexOf('Bridge-1_0') < 0 ? '/upnp/event/basicevent1': '/upnp/event/bridge1',
+ method: 'SUBSCRIBE',
+ headers: {
+ 'SID': sub.sid,
+ 'TIMEOUT': 'Second-300'
+ }
+ };
+
+ var resub_request = http.request(reSubOptions, function(res) {
+ //shoudl raise an error if needed
+ if (res.statusCode != 200) {
+ console.log("problem with resubscription %s - %s", res.statusCode, res.statusMessage);
+ console.log("opts - %s", util.inspect(reSubOptions));
+ console.log("dev - %s", util.inspect(dev));
+ delete subscriptions[dev];
+ delete sub2dev[sub.sid];
+ subscribe({dev: subs[s]});
+ } else {
+ // console.log("resubscription good %s", res.statusCode);
+ // console.log("dev - %s", util.inspect(dev));
+ }
+ });
+
+ resub_request.on('error', function(){
+ //console.log("failed to resubscribe to %s", dev.name );
+ //need to find a way to resubsribe
+ delete subscriptions[dev];
+ delete sub2dev[sub.sid];
+ subscribe({dev: subs[s]});
+ });
+
+ resub_request.end();
+
+ }
+ }
+
+ }
+
+ setInterval(resubscribe, 200000);
+
+ var subscribe = function(node) {
+ var dev = node.dev;
+ var device = wemo.get(dev);
+ if (device){
+ if (subscriptions[dev]) {
+ //exists
+ subscriptions[dev].count++;
+ } else {
+ //new
+
+ var ipAddr;
+ //device.ip
+ var interfaces = os.networkInterfaces();
+ var interfaceNames = Object.keys(interfaces);
+ for (var name in interfaceNames) {
+ if (interfaceNames.hasOwnProperty(name)) {
+ var addrs = interfaces[interfaceNames[name]];
+ for (var add in addrs) {
+ if (addrs[add].netmask){
+ //node 0.12 or better
+ if (!addrs[add].internal && addrs[add].family == 'IPv4') {
+ if (ip.isEqual(ip.mask(addrs[add].address,addrs[add].netmask),ip.mask(device.ip,addrs[add].netmask))) {
+ ipAddr = addrs[add].address;
+ break;
+ }
+ }
+ } else {
+ //node 0.10 not great but best we can do
+ if (!addrs[add].internal && addrs[add].family == 'IPv4') {
+ ipAddr = addrs[add].address;
+ break;
+ }
+ }
+ }
+ if (ipAddr) {
+ break;
+ }
+ }
+ }
+
+ var callback_url = 'http://' + ipAddr + ':' + settings.uiPort;
+ if(settings.httpAdminRoot) {
+ callback_url += settings.httpAdminRoot;
+ }
+
+ if (callback_url.lastIndexOf('/') != (callback_url.length -1)) {
+ callback_url += '/';
+ }
+
+ callback_url += 'wemoNG/notification';
+
+ console.log("Callback URL = %s",callback_url);
+
+ var subscribeOptions = {
+ host: device.ip,
+ port: device.port,
+ path: device.device.UDN.indexOf('Bridge-1_0') < 0 ? '/upnp/event/basicevent1': '/upnp/event/bridge1',
+ method: 'SUBSCRIBE',
+ headers: {
+ 'CALLBACK': '<' + callback_url + '>',
+ 'NT': 'upnp:event',
+ 'TIMEOUT': 'Second-300'
+ }
+ };
+
+ //console.log(util.inspect(subscribeOptions));
+
+ var sub_request = http.request(subscribeOptions, function(res) {
+ //console.log("subscribe: %s - %s", device.name, res.statusCode);
+ if (res.statusCode == 200) {
+ subscriptions[dev] = {'count': 1, 'sid': res.headers.sid};
+ sub2dev[res.headers.sid] = dev;
+ } else {
+ console.log("failed to subsrcibe");
+ }
+ });
+
+ sub_request.end();
+ }
+ }
+ }
+
+ function unsubscribe(node) {
+ var dev = node.dev;
+ if (subscriptions[dev]) {
+ if (subscriptions[dev].count == 1) {
+ var sid = subscriptions[dev].sid;
+
+ var device = wemo.get(dev);
+ //need to unsubsribe properly here
+ var unSubOpts = {
+ host: device.ip,
+ port: device.port,
+ path: device.device.UDN.indexOf('Bridge-1_0') < 0 ? '/upnp/event/basicevent1': '/upnp/event/bridge1',
+ method: 'UNSUBSCRIBE',
+ headers: {
+ 'SID': sid
+ }
+ };
+
+ //console.log(util.inspect(unSubOpts));
+
+ var unSubreq = http.request(unSubOpts, function(res){
+ //console.log("unsubscribe: %s \n %s", device.name, res.statusCode);
+ delete subscriptions[dev];
+ delete sub2dev[sid];
+ });
+
+ unSubreq.end();
+
+ } else {
+ subscriptions[dev].count--;
+ }
+ } else {
+ //shouldn't ever get here
+ }
+ }
+
+ var wemoNGConfig = function(n) {
+ RED.nodes.createNode(this,n);
+ this.device = n.device;
+ }
+ RED.nodes.registerType("wemo-dev", wemoNGConfig);
+
+ var wemoNGNode = function(n) {
+ RED.nodes.createNode(this,n);
+ var node = this;
+ node.device = n.device;
+ node.name = n.name;
+ node.dev = RED.nodes.getNode(node.device).device;
+ node.status({fill:"red",shape:"dot",text:"searching"});
+
+ //console.log("Control - %j" ,this.dev);
+ if (!wemo.get(node.dev)){
+ wemo.on('discovered', function(d){
+ if (node.dev === d) {
+ node.status({fill:"green",shape:"dot",text:"found"});
+ }
+ });
+ } else {
+ node.status({fill:"green",shape:"dot",text:"found"});
+ }
+
+ node.on('input', function(msg){
+ var dev = wemo.get(node.dev);
+
+ if (!dev) {
+ //need to show that dev not currently found
+ console.log("no device found");
+ return;
+ }
+
+ var on = 0;
+ if (typeof msg.payload === 'string') {
+ if (msg.payload == 'on' || msg.payload == '1' || msg.payload == 'true') {
+ on = 1;
+ } else if (msg.payload === 'toggle') {
+ on = 2;
+ }
+ } else if (typeof msg.payload === 'number') {
+ if (msg.payload >= 0 && msg.payload < 3) {
+ on = msg.payload;
+ }
+ } else if (typeof msg.payload === 'object') {
+ //object need to get complicated here
+ if (msg.payload.state && typeof msg.payload.state === 'number') {
+ if (dev.type === 'socket') {
+ if (msg.payload >= 0 && msg.payload < 2) {
+ on = msg.payload.state
+ }
+ } else if (dev.type === 'light' || dev.type === 'group') {
+ if (msg.payload >= 0 && msg.payload < 3) {
+ on = msg.payload.state;
+ }
+ }
+ }
+ } else if (typeof msg.payload === 'boolean') {
+ if (msg.payload) {
+ on = 1;
+ }
+ }
+
+ if (dev.type === 'socket') {
+ //console.log("socket");
+ wemo.toggleSocket(dev, on);
+ } else if (dev.type === 'light`') {
+ //console.log("light");
+ wemo.setStatus(dev,"10006", on);
+ } else {
+ console.log("group");
+ wemo.setStatus(dev, "10006", on);
+ }
+ });
+ }
+ RED.nodes.registerType("wemo out", wemoNGNode);
+
+ var wemoNGEvent = function(n) {
+ RED.nodes.createNode(this,n);
+ var node = this;
+ node.ipaddr = n.ipaddr;
+ node.device = n.device;
+ node.name = n.name;
+ node.topic = n.topic;
+ node.dev = RED.nodes.getNode(node.device).device;
+
+ node.status({fill:"red",shape:"dot",text:"searching"});
+
+ var onEvent = function(notification){
+ var d = sub2dev[notification.sid];
+ if (d == node.dev) {
+ var dd = wemo.get(node.dev);
+ notification.type = dd.type;
+ notification.name = dd.name;
+ if (!notification.id) {
+ notification.id = node.dev;
+ }
+
+ var msg = {
+ topic: node.topic ? node.topic : 'wemo',
+ payload: notification
+ };
+
+ switch (notification.type){
+ case 'light':
+ case 'group':
+ if (dd.id === notification.id) {
+ node.send(msg);
+ }
+ break;
+ case 'socket':
+ node.send(msg);
+ break;
+ default:
+ }
+
+ }
+ };
+
+ wemo.on('event', onEvent);
+
+ if (node.dev) {
+ //subscribe to events
+ if (wemo.get(node.dev)) {
+ node.status({fill:"green",shape:"dot",text:"found"});
+ subscribe(node);
+ } else {
+ wemo.on('discovered', function(d){
+ if (node.dev === d) {
+ node.status({fill:"green",shape:"dot",text:"found"});
+ subscribe(node);
+ }
+ });
+ }
+ } else if (node.ipaddr) {
+ //legacy
+ var devices = Object.keys(wemo.devices);
+ for (var d in devices) {
+ if (devices.hasOwnProperty(d)) {
+ var device = devices[d];
+ if (device.ip === node.ipaddr) {
+ node.dev = device.id;
+ node.status({fill:"green",shape:"circle",text:"reconfigure"});
+ subscribe(node);
+ break;
+ }
+ }
+ }
+ }
+
+
+ node.on('close', function(done){
+ //should un subscribe from events
+ wemo.removeListener('event', onEvent);
+ unsubscribe(node);
+ done();
+ });
+ }
+ RED.nodes.registerType("wemo in", wemoNGEvent)
+
+ RED.httpAdmin.get('/wemoNG/devices', function(req,res){
+ res.json(wemo.devices);
+
+ });
+
+ RED.httpAdmin.use('/wemoNG/notification',bodyParser.raw({type: 'text/xml'}));
+
+ RED.httpAdmin.notify('/wemoNG/notification', function(req, res){
+ var notification = {
+ 'sid': req.headers.sid
+ };
+ //console.log("Incoming Event %s", req.body.toString());
+ wemo.parseEvent(req.body.toString()).then(function(evt){
+ evt.sid = notification.sid;
+ wemo.emit('event',evt);
+ });
+ res.send("");
+ });
+
+}
\ No newline at end of file
diff --git a/hardware/wemo/icons/belkin.png b/hardware/wemo/icons/belkin.png
new file mode 100644
index 00000000..44e7033d
Binary files /dev/null and b/hardware/wemo/icons/belkin.png differ
diff --git a/hardware/wemo/lib/wemo.js b/hardware/wemo/lib/wemo.js
new file mode 100644
index 00000000..d3822fed
--- /dev/null
+++ b/hardware/wemo/lib/wemo.js
@@ -0,0 +1,367 @@
+/**
+ * Copyright 2015 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.
+ **/
+"use strict"
+
+var events = require('events');
+var util = require('util');
+var Client = require('node-ssdp').Client;
+var xml2js = require('xml2js');
+var request = require('request');
+var http = require('http');
+var url = require('url');
+var Q = require('q');
+
+var urn = 'urn:Belkin:service:basicevent:1';
+var postbodyheader = [
+ '',
+ '',
+ ''].join('\n');
+
+
+var postbodyfooter = ['',
+ ''
+].join('\n');
+
+
+var getenddevs = {};
+getenddevs.path = '/upnp/control/bridge1';
+getenddevs.action = '"urn:Belkin:service:bridge:1#GetEndDevices"';
+getenddevs.body = [
+ postbodyheader,
+ '',
+ '%s',
+ 'PAIRED_LIST',
+ '',
+ postbodyfooter
+].join('\n');
+
+var getcapabilities = {};
+getcapabilities.path = '/upnp/control/bridge1';
+getcapabilities.action = '"urn:Belkin:service:bridge:1#GetCapabilityProfileIDList"';
+getcapabilities.body = [
+ postbodyheader,
+ '',
+ '%s',
+ '',
+ postbodyfooter
+].join('\n');
+
+
+var WeMoNG = function () {
+ this.devices = {};
+ this._client;
+ this._interval;
+ events.EventEmitter.call(this);
+
+}
+
+util.inherits(WeMoNG, events.EventEmitter);
+
+WeMoNG.prototype.start = function start() {
+ //console.log("searching");
+ var _wemo = this;
+ _wemo._client = new Client();
+ _wemo._client.setMaxListeners(0);
+ _wemo._client.on('response', function (headers, statusCode, rinfo) {
+ var location = url.parse(headers.LOCATION);
+ var port = location.port;
+ request.get(location.href, function(err, res, xml) {
+ xml2js.parseString(xml, function(err, json) {
+ var device = { ip: location.hostname, port: location.port };
+ for (var key in json.root.device[0]) {
+ device[key] = json.root.device[0][key][0];
+ }
+ if (device.deviceType == "urn:Belkin:device:bridge:1") {
+ //console.log( device.ip + ' -' + device.deviceType);
+ var ip = device.ip;
+ var port = device.port;
+ var udn = device.UDN;
+ var postoptions = {
+ host: ip,
+ port: port,
+ path: getenddevs.path,
+ method: 'POST',
+ headers: {
+ 'SOAPACTION': getenddevs.action,
+ 'Content-Type': 'text/xml; charset="utf-8"',
+ 'Accept': ''
+ }
+ };
+
+ var post_request = http.request(postoptions, function(res) {
+ var data = "";
+ res.setEncoding('utf8');
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+
+ res.on('end',function() {
+ xml2js.parseString(data, function(err, result) {
+ if(!err) {
+ var list = result["s:Envelope"]["s:Body"][0]["u:GetEndDevicesResponse"][0].DeviceLists[0];
+ xml2js.parseString(list, function(err, result2) {
+ if (!err) {
+ var devinfo = result2.DeviceLists.DeviceList[0].DeviceInfos[0].DeviceInfo;
+ for (var i=0; i',
+ '%s',
+ '',
+ postbodyfooter
+ ].join('\n');
+
+ post_request.write(util.format(body, on));
+ post_request.end();
+}
+
+WeMoNG.prototype.setStatus = function setStatus(light, capability, value) {
+ var setdevstatus = {};
+ setdevstatus.path = '/upnp/control/bridge1';
+ setdevstatus.action = '"urn:Belkin:service:bridge:1#SetDeviceStatus"';
+ setdevstatus.body = [
+ postbodyheader,
+ '',
+ '',
+ '<?xml version="1.0" encoding="UTF-8"?><DeviceStatus><IsGroupAction>NO</IsGroupAction><DeviceID available="YES">%s</DeviceID><CapabilityID>%s</CapabilityID><CapabilityValue>%s</CapabilityValue></DeviceStatus>',
+ '',
+ '',
+ postbodyfooter
+ ].join('\n');
+
+ var postoptions = {
+ host: light.ip,
+ port: light.port,
+ path: setdevstatus.path,
+ method: 'POST',
+ headers: {
+ 'SOAPACTION': setdevstatus.action,
+ 'Content-Type': 'text/xml; charset="utf-8"',
+ 'Accept': ''
+ }
+ };
+
+ var post_request = http.request(postoptions, function(res) {
+ var data = "";
+ res.setEncoding('utf8');
+ res.on('data', function(chunk) {
+ data += chunk;
+ });
+
+ res.on('end', function() {
+ //console.log(data);
+ });
+ });
+
+ post_request.on('error', function (e) {
+ console.log(e);
+ console.log("%j", postoptions);
+ });
+
+ //console.log(util.format(setdevstatus.body, light.id, capability, value));
+
+ post_request.write(util.format(setdevstatus.body, light.id, capability, value));
+ post_request.end();
+}
+
+//need to promisify this so it returns
+WeMoNG.prototype.parseEvent = function parseEvent(evt) {
+ var msg = {};
+ msg.raw = evt;
+
+ var def = Q.defer();
+
+ xml2js.parseString(evt, function(err, res){
+ if (!err) {
+ var prop = res['e:propertyset']['e:property'][0];
+ if (prop.hasOwnProperty('StatusChange')) {
+ xml2js.parseString(prop['StatusChange'][0], function(err, res){
+ if (!err && res != null) {
+ msg.id = res['StateEvent']['DeviceID'][0]['_'];
+ msg.capability = res['StateEvent']['CapabilityId'][0];
+ msg.value = res['StateEvent']['Value'][0];
+ def.resolve(msg);
+ }
+ });
+ } else if (prop.hasOwnProperty('BinaryState')) {
+ msg.state = prop['BinaryState'][0];
+ if (msg.state.length > 1) {
+ var parts = msg.state.split('|');
+ msg.state = parts[0];
+ msg.power = parts[7]/1000;
+ }
+
+ def.resolve(msg);
+ } else {
+ console.log("unhandled wemo event type \n%s", util.inspect(prop, {depth:null}));
+ }
+ } else {
+ //error
+ }
+ });
+
+ return def.promise;
+}
+
+
+// Based on https://github.com/theycallmeswift/hue.js/blob/master/lib/helpers.js
+// TODO: Needs to be tweaked for more accurate color representation
+WeMoNG.prototype.rgb2xy = function rgb2xy(red, green, blue) {
+ var xyz;
+ var rgb = [red / 255, green / 255, blue / 255];
+
+ for (var i = 0; i < 3; i++) {
+ if (rgb[i] > 0.04045) {
+ rgb[i] = Math.pow(((rgb[i] + 0.055) / 1.055), 2.4);
+ } else {
+ rgb[i] /= 12.92;
+ }
+ rgb[i] = rgb[i] * 100;
+ }
+
+ xyz = [
+ rgb[0] * 0.4124 + rgb[1] * 0.3576 + rgb[2] * 0.1805,
+ rgb[0] * 0.2126 + rgb[1] * 0.7152 + rgb[2] * 0.0722,
+ rgb[0] * 0.0193 + rgb[1] * 0.1192 + rgb[2] * 0.9505
+ ];
+
+ return [
+ xyz[0] / (xyz[0] + xyz[1] + xyz[2]) * 65535,
+ xyz[1] / (xyz[0] + xyz[1] + xyz[2]) * 65535
+ ];
+};
+
+module.exports = WeMoNG;
\ No newline at end of file
diff --git a/hardware/wemo/package.json b/hardware/wemo/package.json
index 5d31c60a..9717360f 100644
--- a/hardware/wemo/package.json
+++ b/hardware/wemo/package.json
@@ -1,24 +1,35 @@
{
- "name" : "node-red-node-wemo",
- "version" : "0.0.3",
- "description" : "A Node-RED node to control a Belkin Wemo set of devices.",
- "dependencies" : {
- "wemo" : "0.2.*"
- },
- "repository" : {
- "type":"git",
- "url":"https://github.com/node-red/node-red-nodes/tree/master/hardware/wemo"
- },
- "license": "Apache-2.0",
- "keywords": [ "node-red", "wemo" ],
- "node-red" : {
- "nodes" : {
- "wemo": "60-wemo.js"
- }
- },
- "author": {
- "name": "Dave Conway-Jones",
- "email": "ceejay@vnet.ibm.com",
- "url": "http://nodered.org"
+ "name": "node-red-nodes-wemo",
+ "version": "0.1.4",
+ "description": "Input and Output nodes for Belkin WeMo devices",
+ "repository": "https://github.com/node-red/node-red-nodes/tree/master/hardware",
+ "main": "WeMoNG.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [
+ "node-red",
+ "wemo"
+ ],
+ "author": {
+ "email": "hardillb@gmail.com",
+ "name": "Benjamin Hardill",
+ "url": "http://www.hardill.me.uk/wordpress/"
+ },
+ "license": "APACHE-2.0",
+ "dependencies": {
+ "node-ssdp": "~2.6.3",
+ "request": "~2.65.0",
+ "xml2js": "~0.4.13",
+ "util": "~0.10.3",
+ "url": "~0.11.0",
+ "ip": "~1.0.1",
+ "body-parser": "~1.14.1",
+ "q": "~1.4.1"
+ },
+ "node-red": {
+ "nodes": {
+ "wemo": "WeMoNG.js"
}
+ }
}