Support new API in getServices()

See `getServices()` implementation in `accessories/Http.js` for an
example of how to use.

Fixes #114
Fixes #57
This commit is contained in:
Nick Farina
2015-08-22 21:37:42 -07:00
parent d2547f7ae6
commit d8e27910cc
2 changed files with 117 additions and 173 deletions

View File

@@ -1,6 +1,11 @@
var types = require("HAP-NodeJS/accessories/types.js"); var Service = require("HAP-NodeJS").Service;
var Characteristic = require("HAP-NodeJS").Characteristic;
var request = require("request"); var request = require("request");
module.exports = {
accessory: HttpAccessory
}
function HttpAccessory(log, config) { function HttpAccessory(log, config) {
this.log = log; this.log = log;
@@ -9,9 +14,6 @@ function HttpAccessory(log, config) {
this.off_url = config["off_url"]; this.off_url = config["off_url"];
this.brightness_url = config["brightness_url"]; this.brightness_url = config["brightness_url"];
this.http_method = config["http_method"]; this.http_method = config["http_method"];
// device info
this.name = config["name"];
} }
HttpAccessory.prototype = { HttpAccessory.prototype = {
@@ -26,135 +28,59 @@ HttpAccessory.prototype = {
}) })
}, },
setPowerState: function(powerOn) { setPowerState: function(powerOn, callback) {
var url; var url;
if (powerOn) { if (powerOn) {
url = this.on_url url = this.on_url;
this.log("Setting power state on the '"+this.name+"' to on"); this.log("Setting power state to on");
}else{ }
url = this.off_url else {
this.log("Setting power state on the '"+this.name+"' to off"); url = this.off_url;
this.log("Setting power state to off");
} }
this.httpRequest(url, this.http_method, function(error, response, body){ this.httpRequest(url, this.http_method, function(error, response, body) {
if (error) { if (error) {
return console.error('http power function failed:', error); this.log('HTTP power function failed: %s', error.message);
}else{ callback(error);
return console.log('http power function succeeded!');
} }
}); else {
this.log('HTTP power function succeeded!');
callback();
}
}.bind(this));
}, },
setBrightness: function(level) { setBrightness: function(level, callback) {
var url = this.brightness_url.replace("%b", level) var url = this.brightness_url.replace("%b", level)
this.log("Setting brightness on the '"+this.name+"' to " + level); this.log("Setting brightness to %s", level);
this.httpRequest(url, this.http_method, function(error, response, body){ this.httpRequest(url, this.http_method, function(error, response, body) {
if (error) { if (error) {
return console.error('http brightness function failed:', error); this.log('HTTP brightness function failed: %s', error);
}else{ callback(error);
return console.log('http brightness function succeeded!');
} }
}); else {
this.log('HTTP brightness function succeeded!');
callback();
}
}.bind(this));
}, },
getServices: function() { getServices: function() {
var that = this;
return [{ var lightbulbService = new Service.Lightbulb();
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{ lightbulbService
cType: types.NAME_CTYPE, .getCharacteristic(Characteristic.On)
onUpdate: null, .on('set', this.setPowerState.bind(this));
perms: ["pr"],
format: "string", lightbulbService
initialValue: this.name, .addCharacteristic(new Characteristic.Brightness())
supportEvents: false, .on('set', this.setBrightness.bind(this));
supportBonjour: false,
manfDescription: "Name of the accessory", return [lightbulbService];
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Http",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Rev-1",
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "A1S2NASF88EW",
supportEvents: false,
supportBonjour: false,
manfDescription: "SN",
designedMaxLength: 255
},{
cType: types.IDENTIFY_CTYPE,
onUpdate: null,
perms: ["pw"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Identify Accessory",
designedMaxLength: 1
}]
},{
sType: types.LIGHTBULB_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: true,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPowerState(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Change the power state",
designedMaxLength: 1
},{
cType: types.BRIGHTNESS_CTYPE,
onUpdate: function(value) { that.setBrightness(value); },
perms: ["pw","pr","ev"],
format: "int",
initialValue: 0,
supportEvents: true,
supportBonjour: false,
manfDescription: "Adjust Brightness",
designedMinValue: 0,
designedMaxValue: 100,
designedMinStep: 1,
unit: "%"
}]
}];
} }
}; };
module.exports.accessory = HttpAccessory;

128
app.js
View File

@@ -5,6 +5,7 @@ var hap = require('HAP-NodeJS');
var uuid = require('HAP-NodeJS').uuid; var uuid = require('HAP-NodeJS').uuid;
var Bridge = require('HAP-NodeJS').Bridge; var Bridge = require('HAP-NodeJS').Bridge;
var Accessory = require('HAP-NodeJS').Accessory; var Accessory = require('HAP-NodeJS').Accessory;
var Service = require('HAP-NodeJS').Service;
var accessoryLoader = require('HAP-NodeJS').AccessoryLoader; var accessoryLoader = require('HAP-NodeJS').AccessoryLoader;
console.log("Starting HomeBridge server..."); console.log("Starting HomeBridge server...");
@@ -72,28 +73,19 @@ function loadAccessories() {
var accessoryConfig = config.accessories[i]; var accessoryConfig = config.accessories[i];
// Load up the class for this accessory // Load up the class for this accessory
var accessoryName = accessoryConfig["accessory"]; // like "WeMo" var accessoryType = accessoryConfig["accessory"]; // like "WeMo"
var accessoryModule = require('./accessories/' + accessoryName + ".js"); // like "./accessories/WeMo.js" var accessoryModule = require('./accessories/' + accessoryType + ".js"); // like "./accessories/WeMo.js"
var accessoryConstructor = accessoryModule.accessory; // like "WeMoAccessory", a JavaScript constructor var accessoryConstructor = accessoryModule.accessory; // like "WeMoAccessory", a JavaScript constructor
// Create a custom logging function that prepends the device display name for debugging // Create a custom logging function that prepends the device display name for debugging
var name = accessoryConfig["name"]; var accessoryName = accessoryConfig["name"];
var log = function(name) { return function(s) { console.log("[" + name + "] " + s); }; }(name); var log = createLog(accessoryName);
log("Initializing " + accessoryName + " accessory..."); log("Initializing %s accessory...", accessoryType);
var accessoryInstance = new accessoryConstructor(log, accessoryConfig); var accessoryInstance = new accessoryConstructor(log, accessoryConfig);
var accessory = createAccessory(accessoryInstance, accessoryName);
// Extract the raw "services" for this accessory which is a big array of objects describing the various
// hooks in and out of HomeKit for the HAP-NodeJS server.
var services = accessoryInstance.getServices();
// Create the actual HAP-NodeJS "Accessory" instance
var accessory = accessoryLoader.parseAccessoryJSON({
displayName: name,
services: services
});
// add it to the bridge // add it to the bridge
bridge.addBridgedAccessory(accessory); bridge.addBridgedAccessory(accessory);
} }
@@ -108,54 +100,80 @@ function loadPlatforms() {
var platformConfig = config.platforms[i]; var platformConfig = config.platforms[i];
// Load up the class for this accessory // Load up the class for this accessory
var platformName = platformConfig["platform"]; // like "Wink" var platformType = platformConfig["platform"]; // like "Wink"
var platformModule = require('./platforms/' + platformName + ".js"); // like "./platforms/Wink.js" var platformName = platformConfig["name"];
var platformModule = require('./platforms/' + platformType + ".js"); // like "./platforms/Wink.js"
var platformConstructor = platformModule.platform; // like "WinkPlatform", a JavaScript constructor var platformConstructor = platformModule.platform; // like "WinkPlatform", a JavaScript constructor
// Create a custom logging function that prepends the platform display name for debugging // Create a custom logging function that prepends the platform name for debugging
var name = platformConfig["name"]; var log = createLog(platformName);
var log = function(name) { return function(s) { console.log("[" + name + "] " + s); }; }(name);
log("Initializing " + platformName + " platform..."); log("Initializing %s platform...", platformType);
var platformInstance = new platformConstructor(log, platformConfig); var platformInstance = new platformConstructor(log, platformConfig);
loadPlatformAccessories(platformInstance, log);
// wrap name and log in a closure so they don't change in the callback
function getAccessories(name, log) {
asyncCalls++;
platformInstance.accessories(function(foundAccessories){
asyncCalls--;
// loop through accessories adding them to the list and registering them
for (var i = 0; i < foundAccessories.length; i++) {
var accessoryInstance = foundAccessories[i];
log("Initializing device with name " + accessoryInstance.name + "...")
// Extract the raw "services" for this accessory which is a big array of objects describing the various
// hooks in and out of HomeKit for the HAP-NodeJS server.
var services = accessoryInstance.getServices();
// Create the actual HAP-NodeJS "Accessory" instance
var accessory = accessoryLoader.parseAccessoryJSON({
displayName: name,
services: services
});
// add it to the bridge
bridge.addBridgedAccessory(accessory);
}
// were we the last callback?
if (asyncCalls === 0 && !asyncWait)
publish();
})
}
// query for devices
getAccessories(name, log);
} }
} }
function loadPlatformAccessories(platformInstance, log) {
asyncCalls++;
platformInstance.accessories(function(foundAccessories){
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 = createAccessory(accessoryInstance, accessoryName);
// add it to the bridge
bridge.addBridgedAccessory(accessory);
}
// were we the last callback?
if (asyncCalls === 0 && !asyncWait)
publish();
});
}
function createAccessory(accessoryInstance, displayName) {
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(accessoryInstance.constructor.name + ":" + displayName);
var accessory = new Accessory(displayName, accessoryUUID);
services.forEach(function(service) { accessory.addService(service); });
return accessory;
}
}
// Returns a logging function that prepends messages with the given name in [brackets].
function createLog(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);
}
}
function publish() { function publish() {
bridge.publish({ bridge.publish({
username: bridgeConfig.username || "CC:22:3D:E3:CE:30", username: bridgeConfig.username || "CC:22:3D:E3:CE:30",