Update media player more info

This commit is contained in:
Paulus Schoutsen 2015-06-02 23:36:37 -07:00
parent 8e6ccea085
commit 7f788d6be1
7 changed files with 233 additions and 90 deletions

View file

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "ed339673ca129a1a51dcc3975d0a492d" VERSION = "464ae07e4263837b88b3e5bda062eaed"

View file

@ -16975,7 +16975,7 @@ window.hass.uiUtil.domainIcon = function(domain, state) {
case "media_player": case "media_player":
var icon = "hardware:cast"; var icon = "hardware:cast";
if (state && state !== "idle") { if (state && state !== "off" && state !== 'idle') {
icon += "-connected"; icon += "-connected";
} }
@ -22301,8 +22301,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<div class="horizontal justified layout"> <div class="horizontal justified layout">
<state-info state-obj="[[stateObj]]"></state-info> <state-info state-obj="[[stateObj]]"></state-info>
<div class="state"> <div class="state">
<div class="main-text">[[computePrimaryText(stateObj)]]</div> <div class="main-text">[[computePrimaryText(stateObj, isPlaying)]]</div>
<div class="secondary-text">[[computeSecondaryText(stateObj)]]</div> <div class="secondary-text">[[computeSecondaryText(stateObj, isPlaying)]]</div>
</div> </div>
</div> </div>
</template> </template>
@ -22310,6 +22310,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<script> <script>
(function() { (function() {
var PLAYING_STATES = ['playing', 'paused'];
Polymer({ Polymer({
is: 'state-card-media_player', is: 'state-card-media_player',
@ -22317,14 +22318,41 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
stateObj: { stateObj: {
type: Object, type: Object,
}, },
isPlaying: {
type: Boolean,
computed: 'computeIsPlaying(stateObj)',
},
}, },
computePrimaryText: function(stateObj) { computeIsPlaying: function(stateObj) {
return stateObj.attributes.media_title || stateObj.stateDisplay; return PLAYING_STATES.indexOf(stateObj.state) !== -1;
}, },
computeSecondaryText: function(stateObj) { computePrimaryText: function(stateObj, isPlaying) {
return stateObj.attributes.media_title ? stateObj.stateDisplay : ''; return isPlaying ? stateObj.attributes.media_title : stateObj.stateDisplay;
},
computeSecondaryText: function(stateObj, isPlaying) {
var text;
if (stateObj.attributes.media_content_type == 'music') {
return stateObj.attributes.media_artist;
} else if (stateObj.attributes.media_content_type == 'tvshow') {
text = stateObj.attributes.media_series_title;
if (stateObj.attributes.media_season && stateObj.attributes.media_episode) {
text += ' S' + stateObj.attributes.media_season + 'E' + stateObj.attributes.media_episode;
}
return text;
} else if (stateObj.attributes.app_name) {
return stateObj.attributes.app_name;
} else {
return '';
}
}, },
}); });
})(); })();
@ -22342,6 +22370,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
overflow: hidden;
} }
</style> </style>
@ -25761,8 +25790,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
text-transform: capitalize; text-transform: capitalize;
} }
/* Accent the power button because the user should use that first */ paper-icon-button[highlight] {
paper-icon-button[focus] {
color: var(--accent-color); color: var(--accent-color);
} }
@ -25774,7 +25802,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
transition: max-height .5s ease-in; transition: max-height .5s ease-in;
} }
.has-media_volume .volume { .has-volume_level .volume {
max-height: 40px; max-height: 40px;
} }
</style> </style>
@ -25782,19 +25810,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<div class$="[[computeClassNames(stateObj)]]"> <div class$="[[computeClassNames(stateObj)]]">
<div class="layout horizontal"> <div class="layout horizontal">
<div class="flex"> <div class="flex">
<paper-icon-button icon="power-settings-new" focus$="[[isIdle]]" on-tap="handleTogglePower"></paper-icon-button> <paper-icon-button icon="power-settings-new" highlight$="[[isOff]]" on-tap="handleTogglePower"></paper-icon-button>
</div> </div>
<div> <div>
<template is="dom-if" if="[[!isIdle]]"> <template is="dom-if" if="[[!isOff]]">
<paper-icon-button icon="av:skip-previous" on-tap="handlePrevious"></paper-icon-button> <paper-icon-button icon="av:skip-previous" on-tap="handlePrevious" hidden$="[[!supportsPreviousTrack]]"></paper-icon-button>
<paper-icon-button icon="[[computePlayPauseIcon(stateObj)]]" focus$="" on-tap="handlePlayPause"></paper-icon-button> <paper-icon-button icon="[[computePlaybackControlIcon(stateObj)]]" on-tap="handlePlaybackControl" highlight=""></paper-icon-button>
<paper-icon-button icon="av:skip-next" on-tap="handleNext"></paper-icon-button> <paper-icon-button icon="av:skip-next" on-tap="handleNext" hidden$="[[!supportsNextTrack]]"></paper-icon-button>
</template> </template>
</div> </div>
</div> </div>
<div class="volume center horizontal layout"> <div class="volume center horizontal layout" hidden$="[[!supportsVolumeSet]]">
<paper-icon-button on-tap="handleVolumeTap" icon="[[computeMuteVolumeIcon(isMuted)]]"></paper-icon-button> <paper-icon-button on-tap="handleVolumeTap" icon="[[computeMuteVolumeIcon(isMuted)]]"></paper-icon-button>
<paper-slider hidden="[[isMuted]]" min="0" max="100" value="{{volumeSliderValue}}" on-change="volumeSliderChanged" class="flex"> <paper-slider disabled$="[[isMuted]]" min="0" max="100" value="{{volumeSliderValue}}" on-change="volumeSliderChanged" class="flex">
</paper-slider> </paper-slider>
</div> </div>
</div> </div>
@ -25805,7 +25833,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
(function() { (function() {
var serviceActions = window.hass.serviceActions; var serviceActions = window.hass.serviceActions;
var uiUtil = window.hass.uiUtil; var uiUtil = window.hass.uiUtil;
var ATTRIBUTE_CLASSES = ['media_volume']; var ATTRIBUTE_CLASSES = ['volume_level'];
Polymer({ Polymer({
is: 'more-info-media_player', is: 'more-info-media_player',
@ -25816,9 +25844,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
observer: 'stateObjChanged', observer: 'stateObjChanged',
}, },
isIdle: { isOff: {
type: Boolean, type: Boolean,
computed: 'computeIsIdle(stateObj)', value: false,
},
isPlaying: {
type: Boolean,
value: false,
}, },
isMuted: { isMuted: {
@ -25829,13 +25862,58 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
volumeSliderValue: { volumeSliderValue: {
type: Number, type: Number,
value: 0, value: 0,
} },
supportsPause: {
type: Boolean,
value: false,
},
supportsVolumeSet: {
type: Boolean,
value: false,
},
supportsVolumeMute: {
type: Boolean,
value: false,
},
supportsPreviousTrack: {
type: Boolean,
value: false,
},
supportsNextTrack: {
type: Boolean,
value: false,
},
supportsTurnOn: {
type: Boolean,
value: false,
},
supportsTurnOff: {
type: Boolean,
value: false,
},
}, },
stateObjChanged: function(newVal, oldVal) { stateObjChanged: function(newVal, oldVal) {
if (newVal) { if (newVal) {
this.volumeSliderValue = newVal.attributes.media_volume * 100; this.isOff = newVal.state == 'off';
this.isMuted = newVal.attributes.media_is_volume_muted; this.isPlaying = newVal.state == 'playing';
this.volumeSliderValue = newVal.attributes.volume_level * 100;
this.isMuted = newVal.attributes.volume_muted;
this.supportsPause = (newVal.attributes.supported_media_commands & 1) !== 0;
this.supportsVolumeSet = (newVal.attributes.supported_media_commands & 4) !== 0;
this.supportsVolumeMute = (newVal.attributes.supported_media_commands & 8) !== 0;
this.supportsPreviousTrack = (newVal.attributes.supported_media_commands & 16) !== 0;
this.supportsNextTrack = (newVal.attributes.supported_media_commands & 32) !== 0;
this.supportsTurnOn = (newVal.attributes.supported_media_commands & 128) !== 0;
this.supportsTurnOff = (newVal.attributes.supported_media_commands & 256) !== 0;
} }
this.debounce('more-info-volume-animation-finish', function() { this.debounce('more-info-volume-animation-finish', function() {
@ -25847,35 +25925,37 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES); return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
}, },
computeMediaState: function(stateObj) { computeIsOff: function(stateObj) {
return stateObj.state == 'idle' ? 'idle' : stateObj.attributes.media_state; return stateObj.state == 'off';
}, },
computeIsIdle: function(stateObj) { computePowerButtonCaption: function(isOff) {
return stateObj.state == 'idle'; return isOff ? 'Turn on' : 'Turn off';
},
computePowerButtonCaption: function(isIdle) {
return isIdle ? 'Turn on' : 'Turn off';
}, },
computeMuteVolumeIcon: function(isMuted) { computeMuteVolumeIcon: function(isMuted) {
return isMuted ? 'av:volume-off' : 'av:volume-up'; return isMuted ? 'av:volume-off' : 'av:volume-up';
}, },
computePlayPauseIcon: function(stateObj) { computePlaybackControlIcon: function(stateObj) {
return stateObj.attributes.media_state == 'playing' ? 'av:pause' : 'av:play-arrow'; if (this.isPlaying) {
return this.supportsPause ? 'av:pause' : 'av:stop';
}
return 'av:play-arrow';
}, },
handleTogglePower: function() { handleTogglePower: function() {
this.callService(this.isIdle ? 'turn_on' : 'turn_off'); this.callService(this.isOff ? 'turn_on' : 'turn_off');
}, },
handlePrevious: function() { handlePrevious: function() {
this.callService('media_prev_track'); this.callService('media_previous_track');
}, },
handlePlayPause: function() { handlePlaybackControl: function() {
if (this.isPlaying && !this.supportsPause) {
alert('This case is not supported yet');
}
this.callService('media_play_pause'); this.callService('media_play_pause');
}, },
@ -25884,14 +25964,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
handleVolumeTap: function() { handleVolumeTap: function() {
this.callService('volume_mute', { mute: !this.isMuted }); if (!this.supportsVolumeMute) {
return;
}
this.callService('volume_mute', { volume_muted: !this.isMuted });
}, },
volumeSliderChanged: function(ev) { volumeSliderChanged: function(ev) {
var volPercentage = parseFloat(ev.target.value); var volPercentage = parseFloat(ev.target.value);
var vol = volPercentage > 0 ? volPercentage / 100 : 0; var vol = volPercentage > 0 ? volPercentage / 100 : 0;
this.callService('volume_set', { volume_level: vol });
this.callService('volume_set', { volume: vol });
}, },
callService: function(service, data) { callService: function(service, data) {

View file

@ -15,6 +15,7 @@
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
overflow: hidden;
} }
</style> </style>

View file

@ -8,8 +8,7 @@
text-transform: capitalize; text-transform: capitalize;
} }
/* Accent the power button because the user should use that first */ paper-icon-button[highlight] {
paper-icon-button[focus] {
color: var(--accent-color); color: var(--accent-color);
} }
@ -21,7 +20,7 @@
transition: max-height .5s ease-in; transition: max-height .5s ease-in;
} }
.has-media_volume .volume { .has-volume_level .volume {
max-height: 40px; max-height: 40px;
} }
</style> </style>
@ -29,24 +28,24 @@
<div class$='[[computeClassNames(stateObj)]]'> <div class$='[[computeClassNames(stateObj)]]'>
<div class='layout horizontal'> <div class='layout horizontal'>
<div class='flex'> <div class='flex'>
<paper-icon-button icon='power-settings-new' focus$='[[isIdle]]' <paper-icon-button icon='power-settings-new' highlight$='[[isOff]]'
on-tap='handleTogglePower'></paper-icon-button> on-tap='handleTogglePower'></paper-icon-button>
</div> </div>
<div> <div>
<template is='dom-if' if='[[!isIdle]]'> <template is='dom-if' if='[[!isOff]]'>
<paper-icon-button icon='av:skip-previous' <paper-icon-button icon='av:skip-previous' on-tap='handlePrevious'
on-tap='handlePrevious'></paper-icon-button> hidden$='[[!supportsPreviousTrack]]'></paper-icon-button>
<paper-icon-button icon='[[computePlayPauseIcon(stateObj)]]' focus$ <paper-icon-button icon='[[computePlaybackControlIcon(stateObj)]]'
on-tap='handlePlayPause'></paper-icon-button> on-tap='handlePlaybackControl' highlight></paper-icon-button>
<paper-icon-button icon='av:skip-next' <paper-icon-button icon='av:skip-next' on-tap='handleNext'
on-tap='handleNext'></paper-icon-button> hidden$='[[!supportsNextTrack]]'></paper-icon-button>
</template> </template>
</div> </div>
</div> </div>
<div class='volume center horizontal layout'> <div class='volume center horizontal layout' hidden$='[[!supportsVolumeSet]]'>
<paper-icon-button on-tap="handleVolumeTap" <paper-icon-button on-tap="handleVolumeTap"
icon="[[computeMuteVolumeIcon(isMuted)]]"></paper-icon-button> icon="[[computeMuteVolumeIcon(isMuted)]]"></paper-icon-button>
<paper-slider hidden='[[isMuted]]' <paper-slider disabled$='[[isMuted]]'
min='0' max='100' value='{{volumeSliderValue}}' min='0' max='100' value='{{volumeSliderValue}}'
on-change='volumeSliderChanged' class='flex'> on-change='volumeSliderChanged' class='flex'>
</paper-slider> </paper-slider>
@ -59,7 +58,7 @@
(function() { (function() {
var serviceActions = window.hass.serviceActions; var serviceActions = window.hass.serviceActions;
var uiUtil = window.hass.uiUtil; var uiUtil = window.hass.uiUtil;
var ATTRIBUTE_CLASSES = ['media_volume']; var ATTRIBUTE_CLASSES = ['volume_level'];
Polymer({ Polymer({
is: 'more-info-media_player', is: 'more-info-media_player',
@ -70,9 +69,14 @@
observer: 'stateObjChanged', observer: 'stateObjChanged',
}, },
isIdle: { isOff: {
type: Boolean, type: Boolean,
computed: 'computeIsIdle(stateObj)', value: false,
},
isPlaying: {
type: Boolean,
value: false,
}, },
isMuted: { isMuted: {
@ -83,13 +87,58 @@
volumeSliderValue: { volumeSliderValue: {
type: Number, type: Number,
value: 0, value: 0,
} },
supportsPause: {
type: Boolean,
value: false,
},
supportsVolumeSet: {
type: Boolean,
value: false,
},
supportsVolumeMute: {
type: Boolean,
value: false,
},
supportsPreviousTrack: {
type: Boolean,
value: false,
},
supportsNextTrack: {
type: Boolean,
value: false,
},
supportsTurnOn: {
type: Boolean,
value: false,
},
supportsTurnOff: {
type: Boolean,
value: false,
},
}, },
stateObjChanged: function(newVal, oldVal) { stateObjChanged: function(newVal, oldVal) {
if (newVal) { if (newVal) {
this.volumeSliderValue = newVal.attributes.media_volume * 100; this.isOff = newVal.state == 'off';
this.isMuted = newVal.attributes.media_is_volume_muted; this.isPlaying = newVal.state == 'playing';
this.volumeSliderValue = newVal.attributes.volume_level * 100;
this.isMuted = newVal.attributes.volume_muted;
this.supportsPause = (newVal.attributes.supported_media_commands & 1) !== 0;
this.supportsVolumeSet = (newVal.attributes.supported_media_commands & 4) !== 0;
this.supportsVolumeMute = (newVal.attributes.supported_media_commands & 8) !== 0;
this.supportsPreviousTrack = (newVal.attributes.supported_media_commands & 16) !== 0;
this.supportsNextTrack = (newVal.attributes.supported_media_commands & 32) !== 0;
this.supportsTurnOn = (newVal.attributes.supported_media_commands & 128) !== 0;
this.supportsTurnOff = (newVal.attributes.supported_media_commands & 256) !== 0;
} }
this.debounce('more-info-volume-animation-finish', function() { this.debounce('more-info-volume-animation-finish', function() {
@ -101,35 +150,37 @@
return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES); return uiUtil.attributeClassNames(stateObj, ATTRIBUTE_CLASSES);
}, },
computeMediaState: function(stateObj) { computeIsOff: function(stateObj) {
return stateObj.state == 'idle' ? 'idle' : stateObj.attributes.media_state; return stateObj.state == 'off';
}, },
computeIsIdle: function(stateObj) { computePowerButtonCaption: function(isOff) {
return stateObj.state == 'idle'; return isOff ? 'Turn on' : 'Turn off';
},
computePowerButtonCaption: function(isIdle) {
return isIdle ? 'Turn on' : 'Turn off';
}, },
computeMuteVolumeIcon: function(isMuted) { computeMuteVolumeIcon: function(isMuted) {
return isMuted ? 'av:volume-off' : 'av:volume-up'; return isMuted ? 'av:volume-off' : 'av:volume-up';
}, },
computePlayPauseIcon: function(stateObj) { computePlaybackControlIcon: function(stateObj) {
return stateObj.attributes.media_state == 'playing' ? 'av:pause' : 'av:play-arrow'; if (this.isPlaying) {
return this.supportsPause ? 'av:pause' : 'av:stop';
}
return 'av:play-arrow';
}, },
handleTogglePower: function() { handleTogglePower: function() {
this.callService(this.isIdle ? 'turn_on' : 'turn_off'); this.callService(this.isOff ? 'turn_on' : 'turn_off');
}, },
handlePrevious: function() { handlePrevious: function() {
this.callService('media_prev_track'); this.callService('media_previous_track');
}, },
handlePlayPause: function() { handlePlaybackControl: function() {
if (this.isPlaying && !this.supportsPause) {
alert('This case is not supported yet');
}
this.callService('media_play_pause'); this.callService('media_play_pause');
}, },
@ -138,14 +189,16 @@
}, },
handleVolumeTap: function() { handleVolumeTap: function() {
this.callService('volume_mute', { mute: !this.isMuted }); if (!this.supportsVolumeMute) {
return;
}
this.callService('volume_mute', { volume_muted: !this.isMuted });
}, },
volumeSliderChanged: function(ev) { volumeSliderChanged: function(ev) {
var volPercentage = parseFloat(ev.target.value); var volPercentage = parseFloat(ev.target.value);
var vol = volPercentage > 0 ? volPercentage / 100 : 0; var vol = volPercentage > 0 ? volPercentage / 100 : 0;
this.callService('volume_set', { volume_level: vol });
this.callService('volume_set', { volume: vol });
}, },
callService: function(service, data) { callService: function(service, data) {

View file

@ -48,7 +48,7 @@ window.hass.uiUtil.domainIcon = function(domain, state) {
case "media_player": case "media_player":
var icon = "hardware:cast"; var icon = "hardware:cast";
if (state && state !== "idle") { if (state && state !== "off" && state !== 'idle') {
icon += "-connected"; icon += "-connected";
} }

View file

@ -209,26 +209,34 @@ def setup(hass, config):
for service in SERVICE_TO_METHOD: for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, media_player_service_handler) hass.services.register(DOMAIN, service, media_player_service_handler)
def volume_set_service(service, volume): def volume_set_service(service):
""" Set specified volume on the media player. """ """ Set specified volume on the media player. """
target_players = component.extract_from_service(service) target_players = component.extract_from_service(service)
volume = service.data.get(ATTR_MEDIA_VOLUME_LEVEL)
if volume is not None: if ATTR_MEDIA_VOLUME_LEVEL not in service.data:
for player in target_players: return
player.set_volume_level(volume)
if player.should_poll: volume = service.data[ATTR_MEDIA_VOLUME_LEVEL]
player.update_ha_state(True)
for player in target_players:
player.set_volume_level(volume)
if player.should_poll:
player.update_ha_state(True)
hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service) hass.services.register(DOMAIN, SERVICE_VOLUME_SET, volume_set_service)
def volume_mute_service(service, mute): def volume_mute_service(service):
""" Mute (true) or unmute (false) the media player. """ """ Mute (true) or unmute (false) the media player. """
target_players = component.extract_from_service(service) target_players = component.extract_from_service(service)
if ATTR_MEDIA_VOLUME_MUTED not in service.data:
return
mute = service.data[ATTR_MEDIA_VOLUME_MUTED]
for player in target_players: for player in target_players:
player.volume_mute(mute) player.mute_volume(mute)
if player.should_poll: if player.should_poll:
player.update_ha_state(True) player.update_ha_state(True)
@ -236,7 +244,7 @@ def setup(hass, config):
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE, hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
lambda service: lambda service:
volume_mute_service( volume_mute_service(
service, service.data.get('mute'))) service, ))
def play_youtube_video_service(service, media_id): def play_youtube_video_service(service, media_id):
""" Plays specified media_id on the media player. """ """ Plays specified media_id on the media player. """

View file

@ -38,9 +38,8 @@ MUSIC_PLAYER_SUPPORT = \
SUPPORT_NEXT_TRACK SUPPORT_NEXT_TRACK
NETFLIX_PLAYER_SUPPORT = \ NETFLIX_PLAYER_SUPPORT = \
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \ SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
SUPPORT_NEXT_TRACK
class AbstractDemoPlayer(MediaPlayerDevice): class AbstractDemoPlayer(MediaPlayerDevice):