Make config flow previews more generic (#21382)

* Make config flow previews more generic
This commit is contained in:
karwosts 2024-07-14 08:01:21 -07:00 committed by GitHub
parent 29aa57229c
commit dbc2db2591
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 23 additions and 242 deletions

View File

@ -1,10 +1,8 @@
import { import {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
UnsubscribeFunc,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { HomeAssistant } from "../types";
interface GroupEntityAttributes extends HassEntityAttributeBase { interface GroupEntityAttributes extends HassEntityAttributeBase {
entity_id: string[]; entity_id: string[];
@ -17,11 +15,6 @@ export interface GroupEntity extends HassEntityBase {
attributes: GroupEntityAttributes; attributes: GroupEntityAttributes;
} }
export interface GroupPreview {
state: string;
attributes: Record<string, any>;
}
export const computeGroupDomain = ( export const computeGroupDomain = (
stateObj: GroupEntity stateObj: GroupEntity
): string | undefined => { ): string | undefined => {
@ -31,17 +24,3 @@ export const computeGroupDomain = (
]; ];
return uniqueDomains.length === 1 ? uniqueDomains[0] : undefined; return uniqueDomains.length === 1 ? uniqueDomains[0] : undefined;
}; };
export const subscribePreviewGroup = (
hass: HomeAssistant,
flow_id: string,
flow_type: "config_flow" | "options_flow",
user_input: Record<string, any>,
callback: (preview: GroupPreview) => void
): Promise<UnsubscribeFunc> =>
hass.connection.subscribeMessage(callback, {
type: "group/start_preview",
flow_id,
flow_type,
user_input,
});

View File

@ -1,21 +1,27 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
export interface ThresholdPreview { const HAS_CUSTOM_PREVIEW = ["template"];
export interface GenericPreview {
state: string; state: string;
attributes: Record<string, any>; attributes: Record<string, any>;
} }
export const subscribePreviewThreshold = ( export const subscribePreviewGeneric = (
hass: HomeAssistant, hass: HomeAssistant,
domain: string,
flow_id: string, flow_id: string,
flow_type: "config_flow" | "options_flow", flow_type: "config_flow" | "options_flow",
user_input: Record<string, any>, user_input: Record<string, any>,
callback: (preview: ThresholdPreview) => void callback: (preview: GenericPreview) => void
): Promise<UnsubscribeFunc> => ): Promise<UnsubscribeFunc> =>
hass.connection.subscribeMessage(callback, { hass.connection.subscribeMessage(callback, {
type: "threshold/start_preview", type: `${domain}/start_preview`,
flow_id, flow_id,
flow_type, flow_type,
user_input, user_input,
}); });
export const previewModule = (domain: string): string =>
HAS_CUSTOM_PREVIEW.includes(domain) ? domain : "generic";

View File

@ -1,21 +0,0 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
export interface TimeDatePreview {
state: string;
attributes: Record<string, any>;
}
export const subscribePreviewTimeDate = (
hass: HomeAssistant,
flow_id: string,
flow_type: "config_flow" | "options_flow",
user_input: Record<string, any>,
callback: (preview: TimeDatePreview) => void
): Promise<UnsubscribeFunc> =>
hass.connection.subscribeMessage(callback, {
type: "time_date/start_preview",
flow_id,
flow_type,
user_input,
});

View File

@ -2,23 +2,22 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, html } from "lit"; import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { FlowType } from "../../../data/data_entry_flow"; import { FlowType } from "../../../data/data_entry_flow";
import { import { GenericPreview, subscribePreviewGeneric } from "../../../data/preview";
ThresholdPreview,
subscribePreviewThreshold,
} from "../../../data/threshold";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import "./entity-preview-row"; import "./entity-preview-row";
import { debounce } from "../../../common/util/debounce"; import { debounce } from "../../../common/util/debounce";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
@customElement("flow-preview-threshold") @customElement("flow-preview-generic")
class FlowPreviewThreshold extends LitElement { class FlowPreviewGeneric extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public flowType!: FlowType; @property() public flowType!: FlowType;
public handler!: string; public handler!: string;
@property() public domain!: string;
@property() public stepId!: string; @property() public stepId!: string;
@property() public flowId!: string; @property() public flowId!: string;
@ -55,7 +54,7 @@ class FlowPreviewThreshold extends LitElement {
></entity-preview-row>`; ></entity-preview-row>`;
} }
private _setPreview = (preview: ThresholdPreview) => { private _setPreview = (preview: GenericPreview) => {
const now = new Date().toISOString(); const now = new Date().toISOString();
this._preview = { this._preview = {
entity_id: `${this.stepId}.___flow_preview___`, entity_id: `${this.stepId}.___flow_preview___`,
@ -79,14 +78,14 @@ class FlowPreviewThreshold extends LitElement {
return; return;
} }
try { try {
this._unsub = subscribePreviewThreshold( this._unsub = subscribePreviewGeneric(
this.hass, this.hass,
this.domain,
this.flowId, this.flowId,
this.flowType, this.flowType,
this.stepData, this.stepData,
this._setPreview this._setPreview
); );
await this._unsub;
fireEvent(this, "set-flow-errors", { errors: {} }); fireEvent(this, "set-flow-errors", { errors: {} });
} catch (err: any) { } catch (err: any) {
if (typeof err.message === "string") { if (typeof err.message === "string") {
@ -103,6 +102,6 @@ class FlowPreviewThreshold extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"flow-preview-threshold": FlowPreviewThreshold; "flow-preview-generic": FlowPreviewGeneric;
} }
} }

View File

@ -1,90 +0,0 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { FlowType } from "../../../data/data_entry_flow";
import { GroupPreview, subscribePreviewGroup } from "../../../data/group";
import { HomeAssistant } from "../../../types";
import "./entity-preview-row";
import { debounce } from "../../../common/util/debounce";
@customElement("flow-preview-group")
class FlowPreviewGroup extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public flowType!: FlowType;
public handler!: string;
@property() public stepId!: string;
@property() public flowId!: string;
@property({ attribute: false }) public stepData!: Record<string, any>;
@state() private _preview?: HassEntity;
private _unsub?: Promise<UnsubscribeFunc>;
disconnectedCallback(): void {
super.disconnectedCallback();
if (this._unsub) {
this._unsub.then((unsub) => unsub());
this._unsub = undefined;
}
}
willUpdate(changedProps) {
if (changedProps.has("stepData")) {
this._debouncedSubscribePreview();
}
}
protected render() {
return html`<entity-preview-row
.hass=${this.hass}
.stateObj=${this._preview}
></entity-preview-row>`;
}
private _setPreview = (preview: GroupPreview) => {
const now = new Date().toISOString();
this._preview = {
entity_id: `${this.stepId}.___flow_preview___`,
last_changed: now,
last_updated: now,
context: { id: "", parent_id: null, user_id: null },
...preview,
};
};
private _debouncedSubscribePreview = debounce(() => {
this._subscribePreview();
}, 250);
private async _subscribePreview() {
if (this._unsub) {
(await this._unsub)();
this._unsub = undefined;
}
if (this.flowType === "repair_flow") {
return;
}
try {
this._unsub = subscribePreviewGroup(
this.hass,
this.flowId,
this.flowType,
this.stepData,
this._setPreview
);
} catch (err) {
this._preview = undefined;
}
}
}
declare global {
interface HTMLElementTagNameMap {
"flow-preview-group": FlowPreviewGroup;
}
}

View File

@ -1,94 +0,0 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { FlowType } from "../../../data/data_entry_flow";
import {
TimeDatePreview,
subscribePreviewTimeDate,
} from "../../../data/time_date";
import { HomeAssistant } from "../../../types";
import "./entity-preview-row";
import { debounce } from "../../../common/util/debounce";
@customElement("flow-preview-time_date")
class FlowPreviewTimeDate extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public flowType!: FlowType;
public handler!: string;
@property() public stepId!: string;
@property() public flowId!: string;
@property() public stepData!: Record<string, any>;
@state() private _preview?: HassEntity;
private _unsub?: Promise<UnsubscribeFunc>;
disconnectedCallback(): void {
super.disconnectedCallback();
if (this._unsub) {
this._unsub.then((unsub) => unsub());
this._unsub = undefined;
}
}
willUpdate(changedProps) {
if (changedProps.has("stepData")) {
this._debouncedSubscribePreview();
}
}
protected render() {
return html`<entity-preview-row
.hass=${this.hass}
.stateObj=${this._preview}
></entity-preview-row>`;
}
private _setPreview = (preview: TimeDatePreview) => {
const now = new Date().toISOString();
this._preview = {
entity_id: `${this.stepId}.___flow_preview___`,
last_changed: now,
last_updated: now,
context: { id: "", parent_id: null, user_id: null },
...preview,
};
};
private _debouncedSubscribePreview = debounce(() => {
this._subscribePreview();
}, 250);
private async _subscribePreview() {
if (this._unsub) {
(await this._unsub)();
this._unsub = undefined;
}
if (this.flowType === "repair_flow") {
return;
}
try {
this._unsub = subscribePreviewTimeDate(
this.hass,
this.flowId,
this.flowType,
this.stepData,
this._setPreview
);
await this._unsub;
} catch (err) {
this._preview = undefined;
}
}
}
declare global {
interface HTMLElementTagNameMap {
"flow-preview-time_date": FlowPreviewTimeDate;
}
}

View File

@ -25,6 +25,7 @@ import type { HomeAssistant } from "../../types";
import type { FlowConfig } from "./show-dialog-data-entry-flow"; import type { FlowConfig } from "./show-dialog-data-entry-flow";
import { configFlowContentStyles } from "./styles"; import { configFlowContentStyles } from "./styles";
import { haStyle } from "../../resources/styles"; import { haStyle } from "../../resources/styles";
import { previewModule } from "../../data/preview";
@customElement("step-flow-form") @customElement("step-flow-form")
class StepFlowForm extends LitElement { class StepFlowForm extends LitElement {
@ -76,8 +77,9 @@ class StepFlowForm extends LitElement {
"ui.panel.config.integrations.config_flow.preview" "ui.panel.config.integrations.config_flow.preview"
)}: )}:
</h3> </h3>
${dynamicElement(`flow-preview-${this.step.preview}`, { ${dynamicElement(`flow-preview-${previewModule(step.preview)}`, {
hass: this.hass, hass: this.hass,
domain: step.preview,
flowType: this.flowConfig.flowType, flowType: this.flowConfig.flowType,
handler: step.handler, handler: step.handler,
stepId: step.step_id, stepId: step.step_id,
@ -120,7 +122,7 @@ class StepFlowForm extends LitElement {
protected willUpdate(changedProps: PropertyValues): void { protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps); super.willUpdate(changedProps);
if (changedProps.has("step") && this.step?.preview) { if (changedProps.has("step") && this.step?.preview) {
import(`./previews/flow-preview-${this.step.preview}`); import(`./previews/flow-preview-${previewModule(this.step.preview)}`);
} }
} }