diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index b69400142e0f843329876ba33174543a22cedeb5..e1d489125a3440dd621ff5376ddfd7a3364c6c1f 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "6fb0e76d325bb7472fa25355e60539cd" +VERSION = "3c6a5149feced6c49cb97b76fdf14fee" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 1a23b7069882fe08b229e2e8ec2e022b85c7e601..c8837e05d6a93ed712794f64154ad17f16885333 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -25617,11 +25617,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN transition: max-height .5s ease-in; } - .brightness paper-slider::shadow #sliderKnobInner, - .brightness paper-slider::shadow #sliderBar::shadow #activeProgress { - background-color: #039be5; - } - color-picker { display: block; width: 350px; @@ -25633,7 +25628,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN } .has-brightness .brightness { - max-height: 500px; + max-height: 40px; } .has-xy_color color-picker { @@ -25722,21 +25717,40 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN text-transform: capitalize; } - paper-button, paper-icon-button { + paper-icon-button { color: var(--accent-color); } + + .volume { + margin-bottom: 8px; + + max-height: 0px; + overflow: hidden; + transition: max-height .5s ease-in; + } + + .has-media_volume .volume { + max-height: 40px; + } </style> <template> - <div class="layout horizontal"> - <div class="flex"> - <paper-icon-button icon="power-settings-new" on-tap="handleTogglePower"></paper-icon-button> + <div class$="[[computeClassNames(stateObj)]]"> + <div class="layout horizontal"> + <div class="flex"> + <paper-icon-button icon="power-settings-new" on-tap="handleTogglePower"></paper-icon-button> + </div> + <div> + <template is="dom-if" if="[[!isIdle]]"> + <paper-icon-button icon="av:skip-previous" on-tap="handlePrevious"></paper-icon-button> + <paper-icon-button icon="[[computePlayPauseIcon(stateObj)]]" on-tap="handlePlayPause"></paper-icon-button> + <paper-icon-button icon="av:skip-next" on-tap="handleNext"></paper-icon-button> + </template> + </div> </div> - <div> - <template is="dom-if" if="[[!isIdle]]"> - <paper-icon-button icon="av:skip-previous" on-tap="handlePrevious"></paper-icon-button> - <paper-icon-button icon="[[computePlayPauseIcon(stateObj)]]" on-tap="handlePlayPause"></paper-icon-button> - <paper-icon-button icon="av:skip-next" on-tap="handleNext"></paper-icon-button> - </template> + <div class="volume center horizontal layout"> + <div>Volume</div> + <paper-slider min="0" max="100" value="{{volumeSliderValue}}" on-change="volumeSliderChanged" class="flex"> + </paper-slider> </div> </div> </template> @@ -25745,6 +25759,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN <script> (function() { var serviceActions = window.hass.serviceActions; + var uiUtil = window.hass.uiUtil; + var ATTRIBUTE_CLASSES = ['media_volume']; Polymer({ is: 'more-info-media_player', @@ -25752,12 +25768,32 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN properties: { stateObj: { type: Object, + observer: 'stateObjChanged', }, isIdle: { type: Boolean, computed: 'computeIsIdle(stateObj)', }, + + volumeSliderValue: { + type: Number, + value: 0, + } + }, + + stateObjChanged: function(newVal, oldVal) { + if (newVal) { + this.volumeSliderValue = newVal.attributes.media_volume * 100; + } + + this.debounce('more-info-volume-animation-finish', function() { + this.fire('iron-resize'); + }.bind(this), 500); + }, + + computeClassNames: function(stateObj) { + return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES); }, computeMediaState: function(stateObj) { @@ -25792,8 +25828,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN this.callService('media_next_track'); }, - callService: function(service) { - var data = {entity_id: this.stateObj.entityId}; + volumeSliderChanged: function(ev) { + var volPercentage = parseFloat(ev.target.value); + var vol = volPercentage > 0 ? volPercentage / 100 : 0; + + this.callService('volume_set', {volume: vol}); + }, + + callService: function(service, data) { + data = data || {}; + data.entity_id = this.stateObj.entityId; serviceActions.callService('media_player', service, data); }, }); diff --git a/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-light.html b/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-light.html index 32c31948875be0409cc532939d13fec8df834c79..b9d49e1f4747faa62ce96c3ac335172afd2f2c3d 100644 --- a/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-light.html +++ b/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-light.html @@ -13,11 +13,6 @@ transition: max-height .5s ease-in; } - .brightness paper-slider::shadow #sliderKnobInner, - .brightness paper-slider::shadow #sliderBar::shadow #activeProgress { - background-color: #039be5; - } - color-picker { display: block; width: 350px; @@ -29,7 +24,7 @@ } .has-brightness .brightness { - max-height: 500px; + max-height: 40px; } .has-xy_color color-picker { diff --git a/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-media_player.html b/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-media_player.html index 8dd20fdbabe485e8dc5a4c4093cc3747f2048aed..408e863d647c21a379237df76e445b65d0142aba 100644 --- a/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-media_player.html +++ b/homeassistant/components/frontend/www_static/polymer/more-infos/more-info-media_player.html @@ -8,25 +8,46 @@ text-transform: capitalize; } - paper-button, paper-icon-button { + paper-icon-button { color: var(--accent-color); } + + .volume { + margin-bottom: 8px; + + max-height: 0px; + overflow: hidden; + transition: max-height .5s ease-in; + } + + .has-media_volume .volume { + max-height: 40px; + } </style> <template> - <div class='layout horizontal'> - <div class='flex'> - <paper-icon-button icon='power-settings-new' - on-tap='handleTogglePower'></paper-icon-button> + <div class$='[[computeClassNames(stateObj)]]'> + <div class='layout horizontal'> + <div class='flex'> + <paper-icon-button icon='power-settings-new' + on-tap='handleTogglePower'></paper-icon-button> + </div> + <div> + <template is='dom-if' if='[[!isIdle]]'> + <paper-icon-button icon='av:skip-previous' + on-tap='handlePrevious'></paper-icon-button> + <paper-icon-button icon='[[computePlayPauseIcon(stateObj)]]' + on-tap='handlePlayPause'></paper-icon-button> + <paper-icon-button icon='av:skip-next' + on-tap='handleNext'></paper-icon-button> + </template> + </div> </div> - <div> - <template is='dom-if' if='[[!isIdle]]'> - <paper-icon-button icon='av:skip-previous' - on-tap='handlePrevious'></paper-icon-button> - <paper-icon-button icon='[[computePlayPauseIcon(stateObj)]]' - on-tap='handlePlayPause'></paper-icon-button> - <paper-icon-button icon='av:skip-next' - on-tap='handleNext'></paper-icon-button> - </template> + <div class='volume center horizontal layout'> + <div>Volume</div> + <paper-slider + min='0' max='100' value='{{volumeSliderValue}}' + on-change='volumeSliderChanged' class='flex'> + </paper-slider> </div> </div> </template> @@ -35,6 +56,8 @@ <script> (function() { var serviceActions = window.hass.serviceActions; + var uiUtil = window.hass.uiUtil; + var ATTRIBUTE_CLASSES = ['media_volume']; Polymer({ is: 'more-info-media_player', @@ -42,12 +65,32 @@ properties: { stateObj: { type: Object, + observer: 'stateObjChanged', }, isIdle: { type: Boolean, computed: 'computeIsIdle(stateObj)', }, + + volumeSliderValue: { + type: Number, + value: 0, + } + }, + + stateObjChanged: function(newVal, oldVal) { + if (newVal) { + this.volumeSliderValue = newVal.attributes.media_volume * 100; + } + + this.debounce('more-info-volume-animation-finish', function() { + this.fire('iron-resize'); + }.bind(this), 500); + }, + + computeClassNames: function(stateObj) { + return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES); }, computeMediaState: function(stateObj) { @@ -82,8 +125,16 @@ this.callService('media_next_track'); }, - callService: function(service) { - var data = {entity_id: this.stateObj.entityId}; + volumeSliderChanged: function(ev) { + var volPercentage = parseFloat(ev.target.value); + var vol = volPercentage > 0 ? volPercentage / 100 : 0; + + this.callService('volume_set', {volume: vol}); + }, + + callService: function(service, data) { + data = data || {}; + data.entity_id = this.stateObj.entityId; serviceActions.callService('media_player', service, data); }, }); diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 0ce00eae43f5b2c675ec84841e8afcc3bbd52891..2b555faa5030f46366f66b657b4941f46d2c0164 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -10,9 +10,11 @@ from homeassistant.components import discovery from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_UP, - SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, - SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK) + ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, + SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_SET, + SERVICE_VOLUME_MUTE, + SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK) DOMAIN = 'media_player' DEPENDENCIES = [] @@ -86,6 +88,25 @@ def volume_down(hass, entity_id=None): hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN, data) +def volume_mute(hass, entity_id=None): + """ Send the media player the command for volume down. """ + data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} + + hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, data) + + +def volume_set(hass, entity_id=None, volume=None): + """ Send the media player the command for volume down. """ + data = { + key: value for key, value in [ + (ATTR_ENTITY_ID, entity_id), + (ATTR_MEDIA_VOLUME, volume), + ] if value is not None + } + + hass.services.call(DOMAIN, SERVICE_VOLUME_SET, data) + + def media_play_pause(hass, entity_id=None): """ Send the media player the command for play/pause. """ data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} @@ -126,6 +147,7 @@ SERVICE_TO_METHOD = { SERVICE_TURN_OFF: 'turn_off', SERVICE_VOLUME_UP: 'volume_up', SERVICE_VOLUME_DOWN: 'volume_down', + SERVICE_VOLUME_MUTE: 'volume_mute', SERVICE_MEDIA_PLAY_PAUSE: 'media_play_pause', SERVICE_MEDIA_PLAY: 'media_play', SERVICE_MEDIA_PAUSE: 'media_pause', @@ -157,6 +179,19 @@ def setup(hass, config): for service in SERVICE_TO_METHOD: hass.services.register(DOMAIN, service, media_player_service_handler) + def volume_set_service(service, volume): + """ Set specified volume on the media player. """ + target_players = component.extract_from_service(service) + + if volume: + for player in target_players: + player.volume_set(volume) + + hass.services.register(DOMAIN, SERVICE_VOLUME_SET, + lambda service: + volume_set_service( + service, service.data.get('volume'))) + def play_youtube_video_service(service, media_id): """ Plays specified media_id on the media player. """ target_players = component.extract_from_service(service) @@ -200,6 +235,14 @@ class MediaPlayerDevice(Entity): """ volume_down media player. """ pass + def volume_mute(self): + """ mute media player. """ + pass + + def volume_set(self, volume): + """ set volume level of media player. """ + pass + def media_play_pause(self): """ media_play_pause media player. """ pass diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index 33ac14efa1e96da5c96ea7f7b3fb159f2e4deaa6..0ed5b2bc5b62161cd3b5ce87812abd0950ce37d1 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -116,7 +116,7 @@ class CastDevice(MediaPlayerDevice): } if cast_status: - state_attr[ATTR_MEDIA_VOLUME] = cast_status.volume_level, + state_attr[ATTR_MEDIA_VOLUME] = cast_status.volume_level if media_status.content_id: state_attr[ATTR_MEDIA_CONTENT_ID] = media_status.content_id @@ -156,6 +156,14 @@ class CastDevice(MediaPlayerDevice): """ Service to send the chromecast the command for volume down. """ self.cast.volume_down() + def volume_mute(self): + """ Service to send the chromecast the command for volume up. """ + self.cast.set_volume(0) + + def volume_set(self, volume): + """ Service to send the chromecast the command for volume down. """ + self.cast.set_volume(volume) + def media_play_pause(self): """ Service to send the chromecast the command for play/pause. """ media_state = self.media_state diff --git a/homeassistant/const.py b/homeassistant/const.py index cfd37576ff1e9b1ccd1af553aef13de7227c6e6b..a4ea2651d289d49e721186722f245e97729db410 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -99,6 +99,7 @@ SERVICE_TURN_OFF = 'turn_off' SERVICE_VOLUME_UP = "volume_up" SERVICE_VOLUME_DOWN = "volume_down" SERVICE_VOLUME_MUTE = "volume_mute" +SERVICE_VOLUME_SET = "volume_set" SERVICE_MEDIA_PLAY_PAUSE = "media_play_pause" SERVICE_MEDIA_PLAY = "media_play" SERVICE_MEDIA_PAUSE = "media_pause"