mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-24 20:55:49 +00:00
This reverts commit 7c7a4e61f2.
This commit is contained in:
@@ -39,8 +39,6 @@ import type { HomeAssistant, TranslationDict } from "../types";
|
||||
import { isUnavailableState } from "./entity";
|
||||
import { isTTSMediaSource } from "./tts";
|
||||
|
||||
import { generateEntityFilter } from "../common/entity/entity_filter";
|
||||
|
||||
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {
|
||||
media_content_id?: string;
|
||||
media_content_type?: string;
|
||||
@@ -524,33 +522,3 @@ export const mediaPlayerJoin = (
|
||||
|
||||
export const mediaPlayerUnjoin = (hass: HomeAssistant, entity_id: string) =>
|
||||
hass.callService("media_player", "unjoin", {}, { entity_id });
|
||||
|
||||
/**
|
||||
* Compute active media player states in a specific area.
|
||||
* @param hass Home Assistant object
|
||||
* @param areaId Area ID to filter media players by
|
||||
* @returns Array of playing media player entities
|
||||
*/
|
||||
export const computeActiveAreaMediaStates = (
|
||||
hass: HomeAssistant,
|
||||
areaId: string
|
||||
): MediaPlayerEntity[] => {
|
||||
const area = hass.areas[areaId];
|
||||
if (!area) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get all media_player entities in this area
|
||||
const mediaFilter = generateEntityFilter(hass, {
|
||||
area: areaId,
|
||||
domain: "media_player",
|
||||
});
|
||||
|
||||
const mediaEntities = Object.keys(hass.entities).filter(mediaFilter);
|
||||
|
||||
return mediaEntities
|
||||
.map((entityId) => hass.states[entityId] as MediaPlayerEntity | undefined)
|
||||
.filter(
|
||||
(stateObj): stateObj is MediaPlayerEntity => stateObj?.state === "playing"
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mdiPlay, mdiTextureBox } from "@mdi/js";
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@@ -13,10 +13,6 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
computeActiveAreaMediaStates,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../data/media-player";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { BINARY_STATE_ON } from "../../../common/const";
|
||||
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
||||
@@ -289,19 +285,15 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
|
||||
);
|
||||
}
|
||||
|
||||
private _computeActiveAreaMediaStates(): MediaPlayerEntity[] {
|
||||
return computeActiveAreaMediaStates(this.hass, this._config?.area || "");
|
||||
}
|
||||
private _renderAlertSensorBadge(): TemplateResult<1> | typeof nothing {
|
||||
const states = this._computeActiveAlertStates();
|
||||
|
||||
private _renderAlertSensorBadge(
|
||||
alertStates: HassEntity[]
|
||||
): TemplateResult<1> | typeof nothing {
|
||||
if (alertStates.length === 0) {
|
||||
if (states.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
// Only render the first one when using a badge
|
||||
const stateObj = alertStates[0] as HassEntity | undefined;
|
||||
const stateObj = states[0] as HassEntity | undefined;
|
||||
|
||||
return html`
|
||||
<ha-tile-badge class="alert-badge">
|
||||
@@ -310,30 +302,6 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderMediaBadge(): TemplateResult<1> | typeof nothing {
|
||||
const states = this._computeActiveAreaMediaStates();
|
||||
|
||||
if (states.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-tile-badge
|
||||
class="media-badge"
|
||||
.label=${this.hass.localize("ui.card.area.media_playing")}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlay}></ha-svg-icon>
|
||||
</ha-tile-badge>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderCompactBadge(): TemplateResult<1> | typeof nothing {
|
||||
const alertStates = this._computeActiveAlertStates();
|
||||
return alertStates.length > 0
|
||||
? this._renderAlertSensorBadge(alertStates)
|
||||
: this._renderMediaBadge();
|
||||
}
|
||||
|
||||
private _renderAlertSensors(): TemplateResult<1> | typeof nothing {
|
||||
const states = this._computeActiveAlertStates();
|
||||
|
||||
@@ -595,7 +563,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
|
||||
<div class="content ${classMap(contentClasses)}">
|
||||
<ha-tile-icon>
|
||||
${displayType === "compact"
|
||||
? this._renderCompactBadge()
|
||||
? this._renderAlertSensorBadge()
|
||||
: nothing}
|
||||
${icon
|
||||
? html`<ha-icon slot="icon" .icon=${icon}></ha-icon>`
|
||||
@@ -773,9 +741,6 @@ export class HuiAreaCard extends LitElement implements LovelaceCard {
|
||||
.alert-badge {
|
||||
--tile-badge-background-color: var(--orange-color);
|
||||
}
|
||||
.media-badge {
|
||||
--tile-badge-background-color: var(--light-blue-color);
|
||||
}
|
||||
.alerts {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -106,8 +106,7 @@
|
||||
}
|
||||
},
|
||||
"area": {
|
||||
"area_not_found": "Area not found.",
|
||||
"media_playing": "Media playing"
|
||||
"area_not_found": "Area not found."
|
||||
},
|
||||
"automation": {
|
||||
"last_triggered": "Last triggered",
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
computeActiveAreaMediaStates,
|
||||
type MediaPlayerEntity,
|
||||
} from "../../../src/data/media-player";
|
||||
|
||||
describe("computeActiveAreaMediaStates", () => {
|
||||
it("returns playing media entities in the area", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
entities: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
area_id: "living_room",
|
||||
},
|
||||
"media_player.speaker": {
|
||||
entity_id: "media_player.speaker",
|
||||
area_id: "living_room",
|
||||
},
|
||||
},
|
||||
states: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
"media_player.speaker": {
|
||||
entity_id: "media_player.speaker",
|
||||
state: "idle",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].entity_id).toBe("media_player.tv");
|
||||
expect(result[0].state).toBe("playing");
|
||||
});
|
||||
|
||||
it("returns empty array when no area is configured", () => {
|
||||
const hass = {
|
||||
areas: {},
|
||||
entities: {},
|
||||
states: {},
|
||||
} as any;
|
||||
|
||||
const result = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("returns empty array when media player is not assigned to area", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
entities: {
|
||||
"media_player.bedroom": { entity_id: "media_player.bedroom" },
|
||||
},
|
||||
states: {
|
||||
"media_player.bedroom": {
|
||||
entity_id: "media_player.bedroom",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("returns playing speaker when speaker is playing", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
entities: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
area_id: "living_room",
|
||||
},
|
||||
"media_player.speaker": {
|
||||
entity_id: "media_player.speaker",
|
||||
area_id: "living_room",
|
||||
},
|
||||
},
|
||||
states: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
state: "idle",
|
||||
} as MediaPlayerEntity,
|
||||
"media_player.speaker": {
|
||||
entity_id: "media_player.speaker",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].entity_id).toBe("media_player.speaker");
|
||||
expect(result[0].state).toBe("playing");
|
||||
});
|
||||
|
||||
it("returns media entities that inherit area from device", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
devices: {
|
||||
device_tv: {
|
||||
id: "device_tv",
|
||||
area_id: "living_room",
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
device_id: "device_tv", // Entity belongs to device
|
||||
// No direct area_id - inherits from device
|
||||
},
|
||||
},
|
||||
states: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const result = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].entity_id).toBe("media_player.tv");
|
||||
expect(result[0].state).toBe("playing");
|
||||
});
|
||||
});
|
||||
|
||||
describe("computeActiveAreaMediaStates badge priority", () => {
|
||||
it("prioritizes alert badge over media badge", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
entities: {
|
||||
"binary_sensor.door": {
|
||||
entity_id: "binary_sensor.door",
|
||||
area_id: "living_room",
|
||||
},
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
area_id: "living_room",
|
||||
},
|
||||
},
|
||||
states: {
|
||||
"binary_sensor.door": {
|
||||
entity_id: "binary_sensor.door",
|
||||
state: "on",
|
||||
} as MediaPlayerEntity,
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const alertStates = hass.states["binary_sensor.door"]
|
||||
? [hass.states["binary_sensor.door"]]
|
||||
: [];
|
||||
const mediaStates = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
// Alert badge should take priority
|
||||
expect(alertStates.length > 0).toBe(true);
|
||||
expect(mediaStates.length > 0).toBe(true);
|
||||
expect(alertStates.length > 0 ? "alert" : "media").toBe("alert");
|
||||
});
|
||||
|
||||
it("shows media badge when no alerts", () => {
|
||||
const hass = {
|
||||
areas: { living_room: { area_id: "living_room" } },
|
||||
entities: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
area_id: "living_room",
|
||||
},
|
||||
},
|
||||
states: {
|
||||
"media_player.tv": {
|
||||
entity_id: "media_player.tv",
|
||||
state: "playing",
|
||||
} as MediaPlayerEntity,
|
||||
},
|
||||
} as any;
|
||||
|
||||
const alertStates: MediaPlayerEntity[] = [];
|
||||
const mediaStates = computeActiveAreaMediaStates(hass, "living_room");
|
||||
|
||||
expect(alertStates.length).toBe(0);
|
||||
expect(mediaStates.length).toBe(1);
|
||||
expect(alertStates.length > 0 ? "alert" : "media").toBe("media");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user