Skip to content

Instantly share code, notes, and snippets.

@hardillb
Last active December 13, 2020 15:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hardillb/ffa9b458109fb8af7d0f to your computer and use it in GitHub Desktop.
Save hardillb/ffa9b458109fb8af7d0f to your computer and use it in GitHub Desktop.
Wemo lights example - "npm install wemo-js xml2js" then "node wemo-contol.js" for instructions
var wemo = require('wemo-js');
var http = require('http');
var util = require('util');
var xml2js = require('xml2js');
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');
if (process.argv.length < 3) {
console.log("Help:");
console.log("node wemo-lights.js list - Shows all the available bulbs");
console.log("node wemo-lights.js state <friendly name> - Shows the current state of a given bulb");
console.log("node wemo-lights.js control <friendly name> on|off [0-255] - (dim value only used if state is on)");
console.log("node wemo-lights.js dim <friendly name> <0-255> - setting dim value of an off device will turn it on at new level");
console.log("node wemo-lights.js sleep <friendly name> <time> - time in seconds to dim to off");
process.exit(0);
}
function processOptions(lights) {
if (process.argv[2] === 'list') {
console.log("Lights:");
console.log("Friendly name - id");
for (var j=0; j<lights.length; j++) {
console.log("%s - %s", lights[j].name, lights[j].id);
}
console.log("Groups:");
for (var j=0; j<groups.length; j++) {
console.log("%s - %s", groups[j].name, groups[j].id);
}
console.log("-------");
console.log("Sockets:");
for (var j=0; j<sockets.length; j++) {
console.log("%s", sockets[j].name);
}
process.exit(0);
} else if (process.argv[2] === 'state') {
for (var j=0; j<lights.length; j++) {
if (lights[j].name === process.argv[3]) {
console.log("%s %s",lights[j].name, lights[j].state);
process.exit(0);
}
}
for (var j=0; j<groups.length; j++) {
if (groups[j].name === process.argv[3]) {
console.log("%s %s",groups[j].name, groups[j].state);
process.exit(0);
}
}
for (var j=0; j<sockets.length; j++) {
if (sockets[j].name === process.argv[3]) {
console.log("%s %s",sockets[j].name, sockets[j].state);
process.exit(0);
}
}
} else if (process.argv[2] === 'control') {
for (var j=0; j<lights.length; j++) {
if (lights[j].name === process.argv[3]) {
var on = 0;
var level = 255;
if ("on" === process.argv[4]) {
on = 1;
}
control(lights[j], on);
if (on === 1 && process.argv.length === 6) {
console.log("dimming to %d", process.argv[5]);
dim(lights[j], process.argv[5]);
}
return;
}
}
for (var j=0; j<groups.length; j++) {
if (groups[j].name === process.argv[3]) {
var on = 0;
var level = 255;
if ("on" === process.argv[4]) {
on = 1;
}
setGroupStatus(groups[j], "10006", on);
//control(groups[j], on);
// if (on === 1 && process.argv.length === 6) {
// dim(groups[j], process.argv[5]);
// }
return;
}
}
for (var j=0; j<sockets.length; j++) {
if (sockets[j].name === process.argv[3]) {
var on = 0;
if ("on" === process.argv[4]) {
on = 1;
}
toggleSocket(sockets[j], on);
return;
}
}
} else if (process.argv[2] === "dim") {
for (var j=0; j<lights.length; j++) {
if (lights[j].name === process.argv[3]) {
var level = process.argv[4];
dim(lights[j],level);
break;
}
}
} else if (process.argv[2] === "sleep") {
for (var j=0; j<lights.length; j++) {
var time = process.argv[4];
sleep(lights[j], time);
}
}
}
function toggleSocket(socket, on) {
var postoptions = {
host: socket.ip,
port: socket.port,
path: "/upnp/control/basicevent1",
method: 'POST',
headers: {
'SOAPACTION': '"urn:Belkin:service:basicevent:1#SetBinaryState"',
'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);
});
});
var body = [
postbodyheader,
'<u:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1">',
'<BinaryState>%s</BinaryState>',
'</u:SetBinaryState>',
postbodyfooter
].join('\n');
post_request.write(util.format(body, on));
post_request.end();
}
function control(light, on) {
setStatus(light, "10006", on);
}
function dim(light, dim) {
setStatus(light, "10008", dim + ":0");
}
function sleep(light, time) {
var date = new Date();
date = date.getTime()/1000;
time = time * 10;
setStatus(light, "30008", time + ":" + date);
}
function setStatus(light, capability, value) {
var setdevstatus = {};
setdevstatus.path = '/upnp/control/bridge1';
setdevstatus.action = '"urn:Belkin:service:bridge:1#SetDeviceStatus"';
setdevstatus.body = [
postbodyheader,
'<u:SetDeviceStatus xmlns:u="urn:Belkin:service:bridge:1">',
'<DeviceStatusList>',
'&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;DeviceStatus&gt;&lt;IsGroupAction&gt;NO&lt;/IsGroupAction&gt;&lt;DeviceID available=&quot;YES&quot;&gt;%s&lt;/DeviceID&gt;&lt;CapabilityID&gt;%s&lt;/CapabilityID&gt;&lt;CapabilityValue&gt;%s&lt;/CapabilityValue&gt;&lt;/DeviceStatus&gt;',
'</DeviceStatusList>',
'</u:SetDeviceStatus>',
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();
}
function setGroupStatus(light, capability, value) {
var setdevstatus = {};
setdevstatus.path = '/upnp/control/bridge1';
setdevstatus.action = '"urn:Belkin:service:bridge:1#SetDeviceStatus"';
setdevstatus.body = [
postbodyheader,
'<u:SetDeviceStatus xmlns:u="urn:Belkin:service:bridge:1">',
'<DeviceStatusList>',
'&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;DeviceStatus&gt;&lt;IsGroupAction&gt;YES&lt;/IsGroupAction&gt;&lt;DeviceID available=&quot;YES&quot;&gt;%s&lt;/DeviceID&gt;&lt;CapabilityID&gt;%s&lt;/CapabilityID&gt;&lt;CapabilityValue&gt;%s&lt;/CapabilityValue&gt;&lt;/DeviceStatus&gt;',
'</DeviceStatusList>',
'</u:SetDeviceStatus>',
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));
var body = util.format(setdevstatus.body, light.id, capability, value);
console.log("%s", body);
post_request.write(body);
post_request.end();
}
var groups = [];
var lights = [];
var sockets = [];
var timer;
var client = wemo.Search();
client.on('found', function(device) {
//console.log("%j",device);
if (!timer) {
timer = setTimeout(function() {
client.stop();
processOptions(lights);
}, 3000);
}
if (device.deviceType === "urn:Belkin:device:bridge:1") {
//console.log("%j", 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': ''
}
};
//console.log(ip + ":" + port);
//console.log(udn);
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++) {
//console.log("%s[%s]:\t\t%s",devinfo[i].FriendlyName[0], devinfo[i].DeviceID[0], devinfo[i].CurrentState[0]);
var light = {
"ip": ip,
"port": port,
"udn": udn,
"name": devinfo[i].FriendlyName[0],
"id": devinfo[i].DeviceID[0],
"state": devinfo[i].CurrentState[0]
};
lights.push(light);
}
}
var groupinfo = result2.DeviceLists.DeviceList[0].GroupInfos;
if (groupinfo) {
//group found
for (var i=0; i<groupinfo.length; i++) {
var group = {
"ip": ip,
"port": port,
"udn": udn,
"name": groupinfo[i].GroupInfo[0].GroupName[0],
"id": groupinfo[i].GroupInfo[0].GroupID[0],
"state": groupinfo[i].GroupInfo[0].GroupCapabilityValues[0],
"devices" : []
}
for(var j=0; j<groupinfo[i].GroupInfo[0].DeviceInfos[0].DeviceInfo.length; j++) {
group.devices.push(groupinfo[i].GroupInfo[0].DeviceInfos[0].DeviceInfo[j].DeviceID[0]);
}
groups.push(group);
}
}
//console.log("%j",groups);
} else {
console.log(err);
console.log(data);
}
});
}
});
});
});
post_request.write(util.format(getenddevs.body, udn));
post_request.end();
} else if (device.deviceType === 'urn:Belkin:device:controllee:1' || device.deviceType == 'urn:Belkin:device:insight:1') {
//console.log("%s", device.friendlyName);
var socket = {
"ip": device.ip,
"port": device.port,
"name": device.friendlyName,
"type": "socket"
};
var postoptions = {
host: socket.ip,
port: socket.port,
path: "/upnp/control/basicevent1",
method: 'POST',
headers: {
'SOAPACTION': '"urn:Belkin:service:basicevent:1#GetBinaryState"',
'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(socket.name + "\n" + data);
xml2js.parseString(data, function(err, result) {
if (!err) {
var state = result['s:Envelope']['s:Body'][0]['u:GetBinaryStateResponse'][0].BinaryState[0];
socket.state = state;
sockets.push(socket);
} else {
console.log(err);
console.log(data);
}
})
});
});
var body = [
postbodyheader,
'<u:GetBinaryState xmlns:u="urn:Belkin:service:basicevent:1">',
'</u:GetBinaryState>',
postbodyfooter
].join('\n');
post_request.write(body);
post_request.end();
} else {
console.log("unknown");
console.log("%j",device);
}
});
@ericblade
Copy link

Hello -- just obtained some WeMo/Osram bulbs and a Link for them .. would like to build some custom programs for them that are more in depth than what IFTTT can do.. but so far, every node library that I've tried to interact with the bulbs just hangs during the detection process. Any idea what I can do to diagnose and attempt to get there?

@franrios
Copy link

Hi, I would like to know how can get info about available bulbs connected to a specific wemo bridge. I mean, if I get the devices list from the bridge, I get every bulbs linked despite one or more than one could be disconnected.

@hardillb
Copy link
Author

@franrios, there is a Available field in the output from GetDevices, that should signify if they are powered on or off iirc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment