homebridge-misfit-bolt
Advanced tools
Comparing version 0.0.8 to 0.0.9
@@ -1,7 +0,11 @@ | ||
var debug = require('debug')(require('../package').name); | ||
var _ = require('lodash'); | ||
var Color = require('color'); | ||
var app = require('../package').name, | ||
debug = require('debug')(app), | ||
error = require('debug')(app + ':error'), | ||
timeout = require('debug')(app + ':timeout'); | ||
var Service, Characteristic; | ||
var _ = require('lodash'), | ||
Color = require('color'); | ||
var Service, Characteristic, timer; | ||
module.exports = function (hap) { | ||
@@ -17,11 +21,64 @@ Service = hap.Service; | ||
this.log = log; | ||
this.timeout = config.timeout || 5000; | ||
// Local state | ||
this.color = undefined; | ||
this.brightness = undefined; | ||
this.state = undefined; | ||
this.disconnectTimeout = config.disconnectTimeout || 10000; | ||
timer = function (timedOut) { | ||
setTimeout(function() { | ||
timedOut = true; | ||
}, config.timeout || 1000); | ||
}; | ||
} | ||
MisfitBoltAccessory.prototype.initialize = function (callback) { | ||
this.getRGBA(callback); | ||
MisfitBoltAccessory.prototype.getServices = function () { | ||
var service = new Service.Lightbulb(this.name), | ||
that = this; | ||
function bind(method) { | ||
return _.bind(that[method], that); | ||
} | ||
function attach(characteristic, property) { | ||
var property = property[0].toUpperCase() + property.slice(1); | ||
service.getCharacteristic(characteristic) | ||
.on('set', bind('set' + property)) | ||
.on('get', bind('get' + property)) | ||
} | ||
attach(Characteristic.On, 'state'); | ||
attach(Characteristic.Hue, 'hue'); | ||
attach(Characteristic.Saturation, 'saturation'); | ||
attach(Characteristic.Brightness, 'brightness'); | ||
return [service]; | ||
}; | ||
MisfitBoltAccessory.prototype.disconnect = function () { | ||
MisfitBoltAccessory.prototype.initialize = function(callback) { | ||
var that = this; | ||
this.pull(function(err, settings) { | ||
if (err) { | ||
error(err); | ||
return callback(err); | ||
} | ||
that.color = new Color(`rgba(${settings.rgba})`); | ||
that.brightness = settings.rgba[settings.rgba.length - 1]; | ||
that.state = settings.state; | ||
that.rgbaToSet = undefined; | ||
that.log('Initial color', that.color); | ||
that.log('Initial brightness', that.brightness); | ||
that.log('Initial state', that.state); | ||
that.sync(); | ||
callback(); | ||
}); | ||
}; | ||
MisfitBoltAccessory.prototype.disconnect = function() { | ||
var bolt = this.bolt; | ||
@@ -35,75 +92,81 @@ clearTimeout(this.willDisconnect); | ||
}, this.disconnectTimeout); | ||
} | ||
}; | ||
MisfitBoltAccessory.prototype.getServices = function () { | ||
var lightbulbService = new Service.Lightbulb(this.name), | ||
bolt = this.bolt; | ||
MisfitBoltAccessory.prototype.sync = function() { | ||
var that = this, | ||
bolt = this.bolt, | ||
timedOut = false; | ||
lightbulbService | ||
.getCharacteristic(Characteristic.On) | ||
.on('set', _.bind(this.setState, this)) | ||
.on('get', _.bind(this.getState, this)); | ||
timer(timedOut); | ||
lightbulbService | ||
.getCharacteristic(Characteristic.Hue) | ||
.on('set', _.bind(this.setHue, this)) | ||
.on('get', _.bind(this.getHue, this)); | ||
function done () { | ||
setTimeout(function(){ | ||
that.sync(); | ||
}, 1); | ||
} | ||
lightbulbService | ||
.getCharacteristic(Characteristic.Saturation) | ||
.on('set', _.bind(this.setSaturation, this)) | ||
.on('get', _.bind(this.getSaturation, this)); | ||
if (that.rgbaToSet) { | ||
clearTimeout(this.willDisconnect); | ||
debug('will sync'); | ||
bolt.connect(function(err) { | ||
if (timedOut) { | ||
timeout('bolt.connect'); | ||
return done(); | ||
} | ||
if (err) { | ||
error('bolt.connect', err); | ||
return done(); | ||
} | ||
debug('setting light'); | ||
lightbulbService | ||
.getCharacteristic(Characteristic.Brightness) | ||
.on('set', _.bind(this.setBrightness, this)) | ||
.on('get', _.bind(this.getBrightness, this)); | ||
return [lightbulbService]; | ||
}; | ||
MisfitBoltAccessory.prototype.set = function (method, value, callback) { | ||
clearTimeout(this.willDisconnect); | ||
clearTimeout(this.willSet); | ||
var that = this, | ||
bolt = that.bolt, | ||
params = [ | ||
value, | ||
function() { | ||
that.disconnect(); | ||
// don't wait until light is set before clearing value to set | ||
// it might get updated before light is set, and we'd miss an update | ||
var rgbaToSet = that.rgbaToSet; | ||
that.rgbaToSet = undefined; | ||
bolt.setRGBA(rgbaToSet, function(err) { | ||
if (timedOut) { | ||
timeout('bolt.setRGBA'); | ||
} else if (err) { | ||
error('bolt.setRGBA', err); | ||
} else { | ||
debug('light set'); | ||
} | ||
]; | ||
debug('will set'); | ||
this.willSet = setTimeout(function() { | ||
debug('setting'); | ||
bolt.connect(function() { | ||
bolt[method].apply(bolt, params); | ||
debug('Bolt called with: ', method, params); | ||
that.disconnect(); | ||
done(); | ||
}); | ||
}); | ||
}, 100); | ||
callback(); | ||
} else { | ||
done(); | ||
} | ||
}; | ||
MisfitBoltAccessory.prototype.get = function (method, callback) { | ||
clearTimeout(this.willDisconnect); | ||
var that = this, | ||
bolt = this.bolt, | ||
timedout = false, | ||
timer = setTimeout(function() { | ||
timedout = true; | ||
debug('timed out'); | ||
callback(new Error('timed out')); | ||
}, this.timeout); | ||
MisfitBoltAccessory.prototype.pull = function(callback) { | ||
var bolt = this.bolt, | ||
timedOut = false; | ||
debug('will get'); | ||
bolt.connect(function() { | ||
bolt[method](function(error, value) { | ||
that.disconnect(); | ||
clearTimeout(timer); | ||
if (!timedout) { | ||
debug('got value:', value); | ||
callback(error, value); | ||
timer(timedOut); | ||
bolt.connect(function(err) { | ||
if (timedOut) { | ||
timeout('bolt.connect'); | ||
} else if (err) { | ||
error('bolt.connect', err); | ||
return callback(err); | ||
} | ||
bolt.getRGBA(function(err, rgba) { | ||
if (timedOut) { | ||
timeout('bolt.getRGBA'); | ||
} else if (err) { | ||
error('bolt.getRGBA', err); | ||
return callback(err); | ||
} | ||
bolt.getState(function(err, state) { | ||
if (timedOut) { | ||
timeout('bolt.getState'); | ||
} else if (err) { | ||
error('bolt.getState', err); | ||
return callback(err); | ||
} | ||
callback(null, {rgba: rgba, state: state}) | ||
}); | ||
}); | ||
@@ -113,120 +176,53 @@ }); | ||
MisfitBoltAccessory.prototype.push = function(callback) { | ||
var rgb = this.color.rgb(); | ||
this.rgbaToSet = [rgb.r, rgb.g, rgb.b, this.state ? this.brightness : 0]; | ||
debug('setting rgba: ', this.rgbaToSet); | ||
callback(); | ||
}; | ||
MisfitBoltAccessory.prototype.setState = function (state, callback) { | ||
var that = this; | ||
this.log('Setting state: ', state); | ||
this.set('setState', state, function() { | ||
that.log('State set: ', state); | ||
callback(); | ||
}); | ||
this.log('setting state: ', state); | ||
this.state = state; | ||
this.push(callback); | ||
}; | ||
MisfitBoltAccessory.prototype.getState = function (callback) { | ||
var that = this; | ||
this.log('Getting state'); | ||
this.get('getState', function(error, state) { | ||
if (error) { | ||
debug('Error', error); | ||
callback(error); | ||
return; | ||
} | ||
that.log('Got state: ', state); | ||
callback(null, state); | ||
}); | ||
this.log('getting state: ', this.state); | ||
callback(null, this.state); | ||
}; | ||
MisfitBoltAccessory.prototype.setRGBA = function (callback) { | ||
var rgba = this.color.rgbArray().concat(this.brightness); | ||
debug('Setting RGBA: ', rgba); | ||
this.set('setRGBA', rgba, function() { | ||
debug('RGBA set: ', rgba); | ||
callback(); | ||
}); | ||
}; | ||
MisfitBoltAccessory.prototype.getRGBA = function (callback) { | ||
debug('Getting RGBA'); | ||
var that = this; | ||
this.get('getRGBA', function(error, rgba) { | ||
if (error) { | ||
debug('Error', error); | ||
callback(error); | ||
return; | ||
} | ||
debug('Got RGBA: ', rgba); | ||
that.color = new Color(`rgba(${rgba})`); | ||
that.brightness = rgba.pop(); | ||
callback(null, rgba); | ||
}); | ||
}; | ||
MisfitBoltAccessory.prototype.setHue = function (hue, callback) { | ||
var that = this; | ||
this.log('Setting hue: ', hue); | ||
this.log('setting hue: ', hue); | ||
this.color.hue(hue); | ||
this.setRGBA(function() { | ||
that.log('Hue set: ', hue); | ||
callback(); | ||
}); | ||
this.push(callback); | ||
}; | ||
MisfitBoltAccessory.prototype.getHue = function (callback) { | ||
var that = this; | ||
this.log('Getting hue'); | ||
this.getRGBA(function(error) { | ||
if (error) { | ||
debug('Error', error); | ||
callback(error); | ||
return; | ||
} | ||
that.log('Got hue: ', that.color.hue()); | ||
callback(null, that.color.hue()); | ||
}); | ||
this.log('getting hue: ', this.color.hue()); | ||
callback(null, this.color.hue()); | ||
}; | ||
MisfitBoltAccessory.prototype.setSaturation = function (saturation, callback) { | ||
var that = this; | ||
this.log('Setting saturation: ', saturation); | ||
this.log('setting saturation: ', saturation); | ||
this.color.saturationv(saturation); | ||
this.setRGBA(function() { | ||
that.log('Saturation set: ', saturation); | ||
callback(); | ||
}); | ||
this.push(callback); | ||
}; | ||
MisfitBoltAccessory.prototype.getSaturation = function (callback) { | ||
var that = this; | ||
this.log('Getting saturation'); | ||
this.getRGBA(function(error) { | ||
if (error) { | ||
debug('Error', error); | ||
callback(error); | ||
return; | ||
} | ||
that.log('Got saturation: ', that.color.saturation()); | ||
callback(null, that.color.saturation()); | ||
}); | ||
this.log('getting saturation: ', this.color.saturation()); | ||
callback(null, this.color.saturation()); | ||
}; | ||
MisfitBoltAccessory.prototype.setBrightness = function (brightness, callback) { | ||
var that = this; | ||
this.log('Setting brightness: ', brightness); | ||
this.log('setting brightness: ', brightness); | ||
this.brightness = brightness; | ||
this.setRGBA(function() { | ||
that.log('Brightness set: ', brightness); | ||
callback(); | ||
}); | ||
this.state = this.brightness > 0; | ||
this.push(callback); | ||
}; | ||
MisfitBoltAccessory.prototype.getBrightness = function (callback) { | ||
var that = this; | ||
this.log('Getting brightness'); | ||
this.getRGBA(function(error) { | ||
if (error) { | ||
debug('Error', error); | ||
callback(error); | ||
return; | ||
} | ||
that.log('Got brightness: ', that.brightness); | ||
callback(null, that.brightness); | ||
}); | ||
var brightness = this.state > 0 ? this.brightness : 0; | ||
this.log('getting brightness: ', brightness); | ||
callback(null, brightness); | ||
}; |
{ | ||
"name": "homebridge-misfit-bolt", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "Misfit Bolt plugin for homebridge", | ||
@@ -38,3 +38,3 @@ "keywords": [ | ||
"lodash": "^3.10.1", | ||
"misfit-bolt": "^1.0.3" | ||
"misfit-bolt": "^1.0.5" | ||
}, | ||
@@ -41,0 +41,0 @@ "devDependencies": { |
# homebridge-misfit-bolt | ||
Misfit Bolt plugin for [Homebridge](https://github.com/nfarina/homebridge). | ||
Let you control your Misfit Bolt via HomeKit / Siri. | ||
@@ -17,4 +18,11 @@ # Installation | ||
Configuration sample: | ||
## Generate configuration | ||
After installing the plugin, you can run the following command if you want to generate your configuration automatically. This script helps finding surrounding Bolts, and outputs the required JSON configuration. | ||
```bash | ||
/usr/local/lib/node_modules/homebridge-misfit-bolt/generate-config | ||
``` | ||
## Configuration sample | ||
```json | ||
@@ -28,2 +36,3 @@ "platforms": [ | ||
"name": "Bolt", | ||
"timeout": 500, | ||
"disconnectTimeout": 5000 | ||
@@ -35,9 +44,12 @@ }] | ||
The module will fetch all your Misfit Bolts listed in `platform.accessories` and make them available to HomeBridge / HomeKit / Siri. | ||
Each accessory should have the following properties: | ||
## Accessories | ||
#### `id`: | ||
### Required properties | ||
UUID of your misfit bolt as recognized by your system. On linux, you can find out your UUIDs using the following command: | ||
#### `id` | ||
Identifier of your Misfit Bolt. | ||
On Linux, this corresponds to the bluetooth address of the bulb. You can find out your identifier using the following command: | ||
```bash | ||
@@ -59,14 +71,28 @@ $ sudo hcitool lescan | ||
#### `name`: | ||
**Note:** | ||
On OSX, the `id` field is a UUID which differs from the bluetooth address. | ||
#### `name` | ||
Name of your Bolt, as you want it to appear in your Homekit supported app. | ||
#### `disconnectTimeout`: (optional, default to 10000) | ||
### Optional properties | ||
Time in millisecond after which homebridge will disconnect from the bulb, so that it can be discovered by other BLE enabled devices (useful if you want to keep using the original Misfit Bolt app in parallel of using homebridge). | ||
#### `timeout` (in milliseconds, optional, default to `1000`) | ||
Time after which homebridge will abort any pending interaction with the bulb, such as connecting or setting values. | ||
#### `disconnectTimeout` (in milliseconds, optional, default to `10000`) | ||
Time after which homebridge will disconnect from the bulb. | ||
Useful if you want to keep using the original Misfit Bolt app in parallel of using homebridge. Next time a command is sent via homebridge, a reconnection will be attempted. | ||
# TODO | ||
Unit tests. | ||
- Move implementation that keep track of state, and allows manipulation of Hue / Brightness / Saturation to [`misfit-bolt`](https://github.com/flochtililoch/misfit-bolt) module. | ||
- Unit tests. | ||
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
11938
10
95
229
1
Updatedmisfit-bolt@^1.0.5