Merge branch 'nfarina/master'

This commit is contained in:
S'pht'Kr
2015-09-29 05:24:25 +02:00

View File

@@ -1,5 +1,10 @@
var types = require("HAP-NodeJS/accessories/types.js");
var inherits = require('util').inherits;
var debug = require('debug')('YamahaAVR');
var Service = require("HAP-NodeJS").Service;
var Characteristic = require("HAP-NodeJS").Characteristic;
var Yamaha = require('yamaha-nodejs');
var Q = require('q');
var mdns = require('mdns');
//workaround for raspberry pi
var sequence = [
@@ -12,10 +17,53 @@ function YamahaAVRPlatform(log, config){
this.log = log;
this.config = config;
this.playVolume = config["play_volume"];
this.minVolume = config["min_volume"] || -50.0;
this.maxVolume = config["max_volume"] || -20.0;
this.gapVolume = this.maxVolume - this.minVolume;
this.setMainInputTo = config["setMainInputTo"];
this.expectedDevices = config["expected_devices"] || 100;
this.discoveryTimeout = config["discovery_timeout"] || 30;
this.browser = mdns.createBrowser(mdns.tcp('http'), {resolverSequence: sequence});
}
// Custom Characteristics and service...
YamahaAVRPlatform.AudioVolume = function() {
Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377');
this.format = 'uint8';
this.unit = 'percentage';
this.maximumValue = 100;
this.minimumValue = 0;
this.stepValue = 1;
this.readable = true;
this.writable = true;
this.supportsEventNotification = true;
this.value = this.getDefaultValue();
};
inherits(YamahaAVRPlatform.AudioVolume, Characteristic);
YamahaAVRPlatform.Muting = function() {
Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377');
this.format = 'bool';
this.readable = true;
this.writable = true;
this.supportsEventNotification = true;
this.value = this.getDefaultValue();
};
inherits(YamahaAVRPlatform.Muting, Characteristic);
YamahaAVRPlatform.AudioDeviceService = function(displayName, subtype) {
Service.call(this, displayName, '00000001-0000-1000-8000-135D67EC4377', subtype);
// Required Characteristics
this.addCharacteristic(YamahaAVRPlatform.AudioVolume);
// Optional Characteristics
this.addOptionalCharacteristic(YamahaAVRPlatform.Muting);
};
inherits(YamahaAVRPlatform.AudioDeviceService, Service);
YamahaAVRPlatform.prototype = {
accessories: function(callback) {
this.log("Getting Yamaha AVR devices.");
@@ -24,7 +72,9 @@ YamahaAVRPlatform.prototype = {
var browser = this.browser;
browser.stop();
browser.removeAllListeners('serviceUp'); // cleanup listeners
var accessories = [];
var timer, timeElapsed = 0, checkCyclePeriod = 5000;
browser.on('serviceUp', function(service){
var name = service.name;
//console.log('Found HTTP service "' + name + '"');
@@ -36,12 +86,36 @@ YamahaAVRPlatform.prototype = {
var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0];
that.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\"");
var accessory = new YamahaAVRAccessory(that.log, that.config, service, yamaha, sysConfig);
callback([accessory]);
accessories.push(accessory);
if(accessories.length >= this.expectedDevices)
timeoutFunction(); // We're done, call the timeout function now.
//callback([accessory]);
}, function(err){
return;
})
});
});
browser.start();
// The callback can only be called once...so we'll have to find as many as we can
// in a fixed time and then call them in.
var timeoutFunction = function(){
if(accessories.length >= that.expectedDevices){
clearTimeout(timer);
} else {
timeElapsed += checkCyclePeriod;
if(timeElapsed > that.discoveryTimeout * 1000){
that.log("Waited " + that.discoveryTimeout + " seconds, stopping discovery.");
} else {
timer = setTimeout(timeoutFunction, checkCyclePeriod);
return;
}
}
browser.stop();
browser.removeAllListeners('serviceUp');
that.log("Discovery finished, found " + accessories.length + " Yamaha AVR devices.");
callback(accessories);
};
timer = setTimeout(timeoutFunction, checkCyclePeriod);
}
};
@@ -56,6 +130,9 @@ function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) {
this.serviceName = mdnsService.name + " Speakers";
this.setMainInputTo = config["setMainInputTo"];
this.playVolume = this.config["play_volume"];
this.minVolume = config["min_volume"] || -50.0;
this.maxVolume = config["max_volume"] || -20.0;
this.gapVolume = this.maxVolume - this.minVolume;
}
YamahaAVRAccessory.prototype = {
@@ -66,104 +143,74 @@ YamahaAVRAccessory.prototype = {
if (playing) {
yamaha.powerOn().then(function(){
return yamaha.powerOn().then(function(){
if (that.playVolume) return yamaha.setVolumeTo(that.playVolume*10);
else return { then: function(f, r){ f(); } };
else return Q();
}).then(function(){
if (that.setMainInputTo) return yamaha.setMainInputTo(that.setMainInputTo);
else return { then: function(f, r){ f(); } };
else return Q();
}).then(function(){
if (that.setMainInputTo == "AirPlay") return yamaha.SendXMLToReceiver(
'<YAMAHA_AV cmd="PUT"><AirPlay><Play_Control><Playback>Play</Playback></Play_Control></AirPlay></YAMAHA_AV>'
);
else return { then: function(f, r){ f(); } };
//else return Promise.fulfilled(undefined);
else return Q();
});
}
else {
yamaha.powerOff();
return yamaha.powerOff();
}
},
getServices: function() {
var that = this;
return [{
sType: types.ACCESSORY_INFORMATION_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.name,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of the accessory",
designedMaxLength: 255
},{
cType: types.MANUFACTURER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: "Yamaha",
supportEvents: false,
supportBonjour: false,
manfDescription: "Manufacturer",
designedMaxLength: 255
},{
cType: types.MODEL_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0],
supportEvents: false,
supportBonjour: false,
manfDescription: "Model",
designedMaxLength: 255
},{
cType: types.SERIAL_NUMBER_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0],
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.SWITCH_STYPE,
characteristics: [{
cType: types.NAME_CTYPE,
onUpdate: null,
perms: ["pr"],
format: "string",
initialValue: this.serviceName,
supportEvents: false,
supportBonjour: false,
manfDescription: "Name of service",
designedMaxLength: 255
},{
cType: types.POWER_STATE_CTYPE,
onUpdate: function(value) { that.setPlaying(value); },
perms: ["pw","pr","ev"],
format: "bool",
initialValue: false,
supportEvents: false,
supportBonjour: false,
manfDescription: "Change the playback state of the Yamaha AV Receiver",
designedMaxLength: 1
}]
}];
var informationService = new Service.AccessoryInformation();
var yamaha = this.yamaha;
informationService
.setCharacteristic(Characteristic.Name, this.name)
.setCharacteristic(Characteristic.Manufacturer, "Yamaha")
.setCharacteristic(Characteristic.Model, this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0])
.setCharacteristic(Characteristic.SerialNumber, this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]);
var switchService = new Service.Switch("Power State");
switchService.getCharacteristic(Characteristic.On)
.on('get', function(callback, context){
yamaha.isOn().then(function(result){
callback(false, result);
}.bind(this));
}.bind(this))
.on('set', function(powerOn, callback){
this.setPlaying(powerOn).then(function(){
callback(false, powerOn);
}, function(error){
callback(error, !powerOn); //TODO: Actually determine and send real new status.
});
}.bind(this));
var audioDeviceService = new YamahaAVRPlatform.AudioDeviceService("Audio Functions");
audioDeviceService.getCharacteristic(YamahaAVRPlatform.AudioVolume)
.on('get', function(callback, context){
yamaha.getBasicInfo().done(function(basicInfo){
var v = basicInfo.getVolume()/10.0;
var p = 100 * ((v - that.minVolume) / that.gapVolume);
p = p < 0 ? 0 : p > 100 ? 100 : Math.round(p);
debug("Got volume percent of " + p + "%");
callback(false, p);
});
})
.on('set', function(p, callback){
var v = ((p / 100) * that.gapVolume) + that.minVolume;
v = Math.round(v*10.0);
debug("Setting volume to " + v);
yamaha.setVolumeTo(v).then(function(){
callback(false, p);
});
})
.getValue(null, null); // force an asynchronous get
return [informationService, switchService, audioDeviceService];
}
};