Last active
November 3, 2016 08:08
-
-
Save hardillb/1279241bb886ee28c05b to your computer and use it in GitHub Desktop.
Wemo lights example - "npm install wemo-js xml2js" then "node wemo-light.js" for instructions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("-------"); | |
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); | |
} | |
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<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>', | |
'<?xml version="1.0" encoding="UTF-8"?><DeviceStatus><IsGroupAction>NO</IsGroupAction><DeviceID available="YES">%s</DeviceID><CapabilityID>%s</CapabilityID><CapabilityValue>%s</CapabilityValue></DeviceStatus>', | |
'</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(); | |
} | |
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); | |
}, 2000); | |
} | |
if (device.deviceType === "urn:Belkin:device:bridge:1") { | |
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<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); | |
} | |
} 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') { | |
//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(){ | |
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(); | |
} | |
}); |
OK, that didn't give me what I hoped, change the added line to:
console.log('%j',result["s:Envelope"]["s:Body"][0]);
{"u:GetEndDevicesResponse":[{"$":{"xmlns:u":"urn:Belkin:service:bridge:1"},"DeviceLists":["<?xml version=\"1.0\" encoding=\"utf-8\"?><DeviceLists><DeviceList><DeviceListType>Paired</DeviceListType><DeviceInfos /><GroupInfos><GroupInfo><GroupID>1418677505</GroupID><GroupName>Bar</GroupName><GroupCapabilityIDs>10006,10008,30008,30009,3000A</GroupCapabilityIDs><GroupCapabilityValues>0,45:0,0:0,,</GroupCapabilityValues><DeviceInfos><DeviceInfo><DeviceIndex>0</DeviceIndex><DeviceID>94103EA2B2770460</DeviceID><FriendlyName>Bar 1</FriendlyName><IconVersion>1</IconVersion><FirmwareVersion>7E</FirmwareVersion><CapabilityIDs>10006,10008,30008,30009,3000A</CapabilityIDs><CurrentState>0,45:0,0:0,,</CurrentState><Manufacturer>MRVL</Manufacturer><ModelCode>MZ100</ModelCode><WeMoCertified>YES</WeMoCertified></DeviceInfo><DeviceInfo><DeviceIndex>1</DeviceIndex><DeviceID>B4750E1B95784CB1</DeviceID><FriendlyName>Bar 2</FriendlyName><IconVersion>1</IconVersion><FirmwareVersion>7E</FirmwareVersion><CapabilityIDs>10006,10008,30008,30009,3000A</CapabilityIDs><CurrentState>0,45:0,0:0,,</CurrentState><Manufacturer>MRVL</Manufacturer><ModelCode>MZ100</ModelCode><WeMoCertified>YES</WeMoCertified></DeviceInfo></DeviceInfos></GroupInfo></GroupInfos></DeviceList></DeviceLists>\n"]}]}
Ahh, The difference is you have all your bulbs in a group. I've not done much with groups yet. I'm working on a new WEMO node, that should support lights and event notifications (state change updates). When I get some time to look at groups I'll update this.
amadtawakol I have updated the script to support controlling groups (no dimming yet, but should be easy to add) it's here: https://gist.github.com/hardillb/ffa9b458109fb8af7d0f
Hi
Is it normal that it takes about 3-4 seconds for sending a command ?
Thank you
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@hardiilb Yes I have paired the bulbs and I can control them from the wemo app just fine. This is what I'm getting now after adding the above line: