1
mirror of https://github.com/home-assistant/frontend synced 2024-09-28 00:43:28 +02:00

Added entity row for media players. (#1495)

* Added entity row for media players.

* Use artist:track/series:episode for music/tvshow.

* Add controls

* Comments

* Fixes

* Fixes for off states. Added gallery demo.

* Resolve conflicts. Change to use template extension points.

* Fixes
This commit is contained in:
Jerad Meisner 2018-09-17 00:58:43 -07:00 committed by Paulus Schoutsen
parent 5187f3b84f
commit 8b262f3424
4 changed files with 269 additions and 10 deletions

View File

@ -0,0 +1,105 @@
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import getEntity from '../data/entity.js';
import provideHass from '../data/provide_hass.js';
import '../components/demo-cards.js';
const ENTITIES = [
getEntity('media_player', 'bedroom', 'playing', {
media_content_type: 'movie',
media_title: 'Epic sax guy 10 hours',
app_name: 'YouTube',
supported_features: 32
}),
getEntity('media_player', 'family_room', 'paused', {
media_content_type: 'music',
media_title: 'I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)',
media_artist: 'Technohead',
supported_features: 16417
}),
getEntity('media_player', 'family_room_no_play', 'paused', {
media_content_type: 'movie',
media_title: 'Epic sax guy 10 hours',
app_name: 'YouTube',
supported_features: 33
}),
getEntity('media_player', 'living_room', 'playing', {
media_content_type: 'tvshow',
media_title: 'Chapter 1',
media_series_title: 'House of Cards',
app_name: 'Netflix',
supported_features: 1
}),
getEntity('media_player', 'lounge_room', 'idle', {
media_content_type: 'music',
media_title: 'I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)',
media_artist: 'Technohead',
supported_features: 1,
}),
getEntity('media_player', 'theater', 'off', {
media_content_type: 'movie',
media_title: 'Epic sax guy 10 hours',
app_name: 'YouTube',
supported_features: 33
}),
getEntity('media_player', 'android_cast', 'playing', {
media_title: 'Android Screen Casting',
app_name: 'Screen Mirroring',
supported_features: 21437
}),
];
const CONFIGS = [
{
heading: 'Media Players',
config: `
- type: entities
entities:
- entity: media_player.bedroom
name: Skip, no pause
- entity: media_player.family_room
name: Paused, music
- entity: media_player.family_room_no_play
name: Paused, no play
- entity: media_player.living_room
name: Pause, No skip, tvshow
- entity: media_player.android_cast
name: Screen casting
- entity: media_player.lounge_room
name: Chromcast Idle
- entity: media_player.theater
name: 'Player Off'
`
}
];
class DemoHuiMediaPlayerRows extends PolymerElement {
static get template() {
return html`
<demo-cards
id='demos'
hass='[[hass]]'
configs="[[_configs]]"
></demo-cards>
`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS
},
hass: Object,
};
}
ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.addEntities(ENTITIES);
}
}
customElements.define('demo-hui-media-player-rows', DemoHuiMediaPlayerRows);

View File

@ -7,6 +7,7 @@ import '../entity-rows/hui-input-number-entity-row.js';
import '../entity-rows/hui-input-select-entity-row.js';
import '../entity-rows/hui-input-text-entity-row.js';
import '../entity-rows/hui-lock-entity-row.js';
import '../entity-rows/hui-media-player-entity-row.js';
import '../entity-rows/hui-scene-entity-row.js';
import '../entity-rows/hui-script-entity-row.js';
import '../entity-rows/hui-text-entity-row.js';
@ -36,6 +37,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
input_select: 'input-select',
input_text: 'input-text',
light: 'toggle',
media_player: 'media-player',
lock: 'lock',
scene: 'scene',
script: 'script',

View File

@ -54,6 +54,9 @@ class HuiGenericEntityRow extends PolymerElement {
margin-left: 8px;
min-width: 0;
}
.flex ::slotted([slot=secondary]) {
margin-left: 0;
}
.secondary,
ha-relative-time {
display: block;
@ -84,19 +87,22 @@ class HuiGenericEntityRow extends PolymerElement {
return html`
<div class="info">
[[_computeName(config.name, _stateObj)]]
<template is="dom-if" if="[[config.secondary_info]]">
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
<div class="secondary">
<div class="secondary">
<template is="dom-if" if="[[showSecondary]]">
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
[[_stateObj.entity_id]]
</div>
</template>
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
<ha-relative-time
hass="[[hass]]"
datetime="[[_stateObj.last_changed]]"
></ha-relative-time>
</template>
</template>
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
<ha-relative-time
hass="[[hass]]"
datetime="[[_stateObj.last_changed]]"
></ha-relative-time>
<template is="dom-if" if="[[!showSecondary]">
<slot name="secondary"></slot>
</template>
</template>
</div>
</div>
`;
}
@ -108,6 +114,10 @@ class HuiGenericEntityRow extends PolymerElement {
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, config.entity)'
},
showSecondary: {
type: Boolean,
value: true
}
};
}

View File

@ -0,0 +1,142 @@
import '@polymer/paper-icon-button/paper-icon-button.js';
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
import '../components/hui-generic-entity-row.js';
import LocalizeMixin from '../../../mixins/localize-mixin.js';
const SUPPORT_PAUSE = 1;
const SUPPORT_NEXT_TRACK = 32;
const SUPPORTS_PLAY = 16384;
const OFF_STATES = ['off', 'idle'];
/*
* @appliesMixin LocalizeMixin
*/
class HuiMediaPlayerEntityRow extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
${this.styleTemplate}
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
show-secondary="false"
>
${this.mediaPlayerControlTemplate}
</hui-generic-entity-row>
`;
}
static get styleTemplate() {
return html`
<style>
.controls {
white-space: nowrap;
}
</style>
`;
}
static get mediaPlayerControlTemplate() {
return html`
<template is="dom-if" if="[[!_isOff(_stateObj.state)]]">
<div class="controls">
<template is="dom-if" if="[[_computeControlIcon(_stateObj)]]">
<paper-icon-button
icon="[[_computeControlIcon(_stateObj)]]"
on-click="_playPause"
></paper-icon-button>
</template>
<template is="dom-if" if="[[_supportsNext(_stateObj)]]">
<paper-icon-button
icon="hass:skip-next"
on-click="_nextTrack"
></paper-icon-button>
</template>
</div>
</template>
<template is="dom-if" if="[[_isOff(_stateObj.state)]]">
<div>[[_computeState(_stateObj.state)]]</div>
</template>
<div slot="secondary">
[[_computeMediaTitle(_stateObj)]]
</div>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: '_computeStateObj(hass.states, _config.entity)'
}
};
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error('Entity not configured.');
}
this._config = config;
}
_computeControlIcon(stateObj) {
if (stateObj.state !== 'playing') {
return stateObj.attributes.supported_features & SUPPORTS_PLAY ? 'hass:play' : '';
}
return stateObj.attributes.supported_features & SUPPORT_PAUSE ? 'hass:pause' : 'hass:stop';
}
_computeMediaTitle(stateObj) {
if (!stateObj || this._isOff(stateObj.state)) return null;
switch (stateObj.attributes.media_content_type) {
case 'music':
return `${stateObj.attributes.media_artist}: ${stateObj.attributes.media_title}`;
case 'tvshow':
return `${stateObj.attributes.media_series_title}: ${stateObj.attributes.media_title}`;
default:
return stateObj.attributes.media_title || stateObj.attributes.app_name || stateObj.state;
}
}
_computeState(state) {
return this.localize(`state.media_player.${state}`)
|| this.localize(`state.default.${state}`)
|| state;
}
_callService(service) {
this.hass.callService('media_player', service, { entity_id: this._config.entity });
}
_playPause(event) {
event.stopPropagation();
this._callService('media_play_pause');
}
_nextTrack(event) {
event.stopPropagation();
if (this._stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK) {
this._callService('media_next_track');
}
}
_isOff(state) {
return OFF_STATES.includes(state);
}
_supportsNext(stateObj) {
return stateObj && (stateObj.attributes.supported_features & SUPPORT_NEXT_TRACK);
}
}
customElements.define('hui-media-player-entity-row', HuiMediaPlayerEntityRow);