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:
parent
5187f3b84f
commit
8b262f3424
105
gallery/src/demos/demo-hui-media-player-rows.js
Normal file
105
gallery/src/demos/demo-hui-media-player-rows.js
Normal 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);
|
@ -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',
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
142
src/panels/lovelace/entity-rows/hui-media-player-entity-row.js
Normal file
142
src/panels/lovelace/entity-rows/hui-media-player-entity-row.js
Normal 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);
|
Loading…
Reference in New Issue
Block a user