diff --git a/config-sample.json b/config-sample.json index d4760e0..bd81c12 100644 --- a/config-sample.json +++ b/config-sample.json @@ -57,6 +57,11 @@ "platform": "Sonos", "name": "Sonos", "play_volume": 25 + }, + { + "platform": "YamahaAVR", + "play_volume": -35, + "setMainInputTo": "AirPlay" } ], diff --git a/package.json b/package.json index 48916e3..ae1d196 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "hap-nodejs": "git+https://github.com/khaost/HAP-NodeJS#2a1bc8d99a2009317ab5da93faebea34c89f197c", "harmonyhubjs-client": "^1.1.4", "harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git", + "mdns": "^2.2.4", "node-hue-api": "^1.0.5", "node-milight-promise": "0.0.2", "node-persist": "0.0.x", @@ -28,6 +29,7 @@ "wink-js": "0.0.5", "xml2js": "0.4.x", "xmldoc": "0.1.x", + "yamaha-nodejs": "0.4.x", "teslams": "1.0.1" } } diff --git a/platforms/YamahaAVR.js b/platforms/YamahaAVR.js new file mode 100644 index 0000000..f0d10c1 --- /dev/null +++ b/platforms/YamahaAVR.js @@ -0,0 +1,165 @@ +var types = require("HAP-NodeJS/accessories/types.js"); +var Yamaha = require('yamaha-nodejs'); +var mdns = require('mdns'); + +function YamahaAVRPlatform(log, config){ + this.log = log; + this.config = config; + this.playVolume = config["play_volume"]; + this.setMainInputTo = config["setMainInputTo"]; + this.browser = mdns.createBrowser(mdns.tcp('http')); +} + +YamahaAVRPlatform.prototype = { + accessories: function(callback) { + this.log("Getting Yamaha AVR devices."); + var that = this; + + var browser = this.browser; + browser.stop(); + browser.removeAllListeners('serviceUp'); // cleanup listeners + + browser.on('serviceUp', function(service){ + var name = service.name; + //console.log('Found HTTP service "' + name + '"'); + // We can't tell just from mdns if this is an AVR... + if (service.port != 80) return; // yamaha-nodejs assumes this, so finding one on another port wouldn't do any good anyway. + var yamaha = new Yamaha(service.host); + yamaha.getSystemConfig().then(function(sysConfig){ + var sysModel = sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0]; + 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]); + }, function(err){ + return; + }) + }); + browser.start(); + } +}; + +function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) { + this.log = log; + this.config = config; + this.mdnsService = mdnsService; + this.yamaha = yamaha; + this.sysConfig = sysConfig; + + this.name = mdnsService.name; + this.serviceName = mdnsService.name + " Speakers"; + this.setMainInputTo = config["setMainInputTo"]; + this.playVolume = this.config["play_volume"]; +} + +YamahaAVRAccessory.prototype = { + + setPlaying: function(playing) { + var that = this; + var yamaha = this.yamaha; + + if (playing) { + + yamaha.powerOn().then(function(){ + if (that.playVolume) return yamaha.setVolumeTo(that.playVolume*10); + else return { then: function(f, r){ f(); } }; + }).then(function(){ + if (that.setMainInputTo) return yamaha.setMainInputTo(that.setMainInputTo); + else return { then: function(f, r){ f(); } }; + }).then(function(){ + if (that.setMainInputTo == "AirPlay") return yamaha.SendXMLToReceiver( + 'Play' + ); + else return { then: function(f, r){ f(); } }; + //else return Promise.fulfilled(undefined); + }); + } + else { + 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 + }] + }]; + } +}; + +module.exports.accessory = YamahaAVRAccessory; +module.exports.platform = YamahaAVRPlatform;