mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 02:36:37 +00:00
Migrate base automation config to plurals (#22053)
* Migrate base automation config to plurals * revert * Update hat-script-graph.ts * Make traces work with both new and old config * Adjust validateConfig
This commit is contained in:
parent
1bbf45d35e
commit
cbce6f633f
@ -217,20 +217,20 @@ export const basicTrace: DemoTrace = {
|
|||||||
id: "1615419646544",
|
id: "1615419646544",
|
||||||
alias: "Ensure Party mode",
|
alias: "Ensure Party mode",
|
||||||
description: "",
|
description: "",
|
||||||
trigger: [
|
triggers: [
|
||||||
{
|
{
|
||||||
platform: "state",
|
platform: "state",
|
||||||
entity_id: "input_boolean.toggle_1",
|
entity_id: "input_boolean.toggle_1",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
condition: [
|
conditions: [
|
||||||
{
|
{
|
||||||
condition: "template",
|
condition: "template",
|
||||||
alias: "Test if Paulus is home",
|
alias: "Test if Paulus is home",
|
||||||
value_template: "{{ true }}",
|
value_template: "{{ true }}",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
action: [
|
actions: [
|
||||||
{
|
{
|
||||||
action: "input_boolean.toggle",
|
action: "input_boolean.toggle",
|
||||||
target: {
|
target: {
|
||||||
|
@ -31,8 +31,8 @@ export const mockDemoTrace = (
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
trigger: [],
|
triggers: [],
|
||||||
action: [],
|
actions: [],
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
id: "abcd",
|
id: "abcd",
|
||||||
|
@ -133,7 +133,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
config: {
|
config: {
|
||||||
mode: "restart",
|
mode: "restart",
|
||||||
max_exceeded: "silent",
|
max_exceeded: "silent",
|
||||||
trigger: [
|
triggers: [
|
||||||
{
|
{
|
||||||
platform: "state",
|
platform: "state",
|
||||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
@ -141,7 +141,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
to: "on",
|
to: "on",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
action: [
|
actions: [
|
||||||
{
|
{
|
||||||
action: "light.turn_on",
|
action: "light.turn_on",
|
||||||
target: {
|
target: {
|
||||||
|
@ -121,7 +121,7 @@ const ACTIONS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const initialAction: Action = {
|
const initialAction: Action = {
|
||||||
service: "light.turn_on",
|
action: "light.turn_on",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "light.kitchen",
|
entity_id: "light.kitchen",
|
||||||
},
|
},
|
||||||
|
@ -569,10 +569,15 @@ export class HatScriptGraph extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
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 paths = Object.keys(this.trackedNodes);
|
||||||
const trigger_nodes =
|
const trigger_nodes =
|
||||||
"trigger" in this.trace.config
|
triggerKey in this.trace.config
|
||||||
? flattenTriggers(ensureArray(this.trace.config.trigger)).map(
|
? flattenTriggers(ensureArray(this.trace.config[triggerKey])).map(
|
||||||
(trigger, i) => this.render_trigger(trigger, i)
|
(trigger, i) => this.render_trigger(trigger, i)
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
@ -584,14 +589,14 @@ export class HatScriptGraph extends LitElement {
|
|||||||
${trigger_nodes}
|
${trigger_nodes}
|
||||||
</hat-graph-branch>`
|
</hat-graph-branch>`
|
||||||
: ""}
|
: ""}
|
||||||
${"condition" in this.trace.config
|
${conditionKey in this.trace.config
|
||||||
? html`${ensureArray(this.trace.config.condition)?.map(
|
? html`${ensureArray(this.trace.config[conditionKey])?.map(
|
||||||
(condition, i) => this.render_condition(condition, i)
|
(condition, i) => this.render_condition(condition, i)
|
||||||
)}`
|
)}`
|
||||||
: ""}
|
: ""}
|
||||||
${"action" in this.trace.config
|
${actionKey in this.trace.config
|
||||||
? html`${ensureArray(this.trace.config.action).map((action, i) =>
|
? html`${ensureArray(this.trace.config[actionKey]).map(
|
||||||
this.render_action_node(action, `action/${i}`)
|
(action, i) => this.render_action_node(action, `action/${i}`)
|
||||||
)}`
|
)}`
|
||||||
: ""}
|
: ""}
|
||||||
${"sequence" in this.trace.config
|
${"sequence" in this.trace.config
|
||||||
|
@ -27,8 +27,14 @@ export interface ManualAutomationConfig {
|
|||||||
id?: string;
|
id?: string;
|
||||||
alias?: string;
|
alias?: string;
|
||||||
description?: 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[];
|
condition?: Condition | Condition[];
|
||||||
|
actions: Action | Action[];
|
||||||
|
/** @deprecated Use `actions` instead */
|
||||||
action?: Action | Action[];
|
action?: Action | Action[];
|
||||||
mode?: (typeof MODES)[number];
|
mode?: (typeof MODES)[number];
|
||||||
max?: number;
|
max?: number;
|
||||||
@ -362,22 +368,50 @@ export const normalizeAutomationConfig = <
|
|||||||
>(
|
>(
|
||||||
config: T
|
config: T
|
||||||
): T => {
|
): T => {
|
||||||
|
config = migrateAutomationConfig(config);
|
||||||
|
|
||||||
// Normalize data: ensure triggers, actions and conditions are lists
|
// Normalize data: ensure triggers, actions and conditions are lists
|
||||||
// Happens when people copy paste their automations into the config
|
// 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];
|
const value = config[key];
|
||||||
if (value && !Array.isArray(value)) {
|
if (value && !Array.isArray(value)) {
|
||||||
config[key] = [value];
|
config[key] = [value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.action) {
|
if (config.actions) {
|
||||||
config.action = migrateAutomationAction(config.action);
|
config.actions = migrateAutomationAction(config.actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const migrateAutomationConfig = <
|
||||||
|
T extends Partial<AutomationConfig> | 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 = (
|
export const flattenTriggers = (
|
||||||
triggers: undefined | (Trigger | TriggerList)[]
|
triggers: undefined | (Trigger | TriggerList)[]
|
||||||
): Trigger[] => {
|
): Trigger[] => {
|
||||||
|
@ -10,7 +10,7 @@ interface InvalidConfig {
|
|||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidKeys = "trigger" | "action" | "condition";
|
type ValidKeys = "triggers" | "actions" | "conditions";
|
||||||
|
|
||||||
export const validateConfig = <
|
export const validateConfig = <
|
||||||
T extends Partial<{ [key in ValidKeys]: unknown }>,
|
T extends Partial<{ [key in ValidKeys]: unknown }>,
|
||||||
|
@ -404,7 +404,7 @@ export const getActionType = (action: Action): ActionType => {
|
|||||||
if ("set_conversation_response" in action) {
|
if ("set_conversation_response" in action) {
|
||||||
return "set_conversation_response";
|
return "set_conversation_response";
|
||||||
}
|
}
|
||||||
if ("action" in action) {
|
if ("action" in action || "service" in action) {
|
||||||
if ("metadata" in action) {
|
if ("metadata" in action) {
|
||||||
if (is(action, activateSceneActionStruct)) {
|
if (is(action, activateSceneActionStruct)) {
|
||||||
return "activate_scene";
|
return "activate_scene";
|
||||||
|
@ -187,10 +187,21 @@ export const getDataFromPath = (
|
|||||||
const asNumber = Number(raw);
|
const asNumber = Number(raw);
|
||||||
|
|
||||||
if (isNaN(asNumber)) {
|
if (isNaN(asNumber)) {
|
||||||
const tempResult = result[raw];
|
let tempResult = result[raw];
|
||||||
if (!tempResult && raw === "sequence") {
|
if (!tempResult && raw === "sequence") {
|
||||||
continue;
|
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") {
|
if (raw === "trigger") {
|
||||||
result = flattenTriggers(tempResult);
|
result = flattenTriggers(tempResult);
|
||||||
} else {
|
} else {
|
||||||
|
@ -510,15 +510,15 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
|
|
||||||
private async _runAction() {
|
private async _runAction() {
|
||||||
const validated = await validateConfig(this.hass, {
|
const validated = await validateConfig(this.hass, {
|
||||||
action: this.action,
|
actions: this.action,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!validated.action.valid) {
|
if (!validated.actions.valid) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.invalid_action"
|
"ui.panel.config.automation.editor.actions.invalid_action"
|
||||||
),
|
),
|
||||||
text: validated.action.error,
|
text: validated.actions.error,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -431,7 +431,7 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const validateResult = await validateConfig(this.hass, {
|
const validateResult = await validateConfig(this.hass, {
|
||||||
condition,
|
conditions: condition,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Abort if condition changed.
|
// Abort if condition changed.
|
||||||
@ -440,12 +440,12 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateResult.condition.valid) {
|
if (!validateResult.conditions.valid) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.invalid_condition"
|
"ui.panel.config.automation.editor.conditions.invalid_condition"
|
||||||
),
|
),
|
||||||
text: validateResult.condition.error,
|
text: validateResult.conditions.error,
|
||||||
});
|
});
|
||||||
this._testing = false;
|
this._testing = false;
|
||||||
return;
|
return;
|
||||||
|
@ -46,6 +46,7 @@ import {
|
|||||||
fetchAutomationFileConfig,
|
fetchAutomationFileConfig,
|
||||||
getAutomationEditorInitData,
|
getAutomationEditorInitData,
|
||||||
getAutomationStateConfig,
|
getAutomationStateConfig,
|
||||||
|
migrateAutomationConfig,
|
||||||
normalizeAutomationConfig,
|
normalizeAutomationConfig,
|
||||||
saveAutomationConfig,
|
saveAutomationConfig,
|
||||||
showAutomationEditor,
|
showAutomationEditor,
|
||||||
@ -520,9 +521,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const validation = await validateConfig(this.hass, {
|
const validation = await validateConfig(this.hass, {
|
||||||
trigger: this._config.trigger,
|
triggers: this._config.triggers,
|
||||||
condition: this._config.condition,
|
conditions: this._config.conditions,
|
||||||
action: this._config.action,
|
actions: this._config.actions,
|
||||||
});
|
});
|
||||||
this._validationErrors = (
|
this._validationErrors = (
|
||||||
Object.entries(validation) as Entries<typeof validation>
|
Object.entries(validation) as Entries<typeof validation>
|
||||||
@ -530,7 +531,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
value.valid
|
value.valid
|
||||||
? ""
|
? ""
|
||||||
: html`${this.hass.localize(
|
: html`${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.${key}s.name`
|
`ui.panel.config.automation.editor.${key}.name`
|
||||||
)}:
|
)}:
|
||||||
${value.error}<br />`
|
${value.error}<br />`
|
||||||
);
|
);
|
||||||
@ -637,7 +638,10 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
if (!ev.detail.isValid) {
|
if (!ev.detail.isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._config = { id: this._config?.id, ...ev.detail.value };
|
this._config = {
|
||||||
|
id: this._config?.id,
|
||||||
|
...migrateAutomationConfig(ev.detail.value),
|
||||||
|
};
|
||||||
this._errors = undefined;
|
this._errors = undefined;
|
||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
<ha-automation-trigger
|
<ha-automation-trigger
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="triggers-heading"
|
aria-labelledby="triggers-heading"
|
||||||
.triggers=${this.config.trigger || []}
|
.triggers=${this.config.triggers || []}
|
||||||
.path=${["trigger"]}
|
.path=${["trigger"]}
|
||||||
@value-changed=${this._triggerChanged}
|
@value-changed=${this._triggerChanged}
|
||||||
@item-moved=${this._itemMoved}
|
@item-moved=${this._itemMoved}
|
||||||
@ -119,7 +119,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
${!ensureArray(this.config.condition)?.length
|
${!ensureArray(this.config.conditions)?.length
|
||||||
? html`<p>
|
? html`<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.description",
|
"ui.panel.config.automation.editor.conditions.description",
|
||||||
@ -131,7 +131,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="conditions-heading"
|
aria-labelledby="conditions-heading"
|
||||||
.conditions=${this.config.condition || []}
|
.conditions=${this.config.conditions || []}
|
||||||
.path=${["condition"]}
|
.path=${["condition"]}
|
||||||
@value-changed=${this._conditionChanged}
|
@value-changed=${this._conditionChanged}
|
||||||
@item-moved=${this._itemMoved}
|
@item-moved=${this._itemMoved}
|
||||||
@ -160,7 +160,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${!ensureArray(this.config.action)?.length
|
${!ensureArray(this.config.actions)?.length
|
||||||
? html`<p>
|
? html`<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.description"
|
"ui.panel.config.automation.editor.actions.description"
|
||||||
@ -171,7 +171,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
role="region"
|
role="region"
|
||||||
aria-labelledby="actions-heading"
|
aria-labelledby="actions-heading"
|
||||||
.actions=${this.config.action}
|
.actions=${this.config.actions || []}
|
||||||
.path=${["action"]}
|
.path=${["action"]}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
@item-moved=${this._itemMoved}
|
@item-moved=${this._itemMoved}
|
||||||
@ -185,7 +185,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
private _triggerChanged(ev: CustomEvent): void {
|
private _triggerChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: { ...this.config!, trigger: ev.detail.value as Trigger[] },
|
value: { ...this.config!, triggers: ev.detail.value as Trigger[] },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: {
|
value: {
|
||||||
...this.config!,
|
...this.config!,
|
||||||
condition: ev.detail.value as Condition[],
|
conditions: ev.detail.value as Condition[],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
private _actionChanged(ev: CustomEvent): void {
|
private _actionChanged(ev: CustomEvent): void {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: { ...this.config!, action: ev.detail.value as Action[] },
|
value: { ...this.config!, actions: ev.detail.value as Action[] },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,11 +436,11 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const validateResult = await validateConfig(this.hass, {
|
const validateResult = await validateConfig(this.hass, {
|
||||||
trigger,
|
triggers: trigger,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't do anything if trigger not valid or if trigger changed.
|
// Don't do anything if trigger not valid or if trigger changed.
|
||||||
if (!validateResult.trigger.valid || this.trigger !== trigger) {
|
if (!validateResult.triggers.valid || this.trigger !== trigger) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const validation = await validateConfig(this.hass, {
|
const validation = await validateConfig(this.hass, {
|
||||||
action: this._config.sequence,
|
actions: this._config.sequence,
|
||||||
});
|
});
|
||||||
this._validationErrors = (
|
this._validationErrors = (
|
||||||
Object.entries(validation) as Entries<typeof validation>
|
Object.entries(validation) as Entries<typeof validation>
|
||||||
@ -475,7 +475,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
value.valid
|
value.valid
|
||||||
? ""
|
? ""
|
||||||
: html`${this.hass.localize(
|
: html`${this.hass.localize(
|
||||||
`ui.panel.config.automation.editor.${key}s.name`
|
`ui.panel.config.automation.editor.${key}.name`
|
||||||
)}:
|
)}:
|
||||||
${value.error}<br />`
|
${value.error}<br />`
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user