Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb7303eacb | ||
|
|
62cc18a3cd | ||
|
|
bb4c4272d5 | ||
|
|
7500a11bbe | ||
|
|
f9a07a6abb | ||
|
|
17ef2c2487 | ||
|
|
b826823041 | ||
|
|
efb8bd0286 | ||
|
|
b0ce189ff0 |
7 changed files with 277 additions and 7 deletions
132
cli/homekit.js
Normal file
132
cli/homekit.js
Normal 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;
|
||||
|
|
@ -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]]();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
3
homekit/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
exports = module.exports = {
|
||||
light: require('./light')
|
||||
};
|
||||
129
homekit/light.js
Normal file
129
homekit/light.js
Normal 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;
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue