Fix and update step flow create (#22223)

* Fix and update step flow create

* cleanup
This commit is contained in:
Bram Kragten 2024-10-04 14:13:21 +02:00
parent c778b881ab
commit 35aafd45dc
6 changed files with 64 additions and 105 deletions

View File

@ -9,23 +9,15 @@ import {
html, html,
nothing, nothing,
} from "lit"; } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { HASSDomEvent, fireEvent } from "../../common/dom/fire_event"; import { HASSDomEvent, fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-circular-progress"; import "../../components/ha-circular-progress";
import "../../components/ha-dialog"; import "../../components/ha-dialog";
import "../../components/ha-icon-button"; import "../../components/ha-icon-button";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
} from "../../data/area_registry";
import { import {
DataEntryFlowStep, DataEntryFlowStep,
subscribeDataEntryFlowProgressed, subscribeDataEntryFlowProgressed,
} from "../../data/data_entry_flow"; } from "../../data/data_entry_flow";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../../data/device_registry";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { documentationUrl } from "../../util/documentation-url"; import { documentationUrl } from "../../util/documentation-url";
@ -62,7 +54,7 @@ declare global {
@customElement("dialog-data-entry-flow") @customElement("dialog-data-entry-flow")
class DataEntryFlowDialog extends LitElement { class DataEntryFlowDialog extends LitElement {
public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: DataEntryFlowDialogParams; @state() private _params?: DataEntryFlowDialogParams;
@ -76,16 +68,8 @@ class DataEntryFlowDialog extends LitElement {
// Null means we need to pick a config flow // Null means we need to pick a config flow
| null; | null;
@state() private _devices?: DeviceRegistryEntry[];
@state() private _areas?: AreaRegistryEntry[];
@state() private _handler?: string; @state() private _handler?: string;
private _unsubAreas?: UnsubscribeFunc;
private _unsubDevices?: UnsubscribeFunc;
private _unsubDataEntryFlowProgressed?: Promise<UnsubscribeFunc>; private _unsubDataEntryFlowProgressed?: Promise<UnsubscribeFunc>;
public async showDialog(params: DataEntryFlowDialogParams): Promise<void> { public async showDialog(params: DataEntryFlowDialogParams): Promise<void> {
@ -183,16 +167,7 @@ class DataEntryFlowDialog extends LitElement {
this._loading = undefined; this._loading = undefined;
this._step = undefined; this._step = undefined;
this._params = undefined; this._params = undefined;
this._devices = undefined;
this._handler = undefined; this._handler = undefined;
if (this._unsubAreas) {
this._unsubAreas();
this._unsubAreas = undefined;
}
if (this._unsubDevices) {
this._unsubDevices();
this._unsubDevices = undefined;
}
if (this._unsubDataEntryFlowProgressed) { if (this._unsubDataEntryFlowProgressed) {
this._unsubDataEntryFlowProgressed.then((unsub) => { this._unsubDataEntryFlowProgressed.then((unsub) => {
unsub(); unsub();
@ -309,25 +284,13 @@ class DataEntryFlowDialog extends LitElement {
.hass=${this.hass} .hass=${this.hass}
></step-flow-menu> ></step-flow-menu>
` `
: this._devices === undefined || : html`
this._areas === undefined <step-flow-create-entry
? // When it's a create entry result, we will fetch device & area registry .flowConfig=${this._params.flowConfig}
html` .step=${this._step}
<step-flow-loading .hass=${this.hass}
.flowConfig=${this._params.flowConfig} ></step-flow-create-entry>
.hass=${this.hass} `}
loadingReason="loading_devices_areas"
></step-flow-loading>
`
: html`
<step-flow-create-entry
.flowConfig=${this._params.flowConfig}
.step=${this._step}
.hass=${this.hass}
.devices=${this._devices}
.areas=${this._areas}
></step-flow-create-entry>
`}
`} `}
</div> </div>
</ha-dialog> </ha-dialog>
@ -351,32 +314,6 @@ class DataEntryFlowDialog extends LitElement {
// external and progress step will send update event from the backend, so we should subscribe to them // external and progress step will send update event from the backend, so we should subscribe to them
this._subscribeDataEntryFlowProgressed(); this._subscribeDataEntryFlowProgressed();
} }
if (this._step.type === "create_entry") {
if (this._step.result && this._params!.flowConfig.loadDevicesAndAreas) {
this._fetchDevices(this._step.result.entry_id);
this._fetchAreas();
} else {
this._devices = [];
this._areas = [];
}
}
}
private async _fetchDevices(configEntryId) {
this._unsubDevices = subscribeDeviceRegistry(
this.hass.connection,
(devices) => {
this._devices = devices.filter((device) =>
device.config_entries.includes(configEntryId)
);
}
);
}
private async _fetchAreas() {
this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => {
this._areas = areas;
});
} }
private async _processStep( private async _processStep(

View File

@ -20,7 +20,7 @@ export const showConfigFlowDialog = (
): void => ): void =>
showFlowDialog(element, dialogParams, { showFlowDialog(element, dialogParams, {
flowType: "config_flow", flowType: "config_flow",
loadDevicesAndAreas: true, showDevices: true,
createFlow: async (hass, handler) => { createFlow: async (hass, handler) => {
const [step] = await Promise.all([ const [step] = await Promise.all([
createConfigFlow(hass, handler, dialogParams.entryId), createConfigFlow(hass, handler, dialogParams.entryId),

View File

@ -17,7 +17,7 @@ import type { HomeAssistant } from "../../types";
export interface FlowConfig { export interface FlowConfig {
flowType: FlowType; flowType: FlowType;
loadDevicesAndAreas: boolean; showDevices: boolean;
createFlow(hass: HomeAssistant, handler: string): Promise<DataEntryFlowStep>; createFlow(hass: HomeAssistant, handler: string): Promise<DataEntryFlowStep>;
@ -134,8 +134,7 @@ export interface FlowConfig {
export type LoadingReason = export type LoadingReason =
| "loading_handlers" | "loading_handlers"
| "loading_flow" | "loading_flow"
| "loading_step" | "loading_step";
| "loading_devices_areas";
export interface DataEntryFlowDialogParams { export interface DataEntryFlowDialogParams {
startFlowHandler?: string; startFlowHandler?: string;

View File

@ -29,7 +29,7 @@ export const showOptionsFlowDialog = (
}, },
{ {
flowType: "options_flow", flowType: "options_flow",
loadDevicesAndAreas: false, showDevices: false,
createFlow: async (hass, handler) => { createFlow: async (hass, handler) => {
const [step] = await Promise.all([ const [step] = await Promise.all([
createOptionsFlow(hass, handler), createOptionsFlow(hass, handler),

View File

@ -4,6 +4,7 @@ import {
CSSResultGroup, CSSResultGroup,
html, html,
LitElement, LitElement,
nothing,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
@ -34,7 +35,16 @@ class StepFlowCreateEntry extends LitElement {
@property({ attribute: false }) public step!: DataEntryFlowStepCreateEntry; @property({ attribute: false }) public step!: DataEntryFlowStepCreateEntry;
@property({ attribute: false }) public devices!: DeviceRegistryEntry[]; private _devices = memoizeOne(
(
showDevices: boolean,
devices: DeviceRegistryEntry[],
entry_id?: string
) =>
showDevices && entry_id
? devices.filter((device) => device.config_entries.includes(entry_id))
: []
);
private _deviceEntities = memoizeOne( private _deviceEntities = memoizeOne(
( (
@ -50,35 +60,48 @@ class StepFlowCreateEntry extends LitElement {
); );
protected willUpdate(changedProps: PropertyValues) { protected willUpdate(changedProps: PropertyValues) {
if (!changedProps.has("devices") && !changedProps.has("hass")) {
return;
}
const devices = this._devices(
this.flowConfig.showDevices,
Object.values(this.hass.devices),
this.step.result?.entry_id
);
if ( if (
(changedProps.has("devices") || changedProps.has("hass")) && devices.length !== 1 ||
this.devices.length === 1 devices[0].primary_config_entry !== this.step.result?.entry_id
) { ) {
// integration_type === "device" return;
const assistSatellites = this._deviceEntities( }
this.devices[0].id,
Object.values(this.hass.entities), const assistSatellites = this._deviceEntities(
"assist_satellite" devices[0].id,
); Object.values(this.hass.entities),
if ( "assist_satellite"
assistSatellites.length && );
assistSatellites.some((satellite) => if (
assistSatelliteSupportsSetupFlow( assistSatellites.length &&
this.hass.states[satellite.entity_id] assistSatellites.some((satellite) =>
) assistSatelliteSupportsSetupFlow(this.hass.states[satellite.entity_id])
) )
) { ) {
this._flowDone(); this._flowDone();
showVoiceAssistantSetupDialog(this, { showVoiceAssistantSetupDialog(this, {
deviceId: this.devices[0].id, deviceId: devices[0].id,
}); });
}
} }
} }
protected render(): TemplateResult { protected render(): TemplateResult {
const localize = this.hass.localize; const localize = this.hass.localize;
const devices = this._devices(
this.flowConfig.showDevices,
Object.values(this.hass.devices),
this.step.result?.entry_id
);
return html` return html`
<h2>${localize("ui.panel.config.integrations.config_flow.success")}!</h2> <h2>${localize("ui.panel.config.integrations.config_flow.success")}!</h2>
<div class="content"> <div class="content">
@ -89,9 +112,9 @@ class StepFlowCreateEntry extends LitElement {
"ui.panel.config.integrations.config_flow.not_loaded" "ui.panel.config.integrations.config_flow.not_loaded"
)}</span )}</span
>` >`
: ""} : nothing}
${this.devices.length === 0 ${devices.length === 0
? "" ? nothing
: html` : html`
<p> <p>
${localize( ${localize(
@ -99,7 +122,7 @@ class StepFlowCreateEntry extends LitElement {
)}: )}:
</p> </p>
<div class="devices"> <div class="devices">
${this.devices.map( ${devices.map(
(device) => html` (device) => html`
<div class="device"> <div class="device">
<div> <div>

View File

@ -47,7 +47,7 @@ export const showRepairsFlowDialog = (
}, },
{ {
flowType: "repair_flow", flowType: "repair_flow",
loadDevicesAndAreas: false, showDevices: false,
createFlow: async (hass, handler) => { createFlow: async (hass, handler) => {
const [step] = await Promise.all([ const [step] = await Promise.all([
createRepairsFlow(hass, handler, issue.issue_id), createRepairsFlow(hass, handler, issue.issue_id),