Add restore entity id (#25592)

This commit is contained in:
Bram Kragten 2025-05-26 18:23:23 +02:00 committed by GitHub
parent 6c7d750734
commit cde2b436d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 318 additions and 232 deletions

View File

@ -2,12 +2,12 @@ import type { Connection } from "home-assistant-js-websocket";
import { createCollection } from "home-assistant-js-websocket";
import type { Store } from "home-assistant-js-websocket/dist/store";
import memoizeOne from "memoize-one";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import type { HomeAssistant } from "../types";
import type { LightColor } from "./light";
import { computeDomain } from "../common/entity/compute_domain";
import type { RegistryEntry } from "./registry";
type EntityCategory = "config" | "diagnostic";
@ -315,3 +315,12 @@ export const getEntityPlatformLookup = (
}
return entityLookup;
};
export const getAutomaticEntityIds = (
hass: HomeAssistant,
entity_ids: string[]
) =>
hass.callWS<Record<string, string | null>>({
type: "config/entity_registry/get_automatic_entity_ids",
entity_ids,
});

View File

@ -0,0 +1,129 @@
import { html, nothing } from "lit";
import {
showAlertDialog,
showConfirmationDialog,
} from "../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../types";
import {
getAutomaticEntityIds,
updateEntityRegistryEntry,
} from "./entity_registry";
import "../components/ha-expansion-panel";
export const regenerateEntityIds = async (
element: HTMLElement,
hass: HomeAssistant,
entities: string[]
): Promise<void> => {
const entityIdsMapping = await getAutomaticEntityIds(hass, entities);
const entityIdsEntries = Object.entries(entityIdsMapping);
const dialogRename = entityIdsEntries
.filter(([oldId, newId]) => newId && oldId !== newId)
.map(
([oldId, newId]) =>
html`<tr>
<td>${oldId}</td>
<td>${newId}</td>
</tr>`
);
const dialogCantRename = entityIdsEntries
.filter(([_oldId, newId]) => newId === null)
.map(([oldId]) => html`<li>${oldId}</li>`);
const dialogNoRename = entityIdsEntries
.filter(([oldId, newId]) => oldId === newId)
.map(([oldId]) => html`<li>${oldId}</li>`);
if (dialogRename.length) {
showConfirmationDialog(element, {
title: hass.localize(
"ui.dialogs.recreate_entity_ids.confirm_rename_title"
),
text: html`${hass.localize(
"ui.dialogs.recreate_entity_ids.confirm_rename_warning"
)} <br /><br />
<ha-expansion-panel outlined>
<span slot="header"
>${hass.localize("ui.dialogs.recreate_entity_ids.will_rename", {
count: dialogRename.length,
})}</span
>
<div style="overflow: auto;">
<table style="width: 100%; text-align: var(--float-start);">
<tr>
<th>${hass.localize("ui.dialogs.recreate_entity_ids.old")}</th>
<th>${hass.localize("ui.dialogs.recreate_entity_ids.new")}</th>
</tr>
${dialogRename}
</table>
</div>
</ha-expansion-panel>
${dialogCantRename.length
? html`<ha-expansion-panel outlined style="margin-top: 8px;">
<span slot="header"
>${hass.localize("ui.dialogs.recreate_entity_ids.cant_rename", {
count: dialogCantRename.length,
})}</span
>
${dialogCantRename}
</ha-expansion-panel>`
: nothing}
${dialogNoRename.length
? html`<ha-expansion-panel outlined style="margin-top: 8px;">
<span slot="header"
>${hass.localize("ui.dialogs.recreate_entity_ids.wont_change", {
count: dialogNoRename.length,
})}</span
>
${dialogNoRename}
</ha-expansion-panel>`
: nothing}`,
confirmText: hass.localize("ui.common.update"),
dismissText: hass.localize("ui.common.cancel"),
destructive: true,
confirm: () => {
entityIdsEntries
.filter(([oldId, newId]) => newId && oldId !== newId)
.forEach(([oldEntityId, newEntityId]) =>
updateEntityRegistryEntry(hass, oldEntityId, {
new_entity_id: newEntityId!,
}).catch((err: any) => {
showAlertDialog(element, {
title: hass.localize(
"ui.dialogs.recreate_entity_ids.update_entity_error",
{ entityId: oldEntityId }
),
text: err.message,
});
})
);
},
});
} else {
showAlertDialog(element, {
title: hass.localize(
"ui.dialogs.recreate_entity_ids.confirm_no_renamable_entity_ids"
),
text: html`${dialogCantRename.length
? html`<ha-expansion-panel outlined style="margin-top: 8px;">
<span slot="header"
>${hass.localize("ui.dialogs.recreate_entity_ids.cant_rename", {
count: dialogCantRename.length,
})}</span
>
${dialogCantRename}
</ha-expansion-panel>`
: nothing}
${dialogNoRename.length
? html`<ha-expansion-panel outlined style="margin-top: 8px;">
<span slot="header"
>${hass.localize("ui.dialogs.recreate_entity_ids.wont_change", {
count: dialogNoRename.length,
})}</span
>
${dialogNoRename}
</ha-expansion-panel>`
: nothing}`,
});
}
};

View File

@ -10,13 +10,13 @@ import {
} from "../../common/entity/compute_device_name";
import { computeDomain } from "../../common/entity/compute_domain";
import { navigate } from "../../common/navigate";
import { slugify } from "../../common/string/slugify";
import "../../components/ha-area-picker";
import { assistSatelliteSupportsSetupFlow } from "../../data/assist_satellite";
import type { DataEntryFlowStepCreateEntry } from "../../data/data_entry_flow";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { updateDeviceRegistryEntry } from "../../data/device_registry";
import {
getAutomaticEntityIds,
updateEntityRegistryEntry,
type EntityRegistryDisplayEntry,
} from "../../data/entity_registry";
@ -182,19 +182,11 @@ class StepFlowCreateEntry extends LitElement {
private async _flowDone(): Promise<void> {
if (Object.keys(this._deviceUpdate).length) {
const renamedDevices: {
deviceId: string;
oldDeviceName: string | null | undefined;
newDeviceName: string;
}[] = [];
const renamedDevices: string[] = [];
const deviceUpdates = Object.entries(this._deviceUpdate).map(
([deviceId, update]) => {
if (update.name) {
renamedDevices.push({
deviceId,
oldDeviceName: computeDeviceName(this.hass.devices[deviceId]),
newDeviceName: update.name,
});
renamedDevices.push(deviceId);
}
return updateDeviceRegistryEntry(this.hass, deviceId, {
name_by_user: update.name,
@ -209,38 +201,36 @@ class StepFlowCreateEntry extends LitElement {
});
}
);
await Promise.allSettled(deviceUpdates);
const entityUpdates: Promise<any>[] = [];
renamedDevices.forEach(({ deviceId, oldDeviceName, newDeviceName }) => {
if (!oldDeviceName) {
return;
}
const entityIds: string[] = [];
renamedDevices.forEach((deviceId) => {
const entities = this._deviceEntities(
deviceId,
Object.values(this.hass.entities)
);
const oldDeviceSlug = slugify(oldDeviceName);
const newDeviceSlug = slugify(newDeviceName);
entities.forEach((entity) => {
const oldId = entity.entity_id;
if (oldId.includes(oldDeviceSlug)) {
const newEntityId = oldId.replace(oldDeviceSlug, newDeviceSlug);
entityUpdates.push(
updateEntityRegistryEntry(this.hass, entity.entity_id, {
new_entity_id: newEntityId,
}).catch((err) =>
showAlertDialog(this, {
text: this.hass.localize(
"ui.panel.config.integrations.config_flow.error_saving_entity",
{ error: err.message }
),
})
)
);
}
});
entityIds.push(...entities.map((entity) => entity.entity_id));
});
await Promise.allSettled([...deviceUpdates, ...entityUpdates]);
const entityIdsMapping = getAutomaticEntityIds(this.hass, entityIds);
Object.entries(entityIdsMapping).forEach(([oldEntityId, newEntityId]) => {
if (newEntityId) {
entityUpdates.push(
updateEntityRegistryEntry(this.hass, oldEntityId, {
new_entity_id: newEntityId,
}).catch((err) =>
showAlertDialog(this, {
text: this.hass.localize(
"ui.panel.config.integrations.config_flow.error_saving_entity",
{ error: err.message }
),
})
)
);
}
});
await Promise.allSettled(entityUpdates);
}
fireEvent(this, "flow-update", { step: undefined });

View File

@ -8,6 +8,7 @@ import {
mdiOpenInNew,
mdiPencil,
mdiPlusCircle,
mdiRestore,
} from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
@ -22,12 +23,10 @@ import { computeEntityEntryName } from "../../../common/entity/compute_entity_na
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stringCompare } from "../../../common/string/compare";
import { slugify } from "../../../common/string/slugify";
import { groupBy } from "../../../common/util/group-by";
import "../../../components/entity/ha-battery-icon";
import "../../../components/ha-alert";
import "../../../components/ha-button-menu";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import "../../../components/ha-list-item";
@ -63,6 +62,7 @@ import {
} from "../../../data/entity_registry";
import type { IntegrationManifest } from "../../../data/integration";
import { domainToName } from "../../../data/integration";
import { regenerateEntityIds } from "../../../data/regenerate_entity_ids";
import type { SceneEntities } from "../../../data/scene";
import { showSceneEditor } from "../../../data/scene";
import type { RelatedResult } from "../../../data/search";
@ -663,7 +663,7 @@ export class HaConfigDevicePage extends LitElement {
`
: "";
return html` <hass-subpage
return html`<hass-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.header=${deviceName}
@ -674,6 +674,21 @@ export class HaConfigDevicePage extends LitElement {
@click=${this._showSettings}
.label=${this.hass.localize("ui.panel.config.devices.edit_settings")}
></ha-icon-button>
<ha-md-button-menu slot="toolbar-icon">
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-md-menu-item .clickAction=${this._resetEntityIds}>
<ha-svg-icon slot="start" .path=${mdiRestore}></ha-svg-icon>
<div slot="headline">
${this.hass.localize("ui.panel.config.devices.restore_entity_ids")}
</div>
</ha-md-menu-item>
</ha-md-button-menu>
<div class="container">
<div class="header fullwidth">
${area
@ -1255,7 +1270,14 @@ export class HaConfigDevicePage extends LitElement {
}
}
private async _showSettings() {
private _resetEntityIds = () => {
const entities = this._entities(this.deviceId, this._entityReg).map(
(e) => e.entity_id
);
regenerateEntityIds(this, this.hass, entities);
};
private _showSettings = async () => {
const device = this.hass.devices[this.deviceId];
showDeviceRegistryDetailDialog(this, {
device,
@ -1341,153 +1363,34 @@ export class HaConfigDevicePage extends LitElement {
}
const entities = this._entities(this.deviceId, this._entityReg);
let renameEntityid = false;
let entityIdRenames: { oldId: string; newId?: string }[] = [];
if (this.showAdvanced) {
const oldDeviceSlug = slugify(oldDeviceName);
const newDeviceSlug = slugify(newDeviceName);
entityIdRenames = entities.map((entity) => {
const oldId = entity.entity_id;
if (oldId.includes(oldDeviceSlug)) {
const newId = oldId.replace(oldDeviceSlug, newDeviceSlug);
return { oldId, newId };
}
return { oldId };
});
const dialogRenames = entityIdRenames
.filter((entity) => entity.newId)
.map(
(entity) =>
html`<tr>
<td>${entity.oldId}</td>
<td>${entity.newId}</td>
</tr>`
);
const dialogNoRenames = entityIdRenames
.filter((entity) => !entity.newId)
.map((entity) => html`<li>${entity.oldId}</li>`);
if (dialogRenames.length) {
renameEntityid = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_ids"
),
text: html`${this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_ids_warning"
)} <br /><br />
<ha-expansion-panel outlined>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_will_rename",
{ count: dialogRenames.length }
)}</span
>
<div style="overflow: auto;">
<table style="width: 100%; text-align: var(--float-start);">
<tr>
<th>
${this.hass.localize(
"ui.panel.config.devices.confirm_rename_old"
)}
</th>
<th>
${this.hass.localize(
"ui.panel.config.devices.confirm_rename_new"
)}
</th>
</tr>
${dialogRenames}
</table>
</div>
</ha-expansion-panel>
${dialogNoRenames.length
? html`<ha-expansion-panel outlined>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_wont_rename",
{
count: dialogNoRenames.length,
deviceSlug: oldDeviceSlug,
}
)}</span
>
${dialogNoRenames}</ha-expansion-panel
>`
: nothing} `,
confirmText: this.hass.localize("ui.common.rename"),
dismissText: this.hass.localize("ui.common.no"),
warning: true,
});
} else if (dialogNoRenames.length) {
await showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_no_renamable_entity_ids"
),
text: html`<ha-expansion-panel outlined>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.devices.confirm_rename_entity_wont_rename",
{
deviceSlug: oldDeviceSlug,
count: dialogNoRenames.length,
}
)}</span
>
${dialogNoRenames}
</ha-expansion-panel>`,
});
}
}
const updateProms = entities.map((entity) => {
const name = entity.name || entity.stateName;
let newEntityId: string | undefined;
let newName: string | null | undefined;
let shouldUpdateName: boolean;
let shouldUpdateEntityId = false;
if (entity.has_entity_name && !entity.name) {
shouldUpdateName = false;
} else if (
return undefined;
}
if (
entity.has_entity_name &&
(entity.name === oldDeviceName || entity.name === newDeviceName)
) {
shouldUpdateName = true;
// clear name if it matches the device name and it uses the device name (entity naming)
newName = null;
} else if (name && name.includes(oldDeviceName)) {
shouldUpdateName = true;
newName = name.replace(oldDeviceName, newDeviceName);
} else {
shouldUpdateName = false;
}
if (renameEntityid) {
const entityRename = entityIdRenames?.find(
(item) => item.oldId === entity.entity_id
);
if (entityRename?.newId) {
shouldUpdateEntityId = true;
newEntityId = entityRename.newId;
}
}
if (newName === undefined && newEntityId === undefined) {
return undefined;
}
return updateEntityRegistryEntry(this.hass!, entity.entity_id, {
name: shouldUpdateName ? newName : undefined,
new_entity_id: shouldUpdateEntityId ? newEntityId : undefined,
name: newName,
});
});
await Promise.all(updateProms);
},
});
}
};
private async _enableDevice(): Promise<void> {
await updateDeviceRegistryEntry(this.hass, this.deviceId, {

View File

@ -1,5 +1,5 @@
import "@material/mwc-button/mwc-button";
import { mdiContentCopy } from "@mdi/js";
import { mdiContentCopy, mdiRestore } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
@ -60,6 +60,7 @@ import type {
SensorEntityOptions,
} from "../../../data/entity_registry";
import {
getAutomaticEntityIds,
subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
@ -758,11 +759,16 @@ export class EntityRegistrySettingsEditor extends LitElement {
autocorrect="off"
input-spellcheck="false"
>
<ha-icon-button
@click=${this._copyEntityId}
slot="trailingIcon"
.path=${mdiContentCopy}
></ha-icon-button>
<div class="layout horizontal" slot="trailingIcon">
<ha-icon-button
@click=${this._restoreEntityId}
.path=${mdiRestore}
></ha-icon-button>
<ha-icon-button
@click=${this._copyEntityId}
.path=${mdiContentCopy}
></ha-icon-button>
</div>
</ha-textfield>
${!this.entry.device_id
? html`<ha-area-picker
@ -1286,6 +1292,13 @@ export class EntityRegistrySettingsEditor extends LitElement {
this._icon = ev.detail.value;
}
private async _restoreEntityId(): Promise<void> {
const entityIds = await getAutomaticEntityIds(this.hass, [
this._origEntityId,
]);
this._entityId = entityIds[this._origEntityId] || this._origEntityId;
}
private async _copyEntityId(): Promise<void> {
await copyToClipboard(this._entityId);
showToast(this, {
@ -1508,7 +1521,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
--text-field-prefix-padding-right: 0;
--textfield-icon-trailing-padding: 0;
}
ha-textfield.entityId > ha-icon-button {
ha-textfield.entityId ha-icon-button {
position: relative;
right: -8px;
--mdc-icon-button-size: 36px;

View File

@ -10,6 +10,7 @@ import {
mdiMenuDown,
mdiPencilOff,
mdiPlus,
mdiRestore,
mdiRestoreAlert,
mdiToggleSwitch,
mdiToggleSwitchOffOutline,
@ -95,6 +96,7 @@ import {
createLabelRegistryEntry,
subscribeLabelRegistry,
} from "../../../data/label_registry";
import { regenerateEntityIds } from "../../../data/regenerate_entity_ids";
import {
showAlertDialog,
showConfirmationDialog,
@ -952,6 +954,21 @@ ${
)}
</div>
</ha-md-menu-item>
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
<ha-md-menu-item .clickAction=${this._restoreEntityIdSelected}>
<ha-svg-icon
slot="start"
.path=${mdiRestore}
></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.entities.picker.restore_entity_id_selected.button"
)}
</div>
</ha-md-menu-item>
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>
<ha-md-menu-item .clickAction=${this._removeSelected} class="warning">
@ -1371,6 +1388,12 @@ ${rejected
});
};
private _restoreEntityIdSelected = () => {
regenerateEntityIds(this, this.hass, this._selected);
this._clearSelection();
};
private _removeSelected = async () => {
if (!this._entities || !this.hass) {
return;

View File

@ -6,7 +6,6 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeStateName } from "../../../../../common/entity/compute_state_name";
import { stringCompare } from "../../../../../common/string/compare";
import { slugify } from "../../../../../common/string/slugify";
import "../../../../../components/entity/state-badge";
import "../../../../../components/ha-area-picker";
import "../../../../../components/ha-card";
@ -14,6 +13,7 @@ import "../../../../../components/ha-textfield";
import { updateDeviceRegistryEntry } from "../../../../../data/device_registry";
import type { EntityRegistryEntry } from "../../../../../data/entity_registry";
import {
getAutomaticEntityIds,
subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../../../data/entity_registry";
@ -23,7 +23,6 @@ import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import type { EntityRegistryStateEntry } from "../../../devices/ha-config-device-page";
import { getIeeeTail } from "./functions";
@customElement("zha-device-card")
class ZHADeviceCard extends SubscribeMixin(LitElement) {
@ -135,30 +134,35 @@ class ZHADeviceCard extends SubscribeMixin(LitElement) {
}
const entities = this._deviceEntities(device.device_reg_id, this._entities);
const oldDeviceEntityId = slugify(oldDeviceName);
const newDeviceEntityId = slugify(newDeviceName);
const ieeeTail = getIeeeTail(device.ieee);
const entityIdsMapping = getAutomaticEntityIds(
this.hass,
entities.map((entity) => entity.entity_id)
);
const updateProms = entities.map((entity) => {
const name = entity.name || entity.stateName;
let newEntityId: string | null = null;
let newName: string | null = null;
const name = entity.name;
const newEntityId = entityIdsMapping[entity.entity_id];
let newName: string | null | undefined;
if (name && name.includes(oldDeviceName)) {
newName = name.replace(` ${ieeeTail}`, "");
newName = newName.replace(oldDeviceName, newDeviceName);
newEntityId = entity.entity_id.replace(`_${ieeeTail}`, "");
newEntityId = newEntityId.replace(oldDeviceEntityId, newDeviceEntityId);
if (entity.has_entity_name && !entity.name) {
newName = undefined;
} else if (
entity.has_entity_name &&
(entity.name === oldDeviceName || entity.name === newDeviceName)
) {
// clear name if it matches the device name and it uses the device name (entity naming)
newName = null;
} else if (name && name.includes(oldDeviceName)) {
newName = name.replace(oldDeviceName, newDeviceName);
}
if (!newName && !newEntityId) {
if (newName !== undefined && !newEntityId) {
return undefined;
}
return updateEntityRegistryEntry(this.hass!, entity.entity_id, {
name: newName || name,
disabled_by: entity.disabled_by,
new_entity_id: newEntityId || entity.entity_id,
name: newName,
new_entity_id: newEntityId || undefined,
});
});
await Promise.all(updateProms);

View File

@ -49,11 +49,10 @@ import "../../../../../../components/ha-fade-in";
import "../../../../../../components/ha-icon-button";
import "../../../../../../components/ha-qr-scanner";
import { computeStateName } from "../../../../../../common/entity/compute_state_name";
import { navigate } from "../../../../../../common/navigate";
import { slugify } from "../../../../../../common/string/slugify";
import type { EntityRegistryEntry } from "../../../../../../data/entity_registry";
import {
getAutomaticEntityIds,
subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../../../../data/entity_registry";
@ -874,42 +873,47 @@ class DialogZWaveJSAddNode extends SubscribeMixin(LitElement) {
if (nameChanged) {
// rename entities
const oldDeviceEntityId = slugify(oldDeviceName);
const newDeviceEntityId = slugify(newDeviceName);
const entities = this._entities.filter(
(entity) => entity.device_id === this._device!.id
);
const entityIdsMapping = getAutomaticEntityIds(
this.hass,
entities.map((entity) => entity.entity_id)
);
await Promise.all(
this._entities
.filter((entity) => entity.device_id === this._device!.id)
.map((entity) => {
const entityState = this.hass.states[entity.entity_id];
const name =
entity.name ||
(entityState && computeStateName(entityState));
let newEntityId: string | null = null;
let newName: string | null = null;
entities.map((entity) => {
const name = entity.name;
let newName: string | null | undefined;
const newEntityId = entityIdsMapping[entity.entity_id];
if (name && name.includes(oldDeviceName)) {
newName = name.replace(oldDeviceName, newDeviceName);
}
if (entity.has_entity_name && !entity.name) {
newName = undefined;
} else if (
entity.has_entity_name &&
(entity.name === oldDeviceName ||
entity.name === newDeviceName)
) {
// clear name if it matches the device name and it uses the device name (entity naming)
newName = null;
} else if (name && name.includes(oldDeviceName)) {
newName = name.replace(oldDeviceName, newDeviceName);
}
newEntityId = entity.entity_id.replace(
oldDeviceEntityId,
newDeviceEntityId
);
if (
(newName === undefined && !newEntityId) ||
newEntityId === entity.entity_id
) {
return undefined;
}
if (!newName && newEntityId === entity.entity_id) {
return undefined;
}
return updateEntityRegistryEntry(
this.hass!,
entity.entity_id,
{
name: newName || name,
new_entity_id: newEntityId || entity.entity_id,
}
);
})
return updateEntityRegistryEntry(this.hass!, entity.entity_id, {
name: newName || name,
new_entity_id: newEntityId || undefined,
});
})
);
}
} catch (_err: any) {

View File

@ -339,6 +339,7 @@
"remove": "Remove",
"enable": "Enable",
"disable": "Disable",
"update": "Update",
"hide": "Hide",
"close": "Close",
"clear": "Clear",
@ -1535,6 +1536,17 @@
"no_aliases": "Configure aliases and expose settings for voice assistants"
}
},
"recreate_entity_ids": {
"confirm_rename_title": "Are you sure you want to recreate your entity IDs?",
"confirm_rename_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to manually edit them yourself to use the new entity IDs!",
"will_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will be renamed",
"new": "New",
"old": "Old",
"cant_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} can not be regenerated because they are not available",
"wont_change": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will not change",
"update_entity_error": "Updating the entity ID of {entityId} failed",
"confirm_no_renamable_entity_ids": "No renamable entity IDs"
},
"device-registry-detail": {
"name": "[%key:ui::panel::config::devices::name%]",
"enabled_label": "[%key:ui::panel::config::devices::enabled_label%]",
@ -4941,6 +4953,7 @@
"filtering_by_config_entry": "[%key:ui::panel::config::entities::picker::filtering_by_config_entry%]",
"device_info": "{type} info",
"edit_settings": "Edit settings",
"restore_entity_ids": "Recreate entity IDs",
"unnamed_device": "Unnamed {type}",
"unknown_error": "Unknown error",
"name": "Name",
@ -5040,13 +5053,6 @@
"disabled_entities": "+{count} disabled {count, plural,\n one {entity}\n other {entities}\n}",
"hidden": "Hidden"
},
"confirm_rename_entity_ids": "Do you also want to rename the entity IDs of your entities?",
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, dashboards) that is currently using these entities! You will have to manually edit them yourself to use the new entity IDs!",
"confirm_rename_entity_will_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will be renamed",
"confirm_rename_new": "New",
"confirm_rename_old": "Old",
"confirm_rename_entity_wont_rename": "{count} {count, plural,\n one {entity ID}\n other {entity IDs}\n} will not be renamed as they do not contain the current device name ({deviceSlug})",
"confirm_rename_entity_no_renamable_entity_ids": "No renamable entity IDs",
"confirm_disable_config_entry": "There are no more devices for the config entry {entry_name}, do you want to instead disable the config entry?",
"update_device_error": "Updating the device failed",
"disabled": "Disabled",
@ -5122,6 +5128,12 @@
"confirm_title": "Do you want to disable {number} {number, plural,\n one {entity}\n other {entities}\n}?",
"confirm_text": "Disabled entities will not be added to Home Assistant."
},
"restore_entity_id_selected": {
"button": "Recreate entity IDs of selected",
"confirm_title": "Recreate entity IDs?",
"confirm_text": "Are you sure you want to change the entity IDs of these entities? You will have to change you dashboards, automations and scripts to use the new entity IDs.",
"changes": "The following entity IDs will be updated:"
},
"delete_selected": {
"button": "Delete selected",
"confirm_title": "Delete selected entities?",
@ -9081,7 +9093,6 @@
"yes": "[%key:ui::common::yes%]",
"no": "[%key:ui::common::no%]",
"add": "[%key:supervisor::dialog::repositories::add%]",
"description": "Description",
"failed_to_restart_name": "Failed to restart {name}",
"failed_to_update_name": "Failed to update {name}",
"learn_more": "Learn more",