From da727d3a3abd9aa20eb0d6decbe7f3a4a98dda42 Mon Sep 17 00:00:00 2001 From: Simon Zumbrunnen <7323997+simon-zumbrunnen@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:36:56 +0100 Subject: [PATCH] Added "Media player volume slider" card feature. (#23199) * Added "Media player volume" card feature. * Make sure the feature is not displayed on unsupported players Co-authored-by: Petar Petrov * Renamed to Media player volume *slider* * Missed one rename. --------- Co-authored-by: Simon Zumbrunnen Co-authored-by: Petar Petrov --- gallery/src/pages/lovelace/tile-card.ts | 14 +++ ...media-player-volume-slider-card-feature.ts | 97 +++++++++++++++++++ src/panels/lovelace/card-features/types.ts | 5 + .../create-card-feature-element.ts | 2 + .../hui-card-features-editor.ts | 3 + src/translations/en.json | 3 + 6 files changed, 124 insertions(+) create mode 100644 src/panels/lovelace/card-features/hui-media-player-volume-slider-card-feature.ts diff --git a/gallery/src/pages/lovelace/tile-card.ts b/gallery/src/pages/lovelace/tile-card.ts index c5289165ea33..ecfa9ca0b3cc 100644 --- a/gallery/src/pages/lovelace/tile-card.ts +++ b/gallery/src/pages/lovelace/tile-card.ts @@ -4,6 +4,7 @@ import { customElement, query } from "lit/decorators"; import { CoverEntityFeature } from "../../../../src/data/cover"; import { LightColorMode } from "../../../../src/data/light"; import { LockEntityFeature } from "../../../../src/data/lock"; +import { MediaPlayerEntityFeature } from "../../../../src/data/media-player"; import { VacuumEntityFeature } from "../../../../src/data/vacuum"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; @@ -28,6 +29,10 @@ const ENTITIES = [ device_class: "lock", supported_features: LockEntityFeature.OPEN, }), + getEntity("media_player", "living_room", "playing", { + friendly_name: "Living room speaker", + supported_features: MediaPlayerEntityFeature.VOLUME_SET, + }), getEntity("climate", "thermostat", "heat", { current_temperature: 73, min_temp: 45, @@ -197,6 +202,15 @@ const CONFIGS = [ - type: "lock-open-door" `, }, + { + heading: "Media player volume slider feature", + config: ` +- type: tile + entity: media_player.living_room + features: + - type: "media-player-volume-slider" + `, + }, { heading: "Vacuum commands feature", config: ` diff --git a/src/panels/lovelace/card-features/hui-media-player-volume-slider-card-feature.ts b/src/panels/lovelace/card-features/hui-media-player-volume-slider-card-feature.ts new file mode 100644 index 000000000000..3aeffe2b19d2 --- /dev/null +++ b/src/panels/lovelace/card-features/hui-media-player-volume-slider-card-feature.ts @@ -0,0 +1,97 @@ +import type { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { stateActive } from "../../../common/entity/state_active"; +import "../../../components/ha-control-slider"; +import { isUnavailableState } from "../../../data/entity"; +import type { HomeAssistant } from "../../../types"; +import type { LovelaceCardFeature } from "../types"; +import { cardFeatureStyles } from "./common/card-feature-styles"; +import type { MediaPlayerVolumeSliderCardFeatureConfig } from "./types"; +import { MediaPlayerEntityFeature } from "../../../data/media-player"; +import { supportsFeature } from "../../../common/entity/supports-feature"; + +export const supportsMediaPlayerVolumeSliderCardFeature = ( + stateObj: HassEntity +) => { + const domain = computeDomain(stateObj.entity_id); + return ( + domain === "media_player" && + supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET) + ); +}; + +@customElement("hui-media-player-volume-slider-card-feature") +class HuiMediaPlayerVolumeSliderCardFeature + extends LitElement + implements LovelaceCardFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: MediaPlayerVolumeSliderCardFeatureConfig; + + static getStubConfig(): MediaPlayerVolumeSliderCardFeatureConfig { + return { + type: "media-player-volume-slider", + }; + } + + public setConfig(config: MediaPlayerVolumeSliderCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected render() { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsMediaPlayerVolumeSliderCardFeature(this.stateObj) + ) { + return nothing; + } + + const position = + this.stateObj.attributes.volume_level != null + ? Math.round(this.stateObj.attributes.volume_level * 100) + : undefined; + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent) { + ev.stopPropagation(); + const value = ev.detail.value; + + this.hass!.callService("media_player", "volume_set", { + entity_id: this.stateObj!.entity_id, + volume_level: value / 100, + }); + } + + static get styles() { + return cardFeatureStyles; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-media-player-volume-slider-card-feature": HuiMediaPlayerVolumeSliderCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index ec60914086f8..2a031af808a4 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -34,6 +34,10 @@ export interface LockOpenDoorCardFeatureConfig { type: "lock-open-door"; } +export interface MediaPlayerVolumeSliderCardFeatureConfig { + type: "media-player-volume-slider"; +} + export interface FanPresetModesCardFeatureConfig { type: "fan-preset-modes"; style?: "dropdown" | "icons"; @@ -161,6 +165,7 @@ export type LovelaceCardFeatureConfig = | LightColorTempCardFeatureConfig | LockCommandsCardFeatureConfig | LockOpenDoorCardFeatureConfig + | MediaPlayerVolumeSliderCardFeatureConfig | NumericInputCardFeatureConfig | SelectOptionsCardFeatureConfig | TargetHumidityCardFeatureConfig diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index 872ceb0aba06..6c9799d18b13 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -17,6 +17,7 @@ import "../card-features/hui-light-brightness-card-feature"; import "../card-features/hui-light-color-temp-card-feature"; import "../card-features/hui-lock-commands-card-feature"; import "../card-features/hui-lock-open-door-card-feature"; +import "../card-features/hui-media-player-volume-slider-card-feature"; import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-select-options-card-feature"; import "../card-features/hui-target-temperature-card-feature"; @@ -51,6 +52,7 @@ const TYPES: Set = new Set([ "light-color-temp", "lock-commands", "lock-open-door", + "media-player-volume-slider", "numeric-input", "select-options", "target-humidity", diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index 41564ebe5e57..8018de2344d8 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -38,6 +38,7 @@ import { supportsLightBrightnessCardFeature } from "../../card-features/hui-ligh import { supportsLightColorTempCardFeature } from "../../card-features/hui-light-color-temp-card-feature"; import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature"; import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature"; +import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature"; import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature"; import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature"; @@ -71,6 +72,7 @@ const UI_FEATURE_TYPES = [ "light-color-temp", "lock-commands", "lock-open-door", + "media-player-volume-slider", "numeric-input", "select-options", "target-humidity", @@ -123,6 +125,7 @@ const SUPPORTS_FEATURE_TYPES: Record< "light-color-temp": supportsLightColorTempCardFeature, "lock-commands": supportsLockCommandsCardFeature, "lock-open-door": supportsLockOpenDoorCardFeature, + "media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature, "numeric-input": supportsNumericInputCardFeature, "select-options": supportsSelectOptionsCardFeature, "target-humidity": supportsTargetHumidityCardFeature, diff --git a/src/translations/en.json b/src/translations/en.json index 9f685eca8922..a58f72c290a8 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6599,6 +6599,9 @@ "lock-open-door": { "label": "Lock open door" }, + "media-player-volume-slider": { + "label": "Media player volume slider" + }, "vacuum-commands": { "label": "Vacuum commands", "commands": "Commands",