Skip to content

Instantly share code, notes, and snippets.

@hardillb
Created January 14, 2015 12:16
Show Gist options
  • Save hardillb/2fce5fe7643a6f91dc75 to your computer and use it in GitHub Desktop.
Save hardillb/2fce5fe7643a6f91dc75 to your computer and use it in GitHub Desktop.
Wemo Event listener - Get events when sockets and lights turn on and off - Run "npm install node-ssdp express body-parser xml2js request" to install the pre-reqs. It only listens for 10mins at the moment, I'll do subscription updates when I bundle it all into a node module
var Client = require('node-ssdp').Client;
var http = require('http');
var url = require('url');
var request = require('request');
var express = require('express');
var bodyparser = require('body-parser');
var os = require('os');
var xml2js = require('xml2js');
var util = require('util');
//urn:Belkin:device:lightswitch:1
var postbodyheader = [
'<?xml version="1.0" encoding="utf-8"?>',
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">',
'<s:Body>'].join('\n');
var postbodyfooter = ['</s:Body>',
'</s:Envelope>'
].join('\n');
var getenddevs = {};
getenddevs.path = '/upnp/control/bridge1';
getenddevs.action = '"urn:Belkin:service:bridge:1#GetEndDevices"';
getenddevs.body = [
postbodyheader,
'<u:GetEndDevices xmlns:u="urn:Belkin:service:bridge:1">',
'<DevUDN>%s</DevUDN>',
'<ReqListType>PAIRED_LIST</ReqListType>',
'</u:GetEndDevices>',
postbodyfooter
].join('\n');
var interfaces = os.networkInterfaces();
var addresses = [];
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2];
if (address.family === 'IPv4' && !address.internal) {
addresses.push(address.address);
}
}
}
var devices = [];
var subscriptions = [];
var app = express();
app.use(bodyparser.raw({type: 'text/xml'}));
app.all("/", function(req, res) {
//console.log("HEADERS: %j", req.headers);
console.log(subscriptions[req.headers.sid].friendlyName);
xml2js.parseString(req.body, function(err, json){
if (err) {
console.log(err);
}
console.log("EVENT: %j" , json);
//console.log("TYPE: %j", subscriptions[req.headers.sid].deviceType);
var prop = json['e:propertyset']['e:property'][0];
if(subscriptions[req.headers.sid].deviceType === 'urn:Belkin:device:bridge:1') {
//console.log("Bridge");
if (json['e:propertyset']['e:property'][0]['StatusChange']) {
if (json['e:propertyset']['e:property'][0]) {
//console.log("STATUS: %j",json['e:propertyset']['e:property'][0]['StatusChange'][0]);
if (json['e:propertyset']['e:property'][0]['StatusChange'][0] !== '') {
xml2js.parseString(json['e:propertyset']['e:property'][0]['StatusChange'][0], function (err, xml2) {
//console.log("%j",xml2);
for (var i=0; i<devices.length; i++) {
if (devices[i].deviceType === 'urn:Belkin:device:bridge:1') {
for (var j=0; j<devices[i].lights.length; j++) {
if (devices[i].lights[j].id === xml2.StateEvent.DeviceID[0]['_']) {
console.log("%s -> %s %s", devices[i].lights[j].name, xml2.StateEvent.CapabilityId, xml2.StateEvent.Value);
break;
}
}
break;
}
}
});
}
}
}
} else {
//console.log("NOT Bridge");
}
console.log("-----------");
});
res.sendStatus(200);
});
var server = app.listen(3000);
var client = new Client();
var urn = 'urn:Belkin:service:basicevent:1';
//var urn = 'ssdp:all';
client.on('response', function (headers, statusCode, rinfo){
//console.log("%j\n%s\n%j\n-----", 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];
}
devices.push(device);
if(device.deviceType === 'urn:Belkin:device:bridge:1') {
//lookup lights
device.lights = [];
findEndDevices(device);
}
var subscribeoptions = {
host: device.ip,
port: device.port,
path: '/upnp/event/basicevent1',
method: 'SUBSCRIBE',
headers: {
'CALLBACK': '<http://' + addresses[0] +':3000/>',
'NT': 'upnp:event',
'TIMEOUT': 'Second-600'
}
};
if (headers.USN.indexOf('uuid:Bridge-1_0') != -1) {
subscribeoptions.path='/upnp/event/bridge1';
}
var sub_request = (function(d) { return http.request(subscribeoptions, function(res) {
subscriptions[res.headers.sid] = d;
})})(device);
sub_request.on('error', function (e) {
console.log(e);
});
sub_request.end();
});
});
});
setTimeout(function() {
client._stop();
}, 3000);
client.search(urn);
function findEndDevices(device) {
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;
if (devinfo) {
for (var i=0; i<devinfo.length; i++) {
var light = {
"name": devinfo[i].FriendlyName[0],
"id": devinfo[i].DeviceID[0],
"state": devinfo[i].CurrentState[0]
};
device.lights.push(light);
}
}
var groupinfo = result2.DeviceLists.DeviceList[0].GroupInfos;
if (groupinfo) {
//group found
console.log("Groups are currently not supported");
}
} else {
console.log(err);
console.log(data);
}
});
}
});
});
});
post_request.write(util.format(getenddevs.body, udn));
post_request.end();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment