Add preview for template (#17699)

This commit is contained in:
Bram Kragten 2023-08-30 12:56:53 +02:00 committed by GitHub
parent 034ce56da5
commit d350c35c4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 1 deletions

View File

@ -1,4 +1,5 @@
import { Connection, UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
export interface RenderTemplateResult {
result: string;
@ -12,6 +13,17 @@ interface TemplateListeners {
time: boolean;
}
export type TemplatePreview = TemplatePreviewState | TemplatePreviewError;
interface TemplatePreviewState {
state: string;
attributes: Record<string, any>;
}
interface TemplatePreviewError {
error: string;
}
export const subscribeRenderTemplate = (
conn: Connection,
onChange: (result: RenderTemplateResult) => void,
@ -27,3 +39,17 @@ export const subscribeRenderTemplate = (
type: "render_template",
...params,
});
export const subscribePreviewTemplate = (
hass: HomeAssistant,
flow_id: string,
flow_type: "config_flow" | "options_flow",
user_input: Record<string, any>,
callback: (preview: TemplatePreview) => void
): Promise<UnsubscribeFunc> =>
hass.connection.subscribeMessage(callback, {
type: "template/start_preview",
flow_id,
flow_type,
user_input,
});

View File

@ -0,0 +1,114 @@
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { debounce } from "../../../common/util/debounce";
import { FlowType } from "../../../data/data_entry_flow";
import {
TemplatePreview,
subscribePreviewTemplate,
} from "../../../data/ws-templates";
import { HomeAssistant } from "../../../types";
import "./entity-preview-row";
import { fireEvent } from "../../../common/dom/fire_event";
@customElement("flow-preview-template")
class FlowPreviewTemplate 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;
@state() private _error?: string;
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() {
if (this._error) {
return html`<ha-alert alert-type="error">${this._error}</ha-alert>`;
}
return html`<entity-preview-row
.hass=${this.hass}
.stateObj=${this._preview}
></entity-preview-row>`;
}
private _setPreview = (preview: TemplatePreview) => {
if ("error" in preview) {
this._error = preview.error;
this._preview = undefined;
return;
}
this._error = undefined;
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 = subscribePreviewTemplate(
this.hass,
this.flowId,
this.flowType,
this.stepData,
this._setPreview
);
await this._unsub;
fireEvent(this, "set-flow-errors", { errors: {} });
} catch (err: any) {
if (typeof err.message === "string") {
this._error = err.message;
} else {
this._error = undefined;
fireEvent(this, "set-flow-errors", err.message);
}
this._unsub = undefined;
this._preview = undefined;
}
}
}
declare global {
interface HTMLElementTagNameMap {
"flow-preview-template": FlowPreviewTemplate;
}
}

View File

@ -70,7 +70,7 @@ class StepFlowForm extends LitElement {
></ha-form>
</div>
${step.preview
? html`<div class="preview">
? html`<div class="preview" @set-flow-errors=${this._setError}>
<h3>
${this.hass.localize(
"ui.panel.config.integrations.config_flow.preview"
@ -107,6 +107,10 @@ class StepFlowForm extends LitElement {
`;
}
private _setError(ev: CustomEvent) {
this.step = { ...this.step, errors: ev.detail };
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
setTimeout(() => this.shadowRoot!.querySelector("ha-form")!.focus(), 0);
@ -253,6 +257,9 @@ class StepFlowForm extends LitElement {
}
declare global {
interface HASSDomEvents {
"set-flow-errors": { errors: DataEntryFlowStepForm["errors"] };
}
interface HTMLElementTagNameMap {
"step-flow-form": StepFlowForm;
}