Allow entities table to delete helpers (#22248)

* Allow deleting helpers in entities table

* Fix calling the right delete on restored legacy helpers
This commit is contained in:
karwosts 2024-11-12 10:16:24 -08:00 committed by GitHub
parent db03e271f5
commit 28703b39da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 153 additions and 8 deletions

View File

@ -0,0 +1,91 @@
import type { HomeAssistant } from "../../types";
import type { IntegrationManifest } from "../../data/integration";
import { computeDomain } from "./compute_domain";
import { HELPERS_CRUD } from "../../data/helpers_crud";
import type { Helper } from "../../panels/config/helpers/const";
import { isHelperDomain } from "../../panels/config/helpers/const";
import { isComponentLoaded } from "../config/is_component_loaded";
import type { EntityRegistryEntry } from "../../data/entity_registry";
import { removeEntityRegistryEntry } from "../../data/entity_registry";
import type { ConfigEntry } from "../../data/config_entries";
import { deleteConfigEntry } from "../../data/config_entries";
export const isDeletableEntity = (
hass: HomeAssistant,
entity_id: string,
manifests: IntegrationManifest[],
entityRegistry: EntityRegistryEntry[],
configEntries: ConfigEntry[],
fetchedHelpers: Helper[]
): boolean => {
const restored = !!hass.states[entity_id]?.attributes.restored;
if (restored) {
return true;
}
const domain = computeDomain(entity_id);
const entityRegEntry = entityRegistry.find((e) => e.entity_id === entity_id);
if (isHelperDomain(domain)) {
return !!(
isComponentLoaded(hass, domain) &&
entityRegEntry &&
fetchedHelpers.some((e) => e.id === entityRegEntry.unique_id)
);
}
const configEntryId = entityRegEntry?.config_entry_id;
if (!configEntryId) {
return false;
}
const configEntry = configEntries.find((e) => e.entry_id === configEntryId);
return (
manifests.find((m) => m.domain === configEntry?.domain)
?.integration_type === "helper"
);
};
export const deleteEntity = (
hass: HomeAssistant,
entity_id: string,
manifests: IntegrationManifest[],
entityRegistry: EntityRegistryEntry[],
configEntries: ConfigEntry[],
fetchedHelpers: Helper[]
) => {
// This function assumes the entity_id already was validated by isDeletableEntity and does not repeat all those checks.
const domain = computeDomain(entity_id);
const entityRegEntry = entityRegistry.find((e) => e.entity_id === entity_id);
if (isHelperDomain(domain)) {
if (isComponentLoaded(hass, domain)) {
if (
entityRegEntry &&
fetchedHelpers.some((e) => e.id === entityRegEntry.unique_id)
) {
HELPERS_CRUD[domain].delete(hass, entityRegEntry.unique_id);
return;
}
}
const stateObj = hass.states[entity_id];
if (!stateObj?.attributes.restored) {
return;
}
removeEntityRegistryEntry(hass, entity_id);
return;
}
const configEntryId = entityRegEntry?.config_entry_id;
const configEntry = configEntryId
? configEntries.find((e) => e.entry_id === configEntryId)
: undefined;
const isHelperEntryType = configEntry
? manifests.find((m) => m.domain === configEntry.domain)
?.integration_type === "helper"
: false;
if (isHelperEntryType) {
deleteConfigEntry(hass, configEntryId!);
return;
}
removeEntityRegistryEntry(hass, entity_id);
};

View File

@ -27,6 +27,13 @@ import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { storage } from "../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
import {
isDeletableEntity,
deleteEntity,
} from "../../../common/entity/delete_entity";
import type { Helper } from "../helpers/const";
import { isHelperDomain } from "../helpers/const";
import { HELPERS_CRUD } from "../../../data/helpers_crud";
import { computeStateName } from "../../../common/entity/compute_state_name";
import {
PROTOCOL_INTEGRATIONS,
@ -73,12 +80,15 @@ import type {
} from "../../../data/entity_registry";
import {
computeEntityRegistryName,
removeEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import type { IntegrationManifest } from "../../../data/integration";
import {
fetchIntegrationManifests,
domainToName,
} from "../../../data/integration";
import type { EntitySources } from "../../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../../data/entity_sources";
import { domainToName } from "../../../data/integration";
import type { LabelRegistryEntry } from "../../../data/label_registry";
import {
createLabelRegistryEntry,
@ -136,6 +146,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
@state() private _entries?: ConfigEntry[];
@state() private _manifests?: IntegrationManifest[];
@state()
@consume({ context: fullEntitiesContext, subscribe: true })
_entities!: EntityRegistryEntry[];
@ -1280,11 +1292,46 @@ ${rejected
});
}
private _removeSelected() {
const removeableEntities = this._selected.filter((entity) => {
const stateObj = this.hass.states[entity];
return stateObj?.attributes.restored;
private async _removeSelected() {
if (!this._entities || !this.hass) {
return;
}
const manifestsProm = this._manifests
? undefined
: fetchIntegrationManifests(this.hass);
const helperDomains = [
...new Set(this._selected.map((s) => computeDomain(s))),
].filter((d) => isHelperDomain(d));
const configEntriesProm = this._entries
? undefined
: this._loadConfigEntries();
const domainProms = helperDomains.map((d) =>
HELPERS_CRUD[d].fetch(this.hass)
);
const helpersResult = await Promise.all(domainProms);
let fetchedHelpers: Helper[] = [];
helpersResult.forEach((r) => {
fetchedHelpers = fetchedHelpers.concat(r);
});
if (manifestsProm) {
this._manifests = await manifestsProm;
}
if (configEntriesProm) {
await configEntriesProm;
}
const removeableEntities = this._selected.filter((entity_id) =>
isDeletableEntity(
this.hass,
entity_id,
this._manifests!,
this._entities,
this._entries!,
fetchedHelpers
)
);
showConfirmationDialog(this, {
title: this.hass.localize(
`ui.panel.config.entities.picker.delete_selected.confirm_title`
@ -1305,8 +1352,15 @@ ${rejected
dismissText: this.hass.localize("ui.common.cancel"),
destructive: true,
confirm: () => {
removeableEntities.forEach((entity) =>
removeEntityRegistryEntry(this.hass, entity)
removeableEntities.forEach((entity_id) =>
deleteEntity(
this.hass,
entity_id,
this._manifests!,
this._entities,
this._entries!,
fetchedHelpers
)
);
this._clearSelection();
},