mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 00:27:49 +01:00
Add select sound mode card feature for supported media players (#51282)
* Add sound mode card feature for supported media players * Show label * Use shouldUpdate
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
import "../../../components/ha-list-item";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import {
|
||||
MediaPlayerEntityFeature,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { hasConfigChanged } from "../common/has-changed";
|
||||
import type { LovelaceCardFeature } from "../types";
|
||||
import { cardFeatureStyles } from "./common/card-feature-styles";
|
||||
import type {
|
||||
LovelaceCardFeatureContext,
|
||||
MediaPlayerSoundModeCardFeatureConfig,
|
||||
} from "./types";
|
||||
|
||||
export const supportsMediaPlayerSoundModeCardFeature = (
|
||||
hass: HomeAssistant,
|
||||
context: LovelaceCardFeatureContext
|
||||
) => {
|
||||
const stateObj = context.entity_id
|
||||
? hass.states[context.entity_id]
|
||||
: undefined;
|
||||
if (!stateObj) return false;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return (
|
||||
domain === "media_player" &&
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOUND_MODE) &&
|
||||
!!stateObj.attributes.sound_mode_list?.length
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-media-player-sound-mode-card-feature")
|
||||
class HuiMediaPlayerSoundModeCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: MediaPlayerSoundModeCardFeatureConfig;
|
||||
|
||||
@state() private _currentSoundMode?: string;
|
||||
|
||||
private get _stateObj() {
|
||||
if (!this.hass || !this.context || !this.context.entity_id) {
|
||||
return undefined;
|
||||
}
|
||||
return this.hass.states[this.context.entity_id!] as
|
||||
| MediaPlayerEntity
|
||||
| undefined;
|
||||
}
|
||||
|
||||
static getStubConfig(): MediaPlayerSoundModeCardFeatureConfig {
|
||||
return {
|
||||
type: "media-player-sound-mode",
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: MediaPlayerSoundModeCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (
|
||||
(changedProps.has("hass") || changedProps.has("context")) &&
|
||||
this._stateObj
|
||||
) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
const oldStateObj = oldHass?.states[this.context!.entity_id!];
|
||||
if (oldStateObj !== this._stateObj) {
|
||||
this._currentSoundMode = this._stateObj.attributes.sound_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
const entityId = this.context?.entity_id;
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
|
||||
return (
|
||||
changedProps.has("_currentSoundMode") ||
|
||||
changedProps.has("context") ||
|
||||
hasConfigChanged(this, changedProps) ||
|
||||
(changedProps.has("hass") &&
|
||||
(!oldHass ||
|
||||
!entityId ||
|
||||
oldHass.states[entityId] !== this.hass?.states[entityId]))
|
||||
);
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: HaDropdownSelectEvent) {
|
||||
const soundMode = ev.detail.item?.value;
|
||||
const oldSoundMode = this._stateObj!.attributes.sound_mode;
|
||||
|
||||
if (soundMode === oldSoundMode || !soundMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._currentSoundMode = soundMode;
|
||||
|
||||
try {
|
||||
await this.hass!.callService("media_player", "select_sound_mode", {
|
||||
entity_id: this._stateObj!.entity_id,
|
||||
sound_mode: soundMode,
|
||||
});
|
||||
} catch (_err) {
|
||||
this._currentSoundMode = oldSoundMode;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.context ||
|
||||
!this._stateObj ||
|
||||
!supportsMediaPlayerSoundModeCardFeature(this.hass, this.context)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const options = this._stateObj.attributes.sound_mode_list!.map(
|
||||
(soundMode) => ({
|
||||
value: soundMode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this._stateObj!,
|
||||
"sound_mode",
|
||||
soundMode
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-control-select-menu
|
||||
.hass=${this.hass}
|
||||
show-arrow
|
||||
.label=${this.hass.localize("ui.card.media_player.sound_mode")}
|
||||
.value=${this._currentSoundMode}
|
||||
.disabled=${this._stateObj.state === UNAVAILABLE}
|
||||
.options=${options}
|
||||
@wa-select=${this._valueChanged}
|
||||
>
|
||||
</ha-control-select-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return cardFeatureStyles;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-media-player-sound-mode-card-feature": HuiMediaPlayerSoundModeCardFeature;
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,10 @@ export interface MediaPlayerVolumeButtonsCardFeatureConfig {
|
||||
step?: number;
|
||||
}
|
||||
|
||||
export interface MediaPlayerSoundModeCardFeatureConfig {
|
||||
type: "media-player-sound-mode";
|
||||
}
|
||||
|
||||
export interface FanDirectionCardFeatureConfig {
|
||||
type: "fan-direction";
|
||||
}
|
||||
@@ -281,6 +285,7 @@ export type LovelaceCardFeatureConfig =
|
||||
| LockCommandsCardFeatureConfig
|
||||
| LockOpenDoorCardFeatureConfig
|
||||
| MediaPlayerPlaybackCardFeatureConfig
|
||||
| MediaPlayerSoundModeCardFeatureConfig
|
||||
| MediaPlayerVolumeButtonsCardFeatureConfig
|
||||
| MediaPlayerVolumeSliderCardFeatureConfig
|
||||
| NumericInputCardFeatureConfig
|
||||
|
||||
@@ -26,6 +26,7 @@ import "../card-features/hui-light-color-favorites-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-playback-card-feature";
|
||||
import "../card-features/hui-media-player-sound-mode-card-feature";
|
||||
import "../card-features/hui-media-player-volume-buttons-card-feature";
|
||||
import "../card-features/hui-media-player-volume-slider-card-feature";
|
||||
import "../card-features/hui-numeric-input-card-feature";
|
||||
@@ -80,6 +81,7 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
|
||||
"lock-commands",
|
||||
"lock-open-door",
|
||||
"media-player-playback",
|
||||
"media-player-sound-mode",
|
||||
"media-player-volume-buttons",
|
||||
"media-player-volume-slider",
|
||||
"numeric-input",
|
||||
|
||||
@@ -53,6 +53,7 @@ import { supportsLightColorTempCardFeature } from "../../card-features/hui-light
|
||||
import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature";
|
||||
import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature";
|
||||
import { supportsMediaPlayerPlaybackCardFeature } from "../../card-features/hui-media-player-playback-card-feature";
|
||||
import { supportsMediaPlayerSoundModeCardFeature } from "../../card-features/hui-media-player-sound-mode-card-feature";
|
||||
import { supportsMediaPlayerVolumeButtonsCardFeature } from "../../card-features/hui-media-player-volume-buttons-card-feature";
|
||||
import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature";
|
||||
import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature";
|
||||
@@ -112,6 +113,7 @@ const UI_FEATURE_TYPES = [
|
||||
"lock-commands",
|
||||
"lock-open-door",
|
||||
"media-player-playback",
|
||||
"media-player-sound-mode",
|
||||
"media-player-volume-buttons",
|
||||
"media-player-volume-slider",
|
||||
"numeric-input",
|
||||
@@ -191,6 +193,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
|
||||
"lock-commands": supportsLockCommandsCardFeature,
|
||||
"lock-open-door": supportsLockOpenDoorCardFeature,
|
||||
"media-player-playback": supportsMediaPlayerPlaybackCardFeature,
|
||||
"media-player-sound-mode": supportsMediaPlayerSoundModeCardFeature,
|
||||
"media-player-volume-buttons": supportsMediaPlayerVolumeButtonsCardFeature,
|
||||
"media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature,
|
||||
"numeric-input": supportsNumericInputCardFeature,
|
||||
|
||||
@@ -9713,6 +9713,9 @@
|
||||
"media-player-playback": {
|
||||
"label": "Media player playback controls"
|
||||
},
|
||||
"media-player-sound-mode": {
|
||||
"label": "Media player sound mode"
|
||||
},
|
||||
"media-player-volume-buttons": {
|
||||
"label": "Media player volume buttons",
|
||||
"step": "Step size"
|
||||
|
||||
Reference in New Issue
Block a user