Use subscription for config flows in progress (#24985)

This commit is contained in:
Bram Kragten 2025-04-10 16:59:20 +02:00 committed by GitHub
parent 430e47c0fc
commit 9d9522cade
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 53 deletions

View File

@ -1,7 +1,5 @@
import type { Connection } from "home-assistant-js-websocket"; import type { Connection } from "home-assistant-js-websocket";
import { getCollection } from "home-assistant-js-websocket";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import { debounce } from "../common/util/debounce";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import type { import type {
DataEntryFlowProgress, DataEntryFlowProgress,
@ -93,31 +91,20 @@ export const fetchConfigFlowInProgress = (
type: "config_entries/flow/progress", type: "config_entries/flow/progress",
}); });
const subscribeConfigFlowInProgressUpdates = (conn: Connection, store) => export interface ConfigFlowInProgressMessage {
conn.subscribeEvents( type: null | "added" | "removed";
debounce( flow_id: string;
() => flow: DataEntryFlowProgress;
fetchConfigFlowInProgress(conn).then((flows: DataEntryFlowProgress[]) => }
store.setState(flows, true)
),
500,
true
),
"config_entry_discovered"
);
export const getConfigFlowInProgressCollection = (conn: Connection) =>
getCollection<DataEntryFlowProgress[]>(
conn,
"_configFlowProgress",
fetchConfigFlowInProgress,
subscribeConfigFlowInProgressUpdates
);
export const subscribeConfigFlowInProgress = ( export const subscribeConfigFlowInProgress = (
hass: HomeAssistant, hass: HomeAssistant,
onChange: (flows: DataEntryFlowProgress[]) => void onChange: (update: ConfigFlowInProgressMessage[]) => void
) => getConfigFlowInProgressCollection(hass.connection).subscribe(onChange); ) =>
hass.connection.subscribeMessage<ConfigFlowInProgressMessage[]>(
(message) => onChange(message),
{ type: "config_entries/flow/subscribe" }
);
export const localizeConfigFlowTitle = ( export const localizeConfigFlowTitle = (
localize: LocalizeFunc, localize: LocalizeFunc,

View File

@ -35,16 +35,20 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
@state() private _entries: ConfigEntry[] = []; @state() private _entries: ConfigEntry[] = [];
@state() private _discoveredDomains?: Set<string>; @state() private _discoveredDomains = new Set<string>();
public hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] { public hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [ return [
subscribeConfigFlowInProgress(this.hass, (flows) => { subscribeConfigFlowInProgress(this.hass, (messages) => {
this._discoveredDomains = new Set( messages.forEach((message) => {
flows if (
.filter((flow) => !HIDDEN_DOMAINS.has(flow.handler)) message.type === "removed" ||
.map((flow) => flow.handler) HIDDEN_DOMAINS.has(message.flow.handler)
); ) {
return;
}
this._discoveredDomains.add(message.flow.handler);
});
this.hass.loadBackendTranslation( this.hass.loadBackendTranslation(
"title", "title",
Array.from(this._discoveredDomains) Array.from(this._discoveredDomains)
@ -93,7 +97,7 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
} }
protected render() { protected render() {
if (!this._discoveredDomains) { if (!this._discoveredDomains.size) {
return nothing; return nothing;
} }
// Render discovered and existing entries together sorted by localized title. // Render discovered and existing entries together sorted by localized title.

View File

@ -27,7 +27,6 @@ import "../../../components/search-input";
import "../../../components/search-input-outlined"; import "../../../components/search-input-outlined";
import type { ConfigEntry } from "../../../data/config_entries"; import type { ConfigEntry } from "../../../data/config_entries";
import { getConfigEntries } from "../../../data/config_entries"; import { getConfigEntries } from "../../../data/config_entries";
import { getConfigFlowInProgressCollection } from "../../../data/config_flow";
import { fetchDiagnosticHandlers } from "../../../data/diagnostics"; import { fetchDiagnosticHandlers } from "../../../data/diagnostics";
import type { EntityRegistryEntry } from "../../../data/entity_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry";
import { subscribeEntityRegistry } from "../../../data/entity_registry"; import { subscribeEntityRegistry } from "../../../data/entity_registry";
@ -754,7 +753,6 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin(
} }
private _handleFlowUpdated() { private _handleFlowUpdated() {
getConfigFlowInProgressCollection(this.hass.connection).refresh();
this._reScanImprovDevices(); this._reScanImprovDevices();
this._fetchManifests(); this._fetchManifests();
} }

View File

@ -77,6 +77,12 @@ class HaConfigIntegrations extends SubscribeMixin(HassRouterPage) {
subscribeConfigEntries( subscribeConfigEntries(
this.hass, this.hass,
async (messages) => { async (messages) => {
if (messages.length === 0) {
this._configEntries = [];
return;
}
let fullUpdate = this._configEntries === undefined;
const newEntries: ConfigEntryExtended[] = [];
await this._loadTranslationsPromise?.then( await this._loadTranslationsPromise?.then(
() => () =>
// allow hass to update // allow hass to update
@ -84,8 +90,6 @@ class HaConfigIntegrations extends SubscribeMixin(HassRouterPage) {
window.setTimeout(resolve, 0); window.setTimeout(resolve, 0);
}) })
); );
let fullUpdate = this._configEntries === undefined;
const newEntries: ConfigEntryExtended[] = [];
messages.forEach((message) => { messages.forEach((message) => {
if (message.type === null || message.type === "added") { if (message.type === null || message.type === "added") {
newEntries.push({ newEntries.push({
@ -114,30 +118,60 @@ class HaConfigIntegrations extends SubscribeMixin(HassRouterPage) {
); );
} }
}); });
if (!newEntries.length && !fullUpdate) {
return;
}
const existingEntries = fullUpdate ? [] : this._configEntries; const existingEntries = fullUpdate ? [] : this._configEntries;
this._configEntries = [...existingEntries!, ...newEntries]; this._configEntries = [...existingEntries!, ...newEntries];
}, },
{ type: ["device", "hub", "service", "hardware"] } { type: ["device", "hub", "service", "hardware"] }
), ),
subscribeConfigFlowInProgress(this.hass, async (flowsInProgress) => { subscribeConfigFlowInProgress(this.hass, async (messages) => {
const integrations = new Set<string>(); if (messages.length === 0) {
flowsInProgress.forEach((flow) => { this._configEntriesInProgress = [];
// To render title placeholders return;
if (flow.context.title_placeholders) { }
integrations.add(flow.handler);
let fullUpdate = this._configEntriesInProgress === undefined;
const newEntries: DataEntryFlowProgressExtended[] = [];
messages.forEach((message) => {
if (message.type === "removed") {
if (!this._configEntriesInProgress) {
return;
}
this._configEntriesInProgress =
this._configEntriesInProgress.filter(
(flow) => flow.flow_id !== message.flow_id
);
return;
}
if (message.type === null || message.type === "added") {
if (message.type === null) {
fullUpdate = true;
}
newEntries.push(message.flow);
} }
}); });
const localize = await this.hass.loadBackendTranslation(
"config", if (!newEntries.length && !fullUpdate) {
Array.from(integrations) return;
); }
this._configEntriesInProgress = flowsInProgress.map((flow) => ({ const existingEntries = fullUpdate ? [] : this._configEntriesInProgress;
...flow,
localized_title: localizeConfigFlowTitle(localize, flow), const titleIntegrations = newEntries
})); .filter((flow) => flow.context.title_placeholders)
.map((flow) => flow.handler);
const localize = titleIntegrations.length
? await this.hass.loadBackendTranslation("config", titleIntegrations)
: this.hass.localize;
this._configEntriesInProgress = [
...existingEntries!,
...newEntries.map((flow) => ({
...flow,
localized_title: localizeConfigFlowTitle(localize, flow),
})),
];
}), }),
]; ];
} }