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",