Skip to content

Commit 5faa0b0

Browse files
committed
Initial commit, a half-baked library and a demo app.
0 parents  commit 5faa0b0

File tree

9 files changed

+2684
-0
lines changed

9 files changed

+2684
-0
lines changed

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2013 Kevin Bowman
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# JS library for LIFX bulbs
2+
3+
Some experimentation with trying to interpret the network protocol for [LIFX
4+
bulbs](http://lifx.co). This is very much observational, and an evolving
5+
investigation, so there's not very much info here and what there is is most
6+
likely incorrect or inaccurate. You have been warned.
7+
8+
This is based on my setup of 2 bulbs in a single group. That may or may not
9+
affect the network protocol.
10+
11+
Ideally, I'll end up with a nice little NodeJS library which can be used to
12+
build apps with.
13+
14+
## Files
15+
16+
There are 2 files:
17+
18+
* lifx.js which is the actual library
19+
* cli.js which is an example CLI app using the library
20+

cli.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
var lifx = require('./lifx');
2+
var async = require('async');
3+
var util = require('util');
4+
5+
var bulb;
6+
7+
async.series([
8+
9+
// Init the bulb
10+
function(cb) {
11+
lifx.discoverAndInit(function(err, _bulb) {
12+
if (err) {
13+
cb(err);
14+
} else {
15+
bulb = _bulb;
16+
console.log("Found a controller bulb at " + bulb.address);
17+
cb();
18+
}
19+
});
20+
},
21+
22+
// Init the UI
23+
function(cb) {
24+
25+
console.log("Keys:");
26+
console.log("Press 1 to turn the lights on");
27+
console.log("Press 2 to turn the lights off");
28+
console.log("Press 3 to turn the lights a dim red");
29+
console.log("Press 4 to turn the lights a dim purple");
30+
console.log("Press 5 to turn the lights a bright white");
31+
console.log("Press a to request an info update from the lights");
32+
33+
var stdin = process.openStdin();
34+
process.stdin.setRawMode(true)
35+
process.stdin.resume();
36+
37+
stdin.on('data', function (key) {
38+
//process.stdout.write('Got key ' + util.inspect(key) + '\n');
39+
switch (key[0]) {
40+
41+
case 0x31: // 1
42+
console.log("Lights on");
43+
bulb.lightsOn();
44+
break;
45+
46+
case 0x32: // 2
47+
console.log("Lights off");
48+
bulb.lightsOff();
49+
break;
50+
51+
case 0x33: // 3
52+
console.log("Dim red");
53+
bulb.red();
54+
break;
55+
56+
case 0x34: // 4
57+
console.log("Dim purple");
58+
bulb.purple();
59+
break;
60+
61+
case 0x35: // 5
62+
console.log("Bright white");
63+
bulb.brightWhite();
64+
break;
65+
66+
case 0x61: // a
67+
console.log("Requesting info...");
68+
bulb.getInfo();
69+
break;
70+
71+
case 0x03: // ctrl-c
72+
console.log("Closing...");
73+
bulb.close();
74+
process.stdin.pause();
75+
//process.exit();
76+
break;
77+
78+
}
79+
});
80+
81+
82+
}
83+
84+
], function(err, results) {
85+
if (err) {
86+
console.log("Err: " + err);
87+
}
88+
console.log(results);
89+
});
90+

lifx.js

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
var dgram = require('dgram');
2+
var net = require('net');
3+
var util = require('util');
4+
5+
module.exports = {
6+
7+
discover: function(cb) {
8+
var c = dgram.createSocket("udp4");
9+
var port = 56700;
10+
var found = false;
11+
c.on("error", function (err) {
12+
console.log("DGram error " + err);
13+
});
14+
c.on("message", function (msg, rinfo) {
15+
if (msg.length > 4 && msg[3] == 0x54) {
16+
found = true;
17+
c.close(); // Don't do this when we want ongoing status messages
18+
cb(null, {address:rinfo.address, port:rinfo.port, family:rinfo.family} );
19+
}
20+
});
21+
c.bind(port, function() {
22+
c.setBroadcast(true);
23+
var intervalID;
24+
// Now send the discovery packets
25+
intervalID = setInterval(function() {
26+
if (found) {
27+
clearInterval(intervalID);
28+
} else {
29+
var message = new Buffer([0x24, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
30+
c.send(message, 0, message.length, port, "255.255.255.255", function(err, bytes) {
31+
if (err) {
32+
cb(err);
33+
}
34+
});
35+
}
36+
}, 100);
37+
});
38+
},
39+
40+
init: function(addr, cb) {
41+
var b = new bulb(addr);
42+
cb(null, b);
43+
},
44+
45+
discoverAndInit: function(cb) {
46+
module.exports.discover(function(err, addr) {
47+
if (err) {
48+
cb(err);
49+
} else {
50+
module.exports.init(addr, cb);
51+
}
52+
});
53+
},
54+
55+
};
56+
57+
function bulb(addr) {
58+
59+
this.address = null;
60+
this.port = null;
61+
var client = null;
62+
63+
if (typeof addr == 'object' && typeof addr.address == 'string' && typeof addr.port == 'number') {
64+
this.address = addr.address;
65+
this.port = addr.port;
66+
}
67+
68+
client = net.connect(56700, '10.1.0.80', function() { //'connect' listener
69+
//console.log('client connected');
70+
});
71+
client.on('data', function(data) {
72+
console.log(" - " + "TCP got data (" + data.length + " bytes)");
73+
console.log(" - " + data.toString("hex"));
74+
});
75+
client.on('end', function() {
76+
console.log('client disconnected');
77+
});
78+
79+
var self = this;
80+
81+
this.sendRawPacket = function(message) {
82+
client.write(message);
83+
};
84+
85+
this.lightsOn = function() {
86+
self.sendRawPacket(new Buffer([0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00]));
87+
};
88+
89+
this.lightsOff = function() {
90+
self.sendRawPacket(new Buffer([0x26, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00]));
91+
};
92+
93+
this.red = function() {
94+
self.sendRawPacket(new Buffer([0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x9e, 0xd4, 0xff, 0xff, 0x8f, 0x02, 0xac, 0x0d, 0x13, 0x05, 0x00, 0x00]));
95+
};
96+
97+
this.purple = function() {
98+
self.sendRawPacket(new Buffer([0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x15, 0xcc, 0xff, 0xff, 0x8f, 0x02, 0xac, 0x0d, 0x13, 0x05, 0x00, 0x00]));
99+
};
100+
101+
this.brightWhite = function() {
102+
self.sendRawPacket(new Buffer([0x31, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x96, 0xf0, 0x0a, 0x90, 0x01, 0x00, 0x00]));
103+
};
104+
105+
this.getInfo = function() {
106+
self.sendRawPacket(new Buffer([0x24, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x73, 0xd5, 0x00, 0x23, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00]));
107+
};
108+
109+
this.close = function() {
110+
client.end();
111+
};
112+
113+
}

node_modules/async/LICENSE

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)