diff --git a/gallery/src/data/traces/basic_trace.ts b/gallery/src/data/traces/basic_trace.ts index d0965e2f7e..aa2a597f49 100644 --- a/gallery/src/data/traces/basic_trace.ts +++ b/gallery/src/data/traces/basic_trace.ts @@ -217,20 +217,20 @@ export const basicTrace: DemoTrace = { id: "1615419646544", alias: "Ensure Party mode", description: "", - trigger: [ + triggers: [ { platform: "state", entity_id: "input_boolean.toggle_1", }, ], - condition: [ + conditions: [ { condition: "template", alias: "Test if Paulus is home", value_template: "{{ true }}", }, ], - action: [ + actions: [ { action: "input_boolean.toggle", target: { diff --git a/gallery/src/data/traces/mock-demo-trace.ts b/gallery/src/data/traces/mock-demo-trace.ts index 04679bb007..797c4430a5 100644 --- a/gallery/src/data/traces/mock-demo-trace.ts +++ b/gallery/src/data/traces/mock-demo-trace.ts @@ -31,8 +31,8 @@ export const mockDemoTrace = ( ], }, config: { - trigger: [], - action: [], + triggers: [], + actions: [], }, context: { id: "abcd", diff --git a/gallery/src/data/traces/motion-light-trace.ts b/gallery/src/data/traces/motion-light-trace.ts index 851012ae1a..b212f99760 100644 --- a/gallery/src/data/traces/motion-light-trace.ts +++ b/gallery/src/data/traces/motion-light-trace.ts @@ -133,7 +133,7 @@ export const motionLightTrace: DemoTrace = { config: { mode: "restart", max_exceeded: "silent", - trigger: [ + triggers: [ { platform: "state", entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use", @@ -141,7 +141,7 @@ export const motionLightTrace: DemoTrace = { to: "on", }, ], - action: [ + actions: [ { action: "light.turn_on", target: { diff --git a/gallery/src/pages/automation/describe-action.ts b/gallery/src/pages/automation/describe-action.ts index c579ed6aaa..d49527337c 100644 --- a/gallery/src/pages/automation/describe-action.ts +++ b/gallery/src/pages/automation/describe-action.ts @@ -121,7 +121,7 @@ const ACTIONS = [ ]; const initialAction: Action = { - service: "light.turn_on", + action: "light.turn_on", target: { entity_id: "light.kitchen", }, diff --git a/src/components/trace/hat-script-graph.ts b/src/components/trace/hat-script-graph.ts index 28b1fdf185..80ad22cddb 100644 --- a/src/components/trace/hat-script-graph.ts +++ b/src/components/trace/hat-script-graph.ts @@ -569,10 +569,15 @@ export class HatScriptGraph extends LitElement { } protected render() { + const triggerKey = "triggers" in this.trace.config ? "triggers" : "trigger"; + const conditionKey = + "conditions" in this.trace.config ? "conditions" : "condition"; + const actionKey = "actions" in this.trace.config ? "actions" : "action"; + const paths = Object.keys(this.trackedNodes); const trigger_nodes = - "trigger" in this.trace.config - ? flattenTriggers(ensureArray(this.trace.config.trigger)).map( + triggerKey in this.trace.config + ? flattenTriggers(ensureArray(this.trace.config[triggerKey])).map( (trigger, i) => this.render_trigger(trigger, i) ) : undefined; @@ -584,14 +589,14 @@ export class HatScriptGraph extends LitElement { ${trigger_nodes} ` : ""} - ${"condition" in this.trace.config - ? html`${ensureArray(this.trace.config.condition)?.map( + ${conditionKey in this.trace.config + ? html`${ensureArray(this.trace.config[conditionKey])?.map( (condition, i) => this.render_condition(condition, i) )}` : ""} - ${"action" in this.trace.config - ? html`${ensureArray(this.trace.config.action).map((action, i) => - this.render_action_node(action, `action/${i}`) + ${actionKey in this.trace.config + ? html`${ensureArray(this.trace.config[actionKey]).map( + (action, i) => this.render_action_node(action, `action/${i}`) )}` : ""} ${"sequence" in this.trace.config diff --git a/src/data/automation.ts b/src/data/automation.ts index f5f66c0a8f..10c193cd9d 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -27,8 +27,14 @@ export interface ManualAutomationConfig { id?: string; alias?: string; description?: string; - trigger: Trigger | Trigger[]; + triggers: Trigger | Trigger[]; + /** @deprecated Use `triggers` instead */ + trigger?: Trigger | Trigger[]; + conditions?: Condition | Condition[]; + /** @deprecated Use `conditions` instead */ condition?: Condition | Condition[]; + actions: Action | Action[]; + /** @deprecated Use `actions` instead */ action?: Action | Action[]; mode?: (typeof MODES)[number]; max?: number; @@ -362,22 +368,50 @@ export const normalizeAutomationConfig = < >( config: T ): T => { + config = migrateAutomationConfig(config); + // Normalize data: ensure triggers, actions and conditions are lists // Happens when people copy paste their automations into the config - for (const key of ["trigger", "condition", "action"]) { + for (const key of ["triggers", "conditions", "actions"]) { const value = config[key]; if (value && !Array.isArray(value)) { config[key] = [value]; } } - if (config.action) { - config.action = migrateAutomationAction(config.action); + if (config.actions) { + config.actions = migrateAutomationAction(config.actions); } return config; }; +export const migrateAutomationConfig = < + T extends Partial | AutomationConfig, +>( + config: T +) => { + if ("trigger" in config) { + if (!("triggers" in config)) { + config.triggers = config.trigger; + } + delete config.trigger; + } + if ("condition" in config) { + if (!("conditions" in config)) { + config.conditions = config.condition; + } + delete config.condition; + } + if ("action" in config) { + if (!("actions" in config)) { + config.actions = config.action; + } + delete config.action; + } + return config; +}; + export const flattenTriggers = ( triggers: undefined | (Trigger | TriggerList)[] ): Trigger[] => { diff --git a/src/data/config.ts b/src/data/config.ts index 7c872f1279..3d058575be 100644 --- a/src/data/config.ts +++ b/src/data/config.ts @@ -10,7 +10,7 @@ interface InvalidConfig { error: string; } -type ValidKeys = "trigger" | "action" | "condition"; +type ValidKeys = "triggers" | "actions" | "conditions"; export const validateConfig = < T extends Partial<{ [key in ValidKeys]: unknown }>, diff --git a/src/data/script.ts b/src/data/script.ts index 5bc5786c62..1e6c85a458 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -404,7 +404,7 @@ export const getActionType = (action: Action): ActionType => { if ("set_conversation_response" in action) { return "set_conversation_response"; } - if ("action" in action) { + if ("action" in action || "service" in action) { if ("metadata" in action) { if (is(action, activateSceneActionStruct)) { return "activate_scene"; diff --git a/src/data/trace.ts b/src/data/trace.ts index 2e5d3f1a4f..0bdd60f7b5 100644 --- a/src/data/trace.ts +++ b/src/data/trace.ts @@ -187,10 +187,21 @@ export const getDataFromPath = ( const asNumber = Number(raw); if (isNaN(asNumber)) { - const tempResult = result[raw]; + let tempResult = result[raw]; if (!tempResult && raw === "sequence") { continue; } + + if (!tempResult && raw === "trigger") { + tempResult = result.triggers; + } + if (!tempResult && raw === "condition") { + tempResult = result.conditions; + } + if (!tempResult && raw === "action") { + tempResult = result.actions; + } + if (raw === "trigger") { result = flattenTriggers(tempResult); } else { diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index 5da36548e4..c31e5ccc8c 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -510,15 +510,15 @@ export default class HaAutomationActionRow extends LitElement { private async _runAction() { const validated = await validateConfig(this.hass, { - action: this.action, + actions: this.action, }); - if (!validated.action.valid) { + if (!validated.actions.valid) { showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.automation.editor.actions.invalid_action" ), - text: validated.action.error, + text: validated.actions.error, }); return; } diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index 623e3d3cde..b2aa1395b0 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -431,7 +431,7 @@ export default class HaAutomationConditionRow extends LitElement { try { const validateResult = await validateConfig(this.hass, { - condition, + conditions: condition, }); // Abort if condition changed. @@ -440,12 +440,12 @@ export default class HaAutomationConditionRow extends LitElement { return; } - if (!validateResult.condition.valid) { + if (!validateResult.conditions.valid) { showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.automation.editor.conditions.invalid_condition" ), - text: validateResult.condition.error, + text: validateResult.conditions.error, }); this._testing = false; return; diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 63e455e68e..61e3f61119 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -46,6 +46,7 @@ import { fetchAutomationFileConfig, getAutomationEditorInitData, getAutomationStateConfig, + migrateAutomationConfig, normalizeAutomationConfig, saveAutomationConfig, showAutomationEditor, @@ -520,9 +521,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { return; } const validation = await validateConfig(this.hass, { - trigger: this._config.trigger, - condition: this._config.condition, - action: this._config.action, + triggers: this._config.triggers, + conditions: this._config.conditions, + actions: this._config.actions, }); this._validationErrors = ( Object.entries(validation) as Entries @@ -530,7 +531,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { value.valid ? "" : html`${this.hass.localize( - `ui.panel.config.automation.editor.${key}s.name` + `ui.panel.config.automation.editor.${key}.name` )}: ${value.error}
` ); @@ -637,7 +638,10 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { if (!ev.detail.isValid) { return; } - this._config = { id: this._config?.id, ...ev.detail.value }; + this._config = { + id: this._config?.id, + ...migrateAutomationConfig(ev.detail.value), + }; this._errors = undefined; this._dirty = true; } diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index 3e7a23c2f9..6c81607fe1 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -89,7 +89,7 @@ export class HaManualAutomationEditor extends LitElement { - ${!ensureArray(this.config.condition)?.length + ${!ensureArray(this.config.conditions)?.length ? html`

${this.hass.localize( "ui.panel.config.automation.editor.conditions.description", @@ -131,7 +131,7 @@ export class HaManualAutomationEditor extends LitElement { - ${!ensureArray(this.config.action)?.length + ${!ensureArray(this.config.actions)?.length ? html`

${this.hass.localize( "ui.panel.config.automation.editor.actions.description" @@ -171,7 +171,7 @@ export class HaManualAutomationEditor extends LitElement { @@ -475,7 +475,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { value.valid ? "" : html`${this.hass.localize( - `ui.panel.config.automation.editor.${key}s.name` + `ui.panel.config.automation.editor.${key}.name` )}: ${value.error}
` );