mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 01:06:35 +00: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-select-entity-row.js';
|
||||||
import '../entity-rows/hui-input-text-entity-row.js';
|
import '../entity-rows/hui-input-text-entity-row.js';
|
||||||
import '../entity-rows/hui-lock-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-scene-entity-row.js';
|
||||||
import '../entity-rows/hui-script-entity-row.js';
|
import '../entity-rows/hui-script-entity-row.js';
|
||||||
import '../entity-rows/hui-text-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_select: 'input-select',
|
||||||
input_text: 'input-text',
|
input_text: 'input-text',
|
||||||
light: 'toggle',
|
light: 'toggle',
|
||||||
|
media_player: 'media-player',
|
||||||
lock: 'lock',
|
lock: 'lock',
|
||||||
scene: 'scene',
|
scene: 'scene',
|
||||||
script: 'script',
|
script: 'script',
|
||||||
|
@ -54,6 +54,9 @@ class HuiGenericEntityRow extends PolymerElement {
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.flex ::slotted([slot=secondary]) {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
.secondary,
|
.secondary,
|
||||||
ha-relative-time {
|
ha-relative-time {
|
||||||
display: block;
|
display: block;
|
||||||
@ -84,11 +87,10 @@ class HuiGenericEntityRow extends PolymerElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="info">
|
<div class="info">
|
||||||
[[_computeName(config.name, _stateObj)]]
|
[[_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]]
|
[[_stateObj.entity_id]]
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
||||||
<ha-relative-time
|
<ha-relative-time
|
||||||
@ -97,6 +99,10 @@ class HuiGenericEntityRow extends PolymerElement {
|
|||||||
></ha-relative-time>
|
></ha-relative-time>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<template is="dom-if" if="[[!showSecondary]">
|
||||||
|
<slot name="secondary"></slot>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -108,6 +114,10 @@ class HuiGenericEntityRow extends PolymerElement {
|
|||||||
_stateObj: {
|
_stateObj: {
|
||||||
type: Object,
|
type: Object,
|
||||||
computed: '_computeStateObj(hass.states, config.entity)'
|
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…
x
Reference in New Issue
Block a user