diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index 3f8ffc70e39f..e1205b9d74ab 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -121,6 +121,7 @@ type HelperItem = { entity?: HassEntity; category: string | undefined; label_entries: LabelRegistryEntry[]; + disabled?: boolean; }; // This groups items by a key but only returns last entry per key. @@ -191,6 +192,8 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { @state() private _stateItems: HassEntity[] = []; + @state() private _disabledEntityEntries?: EntityRegistryEntry[]; + @state() private _entityEntries?: Record; @state() private _configEntries?: Record; @@ -201,6 +204,8 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { @state() private _activeFilters?: string[]; + @state() private _helperManifests?: { [domain: string]: IntegrationManifest }; + @storage({ storage: "sessionStorage", key: "helpers-table-filters", @@ -427,6 +432,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { ( localize: LocalizeFunc, stateItems: HassEntity[], + disabledEntries: EntityRegistryEntry[], entityEntries: Record, configEntries: Record, entityReg: EntityRegistryEntry[], @@ -466,29 +472,43 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { }; }); - const entries = Object.values(configEntriesCopy).map((configEntry) => { - const entityEntry = Object.values(entityEntries).find( - (entry) => entry.config_entry_id === configEntry.entry_id - ); - const entityIsDisabled = !!entityEntry?.disabled_by; - return { - id: entityIsDisabled ? entityEntry.entity_id : configEntry.entry_id, - entity_id: entityIsDisabled ? entityEntry.entity_id : "", - icon: entityIsDisabled - ? mdiCancel - : configEntry.state === "setup_in_progress" - ? mdiProgressHelper - : mdiAlertCircle, - name: configEntry.title || "", - editable: true, - type: configEntry.domain, - configEntry, - entity: undefined, - selectable: entityIsDisabled, - }; - }); - - return [...states, ...entries] + const entries = Object.values(configEntriesCopy) + .map((configEntry) => { + const entityEntry = Object.values(entityEntries).find( + (entry) => entry.config_entry_id === configEntry.entry_id + ); + return { + id: configEntry.entry_id, + entity_id: "", + icon: + configEntry.state === "setup_in_progress" + ? mdiProgressHelper + : mdiAlertCircle, + name: configEntry.title || "", + editable: true, + type: configEntry.domain, + configEntry, + entity: undefined, + selectable: false, + disabled: !!entityEntry?.disabled_by, + }; + }) + .filter((e) => !e.disabled); + + const disabledItems = (disabledEntries || []).map((e) => ({ + id: e.entity_id, + entity_id: e.entity_id, + icon: mdiCancel, + name: e.original_name || e.entity_id, + editable: true, + type: e.platform, + configEntry: undefined, + entity: undefined, + selectable: true, + disabled: true, + })); + + return [...states, ...entries, ...disabledItems] .filter((item) => filteredStateItems ? filteredStateItems?.includes(item.entity_id) @@ -519,6 +539,14 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { } ); + private _labelsForEntity(entityId: string): string[] { + return ( + this.hass.entities[entityId]?.labels || + this._entityReg.find((e) => e.entity_id === entityId)?.labels || + [] + ); + } + protected render(): TemplateResult { if ( !this.hass || @@ -557,12 +585,12 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { const labelItems = html`${this._labels?.map((label) => { const color = label.color ? computeCssColor(label.color) : undefined; const selected = this._selected.every((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) + this._labelsForEntity(entityId).includes(label.label_id) ); const partial = !selected && this._selected.some((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) + this._labelsForEntity(entityId).includes(label.label_id) ); return html` filter.includes(lbl)) ) .forEach((stateItem) => labelItems.add(stateItem.entity_id)); + (this._disabledEntityEntries || []) + .filter((entry) => entry.labels.some((lbl) => filter.includes(lbl))) + .forEach((entry) => labelItems.add(entry.entity_id)); if (!items) { items = labelItems; continue; @@ -879,6 +911,9 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { )?.categories.helpers ) .forEach((stateItem) => categoryItems.add(stateItem.entity_id)); + (this._disabledEntityEntries || []) + .filter((entry) => filter[0] === entry.categories.helpers) + .forEach((entry) => categoryItems.add(entry.entity_id)); if (!items) { items = categoryItems; continue; @@ -960,14 +995,13 @@ ${rejected private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; this._selected.forEach((entityId) => { + const labels = this._labelsForEntity(entityId); promises.push( updateEntityRegistryEntry(this.hass, entityId, { labels: action === "add" - ? this.hass.entities[entityId].labels.concat(label) - : this.hass.entities[entityId].labels.filter( - (lbl) => lbl !== label - ), + ? labels.concat(label) + : labels.filter((lbl) => lbl !== label), }) ); }); @@ -1012,18 +1046,19 @@ ${rejected const manifests: { [domain: string]: IntegrationManifest } = {}; for (const manifest of fetchedManifests) { - manifests[manifest.domain] = manifest; + if (manifest.integration_type === "helper") { + manifests[manifest.domain] = manifest; + } } + this._helperManifests = manifests; + const entityDomains = {}; const domains = new Set(); for (const [entity, source] of Object.entries(entitySources)) { const domain = source.domain; - if ( - !(domain in manifests) || - manifests[domain].integration_type !== "helper" - ) { + if (!(domain in manifests)) { continue; } entityDomains[entity] = domain; @@ -1101,6 +1136,20 @@ ${rejected return; } + if ( + (changedProps.has("_helperManifests") || + changedProps.has("_entityEntries") || + changedProps.has("_configEntries")) && + this._helperManifests + ) { + this._disabledEntityEntries = Object.values(this._entityEntries).filter( + (e) => + e.disabled_by && + (e.platform in this._helperManifests! || + (e.config_entry_id && e.config_entry_id in this._configEntries!)) + ); + } + let changed = !this._stateItems || changedProps.has("_entityEntries") ||