Compare commits

...

9 commits

Author SHA1 Message Date
Todd Treece
fb7303eacb switch to swagger-client promises 2016-01-28 16:40:10 -05:00
Todd Treece
62cc18a3cd working test version of light accessory 2016-01-28 16:01:11 -05:00
Todd Treece
bb4c4272d5 adds node-persist init to homekit cli 2016-01-28 16:00:03 -05:00
Todd Treece
7500a11bbe adds homekit to gulp syntax check 2016-01-28 15:56:01 -05:00
Todd Treece
f9a07a6abb adds accessory index 2016-01-27 18:53:35 -05:00
Todd Treece
17ef2c2487 adds homekit to main cli 2016-01-27 18:50:57 -05:00
Todd Treece
b826823041 adds homekit light accessory 2016-01-27 18:49:01 -05:00
Todd Treece
efb8bd0286 adds homekit cli 2016-01-27 18:48:49 -05:00
Todd Treece
b0ce189ff0 adds hap-node dependency 2016-01-26 14:29:02 -05:00
7 changed files with 277 additions and 7 deletions

132
cli/homekit.js Normal file
View file

@ -0,0 +1,132 @@
'use strict';
const Client = require('../client'),
CLI = require('./index'),
Yargs = require('yargs'),
uuid = require('hap-nodejs').uuid,
storage = require('node-persist'),
Bridge = require('hap-nodejs').Bridge,
Accessory = require('hap-nodejs').Accessory,
inquirer = require('inquirer'),
Accessories = require('../homekit'),
crypto = require('crypto');
storage.initSync();
class HomekitCLI extends CLI {
constructor() {
super('homekit');
this.completions = [
'help',
'light'
];
this.yargs = Yargs(process.argv.slice(3));
this.client = false;
}
init() {
if(! process.env.AIO_CLIENT_USER || ! process.env.AIO_CLIENT_KEY)
return this.requireAuth(this.yargs);
const options = {
success: this.clientReady.bind(this, this.yargs),
failure: this.error.bind(this)
};
if(process.env.AIO_CLIENT_HOST)
options.host = process.env.AIO_CLIENT_HOST;
if(process.env.AIO_CLIENT_PORT)
options.port = process.env.AIO_CLIENT_PORT;
this.client = new Client(process.env.AIO_CLIENT_USER, process.env.AIO_CLIENT_KEY, options);
}
clientReady(yargs) {
const argv = yargs
.command('light', 'Light')
.command('help', 'Show help')
.demand(1, 'You must supply a valid homekit command')
.argv;
if(! argv)
return;
const command = argv._[0];
if(command === 'help')
return yargs.showHelp();
if(! this[command])
return yargs.showHelp();
this[command](Yargs(process.argv.slice(4)));
}
light(yargs) {
yargs.usage(`Usage: adafruit-io homekit light [options]`)
.command('help', 'Show help')
.alias('n', 'name').demand('name')
.nargs('n', 1).describe('n', 'the name of the light');
const argv = yargs.argv,
command = argv._[0];
if(command === 'help')
return yargs.showHelp();
const name = argv.name || 'light',
bridge = new Bridge('Adafruit IO', uuid.generate('Adafruit IO'));
bridge.on('identify', (paired, cb) => cb());
const light = new Accessories.light(name, this.client);
bridge.addBridgedAccessory(light);
this.logo();
this.info('advertising homekit light accessory...');
this.info('PIN: 100-11-100');
bridge.publish({
username: 'AD:A0:AD:A0:AD:A0',
port: 60000,
pincode: '100-11-100',
category: Accessory.Categories.BRIDGE
});
}
requireAuth(yargs) {
const argv = yargs
.usage('Usage: adafruit-io homekit config [options]')
.alias('h', 'host').nargs('h', 1).default('h', process.env.AIO_CLIENT_HOST || 'io.adafruit.com').describe('h', 'Server hostname')
.alias('p', 'port').nargs('p', 1).default('p', process.env.AIO_CLIENT_PORT || '80').describe('p', 'Server port')
.alias('u', 'username').demand('username').nargs('u', 1).describe('u', 'Adafruit IO Username')
.alias('k', 'key').demand('key').nargs('k', 1).describe('k', 'Adafruit IO Key')
.command('help', 'Show help')
.argv;
process.env.AIO_CLIENT_HOST = argv.host;
process.env.AIO_CLIENT_PORT = argv.port;
process.env.AIO_CLIENT_USER = argv.username;
process.env.AIO_CLIENT_KEY = argv.key;
this.saveEnv();
}
}
exports = module.exports = HomekitCLI;

View file

@ -16,6 +16,7 @@ class CLI {
this.type = type || 'cli';
this.completions = [
'client',
'homekit',
'server',
'tunnel',
'help',
@ -24,6 +25,7 @@ class CLI {
this.sub = {
client: require('./client'),
homekit: require('./homekit'),
server: require('./server'),
tunnel: require('./tunnel')
};
@ -50,6 +52,7 @@ class CLI {
this.yargs
.usage('Usage: adafruit-io <command>')
.command('server', 'Adafruit IO local server')
.command('homekit', 'Adafruit IO homekit bridge')
.command('client', 'Adafruit IO client')
.command('tunnel', 'TLS tunnel to io.adafruit.com')
.command('help', 'Show help')
@ -163,7 +166,7 @@ class CLI {
if(Object.keys(children).indexOf(commands[0]) < 0)
return done([]);
if(commands[0] === 'server' || commands[0] === 'tunnel') {
if(commands[0] === 'server' || commands[0] === 'tunnel' || commands[0] === 'homekit') {
const child = new children[commands[0]]();

View file

@ -1,6 +1,6 @@
'use strict';
const Swagger = require('swagger-client-promises'),
const Swagger = require('swagger-client'),
Stream = require('./lib/stream');
class Client {
@ -13,7 +13,6 @@ class Client {
this.key = key || false;
this.swagger_path = '/api/docs/api.json';
this.success = function() {};
this.failure = function(err) { throw err; };
Object.assign(this, options);
@ -23,13 +22,15 @@ class Client {
if(! this.key)
throw new Error('client key is required');
this.swagger = new Swagger({
new Swagger({
url: `http://${this.host}:${this.port}${this.swagger_path}`,
success: this._defineGetters.bind(this),
failure: this.failure,
usePromise: true,
authorizations: {
HeaderKey: new Swagger.ApiKeyAuthorization('X-AIO-Key', this.key, 'header')
}
}).then((client) => {
this.swagger = client;
this._defineGetters();
});
}

View file

@ -23,6 +23,7 @@ gulp.task('lint', function() {
return gulp.src([
'index.js',
'cli/*.js',
'homekit/*.js',
'client/**/*.js',
'server/index.js',
'server/lib/*.js',

3
homekit/index.js Normal file
View file

@ -0,0 +1,3 @@
exports = module.exports = {
light: require('./light')
};

129
homekit/light.js Normal file
View file

@ -0,0 +1,129 @@
'use strict';
const Hap = require('hap-nodejs'),
Accessory = Hap.Accessory,
Service = Hap.Service,
Characteristic = Hap.Characteristic,
uuid = Hap.uuid;
class Light extends Accessory {
constructor(name, io) {
super(name, uuid.generate(`adafruit:accessories:light-${name}`));
this.getService(Service.AccessoryInformation)
.setCharacteristic(Characteristic.Manufacturer, 'Adafruit Industries')
.setCharacteristic(Characteristic.Model, 'Adafruit IO Light')
.setCharacteristic(Characteristic.SerialNumber, 'AIOLIGHT01');
// just call cb on ident
this.on('identify', (paired, cb) => cb());
this._name = name;
this._io = io;
this._state = {
power: 0,
brightness: 100,
hue: 255,
saturation: 100
};
this._stream = this._io.Groups.writable(this._name);
let timer = setTimeout(() => {
this._writeState();
}, 1000);
this._stream.on('data', (group) => {
if(timer) {
clearTimeout(timer);
timer = false;
}
group = JSON.parse(group.toString());
Object.keys(group.feeds).forEach((key) => {
if(/^brightness/.test(key))
this._state.brightness = parseInt(group.feeds[key]);
if(/^power/.test(key))
this._state.power = parseInt(group.feeds[key]);
if(/^hue/.test(key))
this._state.hue = parseInt(group.feeds[key]);
if(/^saturation/.test(key))
this._state.saturation = parseInt(group.feeds[key]);
});
});
const service = this.addService(Service.Lightbulb, `Adafruit Light`);
service.getCharacteristic(Characteristic.On)
.on('set', this._lightOnSet.bind(this))
.on('get', this._lightOnGet.bind(this));
service.getCharacteristic(Characteristic.Brightness)
.on('set', this._lightBrightnessSet.bind(this))
.on('get', this._lightBrightnessGet.bind(this));
service.getCharacteristic(Characteristic.Hue)
.on('set', this._lightHueSet.bind(this))
.on('get', this._lightHueGet.bind(this));
service.getCharacteristic(Characteristic.Saturation)
.on('set', this._lightSaturationSet.bind(this))
.on('get', this._lightSaturationGet.bind(this));
}
_lightOnGet(cb) {
cb(null, this._state.power);
}
_lightOnSet(value, cb) {
this._state.power = value ? 1 : 0;
this._writeState();
cb();
}
_lightBrightnessGet(cb) {
cb(null, this._state.brightness);
}
_lightBrightnessSet(value, cb) {
this._state.brightness = parseInt(value);
this._writeState();
cb();
}
_lightSaturationGet(cb) {
cb(null, this._state.saturation);
}
_lightSaturationSet(value, cb) {
this._state.saturation = parseInt(value);
this._writeState();
cb();
}
_lightHueGet(cb) {
cb(null, this._state.hue);
}
_lightHueSet(value, cb) {
this._state.hue = parseInt(value);
this._writeState();
cb();
}
_writeState() {
Object.keys(this._state).forEach((key) => {
this._state[key] = this._state[key] || 0;
});
this._stream.write(JSON.stringify({ feeds: this._state }));
}
}
exports = module.exports = Light;

View file

@ -45,12 +45,13 @@
"dotenv": "^1.2.0",
"express": "^4.13.3",
"express-csv": "^0.6.0",
"hap-nodejs": "^0.1.1",
"inquirer": "^0.10.0",
"mkdirp": "^0.5.1",
"mqtt": "^1.4.1",
"nedb": "^1.1.3",
"restler": "^3.2.2",
"swagger-client-promises": "^1.0.2",
"swagger-client": "swagger-api/swagger-js#d7f8bc26306fbfcb8e7be1daff94a07945562808",
"swagger-tools": "^0.9.5",
"winston": "^1.0.1",
"xml": "^1.0.0",