1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Replace computeLovelaceEntityName with hass.formatEntityName (#30351)

This commit is contained in:
Paul Bottein
2026-03-26 17:01:26 +01:00
committed by Bram Kragten
parent 0f9d48a03d
commit 1132cdb364
35 changed files with 94 additions and 344 deletions

View File

@@ -29,14 +29,18 @@ export interface EntityNameOptions {
export const computeEntityNameDisplay = ( export const computeEntityNameDisplay = (
stateObj: HassEntity, stateObj: HassEntity,
name: EntityNameItem | EntityNameItem[] | undefined, name: string | EntityNameItem | EntityNameItem[] | undefined,
entities: HomeAssistant["entities"], entities: HomeAssistant["entities"],
devices: HomeAssistant["devices"], devices: HomeAssistant["devices"],
areas: HomeAssistant["areas"], areas: HomeAssistant["areas"],
floors: HomeAssistant["floors"], floors: HomeAssistant["floors"],
options?: EntityNameOptions options?: EntityNameOptions
) => { ) => {
let items = ensureArray(name || DEFAULT_ENTITY_NAME); if (typeof name === "string") {
return name;
}
let items = ensureArray(name ?? DEFAULT_ENTITY_NAME);
const separator = options?.separator ?? DEFAULT_SEPARATOR; const separator = options?.separator ?? DEFAULT_SEPARATOR;

View File

@@ -19,7 +19,6 @@ import { cameraUrlWithWidthHeight } from "../../../data/camera";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -175,11 +174,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
"--badge-color": color, "--badge-color": color,
}; };
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const stateDisplay = html` const stateDisplay = html`
<state-display <state-display

View File

@@ -28,7 +28,6 @@ import {
subscribeEntityRegistry, subscribeEntityRegistry,
} from "../../../data/entity/entity_registry"; } from "../../../data/entity/entity_registry";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard } from "../types"; import type { LovelaceCard } from "../types";
@@ -233,11 +232,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
const defaultCode = this._entry?.options?.alarm_control_panel?.default_code; const defaultCode = this._entry?.options?.alarm_control_panel?.default_code;
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
return html` return html`
<ha-card> <ha-card>

View File

@@ -41,7 +41,6 @@ import type { FrontendLocaleData } from "../../../data/translation";
import type { Themes } from "../../../data/ws-themes"; import type { Themes } from "../../../data/ws-themes";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -52,6 +51,21 @@ import type {
} from "../types"; } from "../types";
import type { ButtonCardConfig } from "./types"; import type { ButtonCardConfig } from "./types";
const EMPTY_STATE_OBJ = {
state: "unavailable",
attributes: {
friendly_name: "",
},
entity_id: "___.empty",
context: {
id: "",
parent_id: null,
user_id: null,
},
last_changed: "",
last_updated: "",
} satisfies HassEntity;
export const getEntityDefaultButtonAction = (entityId?: string) => export const getEntityDefaultButtonAction = (entityId?: string) =>
entityId && DOMAINS_TOGGLE.has(computeDomain(entityId)) entityId && DOMAINS_TOGGLE.has(computeDomain(entityId))
? "toggle" ? "toggle"
@@ -183,9 +197,8 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(
this.hass, stateObj || EMPTY_STATE_OBJ,
stateObj,
this._config.name this._config.name
); );

View File

@@ -12,7 +12,6 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { normalizeValueBySIPrefix } from "../../../common/number/normalize-by-si-prefix"; import { normalizeValueBySIPrefix } from "../../../common/number/normalize-by-si-prefix";
import { MobileAwareMixin } from "../../../mixins/mobile-aware-mixin"; import { MobileAwareMixin } from "../../../mixins/mobile-aware-mixin";
import type { EntityNameItem } from "../../../common/entity/compute_entity_name_display"; import type { EntityNameItem } from "../../../common/entity/compute_entity_name_display";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import "../../../components/chips/ha-assist-chip"; import "../../../components/chips/ha-assist-chip";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-segmented-bar"; import "../../../components/ha-segmented-bar";
@@ -240,7 +239,7 @@ export class HuiDistributionCard
const color = entity.color const color = entity.color
? computeCssColor(entity.color) ? computeCssColor(entity.color)
: getGraphColorByIndex(entity.originalIndex, computedStyles); : getGraphColorByIndex(entity.originalIndex, computedStyles);
const name = computeLovelaceEntityName(this.hass!, stateObj, entity.name); const name = this.hass!.formatEntityName(stateObj, entity.name);
const formattedValue = this.hass!.formatEntityState(stateObj); const formattedValue = this.hass!.formatEntityState(stateObj);
segments.push({ segments.push({
@@ -276,7 +275,7 @@ export class HuiDistributionCard
const isZeroOrNegative = !stateObj || value <= 0 || isNaN(value); const isZeroOrNegative = !stateObj || value <= 0 || isNaN(value);
const name = stateObj const name = stateObj
? computeLovelaceEntityName(this.hass!, stateObj, entity.name) ? this.hass!.formatEntityName(stateObj, entity.name)
: entity.entity; : entity.entity;
const formattedValue = stateObj const formattedValue = stateObj

View File

@@ -25,7 +25,6 @@ import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action"; import { hasAction, hasAnyAction } from "../common/has-action";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeCardSize } from "../common/compute-card-size"; import { computeCardSize } from "../common/compute-card-size";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -146,11 +145,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
const indexValue = stateParts.findIndex((part) => part.type === "value"); const indexValue = stateParts.findIndex((part) => part.type === "value");
const reversedOrder = indexUnit !== -1 && indexUnit < indexValue; const reversedOrder = indexUnit !== -1 && indexUnit < indexValue;
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const colored = stateObj && this._getStateColor(stateObj, this._config); const colored = stateObj && this._getStateColor(stateObj, this._config);

View File

@@ -14,7 +14,6 @@ import { UNAVAILABLE } from "../../../data/entity/entity";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action"; import { hasAction, hasAnyAction } from "../common/has-action";
@@ -124,11 +123,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
// Use `stateObj.state` as value to keep formatting (e.g trailing zeros) // Use `stateObj.state` as value to keep formatting (e.g trailing zeros)
// for consistent value display across gauge, entity, entity-row, etc. // for consistent value display across gauge, entity, entity-row, etc.

View File

@@ -18,7 +18,6 @@ import type {
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor"; import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action"; import { hasAction, hasAnyAction } from "../common/has-action";
@@ -252,11 +251,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
</div>`; </div>`;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, entityConf.name);
this.hass!,
stateObj,
entityConf.name
);
return html` return html`
<div <div

View File

@@ -19,7 +19,6 @@ import {
import { fetchStatistics } from "../../../data/recorder"; import { fetchStatistics } from "../../../data/recorder";
import { getSensorNumericDeviceClasses } from "../../../data/sensor"; import { getSensorNumericDeviceClasses } from "../../../data/sensor";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntitiesChanged } from "../common/has-changed"; import { hasConfigOrEntitiesChanged } from "../common/has-changed";
import { processConfigEntities } from "../common/process-config-entities"; import { processConfigEntities } from "../common/process-config-entities";
import type { EntityConfig } from "../entity-rows/types"; import type { EntityConfig } from "../entity-rows/types";
@@ -106,7 +105,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
this._entities.forEach((entity) => { this._entities.forEach((entity) => {
const stateObj = this.hass!.states[entity.entity]; const stateObj = this.hass!.states[entity.entity];
this._names[entity.entity] = stateObj this._names[entity.entity] = stateObj
? computeLovelaceEntityName(this.hass!, stateObj, entity.name) ? this.hass!.formatEntityName(stateObj, entity.name)
: entity.entity; : entity.entity;
}); });
} }

View File

@@ -14,7 +14,6 @@ import "../../../state-control/humidifier/ha-state-control-humidifier-humidity";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import "../card-features/hui-card-features"; import "../card-features/hui-card-features";
import type { LovelaceCardFeatureContext } from "../card-features/types"; import type { LovelaceCardFeatureContext } from "../card-features/types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { import type {
@@ -133,11 +132,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const color = stateColorCss(stateObj); const color = stateColorCss(stateObj);

View File

@@ -17,7 +17,6 @@ import { lightSupportsBrightness } from "../../../data/light";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -92,11 +91,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
((stateObj.attributes.brightness || 0) / 255) * 100 ((stateObj.attributes.brightness || 0) / 255) * 100
); );
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
return html` return html`
<ha-card> <ha-card>

View File

@@ -36,7 +36,6 @@ import {
mediaPlayerPlayMedia, mediaPlayerPlayMedia,
} from "../../../data/media-player"; } from "../../../data/media-player";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-marquee"; import "../components/hui-marquee";
@@ -242,8 +241,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
.hass=${this.hass} .hass=${this.hass}
></ha-state-icon> ></ha-state-icon>
<div> <div>
${computeLovelaceEntityName( ${this.hass.formatEntityName(
this.hass,
this.hass!.states[this._config!.entity], this.hass!.states[this._config!.entity],
this._config.name this._config.name
)} )}

View File

@@ -13,7 +13,6 @@ import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { PersonEntity } from "../../../data/person"; import type { PersonEntity } from "../../../data/person";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -126,11 +125,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const entityState = this.hass.formatEntityState(stateObj); const entityState = this.hass.formatEntityState(stateObj);
let footer: TemplateResult | string = ""; let footer: TemplateResult | string = "";

View File

@@ -15,7 +15,6 @@ import "../../../components/ha-card";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -119,7 +118,7 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard {
style="background-image:url(${stateObj.attributes.entity_picture})" style="background-image:url(${stateObj.attributes.entity_picture})"
> >
<div class="header"> <div class="header">
${computeLovelaceEntityName(this.hass, stateObj, this._config.name)} ${this.hass.formatEntityName(stateObj, this._config.name)}
</div> </div>
</div> </div>
<div class="content"> <div class="content">

View File

@@ -23,7 +23,6 @@ import {
} from "../../../data/recorder"; } from "../../../data/recorder";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeCardSize } from "../common/compute-card-size"; import { computeCardSize } from "../common/compute-card-size";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { createHeaderFooterElement } from "../create-element/create-header-footer-element"; import { createHeaderFooterElement } from "../create-element/create-header-footer-element";
@@ -200,7 +199,7 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard {
const stateObj = this.hass.states[this._config.entity]; const stateObj = this.hass.states[this._config.entity];
const name = const name =
(this._config.name (this._config.name
? computeLovelaceEntityName(this.hass, stateObj, this._config.name) ? this.hass.formatEntityName(stateObj, this._config.name)
: "") || : "") ||
getStatisticLabel(this.hass, this._config.entity, this._metadata); getStatisticLabel(this.hass, this._config.entity, this._metadata);

View File

@@ -24,7 +24,6 @@ import {
getStatisticMetadata, getStatisticMetadata,
} from "../../../data/recorder"; } from "../../../data/recorder";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { hasConfigOrEntitiesChanged } from "../common/has-changed"; import { hasConfigOrEntitiesChanged } from "../common/has-changed";
import { processConfigEntities } from "../common/process-config-entities"; import { processConfigEntities } from "../common/process-config-entities";
@@ -189,8 +188,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
this._entities.forEach((config) => { this._entities.forEach((config) => {
const stateObj = this.hass!.states[config.entity]; const stateObj = this.hass!.states[config.entity];
this._names[config.entity] = this._names[config.entity] =
computeLovelaceEntityName(this.hass!, stateObj, config.name) || this.hass!.formatEntityName(stateObj, config.name) || config.entity;
config.entity;
}); });
} }

View File

@@ -15,7 +15,6 @@ import "../../../state-control/water_heater/ha-state-control-water_heater-temper
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import "../card-features/hui-card-features"; import "../card-features/hui-card-features";
import type { LovelaceCardFeatureContext } from "../card-features/types"; import type { LovelaceCardFeatureContext } from "../card-features/types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { import type {
@@ -132,11 +131,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
} }
const domain = computeDomain(stateObj.entity_id); const domain = computeDomain(stateObj.entity_id);
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const color = stateColorCss(stateObj); const color = stateColorCss(stateObj);

View File

@@ -23,7 +23,6 @@ import "../../../state-display/state-display";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import "../card-features/hui-card-features"; import "../card-features/hui-card-features";
import type { LovelaceCardFeatureContext } from "../card-features/types"; import type { LovelaceCardFeatureContext } from "../card-features/types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -252,11 +251,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const active = stateActive(stateObj); const active = stateActive(stateObj);
const color = this._computeStateColor(stateObj, this._config.color); const color = this._computeStateColor(stateObj, this._config.color);

View File

@@ -29,7 +29,6 @@ import {
} from "../../../data/weather"; } from "../../../data/weather";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -231,7 +230,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card class="unavailable" @click=${this._handleAction}> <ha-card class="unavailable" @click=${this._handleAction}>
${this.hass.localize("ui.panel.lovelace.warning.entity_unavailable", { ${this.hass.localize("ui.panel.lovelace.warning.entity_unavailable", {
entity: `${computeLovelaceEntityName(this.hass, stateObj, this._config.name)} (${this._config.entity})`, entity: `${this.hass.formatEntityName(stateObj, this._config.name)} (${this._config.entity})`,
})} })}
</ha-card> </ha-card>
`; `;
@@ -262,11 +261,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
const dayNight = forecastData?.type === "twice_daily"; const dayNight = forecastData?.type === "twice_daily";
const weatherStateIcon = getWeatherStateIcon(stateObj.state, this); const weatherStateIcon = getWeatherStateIcon(stateObj.state, this);
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
const temperatureFractionDigits = this._config.round_temperature const temperatureFractionDigits = this._config.round_temperature
? 0 ? 0

View File

@@ -155,7 +155,7 @@ export interface AreaCardConfig extends LovelaceCardConfig {
export interface ButtonCardConfig extends LovelaceCardConfig { export interface ButtonCardConfig extends LovelaceCardConfig {
entity?: string; entity?: string;
name?: string; name?: string | EntityNameItem | EntityNameItem[];
show_name?: boolean; show_name?: boolean;
icon?: string; icon?: string;
icon_height?: string; icon_height?: string;

View File

@@ -1,41 +0,0 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { ensureArray } from "../../../../common/array/ensure-array";
import {
DEFAULT_ENTITY_NAME,
type EntityNameItem,
} from "../../../../common/entity/compute_entity_name_display";
import type { HomeAssistant } from "../../../../types";
/**
* Computes the display name for an entity in Lovelace (cards and badges).
*
* @param hass - The Home Assistant instance
* @param stateObj - The entity state object
* @param config - The name configuration (string for override, or EntityNameItem[] for structured naming)
* @returns The computed entity name
*/
export const computeLovelaceEntityName = (
hass: HomeAssistant,
stateObj: HassEntity | undefined,
config: string | EntityNameItem | EntityNameItem[] | undefined
): string => {
if (typeof config === "string") {
return config;
}
if (stateObj) {
return hass.formatEntityName(stateObj, config ?? DEFAULT_ENTITY_NAME);
}
if (!config) {
return "";
}
// If entity is not found, fall back to text parts in config
// This allows for static names even when the entity is missing
// e.g. for a card that doesn't require an entity
const textParts = ensureArray(config)
.filter((item) => item.type === "text")
.map((item) => ("text" in item ? item.text : ""));
if (textParts.length) {
return textParts.join(" ");
}
return "";
};

View File

@@ -32,7 +32,6 @@ import type {
} from "../cards/types"; } from "../cards/types";
import type { EntityConfig } from "../entity-rows/types"; import type { EntityConfig } from "../entity-rows/types";
import type { ButtonsHeaderFooterConfig } from "../header-footer/types"; import type { ButtonsHeaderFooterConfig } from "../header-footer/types";
import { computeLovelaceEntityName } from "./entity/compute-lovelace-entity-name";
const HIDE_DOMAIN = new Set([ const HIDE_DOMAIN = new Set([
"ai_task", "ai_task",
@@ -271,14 +270,14 @@ export const computeCards = (
? computeStateName(states[a]) ? computeStateName(states[a])
: "" : ""
: states[a.entity] : states[a.entity]
? computeLovelaceEntityName(hass, states[a.entity], a.name) ? hass.formatEntityName(states[a.entity], a.name)
: "", : "",
typeof b === "string" typeof b === "string"
? states[b] ? states[b]
? computeStateName(states[b]) ? computeStateName(states[b])
: "" : ""
: states[b.entity] : states[b.entity]
? computeLovelaceEntityName(hass, states[b.entity], b.name) ? hass.formatEntityName(states[b.entity], b.name)
: "" : ""
); );
}); });

View File

@@ -18,7 +18,6 @@ import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { EntitiesCardEntityConfig } from "../cards/types"; import type { EntitiesCardEntityConfig } from "../cards/types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action"; import { hasAction, hasAnyAction } from "../common/has-action";
import { createEntityNotFoundWarning } from "./hui-warning"; import { createEntityNotFoundWarning } from "./hui-warning";
@@ -66,11 +65,7 @@ export class HuiGenericEntityRow extends LitElement {
const pointer = hasAnyAction(this.config); const pointer = hasAnyAction(this.config);
const hasSecondary = this.secondaryText || this.config.secondary_info; const hasSecondary = this.secondaryText || this.config.secondary_info;
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this.config.name);
this.hass,
stateObj,
this.config.name
);
return html` return html`
<div <div

View File

@@ -7,7 +7,6 @@ import "../../../components/ha-time-input";
import { setDateTimeValue } from "../../../data/datetime"; import { setDateTimeValue } from "../../../data/datetime";
import { isUnavailableState, UNAVAILABLE } from "../../../data/entity/entity"; import { isUnavailableState, UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistant, ValueChangedEvent } from "../../../types"; import type { HomeAssistant, ValueChangedEvent } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -53,11 +52,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined; const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined;
const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined; const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined;
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -9,7 +9,6 @@ import {
stateToIsoDateString, stateToIsoDateString,
} from "../../../data/input_datetime"; } from "../../../data/input_datetime";
import type { HomeAssistant, ValueChangedEvent } from "../../../types"; import type { HomeAssistant, ValueChangedEvent } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -47,11 +46,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -9,7 +9,6 @@ import type { InputSelectEntity } from "../../../data/input_select";
import { setInputSelectOption } from "../../../data/input_select"; import { setInputSelectOption } from "../../../data/input_select";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { EntitiesCardEntityConfig } from "../cards/types"; import type { EntitiesCardEntityConfig } from "../cards/types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -50,11 +49,7 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -5,7 +5,6 @@ import "../../../components/ha-textfield";
import { isUnavailableState, UNAVAILABLE } from "../../../data/entity/entity"; import { isUnavailableState, UNAVAILABLE } from "../../../data/entity/entity";
import { setValue } from "../../../data/input_text"; import { setValue } from "../../../data/input_text";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -43,11 +42,7 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -9,7 +9,6 @@ import type { SelectEntity } from "../../../data/select";
import { setSelectOption } from "../../../data/select"; import { setSelectOption } from "../../../data/select";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { EntitiesCardEntityConfig } from "../cards/types"; import type { EntitiesCardEntityConfig } from "../cards/types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -68,11 +67,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -6,7 +6,6 @@ import { isUnavailableState, UNAVAILABLE } from "../../../data/entity/entity";
import type { TextEntity } from "../../../data/text"; import type { TextEntity } from "../../../data/text";
import { setValue } from "../../../data/text"; import { setValue } from "../../../data/text";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createEntityNotFoundWarning } from "../components/hui-warning";
@@ -46,11 +45,7 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
`; `;
} }
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<hui-generic-entity-row <hui-generic-entity-row

View File

@@ -17,7 +17,6 @@ import {
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { EntitiesCardEntityConfig } from "../cards/types"; import type { EntitiesCardEntityConfig } from "../cards/types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action"; import { hasAction, hasAnyAction } from "../common/has-action";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
@@ -119,11 +118,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
const forecastData = getForecast(stateObj.attributes, this._forecastEvent); const forecastData = getForecast(stateObj.attributes, this._forecastEvent);
const forecast = forecastData?.forecast; const forecast = forecastData?.forecast;
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(stateObj, this._config.name);
this.hass!,
stateObj,
this._config.name
);
return html` return html`
<div <div

View File

@@ -15,7 +15,6 @@ import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import "../../../state-display/state-display"; import "../../../state-display/state-display";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { findEntities } from "../common/find-entities"; import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
@@ -154,11 +153,7 @@ export class HuiEntityHeadingBadge
"--icon-color": color, "--icon-color": color,
}; };
const name = computeLovelaceEntityName( const name = this.hass.formatEntityName(stateObj, this._config.name);
this.hass,
stateObj,
this._config.name
);
return html` return html`
<ha-heading-badge <ha-heading-badge

View File

@@ -1,3 +1,4 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, state } from "lit/decorators";
import { DOMAINS_TOGGLE } from "../../../common/const"; import { DOMAINS_TOGGLE } from "../../../common/const";
@@ -7,11 +8,25 @@ import "../../../components/ha-state-icon";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler"; import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
import { handleAction } from "../common/handle-action"; import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action"; import { hasAction } from "../common/has-action";
import type { ButtonRowConfig, LovelaceRow } from "../entity-rows/types"; import type { ButtonRowConfig, LovelaceRow } from "../entity-rows/types";
const EMPTY_STATE_OBJ = {
state: "unavailable",
attributes: {
friendly_name: "",
},
entity_id: "___.empty",
context: {
id: "",
parent_id: null,
user_id: null,
},
last_changed: "",
last_updated: "",
} satisfies HassEntity;
@customElement("hui-button-row") @customElement("hui-button-row")
export class HuiButtonRow extends LitElement implements LovelaceRow { export class HuiButtonRow extends LitElement implements LovelaceRow {
public hass?: HomeAssistant; public hass?: HomeAssistant;
@@ -49,9 +64,8 @@ export class HuiButtonRow extends LitElement implements LovelaceRow {
? this.hass.states[this._config.entity] ? this.hass.states[this._config.entity]
: undefined; : undefined;
const name = computeLovelaceEntityName( const name = this.hass!.formatEntityName(
this.hass!, stateObj || EMPTY_STATE_OBJ,
stateObj,
this._config.name this._config.name
); );

View File

@@ -309,7 +309,7 @@ export interface HomeAssistant {
formatEntityAttributeName(stateObj: HassEntity, attribute: string): string; formatEntityAttributeName(stateObj: HassEntity, attribute: string): string;
formatEntityName( formatEntityName(
stateObj: HassEntity, stateObj: HassEntity,
type: EntityNameItem | EntityNameItem[], type: string | EntityNameItem | EntityNameItem[] | undefined,
separator?: EntityNameOptions separator?: EntityNameOptions
): string; ): string;
} }

View File

@@ -13,6 +13,27 @@ import {
} from "./context/context-mock"; } from "./context/context-mock";
describe("computeEntityNameDisplay", () => { describe("computeEntityNameDisplay", () => {
it("returns string name directly", () => {
const stateObj = mockStateObj({ entity_id: "light.kitchen" });
const hass = {
entities: {},
devices: {},
areas: {},
floors: {},
} as unknown as HomeAssistant;
const result = computeEntityNameDisplay(
stateObj,
"Custom Name",
hass.entities,
hass.devices,
hass.areas,
hass.floors
);
expect(result).toBe("Custom Name");
});
it("returns text when all items are text", () => { it("returns text when all items are text", () => {
const stateObj = mockStateObj({ entity_id: "light.kitchen" }); const stateObj = mockStateObj({ entity_id: "light.kitchen" });
const hass = { const hass = {

View File

@@ -1,152 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { computeLovelaceEntityName } from "../../../../../src/panels/lovelace/common/entity/compute-lovelace-entity-name";
import type { HomeAssistant } from "../../../../../src/types";
import { mockStateObj } from "../../../../common/entity/context/context-mock";
const createMockHass = (
mockFormatEntityName: ReturnType<typeof vi.fn>
): HomeAssistant =>
({
formatEntityName: mockFormatEntityName,
}) as unknown as HomeAssistant;
describe("computeLovelaceEntityName", () => {
it("returns the string directly when nameConfig is a string", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const stateObj = mockStateObj({ entity_id: "light.kitchen" });
const result = computeLovelaceEntityName(hass, stateObj, "Custom Name");
expect(result).toBe("Custom Name");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("returns empty string when nameConfig is empty string", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const stateObj = mockStateObj({
entity_id: "light.kitchen",
attributes: { friendly_name: "Kitchen Light" },
});
const result = computeLovelaceEntityName(hass, stateObj, "");
expect(result).toBe("");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("calls formatEntityName with DEFAULT_ENTITY_NAME when nameConfig is undefined", () => {
const mockFormatEntityName = vi.fn(() => "Formatted Name");
const hass = createMockHass(mockFormatEntityName);
const stateObj = mockStateObj({
entity_id: "light.kitchen",
attributes: { friendly_name: "Kitchen Light" },
});
const result = computeLovelaceEntityName(hass, stateObj, undefined);
expect(result).toBe("Formatted Name");
expect(mockFormatEntityName).toHaveBeenCalledTimes(1);
expect(mockFormatEntityName).toHaveBeenCalledWith(stateObj, [
{ type: "device" },
{ type: "entity" },
]);
});
it("calls formatEntityName with EntityNameItem config", () => {
const mockFormatEntityName = vi.fn(() => "Formatted Name");
const hass = createMockHass(mockFormatEntityName);
const stateObj = mockStateObj({ entity_id: "light.bedroom" });
const nameConfig = { type: "device" as const };
const result = computeLovelaceEntityName(hass, stateObj, nameConfig);
expect(result).toBe("Formatted Name");
expect(mockFormatEntityName).toHaveBeenCalledTimes(1);
expect(mockFormatEntityName).toHaveBeenCalledWith(stateObj, nameConfig);
});
it("calls formatEntityName with array of EntityNameItems", () => {
const mockFormatEntityName = vi.fn(() => "Formatted Name");
const hass = createMockHass(mockFormatEntityName);
const stateObj = mockStateObj({ entity_id: "light.kitchen" });
const nameConfig = [
{ type: "device" as const },
{ type: "entity" as const },
];
const result = computeLovelaceEntityName(hass, stateObj, nameConfig);
expect(result).toBe("Formatted Name");
expect(mockFormatEntityName).toHaveBeenCalledTimes(1);
expect(mockFormatEntityName).toHaveBeenCalledWith(stateObj, nameConfig);
});
describe("when stateObj is undefined", () => {
it("returns empty string when nameConfig is undefined", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const result = computeLovelaceEntityName(hass, undefined, undefined);
expect(result).toBe("");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("returns text from single text EntityNameItem", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const nameConfig = { type: "text" as const, text: "Custom Text" };
const result = computeLovelaceEntityName(hass, undefined, nameConfig);
expect(result).toBe("Custom Text");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("returns joined text from multiple text EntityNameItems", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const nameConfig = [
{ type: "text" as const, text: "First" },
{ type: "text" as const, text: "Second" },
];
const result = computeLovelaceEntityName(hass, undefined, nameConfig);
expect(result).toBe("First Second");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("returns only text items when mixed with non-text items", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const nameConfig = [
{ type: "text" as const, text: "Prefix" },
{ type: "device" as const },
{ type: "text" as const, text: "Suffix" },
{ type: "entity" as const },
];
const result = computeLovelaceEntityName(hass, undefined, nameConfig);
expect(result).toBe("Prefix Suffix");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
it("returns empty string when no text items in config", () => {
const mockFormatEntityName = vi.fn();
const hass = createMockHass(mockFormatEntityName);
const nameConfig = [
{ type: "device" as const },
{ type: "entity" as const },
];
const result = computeLovelaceEntityName(hass, undefined, nameConfig);
expect(result).toBe("");
expect(mockFormatEntityName).not.toHaveBeenCalled();
});
});
});