From 54b57e62229fe42906d5becc79e1ee40ed805be0 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Sun, 29 Mar 2020 06:30:52 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20add=20volume=20slider=20to=20media?= =?UTF-8?q?=5Fplayer=20row=20(#4743)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ add volume slider to media_player row * add more controls * flex slider * override width * volume buttons when narrow * address comments * Updates for rebase * attempt to use debounce. not working * remove log * fix observer * address some review comments * unobserve * address comments --- .../hui-media-player-entity-row.ts | 222 +++++++++++++++--- 1 file changed, 191 insertions(+), 31 deletions(-) diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts index 721648a11d..755b8b706a 100644 --- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts @@ -21,14 +21,32 @@ import { SUPPORTS_PLAY, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, + SUPPORT_TURN_ON, + SUPPORT_TURN_OFF, + SUPPORT_PREVIOUS_TRACK, + SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_MUTE, + SUPPORT_VOLUME_BUTTONS, } from "../../../data/media-player"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { computeRTLDirection } from "../../../common/util/compute_rtl"; +import { debounce } from "../../../common/util/debounce"; @customElement("hui-media-player-entity-row") class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { @property() public hass?: HomeAssistant; - @property() private _config?: EntityConfig; + @property() private _narrow?: boolean = false; + @property() private _veryNarrow?: boolean = false; + private _resizeObserver?: ResizeObserver; + private _debouncedResizeListener = debounce( + () => { + this._narrow = (this.parentElement?.clientWidth || 0) < 350; + this._veryNarrow = (this.parentElement?.clientWidth || 0) < 300; + }, + 250, + false + ); public setConfig(config: EntityConfig): void { if (!config || !config.entity) { @@ -38,6 +56,21 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { this._config = config; } + public connectedCallback(): void { + super.connectedCallback(); + if (!this._resizeObserver) { + this._attachObserver(); + } + } + + public disconnectedCallback(): void { + this._resizeObserver?.unobserve(this); + } + + protected firstUpdated(): void { + this._attachObserver(); + } + protected shouldUpdate(changedProps: PropertyValues): boolean { return hasConfigOrEntityChanged(this, changedProps); } @@ -68,46 +101,128 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { .secondaryText=${this._computeMediaTitle(stateObj)} > ${stateObj.state === "off" || stateObj.state === "idle" + ? supportsFeature(stateObj, SUPPORT_TURN_ON) + : supportsFeature(stateObj, SUPPORT_TURN_OFF) ? html` -
- ${this.hass!.localize(`state.media_player.${stateObj.state}`) || - this.hass!.localize(`state.default.${stateObj.state}`) || - stateObj.state} -
+ ` - : html` -
- ${stateObj.state !== "playing" && - !supportsFeature(stateObj, SUPPORTS_PLAY) - ? "" - : html` - - `} - ${supportsFeature(stateObj, SUPPORT_NEXT_TRACK) - ? html` - - ` - : ""} -
- `} + : ""} +
+
+ ${supportsFeature(stateObj, SUPPORT_VOLUME_MUTE) + ? html` + + ` + : ""} + ${!this._narrow && supportsFeature(stateObj, SUPPORT_VOLUME_SET) + ? html` + + ` + : !this._veryNarrow && + supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS) + ? html` + + + ` + : ""} +
+
+ ${!this._veryNarrow && + supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK) + ? html` + + ` + : ""} + ${stateObj.state !== "playing" && + !supportsFeature(stateObj, SUPPORTS_PLAY) + ? "" + : html` + + `} + ${supportsFeature(stateObj, SUPPORT_NEXT_TRACK) + ? html` + + ` + : ""} +
+
`; } static get styles(): CSSResult { return css` + :host { + display: block; + } + .flex { + display: flex; + align-items: center; + padding-left: 48px; + justify-content: space-between; + } + .volume { + display: flex; + flex-grow: 2; + flex-shrink: 2; + } .controls { white-space: nowrap; } + ha-slider { + flex-grow: 2; + flex-shrink: 2; + width: 100%; + } `; } + private _attachObserver(): void { + if (typeof ResizeObserver !== "function") { + import("resize-observer").then((modules) => { + modules.install(); + this._attachObserver(); + }); + return; + } + + this._resizeObserver = new ResizeObserver(() => + this._debouncedResizeListener() + ); + + this._resizeObserver.observe(this); + } + private _computeControlIcon(stateObj: HassEntity): string { if (stateObj.state !== "playing") { return "hass:play"; @@ -143,19 +258,64 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow { return prefix && suffix ? `${prefix}: ${suffix}` : prefix || suffix || ""; } - private _playPause(ev: MouseEvent): void { - ev.stopPropagation(); + private _togglePower(): void { + const stateObj = this.hass!.states[this._config!.entity]; + + this.hass!.callService( + "media_player", + stateObj.state === "off" || stateObj.state === "idle" + ? "turn_on" + : "turn_off", + { + entity_id: this._config!.entity, + } + ); + } + + private _playPause(): void { this.hass!.callService("media_player", "media_play_pause", { entity_id: this._config!.entity, }); } - private _nextTrack(ev: MouseEvent): void { - ev.stopPropagation(); + private _previousTrack(): void { + this.hass!.callService("media_player", "media_previous_track", { + entity_id: this._config!.entity, + }); + } + + private _nextTrack(): void { this.hass!.callService("media_player", "media_next_track", { entity_id: this._config!.entity, }); } + + private _toggleMute() { + this.hass!.callService("media_player", "volume_mute", { + entity_id: this._config!.entity, + is_volume_muted: !this.hass!.states[this._config!.entity].attributes + .is_volume_muted, + }); + } + + private _volumeDown() { + this.hass!.callService("media_player", "volume_down", { + entity_id: this._config!.entity, + }); + } + + private _volumeUp() { + this.hass!.callService("media_player", "volume_up", { + entity_id: this._config!.entity, + }); + } + + private _selectedValueChanged(ev): void { + this.hass!.callService("media_player", "volume_set", { + entity_id: this._config!.entity, + volume_level: ev.target.value / 100, + }); + } } declare global {