diff --git a/platforms/HomeAssistant.js b/platforms/HomeAssistant.js index d9984c8..6088b94 100644 --- a/platforms/HomeAssistant.js +++ b/platforms/HomeAssistant.js @@ -7,6 +7,30 @@ // URL: http://home-assistant.io // GitHub: https://github.com/balloob/home-assistant // +// HA accessories supported: Lights, Switches, Media Players. +// +// Media Player Support +// +// Media players on your Home Assistant will be added to your HomeKit as a light. +// While this seems like a hack at first, it's actually quite useful. You can +// turn them on, off, and set their volume (as a function of brightness). +// +// There are some rules to know about how on/off treats your media player. If +// your media player supports play/pause, then turning them on and off via +// HomeKit will play and pause them. If they do not support play/pause but then +// support on/off they will be turned on and then off. +// +// HomeKit does not have a characteristic of Volume or a Speaker type. So we are +// using the light device type here. So to turn your speaker up and down, you +// will need to use the same language you use to set the brighness of a light. +// You can play around with language to see what fits best. +// +// Examples +// +// Dim the Kitchen Speaker to 40% - sets volume to 40% +// Dim the the Kitchen Speaker 10% - lowers the volume by 10% +// Set the brightness of the Kitchen Speaker to 40% +// // Remember to add platform to config.json. Example: // "platforms": [ // { @@ -99,6 +123,7 @@ HomeAssistantPlatform.prototype = { var foundAccessories = []; var lightsRE = /^light\./i var switchRE = /^switch\./i + var mediaPlayerRE = /^media_player\./i this._request('GET', '/states', {}, function(error, response, data){ @@ -111,6 +136,8 @@ HomeAssistantPlatform.prototype = { accessory = new HomeAssistantLight(that.log, entity, that) }else if (entity.entity_id.match(switchRE)){ accessory = new HomeAssistantSwitch(that.log, entity, that) + }else if (entity.entity_id.match(mediaPlayerRE) && entity.attributes && entity.attributes.supported_media_commands){ + accessory = new HomeAssistantMediaPlayer(that.log, entity, that) } if (accessory) { @@ -229,6 +256,145 @@ HomeAssistantLight.prototype = { } +function HomeAssistantMediaPlayer(log, data, client) { + var SUPPORT_PAUSE = 1 + var SUPPORT_SEEK = 2 + var SUPPORT_VOLUME_SET = 4 + var SUPPORT_VOLUME_MUTE = 8 + var SUPPORT_PREVIOUS_TRACK = 16 + var SUPPORT_NEXT_TRACK = 32 + var SUPPORT_YOUTUBE = 64 + var SUPPORT_TURN_ON = 128 + var SUPPORT_TURN_OFF = 256 + + // device info + this.domain = "media_player" + this.data = data + this.entity_id = data.entity_id + this.supportsVolume = false + this.supportedMediaCommands = data.attributes.supported_media_commands + + if (data.attributes && data.attributes.friendly_name) { + this.name = data.attributes.friendly_name + }else{ + this.name = data.entity_id.split('.').pop().replace(/_/g, ' ') + } + + if ((this.supportedMediaCommands | SUPPORT_PAUSE) == this.supportedMediaCommands) { + this.onState = "playing" + this.offState = "paused" + this.onService = "media_play" + this.offService = "media_pause" + }else if ((this.supportedMediaCommands | SUPPORT_TURN_ON) == this.supportedMediaCommands && (this.supportedMediaCommands | SUPPORT_TURN_OFF) == this.supportedMediaCommands) { + this.onState = "on" + this.offState = "off" + this.onService = "turn_on" + this.offService = "turn_off" + } + + if ((this.supportedMediaCommands | SUPPORT_VOLUME_SET) == this.supportedMediaCommands) { + this.supportsVolume = true + } + + this.client = client + this.log = log; +} + +HomeAssistantMediaPlayer.prototype = { + getPowerState: function(callback){ + this.log("fetching power state for: " + this.name); + + this.client.fetchState(this.entity_id, function(data){ + if (data) { + powerState = data.state == this.onState + callback(null, powerState) + }else{ + callback(communicationError) + } + }.bind(this)) + }, + getVolume: function(callback){ + this.log("fetching volume for: " + this.name); + that = this + this.client.fetchState(this.entity_id, function(data){ + if (data && data.attributes) { + that.log(JSON.stringify(data.attributes)) + level = data.attributes.volume_level ? data.attributes.volume_level*100 : 0 + callback(null, level) + }else{ + callback(communicationError) + } + }.bind(this)) + }, + setPowerState: function(powerOn, callback) { + var that = this; + var service_data = {} + service_data.entity_id = this.entity_id + + if (powerOn) { + this.log("Setting power state on the '"+this.name+"' to on"); + + this.client.callService(this.domain, this.onService, service_data, function(data){ + if (data) { + that.log("Successfully set power state on the '"+that.name+"' to on"); + callback() + }else{ + callback(communicationError) + } + }.bind(this)) + }else{ + this.log("Setting power state on the '"+this.name+"' to off"); + + this.client.callService(this.domain, this.offService, service_data, function(data){ + if (data) { + that.log("Successfully set power state on the '"+that.name+"' to off"); + callback() + }else{ + callback(communicationError) + } + }.bind(this)) + } + }, + setVolume: function(level, callback) { + var that = this; + var service_data = {} + service_data.entity_id = this.entity_id + + service_data.volume_level = level/100.0 + + this.log("Setting volume on the '"+this.name+"' to " + level); + + this.client.callService(this.domain, 'volume_set', service_data, function(data){ + if (data) { + that.log("Successfully set volume on the '"+that.name+"' to " + level); + callback() + }else{ + callback(communicationError) + } + }.bind(this)) + }, + getServices: function() { + var lightbulbService = new Service.Lightbulb(); + + lightbulbService + .getCharacteristic(Characteristic.On) + .on('get', this.getPowerState.bind(this)) + .on('set', this.setPowerState.bind(this)); + + + if (this.supportsVolume) { + lightbulbService + .addCharacteristic(Characteristic.Brightness) + .on('get', this.getVolume.bind(this)) + .on('set', this.setVolume.bind(this)); + } + + return [lightbulbService]; + } + +} + + function HomeAssistantSwitch(log, data, client) { // device info this.domain = "switch" @@ -300,5 +466,6 @@ HomeAssistantSwitch.prototype = { } module.exports.accessory = HomeAssistantLight; +module.exports.accessory = HomeAssistantMediaPlayer; module.exports.accessory = HomeAssistantSwitch; module.exports.platform = HomeAssistantPlatform;