diff --git a/lib/homebridge/cli.js b/lib/homebridge/cli.js index 929097a..2c50dc1 100644 --- a/lib/homebridge/cli.js +++ b/lib/homebridge/cli.js @@ -1,6 +1,7 @@ -import Server from './server'; import program from 'commander'; import { HOMEBRIDGE_VERSION } from '../homebridge'; +import { Server } from './server'; +import { Provider } from './provider'; export default function() { @@ -11,14 +12,59 @@ export default function() { // Run the HomeBridge server program .command('server') - .description('Run the HomeBridge server') - .action((options) => new Server(options).run()); + .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.outputHelp(); + program.help(); } } + +function runServer(options) { + + // get all installed providers + let providers:Array = 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) { + + } +} \ No newline at end of file diff --git a/lib/homebridge/provider.js b/lib/homebridge/provider.js index 0a936f7..144859c 100644 --- a/lib/homebridge/provider.js +++ b/lib/homebridge/provider.js @@ -1,6 +1,7 @@ import path from 'path'; import fs from 'fs'; import semver from 'semver'; +import { User } from './user'; import { HOMEBRIDGE_VERSION } from '../homebridge'; // This class represents a HomeBridge Provider that may or may not be installed. @@ -11,11 +12,16 @@ export class Provider { } get path():string { - return path.join(Provider.installedProvidersDir, this.name); + return path.join(User.providersPath, this.name); } load():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.`) + } + // check for a package.json let pjsonPath:string = path.join(this.path, "package.json"); let pjson:object = null; @@ -52,6 +58,21 @@ export class Provider { // try to require() it let loadedProvider:object = require(mainPath); + // pull out the configuration data, if any + let providerConfig = loadedProvider.config; + + // verify that all required values are present + if (providerConfig) { + for (let key:string in providerConfig) { + + let configParams:object = providerConfig[key]; + + if (configParams.required && !User.config.get(`${this.name}-${key}`)) { + throw new Error(`Provider ${this.name} requires the config value ${key} to be set.`); + } + } + } + return loadedProvider; } @@ -59,12 +80,12 @@ export class Provider { static installed():Array { let providers:Array = []; - let names:Array = fs.readdirSync(Provider.installedProvidersDir); + let names:Array = fs.readdirSync(User.providersPath); for (let name:string of names) { // reconstruct full path - let fullPath:string = path.join(Provider.installedProvidersDir, name); + let fullPath:string = path.join(User.providersPath, name); // we only care about directories if (!fs.statSync(fullPath).isDirectory()) continue; @@ -74,16 +95,4 @@ export class Provider { return providers; } - - // - // Private utility functions - // - - static get userDir():string { - return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; - } - - static get installedProvidersDir():string { - return path.join(Provider.userDir, ".homebridge/providers"); - } } diff --git a/lib/homebridge/server.js b/lib/homebridge/server.js index def008e..52a98e1 100644 --- a/lib/homebridge/server.js +++ b/lib/homebridge/server.js @@ -1,14 +1,12 @@ import { Provider } from './provider'; +import { User, Config } from './user'; -export default class Server { +export class Server { + + constructor(providers:object) { + this.providers = providers; // providers[name] = loaded provider JS module + } run() { - // get all installed providers - let providers:Array = Provider.installed(); - - // validate providers - check for valid package.json, etc. - providers.forEach((provider) => provider.load()); - - console.log(`Loaded ${providers.length} providers.`); } } diff --git a/lib/homebridge/user.js b/lib/homebridge/user.js new file mode 100644 index 0000000..919df07 --- /dev/null +++ b/lib/homebridge/user.js @@ -0,0 +1,56 @@ +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)); + } +} \ No newline at end of file