Always show "off" button if supported by player (#7389)

This commit is contained in:
Philip Allgaier 2020-11-11 14:00:53 +01:00 committed by GitHub
parent e84bef44b7
commit 6ace8307d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 318 additions and 231 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -6,7 +6,9 @@ export const createMediaPlayerEntities = () => [
media_content_type: "music", media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
supported_features: 64063, // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 195135,
entity_picture: "/images/album_cover_2.jpg", entity_picture: "/images/album_cover_2.jpg",
media_duration: 300, media_duration: 300,
media_position: 50, media_position: 50,
@ -14,12 +16,15 @@ export const createMediaPlayerEntities = () => [
// 23 seconds in // 23 seconds in
new Date().getTime() - 23000 new Date().getTime() - 23000
).toISOString(), ).toISOString(),
volume_level: 0.5,
}), }),
getEntity("media_player", "music_playing", "playing", { getEntity("media_player", "music_playing", "playing", {
friendly_name: "Playing The Music", friendly_name: "Playing The Music",
media_content_type: "music", media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063, supported_features: 64063,
entity_picture: "/images/album_cover.jpg", entity_picture: "/images/album_cover.jpg",
media_duration: 300, media_duration: 300,
@ -28,6 +33,7 @@ export const createMediaPlayerEntities = () => [
// 23 seconds in // 23 seconds in
new Date().getTime() - 23000 new Date().getTime() - 23000
).toISOString(), ).toISOString(),
volume_level: 0.5,
}), }),
getEntity("media_player", "stream_playing", "playing", { getEntity("media_player", "stream_playing", "playing", {
friendly_name: "Playing the Stream", friendly_name: "Playing the Stream",
@ -35,50 +41,125 @@ export const createMediaPlayerEntities = () => [
media_title: "Epic sax guy 10 hours", media_title: "Epic sax guy 10 hours",
app_name: "YouTube", app_name: "YouTube",
entity_picture: "/images/frenck.jpg", entity_picture: "/images/frenck.jpg",
supported_features: 33, // Pause + Next Track + Play + Browse Media
supported_features: 147489,
}), }),
getEntity("media_player", "living_room", "playing", { getEntity("media_player", "stream_paused", "paused", {
friendly_name: "Pause, No skip, tvshow", friendly_name: "Paused the Stream",
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Next Track + Play
supported_features: 16417,
}),
getEntity("media_player", "stream_playing_previous", "playing", {
friendly_name: 'Playing the Stream (with "previous" support)',
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Previous Track + Play
supported_features: 16401,
}),
getEntity("media_player", "tv_playing", "playing", {
friendly_name: "Playing non-skip TV Show",
media_content_type: "tvshow", media_content_type: "tvshow",
media_title: "Chapter 1", media_title: "Chapter 1",
media_series_title: "House of Cards", media_series_title: "House of Cards",
app_name: "Netflix", app_name: "Netflix",
entity_picture: "/images/netflix.jpg", entity_picture: "/images/netflix.jpg",
// Pause
supported_features: 1, supported_features: 1,
}), }),
getEntity("media_player", "sonos_idle", "idle", { getEntity("media_player", "sonos_idle", "idle", {
friendly_name: "Sonos Idle", friendly_name: "Sonos Idle",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063, supported_features: 64063,
volume_level: 0.33,
is_volume_muted: true,
}), }),
getEntity("media_player", "theater", "off", { getEntity("media_player", "idle_browse_media", "idle", {
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
// Select Source + Play + Shuffle Set + Browse Media
supported_features: 182839,
volume_level: 0.79,
}),
getEntity("media_player", "theater_off", "off", {
friendly_name: "TV Off", friendly_name: "TV Off",
// On + Off + Play + Next + Pause
supported_features: 16801,
}),
getEntity("media_player", "theater_on", "on", {
friendly_name: "TV On",
// On + Off + Play + Next + Pause
supported_features: 16801,
}),
getEntity("media_player", "theater_off_static", "off", {
friendly_name: "TV Off (cannot be switched on)",
// Off + Next + Pause
supported_features: 289,
}),
getEntity("media_player", "theater_on_static", "on", {
friendly_name: "TV On (cannot be switched off)",
// On + Next + Pause
supported_features: 161, supported_features: 161,
}), }),
getEntity("media_player", "android_cast", "playing", { getEntity("media_player", "android_cast", "playing", {
friendly_name: "Casting App", friendly_name: "Casting App (no supported features)",
media_title: "Android Screen Casting", media_title: "Android Screen Casting",
app_name: "Screen Mirroring", app_name: "Screen Mirroring",
// supported_features: 21437, }),
getEntity("media_player", "image_display", "playing", {
friendly_name: "Digital Picture Frame",
media_content_type: "image",
media_title: "Famous Painting",
media_artist: "Famous Artist",
entity_picture: "/images/sunflowers.jpg",
// On + Off + Browse Media
supported_features: 131456,
}), }),
getEntity("media_player", "unavailable", "unavailable", { getEntity("media_player", "unavailable", "unavailable", {
friendly_name: "Player Unavailable", friendly_name: "Player Unavailable",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437, supported_features: 21437,
}), }),
getEntity("media_player", "unknown", "unknown", { getEntity("media_player", "unknown", "unknown", {
friendly_name: "Player Unknown", friendly_name: "Player Unknown",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437, supported_features: 21437,
}), }),
getEntity("media_player", "playing", "playing", {
friendly_name: "Player Playing (no Pause support)",
// Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21436,
volume_level: 1,
}),
getEntity("media_player", "idle", "idle", {
friendly_name: "Player Idle",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
volume_level: 0,
}),
getEntity("media_player", "receiver_on", "on", { getEntity("media_player", "receiver_on", "on", {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
volume_level: 0.63, volume_level: 0.63,
is_volume_muted: false, is_volume_muted: false,
source: "TV", source: "TV",
friendly_name: "Receiver", friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364, supported_features: 84364,
}), }),
getEntity("media_player", "receiver_off", "off", { getEntity("media_player", "receiver_off", "off", {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"], source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
friendly_name: "Receiver", friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364, supported_features: 84364,
}), }),
]; ];

View File

@ -7,40 +7,61 @@ import { createMediaPlayerEntities } from "../data/media_players";
const CONFIGS = [ const CONFIGS = [
{ {
heading: "Paused music", heading: "Paused Music",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.music_paused entity: media_player.music_paused
`, `,
}, },
{ {
heading: "Playing music", heading: "Playing Music",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.music_playing entity: media_player.music_playing
`, `,
}, },
{ {
heading: "Playing stream", heading: "Playing Stream",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.stream_playing entity: media_player.stream_playing
`, `,
}, },
{ {
heading: "Pause, No skip, tvshow", heading: "Paused Stream",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.living_room entity: media_player.stream_paused
`, `,
}, },
{ {
heading: "Screen casting", heading: 'Playing Stream (with "previous" support)',
config: `
- type: media-control
entity: media_player.stream_playing_previous
`,
},
{
heading: "Playing non-skip TV Show",
config: `
- type: media-control
entity: media_player.tv_playing
`,
},
{
heading: "Screen Casting",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.android_cast entity: media_player.android_cast
`, `,
}, },
{
heading: "Digital Picture Frame",
config: `
- type: media-control
entity: media_player.image_display
`,
},
{ {
heading: "Sonos Idle", heading: "Sonos Idle",
config: ` config: `
@ -48,11 +69,53 @@ const CONFIGS = [
entity: media_player.sonos_idle entity: media_player.sonos_idle
`, `,
}, },
{
heading: "Idle waiting for Browse Media",
config: `
- type: media-control
entity: media_player.idle_browse_media
`,
},
{ {
heading: "Player Off", heading: "Player Off",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.theater entity: media_player.theater_off
`,
},
{
heading: "Player On",
config: `
- type: media-control
entity: media_player.theater_on
`,
},
{
heading: "Player Off (cannot be switched on)",
config: `
- type: media-control
entity: media_player.theater_off_static
`,
},
{
heading: "Player On (cannot be switched off)",
config: `
- type: media-control
entity: media_player.theater_on_static
`,
},
{
heading: "Player Idle",
config: `
- type: media-control
entity: media_player.idle
`,
},
{
heading: "Player Playing",
config: `
- type: media-control
entity: media_player.playing
`, `,
}, },
{ {
@ -70,14 +133,14 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "Receiver On", heading: "Receiver On (selectable sources)",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.receiver_on entity: media_player.receiver_on
`, `,
}, },
{ {
heading: "Receiver Off", heading: "Receiver Off (selectable sources)",
config: ` config: `
- type: media-control - type: media-control
entity: media_player.receiver_off entity: media_player.receiver_off

View File

@ -12,23 +12,45 @@ const CONFIGS = [
- type: entities - type: entities
entities: entities:
- entity: media_player.music_paused - entity: media_player.music_paused
name: Paused music name: Paused Music
- entity: media_player.music_playing - entity: media_player.music_playing
name: Playing music name: Playing Music
- entity: media_player.stream_playing - entity: media_player.stream_playing
name: Paused, no play name: Playing Stream
- entity: media_player.living_room - entity: media_player.stream_paused
name: Pause, No skip, tvshow name: Paused Stream
- entity: media_player.stream_playing_previous
name: Playing Stream (with "previous" support)
- entity: media_player.tv_playing
name: Playing non-skip TV Show
- entity: media_player.android_cast - entity: media_player.android_cast
name: Screen casting name: Screen casting
- entity: media_player.image_display
name: Digital Picture Frame
- entity: media_player.sonos_idle - entity: media_player.sonos_idle
name: Chromcast Idle name: Sonos Idle
- entity: media_player.theater - entity: media_player.idle_browse_media
name: Idle waiting for Browse Media
- entity: media_player.theater_off
name: Player Off name: Player Off
- entity: media_player.theater_on
name: Player On
- entity: media_player.theater_off_static
name: Player Off (cannot be switched on)
- entity: media_player.theater_on_static
name: Player On (cannot be switched off)
- entity: media_player.idle
name: Player Idle
- entity: media_player.playing
name: Player Playing
- entity: media_player.unavailable - entity: media_player.unavailable
name: Player Unavailable name: Player Unavailable
- entity: media_player.unknown - entity: media_player.unknown
name: Player Unknown name: Player Unknown
- entity: media_player.receiver_on
name: Receiver On (selectable sources)
- entity: media_player.receiver_off
name: Receiver Off (selectable sources)
`, `,
}, },
]; ];

View File

@ -18,6 +18,8 @@ import {
} from "@mdi/js"; } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE_STATES } from "./entity";
import { supportsFeature } from "../common/entity/supports-feature";
export const SUPPORT_PAUSE = 1; export const SUPPORT_PAUSE = 1;
export const SUPPORT_SEEK = 2; export const SUPPORT_SEEK = 2;
@ -31,7 +33,7 @@ export const SUPPORT_PLAY_MEDIA = 512;
export const SUPPORT_VOLUME_BUTTONS = 1024; export const SUPPORT_VOLUME_BUTTONS = 1024;
export const SUPPORT_SELECT_SOURCE = 2048; export const SUPPORT_SELECT_SOURCE = 2048;
export const SUPPORT_STOP = 4096; export const SUPPORT_STOP = 4096;
export const SUPPORTS_PLAY = 16384; export const SUPPORT_PLAY = 16384;
export const SUPPORT_SELECT_SOUND_MODE = 65536; export const SUPPORT_SELECT_SOUND_MODE = 65536;
export const SUPPORT_BROWSE_MEDIA = 131072; export const SUPPORT_BROWSE_MEDIA = 131072;
export const CONTRAST_RATIO = 4.5; export const CONTRAST_RATIO = 4.5;
@ -166,6 +168,7 @@ export const computeMediaDescription = (stateObj: HassEntity): string => {
switch (stateObj.attributes.media_content_type) { switch (stateObj.attributes.media_content_type) {
case "music": case "music":
case "image":
secondaryTitle = stateObj.attributes.media_artist; secondaryTitle = stateObj.attributes.media_artist;
break; break;
case "playlist": case "playlist":
@ -187,3 +190,85 @@ export const computeMediaDescription = (stateObj: HassEntity): string => {
return secondaryTitle; return secondaryTitle;
}; };
export const computeMediaControls = (
stateObj: HassEntity
): ControlButton[] | undefined => {
if (!stateObj) {
return undefined;
}
const state = stateObj.state;
if (UNAVAILABLE_STATES.includes(state)) {
return undefined;
}
if (state === "off") {
return supportsFeature(stateObj, SUPPORT_TURN_ON)
? [
{
icon: "hass:power",
action: "turn_on",
},
]
: undefined;
}
const buttons: ControlButton[] = [];
if (supportsFeature(stateObj, SUPPORT_TURN_OFF)) {
buttons.push({
icon: "hass:power",
action: "turn_off",
});
}
if (
(state === "playing" || state === "paused") &&
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
) {
buttons.push({
icon: "hass:skip-previous",
action: "media_previous_track",
});
}
if (
(state === "playing" &&
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
supportsFeature(stateObj, SUPPORT_STOP))) ||
((state === "paused" || state === "idle") &&
supportsFeature(stateObj, SUPPORT_PLAY)) ||
(state === "on" &&
(supportsFeature(stateObj, SUPPORT_PLAY) ||
supportsFeature(stateObj, SUPPORT_PAUSE)))
) {
buttons.push({
icon:
state === "on"
? "hass:play-pause"
: state !== "playing"
? "hass:play"
: supportsFeature(stateObj, SUPPORT_PAUSE)
? "hass:pause"
: "hass:stop",
action:
state === "playing" && !supportsFeature(stateObj, SUPPORT_PAUSE)
? "media_stop"
: "media_play_pause",
});
}
if (
(state === "playing" || state === "paused") &&
supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
) {
buttons.push({
icon: "hass:skip-next",
action: "media_next_track",
});
}
return buttons.length > 0 ? buttons : undefined;
};

View File

@ -25,19 +25,12 @@ import "../../../components/ha-svg-icon";
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog"; import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
import { import {
ControlButton, computeMediaControls,
MediaPickedEvent, MediaPickedEvent,
SUPPORTS_PLAY,
SUPPORT_BROWSE_MEDIA, SUPPORT_BROWSE_MEDIA,
SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE,
SUPPORT_PLAY_MEDIA, SUPPORT_PLAY_MEDIA,
SUPPORT_PREVIOUS_TRACK,
SUPPORT_SELECT_SOUND_MODE, SUPPORT_SELECT_SOUND_MODE,
SUPPORT_SELECT_SOURCE, SUPPORT_SELECT_SOURCE,
SUPPORT_STOP,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON,
SUPPORT_VOLUME_BUTTONS, SUPPORT_VOLUME_BUTTONS,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET, SUPPORT_VOLUME_SET,
@ -57,8 +50,8 @@ class MoreInfoMediaPlayer extends LitElement {
return html``; return html``;
} }
const controls = this._getControls();
const stateObj = this.stateObj; const stateObj = this.stateObj;
const controls = computeMediaControls(stateObj);
return html` return html`
${!controls ${!controls
@ -254,89 +247,6 @@ class MoreInfoMediaPlayer extends LitElement {
`; `;
} }
private _getControls(): ControlButton[] | undefined {
const stateObj = this.stateObj;
if (!stateObj) {
return undefined;
}
const state = stateObj.state;
if (UNAVAILABLE_STATES.includes(state)) {
return undefined;
}
if (state === "off") {
return supportsFeature(stateObj, SUPPORT_TURN_ON)
? [
{
icon: "hass:power",
action: "turn_on",
},
]
: undefined;
}
if (state === "idle") {
return supportsFeature(stateObj, SUPPORTS_PLAY)
? [
{
icon: "hass:play",
action: "media_play",
},
]
: undefined;
}
const buttons: ControlButton[] = [];
if (supportsFeature(stateObj, SUPPORT_TURN_OFF)) {
buttons.push({
icon: "hass:power",
action: "turn_off",
});
}
if (supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)) {
buttons.push({
icon: "hass:skip-previous",
action: "media_previous_track",
});
}
if (
(state === "playing" &&
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
supportsFeature(stateObj, SUPPORT_STOP))) ||
(state === "paused" && supportsFeature(stateObj, SUPPORTS_PLAY)) ||
(state === "on" &&
supportsFeature(stateObj, SUPPORTS_PLAY) ||
supportsFeature(stateObj, SUPPORT_PAUSE))
) {
buttons.push({
icon:
state === "on"
? "hass:play-pause"
: state !== "playing"
? "hass:play"
: supportsFeature(stateObj, SUPPORT_PAUSE)
? "hass:pause"
: "hass:stop",
action: state === "playing" && !supportsFeature(stateObj, SUPPORT_PAUSE) ? "media_stop" : "media_play_pause",
});
}
if (supportsFeature(stateObj, SUPPORT_NEXT_TRACK)) {
buttons.push({
icon: "hass:skip-next",
action: "media_next_track",
});
}
return buttons.length > 0 ? buttons : undefined;
}
private _handleClick(e: MouseEvent): void { private _handleClick(e: MouseEvent): void {
this.hass!.callService( this.hass!.callService(
"media_player", "media_player",

View File

@ -32,18 +32,12 @@ import { showMediaBrowserDialog } from "../../../components/media-player/show-me
import { UNAVAILABLE_STATES } from "../../../data/entity"; import { UNAVAILABLE_STATES } from "../../../data/entity";
import { import {
computeMediaDescription, computeMediaDescription,
computeMediaControls,
CONTRAST_RATIO, CONTRAST_RATIO,
ControlButton,
getCurrentProgress, getCurrentProgress,
MediaPickedEvent, MediaPickedEvent,
SUPPORTS_PLAY,
SUPPORT_BROWSE_MEDIA, SUPPORT_BROWSE_MEDIA,
SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK,
SUPPORT_SEEK, SUPPORT_SEEK,
SUPPORT_STOP,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, SUPPORT_TURN_ON,
} from "../../../data/media-player"; } from "../../../data/media-player";
import type { HomeAssistant, MediaEntity } from "../../../types"; import type { HomeAssistant, MediaEntity } from "../../../types";
@ -297,7 +291,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
UNAVAILABLE_STATES.includes(state) || UNAVAILABLE_STATES.includes(state) ||
(state === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON)); (state === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON));
const hasNoImage = !this._image; const hasNoImage = !this._image;
const controls = this._getControls(); const controls = computeMediaControls(stateObj);
const showControls = const showControls =
controls && controls &&
(!this._veryNarrow || isOffState || state === "idle" || state === "on"); (!this._veryNarrow || isOffState || state === "idle" || state === "on");
@ -361,9 +355,9 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
></ha-icon-button> ></ha-icon-button>
</div> </div>
</div> </div>
${isUnavailable ${!isUnavailable &&
? "" (mediaDescription || stateObj.attributes.media_title || showControls)
: html` ? html`
<div <div
class="title-controls" class="title-controls"
style=${styleMap({ style=${styleMap({
@ -436,7 +430,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
@click=${this._handleSeek} @click=${this._handleSeek}
></paper-progress> ></paper-progress>
`} `}
`} `
: ""}
</div> </div>
</ha-card> </ha-card>
`; `;
@ -518,88 +513,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
} }
} }
private _getControls(): ControlButton[] | undefined {
const stateObj = this._stateObj;
if (!stateObj) {
return undefined;
}
const state = stateObj.state;
if (UNAVAILABLE_STATES.includes(state)) {
return undefined;
}
if (state === "off") {
return supportsFeature(stateObj, SUPPORT_TURN_ON)
? [
{
icon: "hass:power",
action: "turn_on",
},
]
: undefined;
}
if (state === "on") {
return supportsFeature(stateObj, SUPPORT_TURN_OFF)
? [
{
icon: "hass:power",
action: "turn_off",
},
]
: undefined;
}
if (state === "idle") {
return supportsFeature(stateObj, SUPPORTS_PLAY)
? [
{
icon: "hass:play",
action: "media_play",
},
]
: undefined;
}
const buttons: ControlButton[] = [];
if (supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)) {
buttons.push({
icon: "hass:skip-previous",
action: "media_previous_track",
});
}
if (
(state === "playing" &&
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
supportsFeature(stateObj, SUPPORT_STOP))) ||
(state === "paused" && supportsFeature(stateObj, SUPPORTS_PLAY))
) {
buttons.push({
icon:
state !== "playing"
? "hass:play"
: supportsFeature(stateObj, SUPPORT_PAUSE)
? "hass:pause"
: "hass:stop",
action: "media_play_pause",
});
}
if (supportsFeature(stateObj, SUPPORT_NEXT_TRACK)) {
buttons.push({
icon: "hass:skip-next",
action: "media_next_track",
});
}
return buttons.length > 0 ? buttons : undefined;
}
private get _image() { private get _image() {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return undefined; return undefined;
@ -866,6 +779,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
ha-icon-button[action="media_play"], ha-icon-button[action="media_play"],
ha-icon-button[action="media_play_pause"], ha-icon-button[action="media_play_pause"],
ha-icon-button[action="media_stop"],
ha-icon-button[action="turn_on"], ha-icon-button[action="turn_on"],
ha-icon-button[action="turn_off"] { ha-icon-button[action="turn_off"] {
--mdc-icon-button-size: 56px; --mdc-icon-button-size: 56px;

View File

@ -15,12 +15,13 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl"; import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { debounce } from "../../../common/util/debounce"; import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-slider"; import "../../../components/ha-slider";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity"; import { UNAVAILABLE, UNKNOWN, UNAVAILABLE_STATES } from "../../../data/entity";
import { import {
SUPPORTS_PLAY, SUPPORT_PLAY,
SUPPORT_NEXT_TRACK, SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE, SUPPORT_PAUSE,
SUPPORT_PREVIOUS_TRACK, SUPPORT_PREVIOUS_TRACK,
SUPPORT_STOP,
SUPPORT_TURN_OFF, SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, SUPPORT_TURN_ON,
SUPPORT_VOLUME_BUTTONS, SUPPORT_VOLUME_BUTTONS,
@ -80,6 +81,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
} }
const stateObj = this.hass.states[this._config.entity]; const stateObj = this.hass.states[this._config.entity];
const state = stateObj.state;
if (!stateObj) { if (!stateObj) {
return html` return html`
@ -90,7 +92,9 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
} }
const buttons = html` const buttons = html`
${!this._narrow && supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK) ${!this._narrow &&
state === "playing" &&
supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
? html` ? html`
<ha-icon-button <ha-icon-button
icon="hass:skip-previous" icon="hass:skip-previous"
@ -98,16 +102,22 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
></ha-icon-button> ></ha-icon-button>
` `
: ""} : ""}
${stateObj.state !== "playing" && ${(state === "playing" &&
!supportsFeature(stateObj, SUPPORTS_PLAY) (supportsFeature(stateObj, SUPPORT_PAUSE) ||
? "" supportsFeature(stateObj, SUPPORT_STOP))) ||
: html` ((state === "paused" || state === "idle") &&
supportsFeature(stateObj, SUPPORT_PLAY)) ||
(state === "on" &&
(supportsFeature(stateObj, SUPPORT_PLAY) ||
supportsFeature(stateObj, SUPPORT_PAUSE)))
? html`
<ha-icon-button <ha-icon-button
icon=${this._computeControlIcon(stateObj)} icon=${this._computeControlIcon(stateObj)}
@click=${this._playPause} @click=${this._playPause}
></ha-icon-button> ></ha-icon-button>
`} `
${supportsFeature(stateObj, SUPPORT_NEXT_TRACK) : ""}
${state === "playing" && supportsFeature(stateObj, SUPPORT_NEXT_TRACK)
? html` ? html`
<ha-icon-button <ha-icon-button
icon="hass:skip-next" icon="hass:skip-next"
@ -128,7 +138,8 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
> >
<div class="controls"> <div class="controls">
${supportsFeature(stateObj, SUPPORT_TURN_ON) && ${supportsFeature(stateObj, SUPPORT_TURN_ON) &&
stateObj.state === "off" state === "off" &&
!UNAVAILABLE_STATES.includes(state)
? html` ? html`
<ha-icon-button <ha-icon-button
icon="hass:power" icon="hass:power"
@ -138,8 +149,10 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
: !supportsFeature(stateObj, SUPPORT_VOLUME_SET) && : !supportsFeature(stateObj, SUPPORT_VOLUME_SET) &&
!supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS) !supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS)
? buttons ? buttons
: supportsFeature(stateObj, SUPPORT_TURN_OFF) && : ""}
stateObj.state !== "off" ${supportsFeature(stateObj, SUPPORT_TURN_OFF) &&
state !== "off" &&
!UNAVAILABLE_STATES.includes(state)
? html` ? html`
<ha-icon-button <ha-icon-button
icon="hass:power" icon="hass:power"
@ -151,7 +164,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
</hui-generic-entity-row> </hui-generic-entity-row>
${(supportsFeature(stateObj, SUPPORT_VOLUME_SET) || ${(supportsFeature(stateObj, SUPPORT_VOLUME_SET) ||
supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS)) && supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS)) &&
![UNAVAILABLE, UNKNOWN, "off"].includes(stateObj.state) ![UNAVAILABLE, UNKNOWN, "off"].includes(state)
? html` ? html`
<div class="flex"> <div class="flex">
<div class="volume"> <div class="volume">
@ -220,12 +233,11 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
} }
private _computeControlIcon(stateObj: HassEntity): string { private _computeControlIcon(stateObj: HassEntity): string {
if (stateObj.state !== "playing") { return stateObj.state === "on"
return "hass:play"; ? "hass:play-pause"
} : stateObj.state !== "playing"
? "hass:play"
// eslint-disable-next-line:no-bitwise : supportsFeature(stateObj, SUPPORT_PAUSE)
return supportsFeature(stateObj, SUPPORT_PAUSE)
? "hass:pause" ? "hass:pause"
: "hass:stop"; : "hass:stop";
} }