diff --git a/src/common/entity/strip_prefix_from_entity_name.ts b/src/common/entity/strip_prefix_from_entity_name.ts
new file mode 100644
index 0000000000..1efa3704d5
--- /dev/null
+++ b/src/common/entity/strip_prefix_from_entity_name.ts
@@ -0,0 +1,24 @@
+/**
+ * Strips a device name from an entity name.
+ * @param entityName the entity name
+ * @param lowerCasedPrefixWithSpaceSuffix the prefix to strip, lower cased with a space suffix
+ * @returns
+ */
+export const stripPrefixFromEntityName = (
+ entityName: string,
+ lowerCasedPrefixWithSpaceSuffix: string
+) => {
+ if (!entityName.toLowerCase().startsWith(lowerCasedPrefixWithSpaceSuffix)) {
+ return undefined;
+ }
+
+ const newName = entityName.substring(lowerCasedPrefixWithSpaceSuffix.length);
+
+ // If first word already has an upper case letter (e.g. from brand name)
+ // leave as-is, otherwise capitalize the first word.
+ return hasUpperCase(newName.substr(0, newName.indexOf(" ")))
+ ? newName
+ : newName[0].toUpperCase() + newName.slice(1);
+};
+
+const hasUpperCase = (str: string): boolean => str.toLowerCase() !== str;
diff --git a/src/panels/config/devices/device-detail/ha-device-entities-card.ts b/src/panels/config/devices/device-detail/ha-device-entities-card.ts
index a943914264..5c749d927b 100644
--- a/src/panels/config/devices/device-detail/ha-device-entities-card.ts
+++ b/src/panels/config/devices/device-detail/ha-device-entities-card.ts
@@ -15,18 +15,23 @@ import { domainIcon } from "../../../../common/entity/domain_icon";
import "../../../../components/entity/state-badge";
import "../../../../components/ha-card";
import "../../../../components/ha-icon";
-import { HomeAssistant } from "../../../../types";
-import { HuiErrorCard } from "../../../lovelace/cards/hui-error-card";
+import type { LovelaceRowConfig } from "../../../lovelace/entity-rows/types";
+import type { HomeAssistant } from "../../../../types";
+import type { HuiErrorCard } from "../../../lovelace/cards/hui-error-card";
import { createRowElement } from "../../../lovelace/create-element/create-row-element";
import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities-to-view";
import { LovelaceRow } from "../../../lovelace/entity-rows/types";
import { showEntityEditorDialog } from "../../entities/show-dialog-entity-editor";
import { EntityRegistryStateEntry } from "../ha-config-device-page";
+import { computeStateName } from "../../../../common/entity/compute_state_name";
+import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name";
@customElement("ha-device-entities-card")
export class HaDeviceEntitiesCard extends LitElement {
@property() public header!: string;
+ @property() public deviceName!: string;
+
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public entities!: EntityRegistryStateEntry[];
@@ -119,9 +124,21 @@ export class HaDeviceEntitiesCard extends LitElement {
}
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
- const element = createRowElement({ entity: entry.entity_id });
+ const config: LovelaceRowConfig = {
+ entity: entry.entity_id,
+ };
+
+ const element = createRowElement(config);
if (this.hass) {
element.hass = this.hass;
+ const state = this.hass.states[entry.entity_id];
+ const name = stripPrefixFromEntityName(
+ computeStateName(state),
+ `${this.deviceName} `.toLowerCase()
+ );
+ if (name) {
+ config.name = name;
+ }
}
// @ts-ignore
element.entry = entry;
diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts
index 9be745ae4e..f1b6c60837 100644
--- a/src/panels/config/devices/ha-config-device-page.ts
+++ b/src/panels/config/devices/ha-config-device-page.ts
@@ -179,6 +179,7 @@ export class HaConfigDevicePage extends LitElement {
`;
}
+ const deviceName = computeDeviceName(device, this.hass);
const integrations = this._integrations(device, this.entries);
const entities = this._entities(this.deviceId, this.entities);
const entitiesByCategory = this._entitiesByCategory(entities);
@@ -204,9 +205,7 @@ export class HaConfigDevicePage extends LitElement {
${
this.narrow
? html`
-
- ${computeDeviceName(device, this.hass)}
-
+ ${deviceName}
-
${computeDeviceName(device, this.hass)}
+
${deviceName}
${area
? html`
diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts
index 5edc9e313e..2860ca6dff 100644
--- a/src/panels/lovelace/common/generate-lovelace-config.ts
+++ b/src/panels/lovelace/common/generate-lovelace-config.ts
@@ -3,6 +3,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { splitByGroups } from "../../../common/entity/split_by_groups";
+import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_from_entity_name";
import { stringCompare } from "../../../common/string/compare";
import { LocalizeFunc } from "../../../common/translations/localize";
import type { AreaRegistryEntry } from "../../../data/area_registry";
@@ -92,7 +93,7 @@ export const computeCards = (
const entities: Array = [];
const titlePrefix = entityCardOptions.title
- ? `${entityCardOptions.title} `
+ ? `${entityCardOptions.title} `.toLowerCase()
: undefined;
for (const [entityId, stateObj] of states) {
@@ -153,16 +154,18 @@ export const computeCards = (
) {
// Do nothing.
} else {
- let name: string;
+ let name: string | undefined;
const entityConf =
titlePrefix &&
stateObj &&
// eslint-disable-next-line no-cond-assign
- (name = computeStateName(stateObj)) !== titlePrefix &&
- name.startsWith(titlePrefix)
+ (name = stripPrefixFromEntityName(
+ computeStateName(stateObj),
+ titlePrefix
+ ))
? {
entity: entityId,
- name: adjustName(name.substr(titlePrefix.length)),
+ name,
}
: entityId;
@@ -181,15 +184,6 @@ export const computeCards = (
return cards;
};
-const hasUpperCase = (str: string): boolean => str.toLowerCase() !== str;
-
-const adjustName = (name: string): string =>
- // If first word already has an upper case letter (e.g. from brand name)
- // leave as-is, otherwise capitalize the first word.
- hasUpperCase(name.substr(0, name.indexOf(" ")))
- ? name
- : name[0].toUpperCase() + name.slice(1);
-
const computeDefaultViewStates = (
entities: HassEntities,
entityEntries: EntityRegistryEntry[]