-
-
Save hardillb/1279241bb886ee28c05b to your computer and use it in GitHub Desktop.
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(); | |
} | |
}); |
@ahmadtawakol The only reason it would do that is if the device didn't return what is expected.
I've updated the code to support sockets today as well (in preparation to removing the node-wemo dependency) and added a little checking round that bit of code to print out what it got back rather than crash.
I tried the updated code, I got the same thing again. Am I doing something wrong?
~# node wemo-light.js list
events.js:72
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'length' of undefined
at /root/wemo-light.js:257:31
at Parser.<anonymous> (/root/node_modules/xml2js/lib/xml2js.js:384:20)
at Parser.emit (events.js:95:17)
at Object.onclosetag (/root/node_modules/xml2js/lib/xml2js.js:348:26)
at emit (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:615:33)
at emitNode (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:620:3)
at closeTag (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:861:5)
at Object.write (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:1294:29)
at Parser.exports.Parser.Parser.parseString (/root/node_modules/xml2js/lib/xml2js.js:403:31)
at Parser.parseString (/root/node_modules/xml2js/lib/xml2js.js:6:61)
@ahmadtawakol Have you already paired the bulbs with the bridde using the iOS/Android app? The only way I can think of that you will get the error you are seeing is if there are no bulbs paired.
Add the following line to the code just between lines 252 and 253:
console.log(result);
and try again. This won't fix it but it will show the result the bridge is sending back
@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:
~# node wemo-light.js list
{ 's:Envelope':
{ '$':
{ 'xmlns:s': 'http://schemas.xmlsoap.org/soap/envelope/',
's:encodingStyle': 'http://schemas.xmlsoap.org/soap/encoding/' },
's:Body': [ [Object] ] } }
events.js:72
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'length' of undefined
at /root/wemo-light.js:258:31
at Parser.<anonymous> (/root/node_modules/xml2js/lib/xml2js.js:384:20)
at Parser.emit (events.js:95:17)
at Object.onclosetag (/root/node_modules/xml2js/lib/xml2js.js:348:26)
at emit (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:615:33)
at emitNode (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:620:3)
at closeTag (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:861:5)
at Object.write (/root/node_modules/xml2js/node_modules/sax/lib/sax.js:1294:29)
at Parser.exports.Parser.Parser.parseString (/root/node_modules/xml2js/lib/xml2js.js:403:31)
at Parser.parseString (/root/node_modules/xml2js/lib/xml2js.js:6:61)
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
Whenever I try to run anything, I get this error: