Init Plugin 2.0

This commit is contained in:
Khaos Tian
2016-01-30 18:36:55 -08:00
parent 1a356a1783
commit e1334c5196
10 changed files with 717 additions and 117 deletions

View File

@@ -1,3 +0,0 @@
This is an example plugin for homebridge. It is a fully-working implementation of a Lockitron door lock accessory.
Remember to run `npm install` in this directory in order to install the dependencies needed by this plugin. If a user is installing your plugin from npm, this will be done automatically for them.

View File

@@ -1,82 +0,0 @@
var request = require("request");
var Service, Characteristic;
module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerAccessory("homebridge-lockitron", "Lockitron", LockitronAccessory);
}
function LockitronAccessory(log, config) {
this.log = log;
this.name = config["name"];
this.accessToken = config["api_token"];
this.lockID = config["lock_id"];
this.service = new Service.LockMechanism(this.name);
this.service
.getCharacteristic(Characteristic.LockCurrentState)
.on('get', this.getState.bind(this));
this.service
.getCharacteristic(Characteristic.LockTargetState)
.on('get', this.getState.bind(this))
.on('set', this.setState.bind(this));
}
LockitronAccessory.prototype.getState = function(callback) {
this.log("Getting current state...");
request.get({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: { access_token: this.accessToken }
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
var json = JSON.parse(body);
var state = json.state; // "lock" or "unlock"
this.log("Lock state is %s", state);
var locked = state == "lock"
callback(null, locked); // success
}
else {
this.log("Error getting state (status code %s): %s", response.statusCode, err);
callback(err);
}
}.bind(this));
}
LockitronAccessory.prototype.setState = function(state, callback) {
var lockitronState = (state == Characteristic.LockTargetState.SECURED) ? "lock" : "unlock";
this.log("Set state to %s", lockitronState);
request.put({
url: "https://api.lockitron.com/v2/locks/"+this.lockID,
qs: { access_token: this.accessToken, state: lockitronState }
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
this.log("State change complete.");
// we succeeded, so update the "current" state as well
var currentState = (state == Characteristic.LockTargetState.SECURED) ?
Characteristic.LockCurrentState.SECURED : Characteristic.LockCurrentState.UNSECURED;
this.service
.setCharacteristic(Characteristic.LockCurrentState, currentState);
callback(null); // success
}
else {
this.log("Error '%s' setting lock state. Response: %s", err, body);
callback(err || new Error("Error setting lock state."));
}
}.bind(this));
}
LockitronAccessory.prototype.getServices = function() {
return [this.service];
}

View File

@@ -1,23 +0,0 @@
{
"name": "homebridge-lockitron",
"version": "0.0.1",
"description": "Lockitron plugin for homebridge: https://github.com/nfarina/homebridge",
"license": "ISC",
"keywords": [
"homebridge-plugin"
],
"repository": {
"type": "git",
"url": "git://github.com/example/homebridge-lockitron.git"
},
"bugs": {
"url": "http://github.com/example/homebridge-lockitron/issues"
},
"engines": {
"node": ">=0.12.0",
"homebridge": ">=0.2.0"
},
"dependencies": {
"request": "^2.65.0"
}
}

View File

@@ -1,7 +1,10 @@
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var hap = require("hap-nodejs");
var hapLegacyTypes = require("hap-nodejs/accessories/types.js");
var log = require("./logger")._system;
var User = require("./user").User;
var PlatformAccessory = require("./platformAccessory").PlatformAccessory;
// The official homebridge API is the object we feed the plugin's exported initializer function.
@@ -12,7 +15,13 @@ module.exports = {
function API() {
this._accessories = {}; // this._accessories[pluginName.accessoryName] = accessory constructor
this._platforms = {}; // this._platforms[pluginName.platformName] = platform constructor
this._configurableAccessories = {};
this._dynamicPlatforms = {}; // this._dynamicPlatforms[pluginName.platformName] = platform constructor
// expose the homebridge API version
this.version = 2.0;
// expose the User class methods to plugins to get paths. Example: homebridge.user.storagePath()
this.user = User;
@@ -24,8 +33,12 @@ function API() {
// we also need to "bolt on" the legacy "types" constants for older accessories/platforms
// still using the "object literal" style JSON.
this.hapLegacyTypes = hapLegacyTypes;
this.platformAccessory = PlatformAccessory;
}
inherits(API, EventEmitter);
API.prototype.accessory = function(name) {
// if you passed the "short form" name like "Lockitron" instead of "homebridge-lockitron.Lockitron",
@@ -56,7 +69,7 @@ API.prototype.accessory = function(name) {
}
}
API.prototype.registerAccessory = function(pluginName, accessoryName, constructor) {
API.prototype.registerAccessory = function(pluginName, accessoryName, constructor, configurationRequestHandler) {
var fullName = pluginName + "." + accessoryName;
if (this._accessories[fullName])
@@ -65,6 +78,11 @@ API.prototype.registerAccessory = function(pluginName, accessoryName, constructo
log.info("Registering accessory '%s'", fullName);
this._accessories[fullName] = constructor;
// The plugin supports configuration
if (configurationRequestHandler) {
this._configurableAccessories[fullName] = configurationRequestHandler;
}
}
API.prototype.platform = function(name) {
@@ -97,7 +115,7 @@ API.prototype.platform = function(name) {
}
}
API.prototype.registerPlatform = function(pluginName, platformName, constructor) {
API.prototype.registerPlatform = function(pluginName, platformName, constructor, dynamic) {
var fullName = pluginName + "." + platformName;
if (this._platforms[fullName])
@@ -106,4 +124,31 @@ API.prototype.registerPlatform = function(pluginName, platformName, constructor)
log.info("Registering platform '%s'", fullName);
this._platforms[fullName] = constructor;
if (dynamic) {
this._dynamicPlatforms[fullName] = constructor;
}
}
API.prototype.registerPlatformAccessories = function(pluginName, platformName, accessories) {
for (var index in accessories) {
var accessory = accessories[index];
if (!(accessory instanceof PlatformAccessory)) {
throw new Error(pluginName + " - " + platformName + " attempt to register an accessory that isn\'t PlatformAccessory!");
}
accessory._associatedPlugin = pluginName;
accessory._associatedPlatform = platformName;
}
this.emit('registerPlatformAccessories', accessories);
}
API.prototype.unregisterPlatformAccessories = function(pluginName, platformName, accessories) {
for (var index in accessories) {
var accessory = accessories[index];
if (!(accessory instanceof PlatformAccessory)) {
throw new Error(pluginName + " - " + platformName + " attempt to unregister an accessory that isn\'t PlatformAccessory!");
}
}
this.emit('unregisterPlatformAccessories', accessories);
}

96
lib/bridgeSetupManager.js Normal file
View File

@@ -0,0 +1,96 @@
var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var Service = require("hap-nodejs").Service;
var Characteristic = require("hap-nodejs").Characteristic;
var SetupSession = require("./bridgeSetupSession").SetupSession;
'use strict';
module.exports = {
BridgeSetupManager: BridgeSetupManager
}
function BridgeSetupManager() {
this.session;
this.service = new Service(null, "49FB9D4D-0FEA-4BF1-8FA6-E7B18AB86DCE");
this.stateCharacteristic = new Characteristic("State", "77474A2F-FA98-485E-97BE-4762458774D8", {
format: Characteristic.Formats.UINT8,
minValue: 0,
maxValue: 1,
minStep: 1,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]
});
this.stateCharacteristic.value = 0;
this.service.addCharacteristic(this.stateCharacteristic);
this.versionCharacteristic = new Characteristic("Version", "FD9FE4CC-D06F-4FFE-96C6-595D464E1026", {
format: Characteristic.Formats.STRING,
perms: [Characteristic.Perms.READ, Characteristic.Perms.NOTIFY]
});
this.versionCharacteristic.value = "1.0";
this.service.addCharacteristic(this.versionCharacteristic);
this.controlPointCharacteristic = new Characteristic("Control Point", "5819A4C2-E1B0-4C9D-B761-3EB1AFF43073", {
format: Characteristic.Formats.DATA,
perms: [Characteristic.Perms.READ, Characteristic.Perms.WRITE, Characteristic.Perms.NOTIFY]
})
this.controlPointCharacteristic.on('get', function(callback, context) {
this.handleReadRequest(callback, context);
}.bind(this));
this.controlPointCharacteristic.on('set', function(newValue, callback, context) {
this.handleWriteRequest(newValue, callback, context);
}.bind(this));
this.controlPointCharacteristic.value = null;
this.service.addCharacteristic(this.controlPointCharacteristic);
}
inherits(BridgeSetupManager, EventEmitter);
BridgeSetupManager.prototype.handleReadRequest = function(callback, context) {
if (!context) {
return;
}
if (!this.session) {
callback(null);
} else {
this.session.handleReadRequest(callback);
}
}
BridgeSetupManager.prototype.handleWriteRequest = function(value, callback, context) {
if (!context) {
callback();
return;
}
var data = new Buffer(value, 'base64');
var request = JSON.parse(data.toString());
callback();
if (!this.session || this.session.sessionUUID !== request.sid) {
if (this.session) {
this.session.removeAllListeners();
this.session.validSession = false;
}
this.session = new SetupSession(this.stateCharacteristic, this.controlPointCharacteristic);
this.session.configurablePlatformPlugins = this.configurablePlatformPlugins;
this.session.on('newConfig', function(type, name, replace, config) {
this.emit('newConfig', type, name, replace, config);
}.bind(this));
this.session.on('requestCurrentConfig', function(callback) {
this.emit('requestCurrentConfig', callback);
}.bind(this));
this.session.on('end', function() {
this.session = null;
}.bind(this));
}
this.session.handleWriteRequest(request);
}

177
lib/bridgeSetupSession.js Normal file

File diff suppressed because one or more lines are too long

152
lib/platformAccessory.js Normal file
View File

@@ -0,0 +1,152 @@
var uuid = require("hap-nodejs").uuid;
var Accessory = require("hap-nodejs").Accessory;
var Service = require("hap-nodejs").Service;
var Characteristic = require("hap-nodejs").Characteristic;
'use strict';
module.exports = {
PlatformAccessory: PlatformAccessory
}
function PlatformAccessory(displayName, UUID, category) {
if (!displayName) throw new Error("Accessories must be created with a non-empty displayName.");
if (!UUID) throw new Error("Accessories must be created with a valid UUID.");
if (!uuid.isValid(UUID)) throw new Error("UUID '" + UUID + "' is not a valid UUID. Try using the provided 'generateUUID' function to create a valid UUID from any arbitrary string, like a serial number.");
this.displayName = displayName;
this.UUID = UUID;
this.category = category || Accessory.Categories.OTHER;
this.services = [];
this.reachable = false;
this.context = {};
this._associatedPlugin;
this._associatedPlatform;
this._associatedHAPAccessory;
this
.addService(Service.AccessoryInformation)
.setCharacteristic(Characteristic.Name, displayName)
.setCharacteristic(Characteristic.Manufacturer, "Default-Manufacturer")
.setCharacteristic(Characteristic.Model, "Default-Model")
.setCharacteristic(Characteristic.SerialNumber, "Default-SerialNumber");
}
PlatformAccessory.prototype.addService = function(service) {
// service might be a constructor like `Service.AccessoryInformation` instead of an instance
// of Service. Coerce if necessary.
if (typeof service === 'function')
service = new (Function.prototype.bind.apply(service, arguments));
// check for UUID+subtype conflict
for (var index in this.services) {
var existing = this.services[index];
if (existing.UUID === service.UUID) {
// OK we have two Services with the same UUID. Check that each defines a `subtype` property and that each is unique.
if (!service.subtype)
throw new Error("Cannot add a Service with the same UUID '" + existing.UUID + "' as another Service in this Accessory without also defining a unique 'subtype' property.");
if (service.subtype.toString() === existing.subtype.toString())
throw new Error("Cannot add a Service with the same UUID '" + existing.UUID + "' and subtype '" + existing.subtype + "' as another Service in this Accessory.");
}
}
this.services.push(service);
if (this._associatedHAPAccessory) {
this._associatedHAPAccessory.addService(service);
}
return service;
}
PlatformAccessory.prototype.getService = function(name) {
for (var index in this.services) {
var service = this.services[index];
if (typeof name === 'string' && (service.displayName === name || service.name === name))
return service;
else if (typeof name === 'function' && ((service instanceof name) || (name.UUID === service.UUID)))
return service;
}
}
PlatformAccessory.prototype.updateReachability = function(reachable) {
this.reachable = reachable;
if (this._associatedHAPAccessory) {
this._associatedHAPAccessory.updateReachability(reachable);
}
}
PlatformAccessory.prototype._prepareAssociatedHAPAccessory = function () {
this._associatedHAPAccessory = new Accessory(this.displayName, this.UUID);
this._associatedHAPAccessory._sideloadServices(this.services);
this._associatedHAPAccessory.reachable = this.reachable;
}
PlatformAccessory.prototype._dictionaryPresentation = function() {
var accessory = {};
accessory.plugin = this._associatedPlugin;
accessory.platform = this._associatedPlatform;
accessory.displayName = this.displayName;
accessory.UUID = this.UUID;
accessory.category = this.category;
accessory.context = this.context;
var services = [];
for (var index in this.services) {
var service = this.services[index];
var servicePresentation = {};
servicePresentation.displayName = service.displayName;
servicePresentation.UUID = service.UUID;
servicePresentation.subtype = service.subtype;
var characteristics = [];
for (var cIndex in service.characteristics) {
var characteristic = service.characteristics[cIndex];
var characteristicPresentation = {};
characteristicPresentation.displayName = characteristic.displayName;
characteristicPresentation.UUID = characteristic.UUID;
characteristicPresentation.props = characteristic.props;
characteristicPresentation.value = characteristic.value;
characteristics.push(characteristicPresentation);
}
servicePresentation.characteristics = characteristics;
services.push(servicePresentation);
}
accessory.services = services;
return accessory;
}
PlatformAccessory.prototype._configFromData = function(data) {
this._associatedPlugin = data.plugin;
this._associatedPlatform = data.platform;
this.displayName = data.displayName;
this.UUID = data.UUID;
this.category = data.category;
this.context = data.context;
this.reachable = false;
var services = [];
for (var index in data.services) {
var service = data.services[index];
var hapService = new Service(service.displayName, service.UUID, service.subtype);
var characteristics = [];
for (var cIndex in service.characteristics) {
var characteristic = service.characteristics[cIndex];
var hapCharacteristic = new Characteristic(characteristic.displayName, characteristic.UUID, characteristic.props);
hapCharacteristic.value = characteristic.value;
characteristics.push(hapCharacteristic);
}
hapService._sideloadCharacteristics(characteristics);
services.push(hapService);
}
this.services = services;
}

View File

@@ -1,6 +1,7 @@
var path = require('path');
var fs = require('fs');
var uuid = require("hap-nodejs").uuid;
var accessoryStorage = require('node-persist').create();
var Bridge = require("hap-nodejs").Bridge;
var Accessory = require("hap-nodejs").Accessory;
var Service = require("hap-nodejs").Service;
@@ -10,6 +11,8 @@ var once = require("hap-nodejs/lib/util/once").once;
var Plugin = require('./plugin').Plugin;
var User = require('./user').User;
var API = require('./api').API;
var PlatformAccessory = require("./platformAccessory").PlatformAccessory;
var BridgeSetupManager = require("./bridgeSetupManager").BridgeSetupManager;
var log = require("./logger")._system;
var Logger = require('./logger').Logger;
@@ -20,10 +23,32 @@ module.exports = {
}
function Server() {
// Setup Accessory Cache Storage
accessoryStorage.initSync({ dir: User.cachedAccessoryPath() });
this._api = new API(); // object we feed to Plugins
this._api.on('registerPlatformAccessories', function(accessories) {
this._handleRegisterPlatformAccessories(accessories);
}.bind(this));
this._api.on('unregisterPlatformAccessories', function(accessories) {
this._handleUnregisterPlatformAccessories(accessories);
}.bind(this));
this._plugins = this._loadPlugins(); // plugins[name] = Plugin instance
this._config = this._loadConfig();
this._config = this._loadConfig();
this._cachedPlatformAccessories = this._loadCachedPlatformAccessories();
this._bridge = this._createBridge();
this._activeDynamicPlugins = {};
this._configurablePlatformPlugins = {};
this._setupManager = new BridgeSetupManager();
this._setupManager.on('newConfig', this._handleNewConfig.bind(this));
this._setupManager.on('requestCurrentConfig', function(callback) {
callback(this._config);
}.bind(this));
}
Server.prototype.run = function() {
@@ -34,12 +59,18 @@ Server.prototype.run = function() {
if (this._config.platforms) this._loadPlatforms();
if (this._config.accessories) this._loadAccessories();
this._loadDynamicPlatforms();
this._configCachedPlatformAccessories();
this._setupManager.configurablePlatformPlugins = this._configurablePlatformPlugins;
this._bridge.addService(this._setupManager.service);
this._asyncWait = false;
// publish now unless we're waiting on anyone
if (this._asyncCalls == 0)
this._publish();
this._api.emit('didFinishLaunching');
}
Server.prototype._publish = function() {
@@ -108,8 +139,18 @@ Server.prototype._loadConfig = function() {
// Complain and exit if it doesn't exist yet
if (!fs.existsSync(configPath)) {
log.error("Couldn't find a config.json file at '"+configPath+"'. Look at config-sample.json for examples of how to format your config.js and add your home accessories.");
process.exit(1);
var config = {};
config.bridge = {
"name": "Homebridge",
"username": "CC:22:3D:E3:CE:30",
"port": 51826,
"pin": "031-45-154"
};
return config;
// log.error("Couldn't find a config.json file at '"+configPath+"'. 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
@@ -142,6 +183,23 @@ Server.prototype._loadConfig = function() {
return config;
}
Server.prototype._loadCachedPlatformAccessories = function() {
var cachedAccessories = accessoryStorage.getItem("cachedAccessories");
var platformAccessories = [];
if (cachedAccessories) {
for (var index in cachedAccessories) {
var serializedAccessory = cachedAccessories[index];
var platformAccessory = new PlatformAccessory(serializedAccessory.displayName, serializedAccessory.UUID, serializedAccessory.category);
platformAccessory._configFromData(serializedAccessory);
platformAccessories.push(platformAccessory);
}
}
return platformAccessories;
}
Server.prototype._createBridge = function() {
// pull out our custom Bridge settings from config.json, if any
var bridgeConfig = this._config.bridge || {};
@@ -201,11 +259,64 @@ Server.prototype._loadPlatforms = function() {
platformLogger("Initializing %s platform...", platformType);
var platformInstance = new platformConstructor(platformLogger, platformConfig);
this._loadPlatformAccessories(platformInstance, platformLogger, platformType);
var platformInstance = new platformConstructor(platformLogger, platformConfig, this._api);
if (platformInstance.configureAccessory == undefined) {
// Plugin 1.0, load accessories
this._loadPlatformAccessories(platformInstance, platformLogger, platformType);
} else {
this._activeDynamicPlugins[platformType] = platformInstance;
}
if (platformInstance.configurationRequestHandler != undefined) {
this._configurablePlatformPlugins[platformType] = platformInstance;
}
}
}
Server.prototype._loadDynamicPlatforms = function() {
for (var dynamicPluginName in this._api._dynamicPlatforms) {
if (!this._activeDynamicPlugins[dynamicPluginName] && !this._activeDynamicPlugins[dynamicPluginName.split(".")[1]]) {
console.log("Load " + dynamicPluginName);
var platformConstructor = this._api._dynamicPlatforms[dynamicPluginName];
var platformLogger = Logger.withPrefix(dynamicPluginName);
var platformInstance = new platformConstructor(platformLogger, null, this._api);
this._activeDynamicPlugins[dynamicPluginName] = platformInstance;
if (platformInstance.configurationRequestHandler != undefined) {
this._configurablePlatformPlugins[dynamicPluginName] = platformInstance;
}
}
}
}
Server.prototype._configCachedPlatformAccessories = function() {
for (var index in this._cachedPlatformAccessories) {
var accessory = this._cachedPlatformAccessories[index];
if (!(accessory instanceof PlatformAccessory)) {
console.log("Unexpected Accessory!");
continue;
}
var fullName = accessory._associatedPlugin + "." + accessory._associatedPlatform;
var platformInstance = this._activeDynamicPlugins[fullName];
if (!platformInstance) {
platformInstance = this._activeDynamicPlugins[accessory._associatedPlatform];
}
if (platformInstance) {
platformInstance.configureAccessory(accessory);
} else {
console.log("Failed to find plugin to handle accessory " + accessory.displayName);
}
accessory._prepareAssociatedHAPAccessory();
this._bridge.addBridgedAccessory(accessory._associatedHAPAccessory);
}
}
Server.prototype._loadPlatformAccessories = function(platformInstance, log, platformType) {
this._asyncCalls++;
platformInstance.accessories(once(function(foundAccessories){
@@ -280,6 +391,128 @@ Server.prototype._createAccessory = function(accessoryInstance, displayName, acc
}
}
Server.prototype._handleRegisterPlatformAccessories = function(accessories) {
var hapAccessories = [];
for (var index in accessories) {
var accessory = accessories[index];
accessory._prepareAssociatedHAPAccessory();
hapAccessories.push(accessory._associatedHAPAccessory);
this._cachedPlatformAccessories.push(accessory);
}
this._bridge.addBridgedAccessories(hapAccessories);
this._updateCachedAccessories();
}
Server.prototype._handleUnregisterPlatformAccessories = function(accessories) {
var hapAccessories = [];
for (var index in accessories) {
var accessory = accessories[index];
if (accessory._associatedHAPAccessory) {
hapAccessories.push(accessory._associatedHAPAccessory);
}
for (var targetIndex in this._cachedPlatformAccessories) {
var existing = this._cachedPlatformAccessories[targetIndex];
if (existing.UUID === accessory.UUID) {
this._cachedPlatformAccessories.splice(targetIndex, 1);
break;
}
}
}
this._bridge.removeBridgedAccessories(hapAccessories);
this._updateCachedAccessories();
}
Server.prototype._updateCachedAccessories = function() {
var serializedAccessories = [];
for (var index in this._cachedPlatformAccessories) {
var accessory = this._cachedPlatformAccessories[index];
serializedAccessories.push(accessory._dictionaryPresentation());
}
accessoryStorage.setItemSync("cachedAccessories", serializedAccessories);
}
Server.prototype._handleNewConfig = function(type, name, replace, config) {
if (type === "accessory") {
// TODO: Load new accessory
if (!this._config.accessories) {
this._config.accessories = [];
}
if (!replace) {
this._config.accessories.push(config);
} else {
var targetName;
if (name.indexOf('.') == -1) {
targetName = name.split(".")[1];
}
var found = false;
for (var index in this._config.accessories) {
var accessoryConfig = this._config.accessories[index];
if (accessoryConfig.accessory === name) {
this._config.accessories[index] = config;
found = true;
break;
}
if (targetName && (accessoryConfig.accessory === targetName)) {
this._config.accessories[index] = config;
found = true;
break;
}
}
if (!found) {
this._config.accessories.push(config);
}
}
} else if (type === "platform") {
if (!this._config.platforms) {
this._config.platforms = [];
}
if (!replace) {
this._config.platforms.push(config);
} else {
var targetName;
if (name.indexOf('.') == -1) {
targetName = name.split(".")[1];
}
var found = false;
for (var index in this._config.platforms) {
var platformConfig = this._config.platforms[index];
if (platformConfig.platform === name) {
this._config.platforms[index] = config;
found = true;
break;
}
if (targetName && (platformConfig.platform === targetName)) {
this._config.platforms[index] = config;
found = true;
break;
}
}
if (!found) {
this._config.platforms.push(config);
}
}
}
var serializedConfig = JSON.stringify(this._config, null, ' ');
var configPath = User.configPath();
fs.writeFileSync(configPath, serializedConfig, 'utf8');
}
// 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 to pair with Homebridge:");

View File

@@ -38,6 +38,10 @@ User.persistPath = function() {
return path.join(User.storagePath(), "persist");
}
User.cachedAccessoryPath = function() {
return path.join(User.storagePath(), "accessories");
}
User.setStoragePath = function(path) {
customStoragePath = path;
}

View File

@@ -31,7 +31,8 @@
"dependencies": {
"chalk": "^1.1.1",
"commander": "2.8.1",
"hap-nodejs": "0.1.1",
"semver": "5.0.3"
"hap-nodejs": "0.1.6",
"semver": "5.0.3",
"node-persist": "^0.0.8"
}
}