mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 10:16:46 +00:00
Add support for subscribing to config entry changes (#13585)
This commit is contained in:
parent
07b5856190
commit
d323ab6726
@ -1,3 +1,4 @@
|
|||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface ConfigEntry {
|
export interface ConfigEntry {
|
||||||
@ -44,6 +45,29 @@ export const RECOVERABLE_STATES: ConfigEntry["state"][] = [
|
|||||||
"setup_retry",
|
"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 = (
|
export const getConfigEntries = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
filters?: { type?: "helper" | "integration"; domain?: string }
|
filters?: { type?: "helper" | "integration"; domain?: string }
|
||||||
|
@ -170,7 +170,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._params!.entryUpdated(result.config_entry);
|
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err.message || "Unknown error";
|
this._error = err.message || "Unknown error";
|
||||||
|
@ -5,7 +5,6 @@ import { IntegrationManifest } from "../../data/integration";
|
|||||||
export interface ConfigEntrySystemOptionsDialogParams {
|
export interface ConfigEntrySystemOptionsDialogParams {
|
||||||
entry: ConfigEntry;
|
entry: ConfigEntry;
|
||||||
manifest?: IntegrationManifest;
|
manifest?: IntegrationManifest;
|
||||||
entryUpdated(entry: ConfigEntry): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadConfigEntrySystemOptionsDialog = () =>
|
export const loadConfigEntrySystemOptionsDialog = () =>
|
||||||
|
@ -14,7 +14,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
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 { protocolIntegrationPicked } from "../../../common/integrations/protocolIntegrationPicked";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||||
@ -28,7 +28,10 @@ import "../../../components/ha-fab";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/search-input";
|
import "../../../components/search-input";
|
||||||
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
import {
|
||||||
|
ConfigEntry,
|
||||||
|
subscribeConfigEntries,
|
||||||
|
} from "../../../data/config_entries";
|
||||||
import {
|
import {
|
||||||
getConfigFlowHandlers,
|
getConfigFlowHandlers,
|
||||||
getConfigFlowInProgressCollection,
|
getConfigFlowInProgressCollection,
|
||||||
@ -151,7 +154,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _diagnosticHandlers?: Record<string, boolean>;
|
@state() private _diagnosticHandlers?: Record<string, boolean>;
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
|
||||||
return [
|
return [
|
||||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||||
this._entityRegistryEntries = entries;
|
this._entityRegistryEntries = entries;
|
||||||
@ -180,6 +183,53 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
localized_title: localizeConfigFlowTitle(this.hass.localize, flow),
|
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) {
|
protected firstUpdated(changed: PropertyValues) {
|
||||||
super.firstUpdated(changed);
|
super.firstUpdated(changed);
|
||||||
this._loadConfigEntries();
|
|
||||||
const localizePromise = this.hass.loadBackendTranslation(
|
const localizePromise = this.hass.loadBackendTranslation(
|
||||||
"title",
|
"title",
|
||||||
undefined,
|
undefined,
|
||||||
@ -411,11 +460,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
|
||||||
<div
|
<div class="container">
|
||||||
class="container"
|
|
||||||
@entry-removed=${this._handleEntryRemoved}
|
|
||||||
@entry-updated=${this._handleEntryUpdated}
|
|
||||||
>
|
|
||||||
${this._showIgnored
|
${this._showIgnored
|
||||||
? ignoredConfigEntries.map(
|
? ignoredConfigEntries.map(
|
||||||
(entry: ConfigEntryExtended) => html`
|
(entry: ConfigEntryExtended) => html`
|
||||||
@ -542,29 +587,6 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
ev.preventDefault();
|
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() {
|
private async _scanUSBDevices() {
|
||||||
if (!isComponentLoaded(this.hass, "usb")) {
|
if (!isComponentLoaded(this.hass, "usb")) {
|
||||||
return;
|
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() {
|
private _handleFlowUpdated() {
|
||||||
this._loadConfigEntries();
|
|
||||||
getConfigFlowInProgressCollection(this.hass.connection).refresh();
|
getConfigFlowInProgressCollection(this.hass.connection).refresh();
|
||||||
this._fetchManifests();
|
this._fetchManifests();
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
mdiDownload,
|
mdiDownload,
|
||||||
mdiOpenInNew,
|
mdiOpenInNew,
|
||||||
|
mdiReloadAlert,
|
||||||
mdiProgressHelper,
|
mdiProgressHelper,
|
||||||
mdiPlayCircleOutline,
|
mdiPlayCircleOutline,
|
||||||
mdiReload,
|
mdiReload,
|
||||||
@ -24,7 +25,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
@ -184,7 +184,9 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
? html`<span>
|
? html`<span>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
class="error"
|
class="error"
|
||||||
.path=${mdiAlertCircle}
|
.path=${item.state === "setup_retry"
|
||||||
|
? mdiReloadAlert
|
||||||
|
: mdiAlertCircle}
|
||||||
></ha-svg-icon
|
></ha-svg-icon
|
||||||
><paper-tooltip animation-delay="0" position="left">
|
><paper-tooltip animation-delay="0" position="left">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -231,6 +233,9 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
"ui.panel.config.integrations.config_entry.setup_in_progress",
|
"ui.panel.config.integrations.config_entry.setup_in_progress",
|
||||||
];
|
];
|
||||||
} else if (ERROR_STATES.includes(item.state)) {
|
} else if (ERROR_STATES.includes(item.state)) {
|
||||||
|
if (item.state === "setup_retry") {
|
||||||
|
icon = mdiReloadAlert;
|
||||||
|
}
|
||||||
stateText = [
|
stateText = [
|
||||||
`ui.panel.config.integrations.config_entry.state.${item.state}`,
|
`ui.panel.config.integrations.config_entry.state.${item.state}`,
|
||||||
];
|
];
|
||||||
@ -622,10 +627,6 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
showConfigEntrySystemOptionsDialog(this, {
|
showConfigEntrySystemOptionsDialog(this, {
|
||||||
entry: configEntry,
|
entry: configEntry,
|
||||||
manifest: this.manifest,
|
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) {
|
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) {
|
private async _removeIntegration(configEntry: ConfigEntry) {
|
||||||
@ -707,7 +702,6 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await deleteConfigEntry(this.hass, entryId);
|
const result = await deleteConfigEntry(this.hass, entryId);
|
||||||
fireEvent(this, "entry-removed", { entryId });
|
|
||||||
|
|
||||||
if (result.require_restart) {
|
if (result.require_restart) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
@ -743,10 +737,9 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
if (newName === null) {
|
if (newName === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await updateConfigEntry(this.hass, configEntry.entry_id, {
|
await updateConfigEntry(this.hass, configEntry.entry_id, {
|
||||||
title: newName,
|
title: newName,
|
||||||
});
|
});
|
||||||
fireEvent(this, "entry-updated", { entry: result.config_entry });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _signUrl(ev) {
|
private async _signUrl(ev) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user