Skip to content

Commit 9c6a544

Browse files
committed
Very basic header reading
0 parents  commit 9c6a544

15 files changed

+552
-0
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false

.eslintrc

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"env": {
3+
"node": true
4+
},
5+
"rules": {
6+
"quotes": [2, "single"],
7+
"indent": [2, 2, {"indentSwitchCase": true}]
8+
}
9+
}

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# LIFX
2+
3+
A node implementation of the LIFX protocol.
4+
5+
Under development and not, in any way, affiliated or related to LIFX Labs.
6+
Use at your own risk.

cli.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
var Lifx = require('./lib/lifx').Client;
4+
var client = new Lifx();
5+
6+
client.on('error', function (err) {
7+
console.log('LIFX error:\n' + err.stack);
8+
client.destroy();
9+
});
10+
11+
client.on('message', function (msg, rinfo) {
12+
console.log(msg, ' from ' + rinfo.address);
13+
});
14+
15+
client.on('listening', function () {
16+
var address = client.address();
17+
console.log('Started LIFX listening on ' +
18+
address.address + ':' + address.port);
19+
});
20+
21+
client.init();

lib/lifx.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var lifx = exports;
2+
3+
// Export utils
4+
lifx.utils = require('./lifx/utils');
5+
6+
// Export constants
7+
lifx.constants = require('./lifx/constants');
8+
9+
// Export packet parser
10+
lifx.packet = require('./lifx/packet');
11+
12+
// Export client
13+
lifx.Client = require('./lifx/client').Client;
14+
15+
// // Export Connection and Stream
16+
// lifx.Stream = require('./lifx/stream').Stream;
17+
// lifx.Connection = require('./lifx/connection').Connection;
18+
//
19+
// lifx.createAgent = require('./lifx/client').create;

lib/lifx/client.js

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
3+
var util = require('util');
4+
var dgram = require('dgram');
5+
var EventEmitter = require('events').EventEmitter;
6+
var _ = require('lodash');
7+
var Packet = require('../lifx').Packet;
8+
var constants = require('../lifx').constants;
9+
10+
/**
11+
* Creates a lifx client
12+
* @module Lifx/Client
13+
* @extends EventEmitter
14+
*/
15+
function Client() {
16+
EventEmitter.call(this);
17+
18+
this.socket = dgram.createSocket('udp4');
19+
this.gateways = {};
20+
this.bulbs = {};
21+
this.scanning = false;
22+
}
23+
util.inherits(Client, EventEmitter);
24+
25+
/**
26+
* Creates a new socket and starts discovery
27+
* @param {Object} [options]
28+
* @param {string} [options.address] The IPv4 address to bind to
29+
* @param {number} [options.port] The port to bind to
30+
* @param {Function} [callback]
31+
*/
32+
Client.prototype.init = function(options, callback) {
33+
var self = this;
34+
var defaults = {
35+
address: '0.0.0.0',
36+
port: constants.LIFX_DEFAULT_PORT
37+
};
38+
39+
options = options || {};
40+
var opts = _.defaults(options, defaults);
41+
42+
this.socket.on('error', function (err) {
43+
console.error('LIFX UDP packet error');
44+
console.trace(err);
45+
self.emit('error', err);
46+
});
47+
48+
this.socket.on('message', function(msg, rinfo) {
49+
console.log(' U- ' + msg.toString('hex'));
50+
// Parse packet to object
51+
var packet = new Packet(msg);
52+
msg = packet.toObject();
53+
self.emit('message', msg, rinfo);
54+
});
55+
56+
this.socket.bind(opts, function() {
57+
self.socket.setBroadcast(true);
58+
self.emit('listening');
59+
if (typeof callback === 'function') {
60+
callback();
61+
}
62+
});
63+
64+
// Start scanning
65+
this.start();
66+
};
67+
68+
/**
69+
* Destroy an instance
70+
*/
71+
Client.prototype.destroy = function() {
72+
this.socket.close();
73+
};
74+
75+
/**
76+
* Start all network activity
77+
*/
78+
Client.prototype.start = function() {
79+
this.scanning = true;
80+
};
81+
82+
/**
83+
* Stop all network activity
84+
*/
85+
Client.prototype.stop = function() {
86+
this.scanning = false;
87+
};
88+
89+
/**
90+
* Get network address data from used udp socket
91+
* @return {Object} Network address data
92+
*/
93+
Client.prototype.address = function() {
94+
try {
95+
var address = this.socket.address();
96+
} catch(e) {
97+
return null;
98+
}
99+
return address;
100+
};
101+
102+
exports.Client = Client;

lib/lifx/constants.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
module.exports = {
4+
// Ports used by LIFX
5+
LIFX_DEFAULT_PORT: 56700,
6+
LIFX_ANY_PORT: 56800,
7+
8+
// Masks for packet description
9+
ADDRESSABLE_BIT: 0x1000,
10+
TAGGED_BIT: 0x2000,
11+
ORIGIN_BITS: 0xC000,
12+
PROTOCOL_VERSION_BITS: 0xFFF,
13+
14+
// Protocol version mappings
15+
PROTOCOL_VERSION_CURRENT: 1024,
16+
PROTOCOL_VERSION_1: 1024
17+
};

lib/lifx/packet.js

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
'use strict';
2+
var constants = require('../lifx').constants;
3+
4+
// Package headers 36 bit in total consisting of
5+
//
6+
// size - 2 bit
7+
// description - 2 bit
8+
//
9+
// source - 4 bit
10+
// target - 6 bit
11+
// 00 00
12+
// site - 6 bit
13+
//
14+
// 00
15+
// sequence - 1 bit
16+
// time - 8 bit
17+
// type - 2 bit
18+
// 00 00
19+
var Packet = {};
20+
21+
/**
22+
* Parses a lifx packet header
23+
* @param {Buffer} buf Buffer containg lifx packet
24+
* @return {Object} parsed packet object
25+
*/
26+
Packet.toObject = function(buf) {
27+
var obj = {};
28+
var offset = 0;
29+
30+
obj.size = buf.readUInt16LE(offset);
31+
offset += 2;
32+
33+
obj.description = buf.readUInt16LE(offset);
34+
offset += 2;
35+
obj.addressable = (obj.description & constants.ADDRESSABLE_BIT) !== 0;
36+
obj.tagged = (obj.description & constants.TAGGED_BIT) !== 0;
37+
obj.origin = ((obj.description & constants.ORIGIN_BITS) >> 14) !== 0;
38+
obj.protocolVersion = (obj.description & constants.PROTOCOL_VERSION_BITS);
39+
40+
obj.source = buf.toString('hex', offset, offset + 4);
41+
offset += 4;
42+
43+
obj.target = buf.toString('hex', offset, offset + 6);
44+
offset += 6;
45+
46+
obj.reserved1 = buf.slice(offset, offset + 2);
47+
offset += 2;
48+
49+
obj.site = buf.toString('utf8', offset, offset + 6);
50+
obj.site = obj.site.replace(/\0/g, '');
51+
offset += 6;
52+
53+
obj.reserved2 = buf.slice(offset, offset + 1);
54+
offset += 1;
55+
56+
obj.sequence = buf.readUInt8(offset);
57+
offset += 1;
58+
59+
// buf.readUInt64LE
60+
obj.time = (Math.pow(2, 32) * buf.readUInt32LE(offset + 4)) + buf.readUInt32LE(offset);
61+
offset += 8;
62+
if (obj.time !== 0) {
63+
obj.time = obj.time.toString();
64+
obj.time = obj.time.substring(0, 13);
65+
var time = new Date();
66+
time.setTime(obj.time);
67+
obj.time = time;
68+
} else {
69+
obj.time = null;
70+
}
71+
72+
obj.type = buf.readUInt16LE(offset);
73+
offset += 2;
74+
75+
obj.reserved3 = buf.slice(offset, offset + 2);
76+
offset += 2;
77+
78+
return obj;
79+
};
80+
81+
/**
82+
* Creates a buffer for a lifx header from an object
83+
* @param {Object} obj Object containg header data
84+
* @return {Buffer} header buffer
85+
*/
86+
Packet.toBuffer = function(obj) {
87+
var buf = new Buffer(36);
88+
buf.fill(0);
89+
var offset = 0;
90+
91+
buf.writeUInt16LE(obj.size, offset);
92+
offset += 2;
93+
94+
if (obj.description === undefined || obj.description === null) {
95+
if(obj.protocolVersion === undefined) {
96+
obj.protocolVersion = constants.PROTOCOL_VERSION_CURRENT;
97+
}
98+
99+
obj.description = obj.protocolVersion;
100+
101+
if(obj.addressable !== undefined && obj.addressable === true) {
102+
obj.description |= constants.ADDRESSABLE_BIT;
103+
}
104+
105+
if(obj.tagged !== undefined && obj.tagged === true) {
106+
obj.description |= constants.TAGGED_BIT;
107+
}
108+
109+
if(obj.origin !== undefined && obj.origin === true) {
110+
// 0 or 1 to the 14 bit
111+
obj.description |= (1 << 14);
112+
}
113+
}
114+
115+
buf.writeUInt16LE(obj.description, offset);
116+
offset += 2;
117+
118+
if (obj.source !== undefined && obj.source.length > 0) {
119+
buf.write(obj.source, offset, 4, 'hex');
120+
}
121+
offset += 4;
122+
123+
buf.write(obj.target, offset, 6, 'hex');
124+
offset += 6;
125+
126+
// reserved1
127+
offset += 2;
128+
129+
buf.write(obj.site, offset, 6, 'utf8');
130+
offset += 6;
131+
132+
// reserved2
133+
offset += 1;
134+
135+
buf.writeUInt8(obj.sequence, offset);
136+
offset += 1;
137+
138+
// @TODO buf.writeUInt64LE
139+
buf.writeUInt32LE(0, offset);
140+
offset += 4;
141+
buf.writeUInt32LE(0, offset);
142+
offset += 4;
143+
// obj.time = (Math.pow(2, 32) * buf.readUInt32LE(offset + 4)) + buf.readUInt32LE(offset);
144+
145+
buf.writeUInt16LE(obj.type, offset);
146+
offset += 2;
147+
148+
// reserved3
149+
offset += 2;
150+
151+
return buf;
152+
};
153+
154+
module.exports = Packet;

lib/lifx/utils.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
var os = require('os');
4+
var utils = exports;
5+
6+
/**
7+
* Return all ip addresses of the machine
8+
*/
9+
utils.getHostIPs = function() {
10+
var ips = [];
11+
var ifaces = os.networkInterfaces();
12+
Object.keys(ifaces).forEach(function (ifname) {
13+
ifaces[ifname].forEach(function (iface) {
14+
ips.push(iface.address);
15+
});
16+
});
17+
return ips;
18+
};

0 commit comments

Comments
 (0)