Add support for subscribing to config entry changes (#13585)

This commit is contained in:
J. Nick Koston 2022-09-11 10:22:26 -05:00 committed by GitHub
parent 07b5856190
commit d323ab6726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 65 deletions

View File

@ -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<UnsubscribeFunc> => {
const params: any = {
type: "config_entries/subscribe",
};
if (filters && filters.type) {
params.type_filter = filters.type;
}
return hass.connection.subscribeMessage<ConfigEntryUpdate[]>(
(message) => callbackFunction(message),
params
);
};
export const getConfigEntries = (
hass: HomeAssistant,
filters?: { type?: "helper" | "integration"; domain?: string }

View File

@ -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";

View File

@ -5,7 +5,6 @@ import { IntegrationManifest } from "../../data/integration";
export interface ConfigEntrySystemOptionsDialogParams {
entry: ConfigEntry;
manifest?: IntegrationManifest;
entryUpdated(entry: ConfigEntry): void;
}
export const loadConfigEntrySystemOptionsDialog = () =>

View File

@ -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<string, boolean>;
public hassSubscribe(): UnsubscribeFunc[] {
public hassSubscribe(): Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
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) {
</div>
`}
<div
class="container"
@entry-removed=${this._handleEntryRemoved}
@entry-updated=${this._handleEntryUpdated}
>
<div class="container">
${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<ConfigEntryRemovedEvent>) {
this._configEntries = this._configEntries!.filter(
(entry) => entry.entry_id !== ev.detail.entryId
);
}
private _handleEntryUpdated(ev: HASSDomEvent<ConfigEntryUpdatedEvent>) {
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();
}

View File

@ -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`<span>
<ha-svg-icon
class="error"
.path=${mdiAlertCircle}
.path=${item.state === "setup_retry"
? mdiReloadAlert
: mdiAlertCircle}
></ha-svg-icon
><paper-tooltip animation-delay="0" position="left">
${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) {