diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 7a8a944957..087b431479 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -360,3 +360,17 @@ export const cleanupMediaTitle = (title?: string): string | undefined => { const index = title.indexOf("?authSig="); return index > 0 ? title.slice(0, index) : title; }; + +/** + * Set volume of a media player entity. + * @param hass Home Assistant object + * @param entity_id entity ID of media player + * @param volume_level number between 0..1 + * @returns + */ +export const setMediaPlayerVolume = ( + hass: HomeAssistant, + entity_id: string, + volume_level: number +) => + hass.callService("media_player", "volume_set", { entity_id, volume_level }); diff --git a/src/panels/media-browser/browser-media-player.ts b/src/panels/media-browser/browser-media-player.ts index 40b05fdae4..182f944e3f 100644 --- a/src/panels/media-browser/browser-media-player.ts +++ b/src/panels/media-browser/browser-media-player.ts @@ -4,6 +4,7 @@ import { MediaPlayerItem, SUPPORT_PAUSE, SUPPORT_PLAY, + SUPPORT_VOLUME_SET, } from "../../data/media-player"; import { ResolvedMediaSource } from "../../data/media_source"; import { HomeAssistant } from "../../types"; @@ -20,9 +21,11 @@ export class BrowserMediaPlayer { public hass: HomeAssistant, public item: MediaPlayerItem, public resolved: ResolvedMediaSource, + volume: number, private onChange: () => void ) { const player = new Audio(this.resolved.url); + player.volume = volume; player.addEventListener("play", this._handleChange); player.addEventListener("playing", () => { this.buffering = false; @@ -57,6 +60,11 @@ export class BrowserMediaPlayer { this.player.play(); } + public setVolume(volume: number) { + this.player.volume = volume; + this.onChange(); + } + public remove() { this._removed = true; // @ts-ignore @@ -89,8 +97,9 @@ export class BrowserMediaPlayer { base.attributes = { media_title: this.item.title, entity_picture: this.item.thumbnail, + volume_level: this.player.volume, // eslint-disable-next-line no-bitwise - supported_features: SUPPORT_PLAY | SUPPORT_PAUSE, + supported_features: SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_VOLUME_SET, }; if (this.player.duration) { diff --git a/src/panels/media-browser/ha-bar-media-player.ts b/src/panels/media-browser/ha-bar-media-player.ts index 4bc0a6a767..5f0a75c611 100644 --- a/src/panels/media-browser/ha-bar-media-player.ts +++ b/src/panels/media-browser/ha-bar-media-player.ts @@ -9,6 +9,7 @@ import { mdiPlay, mdiPlayPause, mdiStop, + mdiVolumeHigh, } from "@mdi/js"; import { css, @@ -40,10 +41,12 @@ import { getCurrentProgress, MediaPlayerEntity, MediaPlayerItem, + setMediaPlayerVolume, SUPPORT_BROWSE_MEDIA, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_STOP, + SUPPORT_VOLUME_SET, } from "../../data/media-player"; import { ResolvedMediaSource } from "../../data/media_source"; import type { HomeAssistant } from "../../types"; @@ -77,6 +80,8 @@ export class BarMediaPlayer extends LitElement { private _progressInterval?: number; + private _browserPlayerVolume = 0.8; + public connectedCallback(): void { super.connectedCallback(); @@ -124,6 +129,7 @@ export class BarMediaPlayer extends LitElement { this.hass, item, resolved, + this._browserPlayerVolume, () => this.requestUpdate("_browserPlayer") ); this._newMediaExpected = false; @@ -230,7 +236,7 @@ export class BarMediaPlayer extends LitElement { )} .path=${control.icon} action=${control.action} - @click=${this._handleClick} + @click=${this._handleControlClick} > ` @@ -257,6 +263,27 @@ export class BarMediaPlayer extends LitElement { const isBrowser = this.entityId === BROWSER_PLAYER; return html`
+ ${ + stateObj && supportsFeature(stateObj, SUPPORT_VOLUME_SET) + ? html` + + + + + + ` + : "" + } + ${ this.narrow @@ -441,7 +468,7 @@ export class BarMediaPlayer extends LitElement { } } - private _handleClick(e: MouseEvent): void { + private _handleControlClick(e: MouseEvent): void { const action = (e.currentTarget! as HTMLElement).getAttribute("action")!; if (!this._browserPlayer) { @@ -474,6 +501,17 @@ export class BarMediaPlayer extends LitElement { fireEvent(this, "player-picked", { entityId }); } + private async _handleVolumeChange(ev) { + ev.stopPropagation(); + const value = Number(ev.target.value) / 100; + if (this._browserPlayer) { + this._browserPlayerVolume = value; + this._browserPlayer.setVolume(value); + } else { + await setMediaPlayerVolume(this.hass, this.entityId, value); + } + } + static get styles(): CSSResultGroup { return css` :host {