diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index 6be50e0191..8da1a98b4f 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -174,6 +174,7 @@ export class HaServiceControl extends LitElement { if (this._value && serviceData) { const loadDefaults = this.value && !("data" in this.value); // Set mandatory bools without a default value to false + this._value = { ...this._value }; if (!this._value.data) { this._value.data = {}; } diff --git a/src/data/lovelace/config/action.ts b/src/data/lovelace/config/action.ts index 800a54dc3f..04562c3e6d 100644 --- a/src/data/lovelace/config/action.ts +++ b/src/data/lovelace/config/action.ts @@ -1,4 +1,5 @@ import type { HassServiceTarget } from "home-assistant-js-websocket"; +import { Action } from "../../script"; export interface ToggleActionConfig extends BaseActionConfig { action: "toggle"; @@ -31,6 +32,11 @@ export interface MoreInfoActionConfig extends BaseActionConfig { entity_id?: string; } +export interface SequenceActionConfig extends BaseActionConfig { + action: "sequence"; + actions?: Action[]; +} + export interface AssistActionConfig extends BaseActionConfig { action: "assist"; pipeline_id?: string; @@ -67,4 +73,5 @@ export type ActionConfig = | MoreInfoActionConfig | AssistActionConfig | NoActionConfig - | CustomActionConfig; + | CustomActionConfig + | SequenceActionConfig; diff --git a/src/panels/lovelace/common/handle-action.ts b/src/panels/lovelace/common/handle-action.ts index 3c51b0d468..16b7d57ff3 100644 --- a/src/panels/lovelace/common/handle-action.ts +++ b/src/panels/lovelace/common/handle-action.ts @@ -3,6 +3,7 @@ import { navigate } from "../../../common/navigate"; import { forwardHaptic } from "../../../data/haptics"; import { domainToName } from "../../../data/integration"; import { ActionConfig } from "../../../data/lovelace/config/action"; +import { callExecuteScript } from "../../../data/service"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog"; import { HomeAssistant } from "../../../types"; @@ -177,6 +178,13 @@ export const handleAction = async ( }); break; } + case "sequence": { + if (!actionConfig.actions) { + return; + } + callExecuteScript(hass, actionConfig.actions); + break; + } case "fire-dom-event": { fireEvent(node, "ll-custom", actionConfig); } diff --git a/src/panels/lovelace/components/hui-action-editor.ts b/src/panels/lovelace/components/hui-action-editor.ts index dad65137ad..044a862e45 100644 --- a/src/panels/lovelace/components/hui-action-editor.ts +++ b/src/panels/lovelace/components/hui-action-editor.ts @@ -34,6 +34,7 @@ const DEFAULT_ACTIONS: UiAction[] = [ "navigate", "url", "perform-action", + "sequence", "assist", "none", ]; @@ -70,6 +71,15 @@ const ASSIST_SCHEMA = [ }, ] as const satisfies readonly HaFormSchema[]; +const SEQUENCE_SCHEMA = [ + { + name: "actions", + selector: { + action: {}, + }, + }, +] as const satisfies readonly HaFormSchema[]; + @customElement("hui-action-editor") export class HuiActionEditor extends LitElement { @property({ attribute: false }) public config?: ActionConfig; @@ -120,6 +130,10 @@ export class HuiActionEditor extends LitElement { } } + protected firstUpdated(_changedProperties: PropertyValues): void { + this.hass!.loadFragmentTranslation("config"); + } + protected render() { if (!this.hass) { return nothing; @@ -218,6 +232,17 @@ export class HuiActionEditor extends LitElement { ` : nothing} + ${this.config?.action === "sequence" + ? html` + + ` + : nothing} `; } @@ -289,7 +314,15 @@ export class HuiActionEditor extends LitElement { }); } - private _computeFormLabel(schema: SchemaUnion) { + private _computeFormLabel( + schema: + | SchemaUnion + | SchemaUnion + | SchemaUnion + ) { + if (schema.name === "actions") { + return ""; + } return this.hass?.localize( `ui.panel.lovelace.editor.action-editor.${schema.name}` ); diff --git a/src/panels/lovelace/editor/structs/action-struct.ts b/src/panels/lovelace/editor/structs/action-struct.ts index d1f8d90c48..12632cc852 100644 --- a/src/panels/lovelace/editor/structs/action-struct.ts +++ b/src/panels/lovelace/editor/structs/action-struct.ts @@ -48,6 +48,12 @@ const actionConfigStructService = object({ confirmation: optional(actionConfigStructConfirmation), }); +const actionConfigStructSequence = object({ + action: literal("sequence"), + actions: optional(array(object())), + confirmation: optional(actionConfigStructConfirmation), +}); + const actionConfigStructNavigate = object({ action: literal("navigate"), navigation_path: string(), @@ -101,6 +107,9 @@ export const actionConfigStruct = dynamic((value) => { case "more-info": { return actionConfigStructMoreInfo; } + case "sequence": { + return actionConfigStructSequence; + } } } diff --git a/src/translations/en.json b/src/translations/en.json index 029a4cffc8..e0df4d17d9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5752,10 +5752,12 @@ "more-info": "More info", "toggle": "Toggle", "navigate": "Navigate", + "sequence": "Sequence", "assist": "Assist", "url": "URL", "none": "Nothing" - } + }, + "sequence_actions": "Actions" }, "condition-editor": { "explanation": "The card will be shown when ALL conditions below are fulfilled.",