mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-28 21:03:57 +00:00
Plugin support
- Homebridge is now designed to be `npm install`d globally and executed via "homebridge" script - Remove all specific accessories/platforms except for an example - New internal structure and "cli"
This commit is contained in:
29
lib/cli.js
Normal file
29
lib/cli.js
Normal file
@@ -0,0 +1,29 @@
|
||||
var program = require('commander');
|
||||
var hap = require("hap-nodejs");
|
||||
var version = require('./version');
|
||||
var Server = require('./server').Server;
|
||||
var Plugin = require('./plugin').Plugin;
|
||||
var User = require('./user').User;
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
console.log("_____________________________________________________________________");
|
||||
console.log("IMPORTANT: Homebridge is in the middle of some big changes.");
|
||||
console.log(" Read more about it here:");
|
||||
console.log(" https://github.com/nfarina/homebridge/wiki/Migration-Guide");
|
||||
console.log("_____________________________________________________________________");
|
||||
console.log("");
|
||||
|
||||
program
|
||||
.version(version)
|
||||
.option('-P, --plugin-path [path]', 'look for plugins installed at [path] as well as node_modules', function(p) { Plugin.addPluginPath(p); })
|
||||
.option('-D, --debug', 'turn on debug level logging', function() { logger.setDebugEnabled(true) })
|
||||
.parse(process.argv);
|
||||
|
||||
// Initialize HAP-NodeJS with a custom persist directory
|
||||
hap.init(User.persistPath());
|
||||
|
||||
new Server().run();
|
||||
}
|
||||
64
lib/logger.js
Normal file
64
lib/logger.js
Normal file
@@ -0,0 +1,64 @@
|
||||
var chalk = require('chalk');
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
Logger: Logger,
|
||||
setDebugEnabled: setDebugEnabled,
|
||||
_system: new Logger() // system logger, for internal use only
|
||||
}
|
||||
|
||||
var DEBUG_ENABLED = false;
|
||||
|
||||
// Turns on debug level logging
|
||||
function setDebugEnabled(enabled) {
|
||||
DEBUG_ENABLED = enabled;
|
||||
}
|
||||
|
||||
// global cache of logger instances by plugin name
|
||||
var loggerCache = {};
|
||||
|
||||
/**
|
||||
* Logger class
|
||||
*/
|
||||
|
||||
function Logger(pluginName) {
|
||||
this.pluginName = pluginName;
|
||||
}
|
||||
|
||||
Logger.prototype.debug = function(msg) {
|
||||
if (DEBUG_ENABLED)
|
||||
this.log('debug', msg);
|
||||
}
|
||||
|
||||
Logger.prototype.info = function(msg) {
|
||||
this.log('info', msg);
|
||||
}
|
||||
|
||||
Logger.prototype.warn = function(msg) {
|
||||
this.log('warn', msg);
|
||||
}
|
||||
|
||||
Logger.prototype.error = function(msg) {
|
||||
this.log('error', msg);
|
||||
}
|
||||
|
||||
Logger.prototype.log = function(level, msg) {
|
||||
|
||||
if (level == 'debug')
|
||||
msg = chalk.gray(msg);
|
||||
else if (level == 'warn')
|
||||
msg = chalk.yellow(msg);
|
||||
else if (level == 'error')
|
||||
msg = chalk.bold.red(msg);
|
||||
|
||||
// prepend plugin name if applicable
|
||||
if (this.pluginName)
|
||||
msg = chalk.cyan("[" + this.pluginName + "]") + " " + msg;
|
||||
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
Logger.forPlugin = function(pluginName) {
|
||||
return loggerCache[pluginName] || (loggerCache[pluginName] = new Logger(pluginName));
|
||||
}
|
||||
176
lib/plugin.js
Normal file
176
lib/plugin.js
Normal file
@@ -0,0 +1,176 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var semver = require('semver');
|
||||
var User = require('./user').User;
|
||||
var version = require('./version');
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
Plugin: Plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Homebridge Plugin.
|
||||
*
|
||||
* Allows for discovering and loading installed Homebridge plugins.
|
||||
*/
|
||||
|
||||
function Plugin(pluginPath) {
|
||||
this.pluginPath = pluginPath; // like "/usr/local/lib/node_modules/plugin-lockitron"
|
||||
|
||||
// these are exports pulled from the loaded plugin module
|
||||
this.accessory = null; // single exposed accessory
|
||||
this.platform = null; // single exposed platform
|
||||
this.accessories = []; // array of exposed accessories
|
||||
this.platforms = []; // array of exposed platforms
|
||||
}
|
||||
|
||||
Plugin.prototype.name = function() {
|
||||
return path.basename(this.pluginPath);
|
||||
}
|
||||
|
||||
Plugin.prototype.load = function(options) {
|
||||
options = options || {};
|
||||
|
||||
// does this plugin exist at all?
|
||||
if (!fs.existsSync(this.pluginPath)) {
|
||||
throw new Error("Plugin " + this.pluginPath + " was not found. Make sure the module '" + this.pluginPath + "' is installed.");
|
||||
}
|
||||
|
||||
// attempt to load package.json
|
||||
var pjson = Plugin.loadPackageJSON(this.pluginPath);
|
||||
|
||||
// pluck out the HomeBridge version requirement
|
||||
if (!pjson.peerDepdendencies || !pjson.peerDepdendencies.homebridge) {
|
||||
throw new Error("Plugin " + this.pluginPath + " does not contain the 'homebridge' package in 'peerDepdendencies'.");
|
||||
}
|
||||
|
||||
var versionRequired = pjson.peerDepdendencies.homebridge;
|
||||
|
||||
// make sure the version is satisfied by the currently running version of HomeBridge
|
||||
if (!semver.satisfies(version, versionRequired)) {
|
||||
throw new Error("Plugin " + this.pluginPath + " requires a HomeBridge version of " + versionRequired + " which does not satisfy the current HomeBridge version of " + version + ". You may need to upgrade your installation of HomeBridge.");
|
||||
}
|
||||
|
||||
// figure out the main module - index.js unless otherwise specified
|
||||
var main = pjson.main || "./index.js";
|
||||
|
||||
var mainPath = path.join(this.pluginPath, main);
|
||||
|
||||
// try to require() it
|
||||
var pluginModule = require(mainPath);
|
||||
|
||||
// extract all exposed accessories and platforms
|
||||
this.accessories = pluginModule.accessories || {};
|
||||
this.platforms = pluginModule.platforms || {};
|
||||
}
|
||||
|
||||
Plugin.loadPackageJSON = function(pluginPath) {
|
||||
// check for a package.json
|
||||
var pjsonPath = path.join(pluginPath, "package.json");
|
||||
var pjson = null;
|
||||
|
||||
if (!fs.existsSync(pjsonPath)) {
|
||||
throw new Error("Plugin " + pluginPath + " does not contain a package.json.");
|
||||
}
|
||||
|
||||
try {
|
||||
// attempt to parse package.json
|
||||
pjson = JSON.parse(fs.readFileSync(pjsonPath));
|
||||
}
|
||||
catch (err) {
|
||||
throw new Error("Plugin " + pluginPath + " contains an invalid package.json. Error: " + err);
|
||||
}
|
||||
|
||||
// verify that it's tagged with the correct keyword
|
||||
if (!pjson.keywords || pjson.keywords.indexOf("homebridge-plugin") == -1) {
|
||||
throw new Error("Plugin " + pluginPath + " package.json does not contain the keyword 'homebridge-plugin'.");
|
||||
}
|
||||
|
||||
return pjson;
|
||||
}
|
||||
|
||||
Plugin.getDefaultPaths = function() {
|
||||
var win32 = process.platform === 'win32';
|
||||
var paths = [];
|
||||
|
||||
// add the paths used by require()
|
||||
paths = paths.concat(require.main.paths);
|
||||
|
||||
// THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js
|
||||
|
||||
// Adding global npm directories
|
||||
// We tried using npm to get the global modules path, but it haven't work out
|
||||
// because of bugs in the parseable implementation of `ls` command and mostly
|
||||
// performance issues. So, we go with our best bet for now.
|
||||
if (process.env.NODE_PATH) {
|
||||
paths = process.env.NODE_PATH.split(path.delimiter)
|
||||
.filter(function(p) { return !!p; }) // trim out empty values
|
||||
.concat(paths);
|
||||
} else {
|
||||
// Default paths for each system
|
||||
if (win32) {
|
||||
paths.push(path.join(process.env.APPDATA, 'npm/node_modules'));
|
||||
} else {
|
||||
paths.push('/usr/local/lib/node_modules');
|
||||
paths.push('/usr/lib/node_modules');
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
// All search paths we will use to discover installed plugins
|
||||
Plugin.paths = Plugin.getDefaultPaths();
|
||||
|
||||
Plugin.addPluginPath = function(pluginPath) {
|
||||
Plugin.paths.unshift(path.resolve(process.cwd(), pluginPath));
|
||||
}
|
||||
|
||||
// Gets all plugins installed on the local system
|
||||
Plugin.installed = function() {
|
||||
|
||||
var plugins = [];
|
||||
var pluginsByName = {}; // don't add duplicate plugins
|
||||
|
||||
// search for plugins among all known paths, in order
|
||||
for (var index in Plugin.paths) {
|
||||
var requirePath = Plugin.paths[index];
|
||||
|
||||
// just because this path is in require.main.paths doesn't mean it necessarily exists!
|
||||
if (!fs.existsSync(requirePath))
|
||||
continue;
|
||||
|
||||
var names = fs.readdirSync(requirePath);
|
||||
|
||||
// read through each directory in this node_modules folder
|
||||
for (var index2 in names) {
|
||||
var name = names[index2];
|
||||
|
||||
// reconstruct full path
|
||||
var pluginPath = path.join(requirePath, name);
|
||||
|
||||
// we only care about directories
|
||||
if (!fs.statSync(pluginPath).isDirectory()) continue;
|
||||
|
||||
// does this module contain a package.json?
|
||||
try {
|
||||
// throws an Error if this isn't a homebridge plugin
|
||||
Plugin.loadPackageJSON(pluginPath);
|
||||
}
|
||||
catch (err) {
|
||||
// swallow error and skip this module
|
||||
continue;
|
||||
}
|
||||
|
||||
// add it to the return list
|
||||
if (!pluginsByName[name]) {
|
||||
pluginsByName[name] = true;
|
||||
plugins.push(new Plugin(pluginPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
297
lib/server.js
Normal file
297
lib/server.js
Normal file
@@ -0,0 +1,297 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var uuid = require("hap-nodejs").uuid;
|
||||
var Bridge = require("hap-nodejs").Bridge;
|
||||
var Accessory = require("hap-nodejs").Accessory;
|
||||
var Service = require("hap-nodejs").Service;
|
||||
var Characteristic = require("hap-nodejs").Characteristic;
|
||||
var Plugin = require('./plugin').Plugin;
|
||||
var User = require('./user').User;
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
Server: Server
|
||||
}
|
||||
|
||||
function Server() {
|
||||
this._accessories = {}; // this._accessories[name] = accessory constructor
|
||||
this._platforms = {}; // this._platforms[name] = platform constructor
|
||||
this._plugins = this._loadPlugins(this._accessories, this._platforms); // plugins[name] = plugin
|
||||
this._config = this._loadConfig();
|
||||
this._bridge = this._createBridge();
|
||||
}
|
||||
|
||||
Server.prototype.run = function() {
|
||||
|
||||
// keep track of async calls we're waiting for callbacks on before we can start up
|
||||
this._asyncCalls = 0;
|
||||
this._asyncWait = true;
|
||||
|
||||
if (this._config.platforms) this._loadPlatforms();
|
||||
if (this._config.accessories) this._loadAccessories();
|
||||
|
||||
this._asyncWait = false;
|
||||
|
||||
// publish now unless we're waiting on anyone
|
||||
if (this._asyncCalls == 0)
|
||||
this._publish();
|
||||
}
|
||||
|
||||
Server.prototype._publish = function() {
|
||||
// pull out our custom Bridge settings from config.json, if any
|
||||
var bridgeConfig = this._config.bridge || {};
|
||||
|
||||
this._printPin(bridgeConfig.pin);
|
||||
this._bridge.publish({
|
||||
username: bridgeConfig.username || "CC:22:3D:E3:CE:30",
|
||||
port: bridgeConfig.port || 51826,
|
||||
pincode: bridgeConfig.pin || "031-45-154",
|
||||
category: Accessory.Categories.OTHER
|
||||
});
|
||||
}
|
||||
|
||||
Server.prototype._loadPlugins = function(accessories, platforms) {
|
||||
|
||||
var plugins = {};
|
||||
|
||||
// load and validate plugins - check for valid package.json, etc.
|
||||
Plugin.installed().forEach(function(plugin) {
|
||||
|
||||
// attempt to load it
|
||||
try {
|
||||
plugin.load();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
plugin.loadError = err;
|
||||
}
|
||||
|
||||
// add it to our dict for easy lookup later
|
||||
plugins[plugin.name()] = plugin;
|
||||
|
||||
console.log("Loaded plugin: " + plugin.name());
|
||||
|
||||
if (plugin.accessories) {
|
||||
var sep = ""
|
||||
var line = "Accessories: [";
|
||||
for (var name in plugin.accessories) {
|
||||
if (accessories[name])
|
||||
throw new Error("Plugin " + plugin.name() + " wants to publish an accessory '" + name + "' which has already been published by another plugin!");
|
||||
|
||||
accessories[name] = plugin.accessories[name]; // copy to global dict
|
||||
line += sep + name; sep = ",";
|
||||
}
|
||||
line += "]";
|
||||
if (sep) console.log(line);
|
||||
}
|
||||
|
||||
if (plugin.platforms) {
|
||||
var sep = ""
|
||||
var line = "Platforms: [";
|
||||
for (var name in plugin.platforms) {
|
||||
if (plugin.platforms[name])
|
||||
throw new Error("Plugin " + plugin.name() + " wants to publish a platform '" + name + "' which has already been published by another plugin!");
|
||||
|
||||
platforms[name] = plugin.platforms[name]; // copy to global dict
|
||||
line += sep + name; sep = ",";
|
||||
}
|
||||
line += "]";
|
||||
if (sep) console.log(line);
|
||||
}
|
||||
|
||||
console.log("---");
|
||||
|
||||
}.bind(this));
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
Server.prototype._loadConfig = function() {
|
||||
|
||||
// Look for the configuration file
|
||||
var configPath = User.configPath();
|
||||
|
||||
// Complain and exit if it doesn't exist yet
|
||||
if (!fs.existsSync(configPath)) {
|
||||
console.log("Couldn't find a config.json file in the same directory as app.js. Look at config-sample.json for examples of how to format your config.js and add your home accessories.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Load up the configuration file
|
||||
var config;
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync(configPath));
|
||||
}
|
||||
catch (err) {
|
||||
console.log("There was a problem reading your config.json file.");
|
||||
console.log("Please try pasting your config.json file here to validate it: http://jsonlint.com");
|
||||
console.log("");
|
||||
throw err;
|
||||
}
|
||||
|
||||
var accessoryCount = (config.accessories && config.accessories.length) || 0;
|
||||
var platformCount = (config.platforms && config.platforms.length) || 0;
|
||||
console.log("Loaded config.json with %s accessories and %s platforms.", accessoryCount, platformCount);
|
||||
|
||||
console.log("---");
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
Server.prototype._createBridge = function() {
|
||||
// pull out our custom Bridge settings from config.json, if any
|
||||
var bridgeConfig = this._config.bridge || {};
|
||||
|
||||
// Create our Bridge which will host all loaded Accessories
|
||||
return new Bridge(bridgeConfig.name || 'Homebridge', uuid.generate("HomeBridge"));
|
||||
}
|
||||
|
||||
Server.prototype._loadAccessories = function() {
|
||||
|
||||
// Instantiate all accessories in the config
|
||||
console.log("Loading " + this._config.accessories.length + " accessories...");
|
||||
|
||||
for (var i=0; i<this._config.accessories.length; i++) {
|
||||
|
||||
var accessoryConfig = this._config.accessories[i];
|
||||
|
||||
// Load up the class for this accessory
|
||||
var accessoryType = accessoryConfig["accessory"]; // like "Lockitron"
|
||||
var accessoryConstructor = this._accessories[accessoryType]; // like "LockitronAccessory", a JavaScript constructor
|
||||
|
||||
if (!accessoryConstructor)
|
||||
throw new Error("Your config.json is requesting the accessory '" + accessoryType + "' which has not been published by any installed plugins.");
|
||||
|
||||
// Create a custom logging function that prepends the device display name for debugging
|
||||
var accessoryName = accessoryConfig["name"];
|
||||
var log = this._createLog(accessoryName);
|
||||
|
||||
log("Initializing %s accessory...", accessoryType);
|
||||
|
||||
var accessoryInstance = new accessoryConstructor(log, accessoryConfig);
|
||||
var accessory = this._createAccessory(accessoryInstance, accessoryName, accessoryType, accessoryConfig.uuid_base); //pass accessoryType for UUID generation, and optional parameter uuid_base which can be used instead of displayName for UUID generation
|
||||
|
||||
// add it to the bridge
|
||||
this._bridge.addBridgedAccessory(accessory);
|
||||
}
|
||||
}
|
||||
|
||||
Server.prototype._loadPlatforms = function() {
|
||||
|
||||
console.log("Loading " + this._config.platforms.length + " platforms...");
|
||||
|
||||
for (var i=0; i<this._config.platforms.length; i++) {
|
||||
|
||||
var platformConfig = this._config.platforms[i];
|
||||
|
||||
// Load up the class for this accessory
|
||||
var platformType = platformConfig["platform"]; // like "Wink"
|
||||
var platformName = platformConfig["name"];
|
||||
var platformConstructor = this._platforms[platformType]; // like "WinkPlatform", a JavaScript constructor
|
||||
|
||||
if (!platformConstructor)
|
||||
throw new Error("Your config.json is requesting the platform '" + platformType + "' which has not been published by any installed plugins.");
|
||||
|
||||
// Create a custom logging function that prepends the platform name for debugging
|
||||
var log = this._createLog(platformName);
|
||||
|
||||
log("Initializing %s platform...", platformType);
|
||||
|
||||
var platformInstance = new platformConstructor(log, platformConfig);
|
||||
this._loadPlatformAccessories(platformInstance, log, platformType);
|
||||
}
|
||||
}
|
||||
|
||||
Server.prototype._loadPlatformAccessories = function(platformInstance, log, platformType) {
|
||||
this._asyncCalls++;
|
||||
platformInstance.accessories(once(function(foundAccessories){
|
||||
this._asyncCalls--;
|
||||
|
||||
// loop through accessories adding them to the list and registering them
|
||||
for (var i = 0; i < foundAccessories.length; i++) {
|
||||
var accessoryInstance = foundAccessories[i];
|
||||
var accessoryName = accessoryInstance.name; // assume this property was set
|
||||
|
||||
log("Initializing platform accessory '%s'...", accessoryName);
|
||||
|
||||
var accessory = this._createAccessory(accessoryInstance, accessoryName, platformType, accessoryInstance.uuid_base);
|
||||
|
||||
// add it to the bridge
|
||||
this._bridge.addBridgedAccessory(accessory);
|
||||
}
|
||||
|
||||
// were we the last callback?
|
||||
if (this._asyncCalls === 0 && !this._asyncWait)
|
||||
this._publish();
|
||||
}.bind(this)));
|
||||
}
|
||||
|
||||
Server.prototype._createAccessory = function(accessoryInstance, displayName, accessoryType, uuid_base) {
|
||||
|
||||
var services = accessoryInstance.getServices();
|
||||
|
||||
if (!(services[0] instanceof Service)) {
|
||||
// The returned "services" for this accessory is assumed to be the old style: a big array
|
||||
// of JSON-style objects that will need to be parsed by HAP-NodeJS's AccessoryLoader.
|
||||
|
||||
// Create the actual HAP-NodeJS "Accessory" instance
|
||||
return accessoryLoader.parseAccessoryJSON({
|
||||
displayName: displayName,
|
||||
services: services
|
||||
});
|
||||
}
|
||||
else {
|
||||
// The returned "services" for this accessory are simply an array of new-API-style
|
||||
// Service instances which we can add to a created HAP-NodeJS Accessory directly.
|
||||
|
||||
var accessoryUUID = uuid.generate(accessoryType + ":" + (uuid_base || displayName));
|
||||
|
||||
var accessory = new Accessory(displayName, accessoryUUID);
|
||||
|
||||
// listen for the identify event if the accessory instance has defined an identify() method
|
||||
if (accessoryInstance.identify)
|
||||
accessory.on('identify', function(paired, callback) { accessoryInstance.identify(callback); });
|
||||
|
||||
services.forEach(function(service) {
|
||||
|
||||
// if you returned an AccessoryInformation service, merge its values with ours
|
||||
if (service instanceof Service.AccessoryInformation) {
|
||||
var existingService = accessory.getService(Service.AccessoryInformation);
|
||||
|
||||
// pull out any values you may have defined
|
||||
var manufacturer = service.getCharacteristic(Characteristic.Manufacturer).value;
|
||||
var model = service.getCharacteristic(Characteristic.Model).value;
|
||||
var serialNumber = service.getCharacteristic(Characteristic.SerialNumber).value;
|
||||
|
||||
if (manufacturer) existingService.setCharacteristic(Characteristic.Manufacturer, manufacturer);
|
||||
if (model) existingService.setCharacteristic(Characteristic.Model, model);
|
||||
if (serialNumber) existingService.setCharacteristic(Characteristic.SerialNumber, serialNumber);
|
||||
}
|
||||
else {
|
||||
accessory.addService(service);
|
||||
}
|
||||
});
|
||||
|
||||
return accessory;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the setup code in a scannable format.
|
||||
Server.prototype._printPin = function(pin) {
|
||||
console.log("Scan this code with your HomeKit App on your iOS device:");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ┌────────────┐ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " │ " + pin + " │ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " └────────────┘ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ");
|
||||
}
|
||||
|
||||
// Returns a logging function that prepends messages with the given name in [brackets].
|
||||
Server.prototype._createLog = function(name) {
|
||||
return function(message) {
|
||||
var rest = Array.prototype.slice.call(arguments, 1 ); // any arguments after message
|
||||
var args = ["[%s] " + message, name].concat(rest);
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
}
|
||||
35
lib/user.js
Normal file
35
lib/user.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
User: User
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages user settings and storage locations.
|
||||
*/
|
||||
|
||||
// global cached config
|
||||
var config;
|
||||
|
||||
function User() {
|
||||
}
|
||||
|
||||
User.config = function() {
|
||||
return config || (config = Config.load(User.configPath()));
|
||||
}
|
||||
|
||||
User.storagePath = function() {
|
||||
var home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
|
||||
return path.join(home, ".homebridge");
|
||||
}
|
||||
|
||||
User.configPath = function() {
|
||||
return path.join(User.storagePath(), "config.json");
|
||||
}
|
||||
|
||||
User.persistPath = function() {
|
||||
return path.join(User.storagePath(), "persist");
|
||||
}
|
||||
12
lib/version.js
Normal file
12
lib/version.js
Normal file
@@ -0,0 +1,12 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = getVersion();
|
||||
|
||||
function getVersion() {
|
||||
var packageJSONPath = path.join(__dirname, '../package.json');
|
||||
var packageJSON = JSON.parse(fs.readFileSync(packageJSONPath));
|
||||
return packageJSON.version;
|
||||
}
|
||||
Reference in New Issue
Block a user