Add area and device context to media player join dialog (#28926)

* Add area and device context to media player join dialog

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add memoization to avoid recomputing display data

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
JLo 2026-01-12 11:08:44 +01:00 committed by GitHub
parent 07d8219136
commit 00907ecd17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,14 +1,15 @@
import { type CSSResultGroup, LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { mdiSpeaker } from "@mdi/js";
import { mdiSpeaker, mdiSpeakerPause, mdiSpeakerPlay } from "@mdi/js";
import memoizeOne from "memoize-one";
import type { HomeAssistant } from "../../types";
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeEntityNameList } from "../../common/entity/compute_entity_name_display";
import { computeRTL } from "../../common/util/compute_rtl";
import { fireEvent } from "../../common/dom/fire_event";
import "../ha-switch";
import "../ha-svg-icon";
import type { MediaPlayerEntity } from "../../data/media-player";
@customElement("ha-media-player-toggle")
class HaMediaPlayerToggle extends LitElement {
@ -20,15 +21,61 @@ class HaMediaPlayerToggle extends LitElement {
@property({ type: Boolean }) public disabled = false;
private _computeDisplayData = memoizeOne(
(
entityId: string,
entities: HomeAssistant["entities"],
devices: HomeAssistant["devices"],
areas: HomeAssistant["areas"],
floors: HomeAssistant["floors"],
isRTL: boolean,
stateObj: HomeAssistant["states"][string]
) => {
const [entityName, deviceName, areaName] = computeEntityNameList(
stateObj,
[{ type: "entity" }, { type: "device" }, { type: "area" }],
entities,
devices,
areas,
floors
);
const primary = entityName || deviceName || entityId;
const secondary = [areaName, entityName ? deviceName : undefined]
.filter(Boolean)
.join(isRTL ? " ◂ " : " ▸ ");
return { primary, secondary };
}
);
protected render() {
const stateObj = this.hass.states[this.entityId];
let icon = mdiSpeaker;
if (stateObj.state === "playing") {
icon = mdiSpeakerPlay;
} else if (stateObj.state === "paused") {
icon = mdiSpeakerPause;
}
const isRTL = computeRTL(this.hass);
const { primary, secondary } = this._computeDisplayData(
this.entityId,
this.hass.entities,
this.hass.devices,
this.hass.areas,
this.hass.floors,
isRTL,
stateObj
);
return html`<div class="list-item">
<ha-svg-icon .path=${mdiSpeaker}></ha-svg-icon>
<ha-svg-icon .path=${icon}></ha-svg-icon>
<div class="info">
<div class="main-text">${computeStateName(stateObj)}</div>
<div class="secondary-text">
${this._formatSecondaryText(stateObj as MediaPlayerEntity)}
</div>
<div class="main-text">${primary}</div>
<div class="secondary-text">${secondary}</div>
</div>
<ha-switch
.disabled=${this.disabled}
@ -38,16 +85,6 @@ class HaMediaPlayerToggle extends LitElement {
</div>`;
}
private _formatSecondaryText(stateObj: MediaPlayerEntity): string {
if (stateObj.state !== "playing") {
return this.hass.localize("ui.card.media_player.idle");
}
return [stateObj.attributes.media_title, stateObj.attributes.media_artist]
.filter((segment) => segment)
.join(" · ");
}
static get styles(): CSSResultGroup {
return [
css`