diff --git a/src/data/config_entries.ts b/src/data/config_entries.ts index 5a3757b8e4..42124f53d6 100644 --- a/src/data/config_entries.ts +++ b/src/data/config_entries.ts @@ -1,3 +1,4 @@ +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { HomeAssistant } from "../types"; export interface ConfigEntry { @@ -44,6 +45,29 @@ export const RECOVERABLE_STATES: ConfigEntry["state"][] = [ "setup_retry", ]; +export interface ConfigEntryUpdate { + // null means no update as is the current state + type: null | "added" | "removed" | "updated"; + entry: ConfigEntry; +} + +export const subscribeConfigEntries = ( + hass: HomeAssistant, + callbackFunction: (message: ConfigEntryUpdate[]) => void, + filters?: { type?: "helper" | "integration"; domain?: string } +): Promise => { + const params: any = { + type: "config_entries/subscribe", + }; + if (filters && filters.type) { + params.type_filter = filters.type; + } + return hass.connection.subscribeMessage( + (message) => callbackFunction(message), + params + ); +}; + export const getConfigEntries = ( hass: HomeAssistant, filters?: { type?: "helper" | "integration"; domain?: string } diff --git a/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts b/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts index aaced030d6..8329685a6e 100644 --- a/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts +++ b/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts @@ -170,7 +170,6 @@ class DialogConfigEntrySystemOptions extends LitElement { ), }); } - this._params!.entryUpdated(result.config_entry); this.closeDialog(); } catch (err: any) { this._error = err.message || "Unknown error"; diff --git a/src/dialogs/config-entry-system-options/show-dialog-config-entry-system-options.ts b/src/dialogs/config-entry-system-options/show-dialog-config-entry-system-options.ts index 77415e02cd..0ea3b9d966 100644 --- a/src/dialogs/config-entry-system-options/show-dialog-config-entry-system-options.ts +++ b/src/dialogs/config-entry-system-options/show-dialog-config-entry-system-options.ts @@ -5,7 +5,6 @@ import { IntegrationManifest } from "../../data/integration"; export interface ConfigEntrySystemOptionsDialogParams { entry: ConfigEntry; manifest?: IntegrationManifest; - entryUpdated(entry: ConfigEntry): void; } export const loadConfigEntrySystemOptionsDialog = () => diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index 82ce8bf60f..3ec709d2c5 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -14,7 +14,7 @@ import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; +import { fireEvent } from "../../../common/dom/fire_event"; import { protocolIntegrationPicked } from "../../../common/integrations/protocolIntegrationPicked"; import { navigate } from "../../../common/navigate"; import { caseInsensitiveStringCompare } from "../../../common/string/compare"; @@ -28,7 +28,10 @@ import "../../../components/ha-fab"; import "../../../components/ha-icon-button"; import "../../../components/ha-svg-icon"; import "../../../components/search-input"; -import { ConfigEntry, getConfigEntries } from "../../../data/config_entries"; +import { + ConfigEntry, + subscribeConfigEntries, +} from "../../../data/config_entries"; import { getConfigFlowHandlers, getConfigFlowInProgressCollection, @@ -151,7 +154,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { @state() private _diagnosticHandlers?: Record; - public hassSubscribe(): UnsubscribeFunc[] { + public hassSubscribe(): Array> { return [ subscribeEntityRegistry(this.hass.connection, (entries) => { this._entityRegistryEntries = entries; @@ -180,6 +183,53 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { localized_title: localizeConfigFlowTitle(this.hass.localize, flow), })); }), + subscribeConfigEntries( + this.hass, + (messages) => { + let fullUpdate = false; + const newEntries: ConfigEntryExtended[] = []; + messages.forEach((message) => { + if (message.type === null || message.type === "added") { + newEntries.push({ + ...message.entry, + localized_domain_name: domainToName( + this.hass.localize, + message.entry.domain + ), + }); + if (message.type === null) { + fullUpdate = true; + } + } else if (message.type === "removed") { + this._configEntries = this._configEntries!.filter( + (entry) => entry.entry_id !== message.entry.entry_id + ); + } else if (message.type === "updated") { + const newEntry = message.entry; + this._configEntries = this._configEntries!.map((entry) => + entry.entry_id === newEntry.entry_id + ? { + ...newEntry, + localized_domain_name: entry.localized_domain_name, + } + : entry + ); + } + }); + if (!newEntries.length && !fullUpdate) { + return; + } + const existingEntries = fullUpdate ? [] : this._configEntries; + this._configEntries = [...existingEntries!, ...newEntries].sort( + (conf1, conf2) => + caseInsensitiveStringCompare( + conf1.localized_domain_name + conf1.title, + conf2.localized_domain_name + conf2.title + ) + ); + }, + { type: "integration" } + ), ]; } @@ -257,7 +307,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { protected firstUpdated(changed: PropertyValues) { super.firstUpdated(changed); - this._loadConfigEntries(); const localizePromise = this.hass.loadBackendTranslation( "title", undefined, @@ -411,11 +460,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { `} -
+
${this._showIgnored ? ignoredConfigEntries.map( (entry: ConfigEntryExtended) => html` @@ -542,29 +587,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { ev.preventDefault(); } - private _loadConfigEntries() { - getConfigEntries(this.hass, { type: "integration" }).then( - (configEntries) => { - this._configEntries = configEntries - .map( - (entry: ConfigEntry): ConfigEntryExtended => ({ - ...entry, - localized_domain_name: domainToName( - this.hass.localize, - entry.domain - ), - }) - ) - .sort((conf1, conf2) => - caseInsensitiveStringCompare( - conf1.localized_domain_name + conf1.title, - conf2.localized_domain_name + conf2.title - ) - ); - } - ); - } - private async _scanUSBDevices() { if (!isComponentLoaded(this.hass, "usb")) { return; @@ -602,23 +624,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { } } - private _handleEntryRemoved(ev: HASSDomEvent) { - this._configEntries = this._configEntries!.filter( - (entry) => entry.entry_id !== ev.detail.entryId - ); - } - - private _handleEntryUpdated(ev: HASSDomEvent) { - const newEntry = ev.detail.entry; - this._configEntries = this._configEntries!.map((entry) => - entry.entry_id === newEntry.entry_id - ? { ...newEntry, localized_domain_name: entry.localized_domain_name } - : entry - ); - } - private _handleFlowUpdated() { - this._loadConfigEntries(); getConfigFlowInProgressCollection(this.hass.connection).refresh(); this._fetchManifests(); } diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 843bf2223c..59d61f189a 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -11,6 +11,7 @@ import { mdiDotsVertical, mdiDownload, mdiOpenInNew, + mdiReloadAlert, mdiProgressHelper, mdiPlayCircleOutline, mdiReload, @@ -24,7 +25,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { fireEvent } from "../../../common/dom/fire_event"; import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import "../../../components/ha-button-menu"; import "../../../components/ha-card"; @@ -184,7 +184,9 @@ export class HaIntegrationCard extends LitElement { ? html` ${this.hass.localize( @@ -231,6 +233,9 @@ export class HaIntegrationCard extends LitElement { "ui.panel.config.integrations.config_entry.setup_in_progress", ]; } else if (ERROR_STATES.includes(item.state)) { + if (item.state === "setup_retry") { + icon = mdiReloadAlert; + } stateText = [ `ui.panel.config.integrations.config_entry.state.${item.state}`, ]; @@ -622,10 +627,6 @@ export class HaIntegrationCard extends LitElement { showConfigEntrySystemOptionsDialog(this, { entry: configEntry, manifest: this.manifest, - entryUpdated: (entry) => - fireEvent(this, "entry-updated", { - entry, - }), }); } @@ -660,9 +661,6 @@ export class HaIntegrationCard extends LitElement { ), }); } - fireEvent(this, "entry-updated", { - entry: { ...configEntry, disabled_by: "user" }, - }); } private async _enableIntegration(configEntry: ConfigEntry) { @@ -688,9 +686,6 @@ export class HaIntegrationCard extends LitElement { ), }); } - fireEvent(this, "entry-updated", { - entry: { ...configEntry, disabled_by: null }, - }); } private async _removeIntegration(configEntry: ConfigEntry) { @@ -707,7 +702,6 @@ export class HaIntegrationCard extends LitElement { return; } const result = await deleteConfigEntry(this.hass, entryId); - fireEvent(this, "entry-removed", { entryId }); if (result.require_restart) { showAlertDialog(this, { @@ -743,10 +737,9 @@ export class HaIntegrationCard extends LitElement { if (newName === null) { return; } - const result = await updateConfigEntry(this.hass, configEntry.entry_id, { + await updateConfigEntry(this.hass, configEntry.entry_id, { title: newName, }); - fireEvent(this, "entry-updated", { entry: result.config_entry }); } private async _signUrl(ev) {