diff --git a/gallery/src/pages/lovelace/tile-card.ts b/gallery/src/pages/lovelace/tile-card.ts
index c5289165ea33..dc0743a81318 100644
--- a/gallery/src/pages/lovelace/tile-card.ts
+++ b/gallery/src/pages/lovelace/tile-card.ts
@@ -28,6 +28,9 @@ const ENTITIES = [
device_class: "lock",
supported_features: LockEntityFeature.OPEN,
}),
+ getEntity("media_player", "living_room", "playing", {
+ friendly_name: "Living room speaker",
+ }),
getEntity("climate", "thermostat", "heat", {
current_temperature: 73,
min_temp: 45,
@@ -197,6 +200,15 @@ const CONFIGS = [
- type: "lock-open-door"
`,
},
+ {
+ heading: "Media player volume feature",
+ config: `
+- type: tile
+ entity: media_player.living_room
+ features:
+ - type: "media-player-volume"
+ `,
+ },
{
heading: "Vacuum commands feature",
config: `
diff --git a/src/panels/lovelace/card-features/hui-media-player-volume-card-feature.ts b/src/panels/lovelace/card-features/hui-media-player-volume-card-feature.ts
new file mode 100644
index 000000000000..6f68e72cc0fd
--- /dev/null
+++ b/src/panels/lovelace/card-features/hui-media-player-volume-card-feature.ts
@@ -0,0 +1,90 @@
+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 { MediaPlayerCardFeatureConfig } from "./types";
+
+export const supportsMediaPlayerVolumeCardFeature = (stateObj: HassEntity) => {
+ const domain = computeDomain(stateObj.entity_id);
+ return domain === "media_player";
+};
+
+@customElement("hui-media-player-volume-card-feature")
+class HuiMediaPlayerVolumeCardFeature
+ extends LitElement
+ implements LovelaceCardFeature
+{
+ @property({ attribute: false }) public hass?: HomeAssistant;
+
+ @property({ attribute: false }) public stateObj?: HassEntity;
+
+ @state() private _config?: MediaPlayerCardFeatureConfig;
+
+ static getStubConfig(): MediaPlayerCardFeatureConfig {
+ return {
+ type: "media-player-volume",
+ };
+ }
+
+ public setConfig(config: MediaPlayerCardFeatureConfig): void {
+ if (!config) {
+ throw new Error("Invalid configuration");
+ }
+ this._config = config;
+ }
+
+ protected render() {
+ if (
+ !this._config ||
+ !this.hass ||
+ !this.stateObj ||
+ !supportsMediaPlayerVolumeCardFeature(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-card-feature": HuiMediaPlayerVolumeCardFeature;
+ }
+}
diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts
index ec60914086f8..17251bc22c79 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 MediaPlayerCardFeatureConfig {
+ type: "media-player-volume";
+}
+
export interface FanPresetModesCardFeatureConfig {
type: "fan-preset-modes";
style?: "dropdown" | "icons";
@@ -161,6 +165,7 @@ export type LovelaceCardFeatureConfig =
| LightColorTempCardFeatureConfig
| LockCommandsCardFeatureConfig
| LockOpenDoorCardFeatureConfig
+ | MediaPlayerCardFeatureConfig
| 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..f8e3e49abdfa 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-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",
"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..01980ab7eb5e 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 { supportsMediaPlayerVolumeCardFeature } from "../../card-features/hui-media-player-volume-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",
"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": supportsMediaPlayerVolumeCardFeature,
"numeric-input": supportsNumericInputCardFeature,
"select-options": supportsSelectOptionsCardFeature,
"target-humidity": supportsTargetHumidityCardFeature,
diff --git a/src/translations/en.json b/src/translations/en.json
index e8bfe80a397a..416e294c1849 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -6597,6 +6597,9 @@
"lock-open-door": {
"label": "Lock open door"
},
+ "media-player-volume": {
+ "label": "Media player volume"
+ },
"vacuum-commands": {
"label": "Vacuum commands",
"commands": "Commands",