diff --git a/src/data/automation.ts b/src/data/automation.ts index 6e04358cb8f3..ea20b874c811 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -352,9 +352,11 @@ export const saveAutomationConfig = ( config: AutomationConfig ) => hass.callApi("POST", `config/automation/config/${id}`, config); -export const normalizeAutomationConfig = ( - config: AutomationConfig -): AutomationConfig => { +export const normalizeAutomationConfig = < + T extends Partial | AutomationConfig, +>( + config: T +): T => { // Normalize data: ensure trigger, action and condition are lists // Happens when people copy paste their automations into the config for (const key of ["trigger", "condition", "action"]) { @@ -367,7 +369,7 @@ export const normalizeAutomationConfig = ( }; export const showAutomationEditor = (data?: Partial) => { - initialAutomationEditorData = normalizeAutomationConfig(data); + initialAutomationEditorData = data; navigate("/config/automation/edit/new"); }; diff --git a/src/data/blueprint.ts b/src/data/blueprint.ts index 1e450707bb09..52163542315a 100644 --- a/src/data/blueprint.ts +++ b/src/data/blueprint.ts @@ -1,4 +1,5 @@ import { HomeAssistant } from "../types"; +import { ManualAutomationConfig } from "./automation"; import { Selector } from "./selector"; export type BlueprintDomain = "automation" | "script"; @@ -42,6 +43,10 @@ export interface BlueprintImportResult { validation_errors: string[] | null; } +export interface BlueprintSubstituteResult { + substituted_config: ManualAutomationConfig; +} + export const fetchBlueprints = (hass: HomeAssistant, domain: BlueprintDomain) => hass.callWS({ type: "blueprint/list", domain }); @@ -98,7 +103,7 @@ export const substituteBlueprint = ( path: string, input: Record ) => - hass.callWS({ + hass.callWS({ type: "blueprint/substitute", domain, path, diff --git a/src/panels/config/automation/blueprint-automation-editor.ts b/src/panels/config/automation/blueprint-automation-editor.ts index 0d3ead939aaf..799fdc1c5def 100644 --- a/src/panels/config/automation/blueprint-automation-editor.ts +++ b/src/panels/config/automation/blueprint-automation-editor.ts @@ -2,14 +2,19 @@ import "@material/mwc-button/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; import { html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; +import { navigate } from "../../../common/navigate"; +import { nextRender } from "../../../common/util/render-status"; import "../../../components/ha-alert"; +import "../../../components/ha-markdown"; import { BlueprintAutomationConfig, + normalizeAutomationConfig, showAutomationEditor, } from "../../../data/automation"; import { fetchBlueprints, substituteBlueprint } from "../../../data/blueprint"; +import { showConfirmationDialog } from "../../lovelace/custom-card-helpers"; import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; -import "../../../components/ha-markdown"; +import "./manual-automation-editor"; @customElement("blueprint-automation-editor") export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor { @@ -69,18 +74,59 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor { }); } - private async _substituteBlueprint(): Promise { - const substitute = await substituteBlueprint( - this.hass, - "automation", - this.config.use_blueprint.path, - this.config.use_blueprint.input || {} - ); - showAutomationEditor({ - alias: this.config.alias, - description: `${this.config.description ? this.config.description : this._blueprint?.metadata.description} (Originated from blueprint ${this._blueprint?.metadata.name})`, - ...substitute.substituted_config, - }); + public async substituteBlueprint(): Promise { + try { + const substitute = await substituteBlueprint( + this.hass, + "automation", + this.config.use_blueprint.path, + this.config.use_blueprint.input || {} + ); + + const config = normalizeAutomationConfig(substitute.substituted_config); + + const convert = await showConfirmationDialog(this, { + title: "Take control of blueprint automation", + text: html``, + confirmText: "Create automation", + }); + + if (convert) { + if (this.dirty) { + const confirmed = await showConfirmationDialog(this, { + title: "Unsaved changes", + text: "You have unsaved changes. Do you want to continue and lose these changes?", + confirmText: "Continue", + }); + if (!confirmed) { + return; + } + } + while ( + location.pathname === "/config/automation/edit/new" && + history.length > 1 + ) { + history.back(); + // eslint-disable-next-line no-await-in-loop + await nextRender(); + } + if (location.pathname === "/config/automation/edit/new") { + navigate("/config/automation"); + await nextRender(); + } + showAutomationEditor({ + alias: this.config.alias, + description: `${this.config.description ? this.config.description : this._blueprint && "metadata" in this._blueprint ? this._blueprint.metadata.description : ""}${this._blueprint && "metadata" in this._blueprint ? `(Originated from blueprint ${this._blueprint?.metadata.name})` : ""}`, + ...substitute.substituted_config, + }); + } + } catch (err: any) { + alert(`Failed to substitute blueprint: ${err.message}`); + } } } declare global { diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 89b7f573efb3..30dccd80551c 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -6,6 +6,7 @@ import { mdiDebugStepOver, mdiDelete, mdiDotsVertical, + mdiFileEdit, mdiInformationOutline, mdiPlay, mdiPlayCircleOutline, @@ -218,7 +219,16 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { > ` - : nothing} + : html` + ${this.hass.localize( + "ui.panel.config.automation.editor.subtitute_blueprint" + )} + + `} @@ -344,6 +355,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { .stateObj=${stateObj} .config=${this._config} .disabled=${Boolean(this._readOnly)} + .readOnly=${Boolean(this._readOnly)} + .dirty=${this._dirty} @value-changed=${this._valueChanged} @duplicate=${this._duplicate} > @@ -433,7 +446,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { } this._config = { ...baseConfig, - ...initData, + ...(initData ? normalizeAutomationConfig(initData) : initData), } as AutomationConfig; this._entityId = undefined; this._readOnly = false; @@ -627,6 +640,12 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { } }; + private _substituteBlueprint() { + this.renderRoot + .querySelector("blueprint-automation-editor") + ?.substituteBlueprint(); + } + private async _duplicate() { const result = this._readOnly ? await showConfirmationDialog(this, { diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index ceb4da52e033..fcfdb0d74a44 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -32,13 +32,15 @@ export class HaManualAutomationEditor extends LitElement { @property({ type: Boolean }) public disabled = false; + @property({ type: Boolean }) public readOnly = false; + @property({ attribute: false }) public config!: ManualAutomationConfig; @property({ attribute: false }) public stateObj?: HassEntity; protected render() { return html` - ${this.disabled + ${this.readOnly ? html` ${this.hass.localize("ui.panel.config.automation.editor.read_only")} diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index bae63f6dc7e3..640f4727866b 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -29,6 +29,8 @@ export abstract class HaBlueprintGenericEditor extends LitElement { @property({ type: Boolean }) public disabled = false; + @property({ type: Boolean }) public dirty = false; + @property({ type: Boolean, reflect: true }) public narrow = false; @state() protected _blueprints?: Blueprints; diff --git a/src/translations/en.json b/src/translations/en.json index e5a7b264ce26..9a5f4d015247 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2759,6 +2759,7 @@ "unavailable": "Automation is unavailable", "migrate": "Migrate", "duplicate": "[%key:ui::common::duplicate%]", + "subtitute_blueprint": "Take control of automation", "run": "[%key:ui::panel::config::automation::editor::actions::run%]", "rename": "[%key:ui::panel::config::automation::editor::triggers::rename%]", "show_trace": "Traces",