implement the LiFX LAN API as a configurable option for higher lantency connections

This commit is contained in:
David Parry
2015-09-11 13:04:09 +10:00
parent 7e6df6191e
commit 17fc8f1829
2 changed files with 200 additions and 46 deletions

View File

@@ -20,6 +20,7 @@
"harmonyhubjs-client": "^1.1.4", "harmonyhubjs-client": "^1.1.4",
"harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git",
"lifx-api": "^1.0.1", "lifx-api": "^1.0.1",
"lifx": "https://github.com/magicmonkey/lifxjs.git",
"mdns": "^2.2.4", "mdns": "^2.2.4",
"node-hue-api": "^1.0.5", "node-hue-api": "^1.0.5",
"node-icontrol": "^0.1.4", "node-icontrol": "^0.1.4",

View File

@@ -1,16 +1,45 @@
'use strict';
// LiFX Platform Shim for HomeBridge
//
// Remember to add platform to config.json. Example:
// "platforms": [
// {
// "platform": "LIFx", // required
// "name": "LIFx", // required
// "access_token": "access token", // required
// "use_lan": "true" // optional set to "true" (gets and sets over the lan) or "get" (gets only over the lan)
// }
// ],
//
// When you attempt to add a device, it will ask for a "PIN code".
// The default code for all HomeBridge accessories is 031-45-154.
//
var Service = require("HAP-NodeJS").Service; var Service = require("HAP-NodeJS").Service;
var Characteristic = require("HAP-NodeJS").Characteristic; var Characteristic = require("HAP-NodeJS").Characteristic;
var lifxObj = require('lifx-api'); var lifxRemoteObj = require('lifx-api');
var lifx; var lifx_remote;
var lifxLanObj;
var lifx_lan;
var use_lan;
function LIFxPlatform(log, config){ function LIFxPlatform(log, config){
// auth info
this.access_token = config["access_token"];
// auth info lifx_remote = new lifxRemoteObj(this.access_token);
this.access_token = config["access_token"];
lifx = new lifxObj(this.access_token); // use remote or lan api ?
use_lan = config["use_lan"] || false;
this.log = log; if (use_lan != false) {
lifxLanObj = require('lifx');
lifx_lan = lifxLanObj.init();
}
this.log = log;
} }
LIFxPlatform.prototype = { LIFxPlatform.prototype = {
@@ -20,7 +49,7 @@ LIFxPlatform.prototype = {
var that = this; var that = this;
var foundAccessories = []; var foundAccessories = [];
lifx.listLights("all", function(body) { lifx_remote.listLights("all", function(body) {
var bulbs = JSON.parse(body); var bulbs = JSON.parse(body);
for(var i = 0; i < bulbs.length; i ++) { for(var i = 0; i < bulbs.length; i ++) {
@@ -33,20 +62,54 @@ LIFxPlatform.prototype = {
} }
function LIFxBulbAccessory(log, bulb) { function LIFxBulbAccessory(log, bulb) {
// device info // device info
this.name = bulb.label; this.name = bulb.label;
this.model = bulb.product_name; this.model = bulb.product_name;
this.deviceId = bulb.id; this.deviceId = bulb.id;
this.serial = bulb.uuid; this.serial = bulb.uuid;
this.capabilities = bulb.capabilities; this.capabilities = bulb.capabilities;
this.log = log; this.log = log;
} }
LIFxBulbAccessory.prototype = { LIFxBulbAccessory.prototype = {
get: function(type, callback){ getLan: function(type, callback){
var that = this; var that = this;
lifx.listLights("id:"+ that.deviceId, function(body) { if (!lifx_lan.bulbs[this.deviceId]) {
callback(new Error("Device not found"), false);
return;
}
lifx_lan.requestStatus();
lifx_lan.on('bulbstate', function(bulb) {
if (callback == null) {
return;
}
if (bulb.addr.toString('hex') == that.deviceId) {
switch(type) {
case "power":
callback(null, bulb.state.power > 0);
break;
case "brightness":
callback(null, Math.round(bulb.state.brightness * 100 / 65535));
break;
case "hue":
callback(null, Math.round(bulb.state.hue * 360 / 65535));
break;
case "saturation":
callback(null, Math.round(bulb.state.saturation * 100 / 65535));
break;
}
callback = null
}
});
},
getRemote: function(type, callback){
var that = this;
lifx_remote.listLights("id:"+ that.deviceId, function(body) {
var bulb = JSON.parse(body); var bulb = JSON.parse(body);
if (bulb.connected != true) { if (bulb.connected != true) {
@@ -71,66 +134,156 @@ LIFxBulbAccessory.prototype = {
}); });
}, },
identify: function(callback) { identify: function(callback) {
var that = this; lifx_remote.breatheEffect("id:"+ this.deviceId, 'green', null, 1, 3, false, true, 0.5, function (body) {
lifx.breatheEffect("id:"+ that.deviceId, 'green', null, 1, 3, false, true, 0.5, function (body) {
callback(); callback();
}); });
}, },
setColor: function(type, state, callback){ setLanColor: function(type, value, callback){
var that = this; var bulb = lifx_lan.bulbs[this.deviceId];
if (!bulb) {
callback(new Error("Device not found"), false);
return;
}
var state = {
hue: bulb.state.hue,
saturation: bulb.state.saturation,
brightness: bulb.state.brightness,
kelvin: bulb.state.kelvin
};
var scale = type == "hue" ? 360 : 100;
state[type] = Math.round(value * 65535 / scale) & 0xffff;
lifx_lan.lightsColour(state.hue, state.saturation, state.brightness, state.kelvin, 0, bulb);
callback(null);
},
setLanPower: function(state, callback){
var bulb = lifx_lan.bulbs[this.deviceId];
if (!bulb) {
callback(new Error("Device not found"), false);
return;
}
if (state) {
lifx_lan.lightsOn(bulb);
}
else {
lifx_lan.lightsOff(bulb);
}
callback(null);
},
setRemoteColor: function(type, value, callback){
var color; var color;
switch(type) { switch(type) {
case "brightness": case "brightness":
color = "brightness:" + (state / 100); color = "brightness:" + (value / 100);
break; break;
case "hue": case "hue":
color = "hue:" + state; color = "hue:" + value;
break; break;
case "saturation": case "saturation":
color = "saturation:" + (state / 100); color = "saturation:" + (value / 100);
break; break;
} }
lifx.setColor("id:"+ that.deviceId, color, 0, null, function (body) { lifx_remote.setColor("id:"+ this.deviceId, color, 0, null, function (body) {
callback(); callback();
}); });
}, },
setPower: function(state, callback){ setRemotePower: function(state, callback){
var that = this; var that = this;
lifx.setPower("id:"+ that.deviceId, (state == 1 ? "on" : "off"), 0, function (body) { lifx_remote.setPower("id:"+ that.deviceId, (state == 1 ? "on" : "off"), 0, function (body) {
callback(); callback();
}); });
}, },
getServices: function() { getServices: function() {
var that = this; var that = this;
var services = [] var services = []
var service = new Service.Lightbulb(this.name); var service = new Service.Lightbulb(this.name);
service switch(use_lan) {
.getCharacteristic(Characteristic.On) case true:
.on('identify', function(callback) {}) case "true":
.on('get', function(callback) { that.get("power", callback);}) // gets and sets over the lan api
.on('set', function(value, callback) {that.setPower(value, callback);}); service
.getCharacteristic(Characteristic.On)
.on('identify', function(callback) {})
.on('get', function(callback) { that.getLan("power", callback);})
.on('set', function(value, callback) {that.setLanPower(value, callback);});
service service
.addCharacteristic(Characteristic.Brightness) .addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) { that.get("brightness", callback);}) .on('get', function(callback) { that.getLan("brightness", callback);})
.on('set', function(value, callback) { that.setColor("brightness", value, callback);}); .on('set', function(value, callback) { that.setLanColor("brightness", value, callback);});
if (this.capabilities.has_color == true) { if (this.capabilities.has_color == true) {
service service
.addCharacteristic(Characteristic.Hue) .addCharacteristic(Characteristic.Hue)
.on('get', function(callback) { that.get("hue", callback);}) .on('get', function(callback) { that.getLan("hue", callback);})
.on('set', function(value, callback) { that.setColor("hue", value, callback);}); .on('set', function(value, callback) { that.setLanColor("hue", value, callback);});
service service
.addCharacteristic(Characteristic.Saturation) .addCharacteristic(Characteristic.Saturation)
.on('get', function(callback) { that.get("saturation", callback);}) .on('get', function(callback) { that.getLan("saturation", callback);})
.on('set', function(value, callback) { that.setColor("saturation", value, callback);}); .on('set', function(value, callback) { that.setLanColor("saturation", value, callback);});
}
break;
case "get":
// gets over the lan api, sets over the remote api
service
.getCharacteristic(Characteristic.On)
.on('identify', function(callback) {})
.on('get', function(callback) { that.getLan("power", callback);})
.on('set', function(value, callback) {that.setRemotePower(value, callback);});
service
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) { that.getLan("brightness", callback);})
.on('set', function(value, callback) { that.setRemoteColor("brightness", value, callback);});
if (this.capabilities.has_color == true) {
service
.addCharacteristic(Characteristic.Hue)
.on('get', function(callback) { that.getLan("hue", callback);})
.on('set', function(value, callback) { that.setRemoteColor("hue", value, callback);});
service
.addCharacteristic(Characteristic.Saturation)
.on('get', function(callback) { that.getLan("saturation", callback);})
.on('set', function(value, callback) { that.setRemoteColor("saturation", value, callback);});
}
break;
default:
// gets and sets over the remote api
service
.getCharacteristic(Characteristic.On)
.on('identify', function(callback) {})
.on('get', function(callback) { that.getRemote("power", callback);})
.on('set', function(value, callback) {that.setRemotePower(value, callback);});
service
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) { that.getRemote("brightness", callback);})
.on('set', function(value, callback) { that.setRemoteColor("brightness", value, callback);});
if (this.capabilities.has_color == true) {
service
.addCharacteristic(Characteristic.Hue)
.on('get', function(callback) { that.getRemote("hue", callback);})
.on('set', function(value, callback) { that.setRemoteColor("hue", value, callback);});
service
.addCharacteristic(Characteristic.Saturation)
.on('get', function(callback) { that.getRemote("saturation", callback);})
.on('set', function(value, callback) { that.setRemoteColor("saturation", value, callback);});
}
} }
services.push(service); services.push(service);