-
Notifications
You must be signed in to change notification settings - Fork 21
added the TileApi to control real colorful lifx tiles #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| 'use strict'; | ||
|
|
||
| const LifxClient = require('../lib/lifx').Client; | ||
|
|
||
| const client = new LifxClient(); | ||
|
|
||
| const LOOPTIME = 10000; | ||
| const TILE_LABEL = '*'; | ||
|
|
||
| /* | ||
| * This example should show on all tiles of | ||
| * your chain a random pattern. After all bits | ||
| * of the tiles are set with the random pattern, | ||
| * the example reads back all set values | ||
| * from the tiles just to test the read back function. | ||
| * I could not compare the set values out of the reason | ||
| * that the setvalues are usally a bit modified. | ||
| */ | ||
|
|
||
| function getBits(light, idx, chain) { | ||
| if (idx >= chain.total_count) { | ||
| console.log('All Bits get'); | ||
| setTimeout(() => setBits(light, 0, chain), LOOPTIME); | ||
| return; | ||
| } | ||
| light.getTileState64(idx, (err) => { | ||
| if (err) { | ||
| console.error('getTileState64:', err); | ||
| return; | ||
| } | ||
| getBits(light, idx + 1, chain); | ||
| }); | ||
| } | ||
|
|
||
| function setBits(light, idx, chain) { | ||
| if (idx >= chain.total_count) { | ||
| setTimeout(() => getBits(light, 0, chain), LOOPTIME); | ||
| return; | ||
| } | ||
| const ofs = ~~(Math.random() * (65536 - (65536 / 64))); | ||
| light.setTileState64(idx, {duration: 100}, | ||
| (new Array(64)).fill(undefined).map((_, idx) => ({ | ||
| hue: (ofs + (idx * (65536 / 64))) & 0xffff, | ||
| saturation: 50000, | ||
| brightness: 16384, | ||
| kelvin: 4096 | ||
| })), () => setBits(light, idx + 1, chain)); | ||
| } | ||
|
|
||
| client.on('light-new', (light) => { | ||
| if (!(TILE_LABEL === '*' || TILE_LABEL === light.id)) { | ||
| return; | ||
| } | ||
| console.log('New light found.'); | ||
| console.log('ID: ' + light.id); | ||
|
|
||
| light.getDeviceChain(function(err, chain) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would now attempt to call
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not work as light label has not resolved at this point. It was partly my bad as I didn't remember this while reviewing this the first time. It would improve the example a lot if a label could be used but for this purpose it would be enough to just rename |
||
| if (err) { | ||
| console.log(err); | ||
| } | ||
| setBits(light, 0, chain); | ||
| }); | ||
| }); | ||
|
|
||
| // Give feedback when running | ||
| client.on('listening', function() { | ||
| const address = client.address(); | ||
| console.log( | ||
| 'Started LIFX listening on ' + | ||
| address.address + ':' + address.port + '\n' | ||
| ); | ||
| }); | ||
|
|
||
| client.init(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -529,4 +529,159 @@ Light.prototype.colorZones = function(startIndex, endIndex, hue, saturation, bri | |
| this.client.send(packetObj, callback); | ||
| }; | ||
|
|
||
| /** | ||
| * Requests tile getDeviceChain 701 | ||
| * @param {Function} callback a function to accept the data | ||
| */ | ||
| Light.prototype.getDeviceChain = function(callback) { | ||
| validate.callback(callback, 'light getDeviceChain method'); | ||
|
|
||
| const packetObj = packet.create('getDeviceChain', {}, this.client.source); | ||
| packetObj.target = this.id; | ||
| const sqnNumber = this.client.send(packetObj); | ||
| this.client.addMessageHandler('stateDeviceChain', function(err, msg) { | ||
| if (err) { | ||
| return callback(err, null); | ||
| } | ||
| return callback(null, msg); | ||
| }, sqnNumber); | ||
| }; | ||
|
|
||
| /** | ||
| * Sets Tile Position | ||
| * @example light.setUserPosition(0, 12, 13, 0, () => {}) | ||
| * @param {Number} tileIndex unsigned 8-bit integer | ||
| * @param {Number} userX 32-bit float | ||
| * @param {Number} userY 32-bit float | ||
| * @param {Number} reserved 16-bit integer | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this |
||
| * @param {Function} callback called when light did receive message | ||
| */ | ||
| Light.prototype.setUserPosition = function(tileIndex, userX, userY, reserved, callback) { | ||
| validate.isUInt8(tileIndex, 'setUserPosition', 'tileIndex'); | ||
| validate.isXY(userX, userY, 'setUserPosition', 'user'); | ||
| validate.optionalCallback(callback, 'light setUserPosition method'); | ||
|
|
||
| const packetObj = packet.create('setUserPositiion', { | ||
| tileIndex, | ||
| userX, | ||
| userY, | ||
| reserved: reserved || 0 | ||
| }, this.client.source); | ||
| packetObj.target = this.id; | ||
| this.client.send(packetObj, callback); | ||
| }; | ||
|
|
||
| function defaultOptionsTileState64(options) { | ||
| const ret = { | ||
| length: options.length, | ||
| x: options.x, | ||
| y: options.y, | ||
| width: options.width, | ||
| reserved: options.reserved, | ||
| duration: options.duration | ||
| }; | ||
| if (typeof ret.length !== 'number') { | ||
| ret.length = 64; | ||
| } | ||
| if (typeof ret.width !== 'number') { | ||
| ret.length = 8; | ||
| } | ||
| if (typeof ret.x !== 'number') { | ||
| ret.x = 0; | ||
| } | ||
| if (typeof ret.y !== 'number') { | ||
| ret.y = 0; | ||
| } | ||
| if (typeof ret.duration !== 'number') { | ||
| ret.duration = 8; | ||
| } | ||
| return ret; | ||
| } | ||
|
|
||
| /** | ||
| * Requests tile GetTileState64 707 | ||
| * | ||
| * Get the state of 64 pixels in the tile in a rectangle that has | ||
| * a starting point and width. | ||
| * The tileIndex is used to control the starting tile in the chain | ||
| * and length is used to get the state of that many tiles beginning | ||
| * from the tileIndex. This will result in a separate response from | ||
| * each tile. | ||
| * @param {Number} tileIndex unsigned 8-bit integer | ||
| * @param {Object} options - options passed to | ||
| * @param {Number} options.length unsigned 8-bit integer (default 64) | ||
| * @param {Number} options.x unsigned 8-bit integer (default 0) | ||
| * @param {Number} options.y unsigned 8-bit integer (default 0) | ||
| * @param {Number} options.width unsigned 8-bit integer (default 8) | ||
| * @param {Number} options.reserved unsigned 8-bit integer | ||
| * @param {Function} callback a function to accept the data | ||
| */ | ||
| Light.prototype.getTileState64 = function(tileIndex, options, callback) { | ||
| validate.isUInt8(tileIndex, 'getTileState64', 'tileIndex'); | ||
| if (typeof options === 'function') { | ||
| callback = options; | ||
| options = {}; | ||
| } | ||
|
mabels marked this conversation as resolved.
|
||
| validate.callback(callback, 'light getTileState64 method'); | ||
| const {length, x, y, width, reserved} = defaultOptionsTileState64(options); | ||
| const packetObj = packet.create('getTileState64', { | ||
| tileIndex, | ||
| length, | ||
| reserved: reserved || 0, | ||
| x, | ||
| y, | ||
| width | ||
| }, | ||
| this.client.source | ||
| ); | ||
| packetObj.target = this.id; | ||
| const sqnNumber = this.client.send(packetObj); | ||
| this.client.addMessageHandler('stateTileState64', function(err, msg) { | ||
| if (err) { | ||
| return callback(err, null); | ||
| } | ||
| return callback(null, msg); | ||
| }, sqnNumber); | ||
| }; | ||
|
|
||
| /** | ||
| * This lets you set 64 pixels from a starting x and y for | ||
| * a rectangle with the specified width. | ||
| * For the LIFX Tile it really only makes sense to set x | ||
| * and y to zero, and width to 8. | ||
| * @param {Number} tileIndex unsigned 8-bit integer | ||
| * @param {Number} colors[64] 64 HSBK values | ||
| * @param {Object} options - options passed to | ||
| * @param {Number} options.length unsigned 8-bit integer (default 0) | ||
| * @param {Number} options.x unsigned 8-bit integer (default 8) | ||
| * @param {Number} options.y unsigned 8-bit integer (default 8) | ||
| * @param {Number} options.width unsigned 8-bit integer (default 8) | ||
| * @param {Number} options.duration unsigned 32-bit integer (default 0) | ||
| * @param {Number} options.reserved unsigned 8-bit integer | ||
| * @param {Function} [callback] called when light did receive message | ||
| */ | ||
| Light.prototype.setTileState64 = function(tileIndex, colors, options, callback) { | ||
| validate.isUInt8(tileIndex, 'setTileState64', 'tileIndex'); | ||
| if (typeof options === 'function') { | ||
| callback = options; | ||
| options = {}; | ||
| } | ||
|
mabels marked this conversation as resolved.
|
||
| const {length, x, y, width, duration, reserved} = defaultOptionsTileState64(options); | ||
| const set64colors = utils.buildColorsHsbk(colors, 64); | ||
| validate.optionalCallback(callback, 'light setTileState64 method'); | ||
|
|
||
| const packetObj = packet.create('setTileState64', { | ||
| tileIndex, | ||
| length, | ||
| reserved: reserved || 0, | ||
| x, | ||
| y, | ||
| width, | ||
| duration, | ||
| colors: set64colors | ||
| }, this.client.source); | ||
| packetObj.target = this.id; | ||
| this.client.send(packetObj, callback); | ||
| }; | ||
|
|
||
| exports.Light = Light; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use strict'; | ||
|
|
||
| const Packet = { | ||
| size: 0 | ||
| }; | ||
|
|
||
| module.exports = Packet; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| 'use strict'; | ||
|
|
||
| const {validate} = require('../../lifx'); | ||
|
|
||
| const Packet = { | ||
| size: 6 | ||
| }; | ||
|
|
||
| /** | ||
| * Converts the given packet specific object into a packet | ||
| * @param {Object} obj object with configuration data | ||
| * @param {Number} obj.tileIndex an 8bit value | ||
| * @param {Number} obj.length an 8bit value | ||
| * @param {Number} obj.reserved an 8bit value | ||
| * @param {Number} obj.x an 8bit value | ||
| * @param {Number} obj.y an 8bit value | ||
| * @param {Number} obj.width an 8bit value | ||
| * @return {Buffer} packet | ||
| */ | ||
| Packet.toBuffer = function(obj) { | ||
| const buf = Buffer.alloc(this.size); | ||
| buf.fill(0); | ||
| let offset = 0; | ||
|
|
||
| ['tileIndex', 'length', 'reserved', 'x', 'y', 'width'].forEach((field) => { | ||
| validate.isUInt8(obj[field], `getTileState64:${field}`); | ||
| }); | ||
| buf.writeUInt8(obj.tileIndex, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.length, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.reserved, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.x, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.y, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.width, offset); | ||
| offset += 1; | ||
|
|
||
| return buf; | ||
| }; | ||
|
|
||
| module.exports = Packet; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| 'use strict'; | ||
|
|
||
| const {validate} = require('../../lifx'); | ||
|
|
||
| const Packet = { | ||
| size: (obj) => 10 + (obj.colors.length * (8)), | ||
| HSBK: { | ||
| toBuffer: (obj, buf, offset) => { | ||
| validate.isUInt16(obj.hue, 'setTileState64:HSBK:hue'); | ||
| buf.writeUInt16LE(obj.hue, offset); | ||
| offset += 2; | ||
|
|
||
| validate.isUInt16(obj.saturation, 'setTileState64:HSBK:saturation'); | ||
| buf.writeUInt16LE(obj.saturation, offset); | ||
| offset += 2; | ||
|
|
||
| validate.isUInt16(obj.brightness, 'setTileState64:HSBK:brightness'); | ||
| buf.writeUInt16LE(obj.brightness, offset); | ||
| offset += 2; | ||
|
|
||
| validate.isUInt16(obj.kelvin, 'setTileState64:HSBK:kelvin'); | ||
| buf.writeUInt16LE(obj.kelvin, offset); | ||
| offset += 2; | ||
|
|
||
| return offset; | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * Converts the given packet specific object into a packet | ||
| * @param {Object} obj object with configuration data | ||
| * @param {Number} obj.tileIndex 8bit value | ||
| * @param {Number} obj.length 8bit value | ||
| * @param {Number} obj.reserved 8bit value | ||
| * @param {Number} obj.x 8bit value | ||
| * @param {Number} obj.y 8bit value | ||
| * @param {Number} obj.width 8bit value | ||
| * @param {Number} obj.duration 8bit value | ||
| * @param {Number} [obj.duration] transition time in milliseconds | ||
| * @param {Array} obj.colors an array of HSBK values | ||
| * @return {Buffer} packet | ||
| */ | ||
| Packet.toBuffer = function(obj) { | ||
| const buf = Buffer.alloc(Packet.size(obj)); | ||
| buf.fill(0); | ||
| let offset = 0; | ||
|
|
||
| ['tileIndex', 'length', 'reserved', 'x', 'y', 'width'].forEach((field) => { | ||
| validate.isUInt8(obj[field], `setTileState64:${field}`); | ||
| }); | ||
| // obj.stream field has unknown function so leave it as 0 | ||
| buf.writeUInt8(obj.tileIndex, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.length, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.reserved || 0, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.x, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.y, offset); | ||
| offset += 1; | ||
| buf.writeUInt8(obj.width, offset); | ||
| offset += 1; | ||
| validate.isUInt32(obj.duration, 'setTileState64:duration'); | ||
| buf.writeUInt32LE(obj.duration, offset); | ||
| offset += 4; | ||
| obj.colors.forEach((color) => { | ||
| offset = Packet.HSBK.toBuffer(color, buf, offset); | ||
| }); | ||
| return buf; | ||
| }; | ||
|
|
||
| module.exports = Packet; |
Uh oh!
There was an error while loading. Please reload this page.