mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-08 05:31:55 +00:00
Provider config and cli-based setup
This commit is contained in:
@@ -20,4 +20,4 @@ var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
|
||||
global.homebridge = require(lib + '/homebridge');
|
||||
|
||||
// Run the HomeBridge CLI
|
||||
homebridge.cli();
|
||||
require(lib + '/cli')();
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
import request from 'request';
|
||||
|
||||
// Demonstrate that we were loaded
|
||||
console.log("Lockitron provider loaded!");
|
||||
|
||||
module.exports = {
|
||||
|
||||
config: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
description: "You can find your personal Access Token at: https://api.lockitron.com",
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
validateConfig: function(callback) {
|
||||
|
||||
// validate the accessToken
|
||||
let accessToken = homebridge.config.get('homebridge-lockitron.accessToken');
|
||||
|
||||
// prove that we got a value
|
||||
console.log(`Access Token: ${accessToken}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
},
|
||||
"keywords": [
|
||||
"homebridge-provider"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"request": "^2.58.0"
|
||||
}
|
||||
}
|
||||
|
||||
118
lib/cli.js
Normal file
118
lib/cli.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import program from 'commander';
|
||||
import log from 'npmlog';
|
||||
import prompt from 'prompt';
|
||||
import { HOMEBRIDGE_VERSION } from './homebridge';
|
||||
import { User } from './user';
|
||||
import { Server } from './server';
|
||||
import { Provider } from './provider';
|
||||
import { camelCaseToRegularForm } from './util';
|
||||
|
||||
export default function() {
|
||||
|
||||
// Global options (none currently) and version printout
|
||||
program
|
||||
.version(HOMEBRIDGE_VERSION);
|
||||
|
||||
// Run the HomeBridge server
|
||||
program
|
||||
.command('server')
|
||||
.description('Run the HomeBridge server.')
|
||||
.action(runServer);
|
||||
|
||||
program
|
||||
.command('providers')
|
||||
.description('List installed providers.')
|
||||
.action(listInstalledProviders);
|
||||
|
||||
program
|
||||
.command('setup [provider]')
|
||||
.description('Sets up a new HomeBridge provider or re-configures an existing one.')
|
||||
.action(setupProvider);
|
||||
|
||||
// Parse options and execute HomeBridge
|
||||
program.parse(process.argv);
|
||||
|
||||
// Display help by default if no commands or options given
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.help();
|
||||
}
|
||||
}
|
||||
|
||||
function runServer(options) {
|
||||
|
||||
// get all installed providers
|
||||
let providers:Array<Provider> = Provider.installed();
|
||||
|
||||
// load and validate providers - check for valid package.json, etc.
|
||||
try {
|
||||
this.providerModules = providers.map((provider) => provider.load());
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function listInstalledProviders(options) {
|
||||
Provider.installed().forEach((provider) => console.log(provider.name));
|
||||
}
|
||||
|
||||
function setupProvider(providerName, options) {
|
||||
|
||||
// if you didn't specify a provider, print help
|
||||
if (!providerName) {
|
||||
log.error("You must specify the name of the provider to setup. Type 'homebridge providers' to list the providers currently installed.");
|
||||
program.help();
|
||||
}
|
||||
|
||||
try {
|
||||
let provider = new Provider(providerName);
|
||||
let providerModule:object = provider.load({skipConfigCheck: true});
|
||||
|
||||
if (providerModule.config) {
|
||||
|
||||
prompt.message = "";
|
||||
prompt.delimiter = "";
|
||||
prompt.start();
|
||||
prompt.get(buildPromptSchema(providerName, providerModule.config), (err, result) => {
|
||||
|
||||
// apply configuration values entered by the user
|
||||
for (let key:string in result) {
|
||||
let value:object = result[key];
|
||||
|
||||
User.config.set(`${providerName}.${key}`, value);
|
||||
}
|
||||
|
||||
providerModule.validateConfig();
|
||||
});
|
||||
}
|
||||
else {
|
||||
providerModule.validateConfig();
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
log.error(`Setup failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// builds a "schema" obejct for the prompt lib based on the provider's config spec
|
||||
function buildPromptSchema(providerName: string, providerConfig: object): object {
|
||||
let properties = {};
|
||||
|
||||
for (let key:string in providerConfig) {
|
||||
let spec:object = providerConfig[key];
|
||||
|
||||
// do we have a value for this config key currently?
|
||||
let currentValue = User.config.get(`${providerName}.${key}`);
|
||||
|
||||
// copy over config spec with some modifications
|
||||
properties[key] = {
|
||||
description: `\n${spec.description}\n${camelCaseToRegularForm(key).white}:`,
|
||||
type: spec.type,
|
||||
required: spec.required,
|
||||
default: currentValue
|
||||
}
|
||||
}
|
||||
|
||||
return { properties };
|
||||
}
|
||||
40
lib/config.js
Normal file
40
lib/config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import fs from 'fs';
|
||||
|
||||
export class Config {
|
||||
|
||||
constructor(path:string, data:object = {}) {
|
||||
this.path = path;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get(key:string) {
|
||||
this.validateKey(key);
|
||||
let [providerName, keyName] = key.split(".");
|
||||
return this.data[providerName] && this.data[providerName][keyName];
|
||||
}
|
||||
|
||||
set(key:string, value:object) {
|
||||
this.validateKey(key);
|
||||
let [providerName, keyName] = key.split(".");
|
||||
this.data[providerName] = this.data[providerName] || {};
|
||||
this.data[providerName][keyName] = value;
|
||||
this.save();
|
||||
}
|
||||
|
||||
validateKey(key:string) {
|
||||
if (key.split(".").length != 2)
|
||||
throw new Error(`The config key '${key}' is invalid. Configuration keys must be in the form [my-provider].[myKey]`);
|
||||
}
|
||||
|
||||
static load(configPath: string): Config {
|
||||
// load up the previous config if found
|
||||
if (fs.existsSync(configPath))
|
||||
return new Config(configPath, JSON.parse(fs.readFileSync(configPath)));
|
||||
else
|
||||
return new Config(configPath); // empty initial config
|
||||
}
|
||||
|
||||
save() {
|
||||
fs.writeFileSync(this.path, JSON.stringify(this.data, null, 2));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import cli from './homebridge/cli';
|
||||
import { User } from './user';
|
||||
|
||||
//
|
||||
// Main HomeBridge Module with global exports.
|
||||
@@ -8,7 +8,5 @@ import cli from './homebridge/cli';
|
||||
// HomeBridge version
|
||||
export const HOMEBRIDGE_VERSION = JSON.parse(fs.readFileSync('package.json')).version;
|
||||
|
||||
// HomeBridge CLI
|
||||
export { cli }
|
||||
|
||||
// HomeBridge API
|
||||
export let config = User.config;
|
||||
@@ -1,70 +0,0 @@
|
||||
import program from 'commander';
|
||||
import { HOMEBRIDGE_VERSION } from '../homebridge';
|
||||
import { Server } from './server';
|
||||
import { Provider } from './provider';
|
||||
|
||||
export default function() {
|
||||
|
||||
// Global options (none currently) and version printout
|
||||
program
|
||||
.version(HOMEBRIDGE_VERSION);
|
||||
|
||||
// Run the HomeBridge server
|
||||
program
|
||||
.command('server')
|
||||
.description('Run the HomeBridge server.')
|
||||
.action(runServer);
|
||||
|
||||
program
|
||||
.command('providers')
|
||||
.description('List installed providers.')
|
||||
.action(listInstalledProviders);
|
||||
|
||||
program
|
||||
.command('setup [provider]')
|
||||
.description('Sets up a new HomeBridge provider or re-configures an existing one.')
|
||||
.action(setupProvider);
|
||||
|
||||
// Parse options and execute HomeBridge
|
||||
program.parse(process.argv);
|
||||
|
||||
// Display help by default if no commands or options given
|
||||
if (!process.argv.slice(2).length) {
|
||||
program.help();
|
||||
}
|
||||
}
|
||||
|
||||
function runServer(options) {
|
||||
|
||||
// get all installed providers
|
||||
let providers:Array<Provider> = Provider.installed();
|
||||
|
||||
// load and validate providers - check for valid package.json, etc.
|
||||
try {
|
||||
this.providerModules = providers.map((provider) => provider.load());
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function listInstalledProviders(options) {
|
||||
Provider.installed().forEach((provider) => console.log(provider.name));
|
||||
}
|
||||
|
||||
function setupProvider(providerName, options) {
|
||||
|
||||
// if you didn't specify a provider, print help
|
||||
if (!providerName) {
|
||||
console.log("You must specify the name of the provider to setup. Type 'homebridge providers' to list the providers currently installed.");
|
||||
program.help();
|
||||
}
|
||||
|
||||
try {
|
||||
let provider = new Provider(providerName);
|
||||
}
|
||||
catch (err) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
//
|
||||
// Manages user settings and storage locations.
|
||||
//
|
||||
|
||||
// global cached config
|
||||
let config:Config;
|
||||
|
||||
export class User {
|
||||
|
||||
static get config():Config {
|
||||
return config || (config = new Config());
|
||||
}
|
||||
|
||||
static get storagePath():string {
|
||||
let home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
|
||||
return path.join(home, ".homebridge");
|
||||
}
|
||||
|
||||
static get configPath():string {
|
||||
return path.join(User.storagePath, "config.json");
|
||||
}
|
||||
|
||||
static get providersPath():string {
|
||||
return path.join(User.storagePath, "providers");
|
||||
}
|
||||
}
|
||||
|
||||
export class Config {
|
||||
|
||||
constructor(data:object = {}) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get(key:string) {
|
||||
return this.data[key];
|
||||
}
|
||||
|
||||
set(key:string, value:object) {
|
||||
this.data[key] = value;
|
||||
}
|
||||
|
||||
static load():Config {
|
||||
// load up the previous config if found
|
||||
if (fs.existsSync(User.configPath))
|
||||
return new Config(JSON.parse(fs.readFileSync(User.configPath)));
|
||||
else
|
||||
return new Config(); // empty initial config
|
||||
}
|
||||
|
||||
save() {
|
||||
fs.writeFileSync(User.configPath, JSON.stringify(this.data));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import semver from 'semver';
|
||||
import { User } from './user';
|
||||
import { HOMEBRIDGE_VERSION } from '../homebridge';
|
||||
import { HOMEBRIDGE_VERSION } from './homebridge';
|
||||
|
||||
// This class represents a HomeBridge Provider that may or may not be installed.
|
||||
export class Provider {
|
||||
@@ -15,11 +15,11 @@ export class Provider {
|
||||
return path.join(User.providersPath, this.name);
|
||||
}
|
||||
|
||||
load():object {
|
||||
load(options:object = {}):object {
|
||||
|
||||
// does this provider exist at all?
|
||||
if (!fs.existsSync(this.path)) {
|
||||
throw new Error(`Provider ${this.name} was not found. Make sure a directory matching the name '${this.name}' exists in your ~/.homebridge/providers folder.`)
|
||||
throw new Error(`Provider ${this.name} was not found. Make sure the directory '~/.homebridge/providers/${this.name}' exists.`)
|
||||
}
|
||||
|
||||
// check for a package.json
|
||||
@@ -62,7 +62,7 @@ export class Provider {
|
||||
let providerConfig = loadedProvider.config;
|
||||
|
||||
// verify that all required values are present
|
||||
if (providerConfig) {
|
||||
if (providerConfig && !options.skipConfigCheck) {
|
||||
for (let key:string in providerConfig) {
|
||||
|
||||
let configParams:object = providerConfig[key];
|
||||
31
lib/user.js
Normal file
31
lib/user.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { Config } from './config';
|
||||
|
||||
//
|
||||
// Manages user settings and storage locations.
|
||||
//
|
||||
|
||||
// global cached config
|
||||
let config:Config;
|
||||
|
||||
export class User {
|
||||
|
||||
static get config():Config {
|
||||
return config || (config = Config.load(User.configPath));
|
||||
}
|
||||
|
||||
static get storagePath():string {
|
||||
let home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
|
||||
return path.join(home, ".homebridge");
|
||||
}
|
||||
|
||||
static get configPath():string {
|
||||
return path.join(User.storagePath, "config.json");
|
||||
}
|
||||
|
||||
static get providersPath():string {
|
||||
return path.join(User.storagePath, "providers");
|
||||
}
|
||||
}
|
||||
|
||||
9
lib/util.js
Normal file
9
lib/util.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// Converts "accessToken" to "Access Token"
|
||||
export function camelCaseToRegularForm(camelCase: string): string {
|
||||
return camelCase
|
||||
// insert a space before all caps
|
||||
.replace(/([A-Z])/g, ' $1')
|
||||
// uppercase the first character
|
||||
.replace(/^./, function(str){ return str.toUpperCase(); })
|
||||
}
|
||||
@@ -29,6 +29,8 @@
|
||||
"babel": "^5.6.14",
|
||||
"commander": "^2.8.1",
|
||||
"hap-nodejs": "git+https://github.com/khaost/HAP-NodeJS#2a1bc8d99a2009317ab5da93faebea34c89f197c",
|
||||
"npmlog": "^1.2.1",
|
||||
"prompt": "^0.2.14",
|
||||
"semver": "^4.3.6"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user