diff --git a/src/data/automation.ts b/src/data/automation.ts
index cf4ce18ebf..f495c59b48 100644
--- a/src/data/automation.ts
+++ b/src/data/automation.ts
@@ -20,6 +20,8 @@ export interface AutomationConfig {
trigger: Trigger[];
condition?: Condition[];
action: Action[];
+ mode?: "single" | "restart" | "queued" | "parallel";
+ max?: number;
}
export interface ForDict {
diff --git a/src/data/script.ts b/src/data/script.ts
index 5f027ce413..5fdbbcfa46 100644
--- a/src/data/script.ts
+++ b/src/data/script.ts
@@ -16,6 +16,8 @@ export interface ScriptEntity extends HassEntityBase {
export interface ScriptConfig {
alias: string;
sequence: Action[];
+ mode?: "single" | "restart" | "queued" | "parallel";
+ max?: number;
}
export interface EventAction {
diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts
index 8129605c23..cc70db4fe5 100644
--- a/src/panels/config/automation/ha-automation-editor.ts
+++ b/src/panels/config/automation/ha-automation-editor.ts
@@ -1,5 +1,6 @@
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
+import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/paper-input/paper-textarea";
import "../../../components/ha-icon-button";
import {
@@ -43,6 +44,10 @@ import "./condition/ha-automation-condition";
import "./trigger/ha-automation-trigger";
import { HaDeviceTrigger } from "./trigger/types/ha-automation-trigger-device";
import { mdiContentSave } from "@mdi/js";
+import { PaperListboxElement } from "@polymer/paper-listbox";
+
+const MODES = ["parallel", "single", "restart", "queued"];
+const MODES_MAX = ["queued", "parallel"];
export class HaAutomationEditor extends LitElement {
@property() public hass!: HomeAssistant;
@@ -129,6 +134,57 @@ export class HaAutomationEditor extends LitElement {
.value=${this._config.description}
@value-changed=${this._valueChanged}
>
+
+ ${this.hass.localize(
+ "ui.panel.config.automation.editor.modes.description",
+ "documentation_link",
+ html`${this.hass.localize(
+ "ui.panel.config.automation.editor.modes.documentation"
+ )}`
+ )}
+
+
+
+ ${MODES.map(
+ (mode) => html`
+
+ ${this.hass.localize(
+ `ui.panel.config.automation.editor.modes.${mode}`
+ ) || mode}
+
+ `
+ )}
+
+
+ ${this._config.mode &&
+ MODES_MAX.includes(this._config.mode)
+ ? html`
+ `
+ : html``}
${stateObj
? html`
@@ -343,14 +399,28 @@ export class HaAutomationEditor extends LitElement {
this._entityId = automation?.entity_id;
}
+ private _modeChanged(ev: CustomEvent) {
+ const mode = ((ev.target as PaperListboxElement)?.selectedItem as any)
+ ?.mode;
+
+ this._config = { ...this._config!, mode };
+ if (!MODES_MAX.includes(mode)) {
+ delete this._config.max;
+ }
+ this._dirty = true;
+ }
+
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
- const name = (ev.target as any)?.name;
+ const target = ev.target as any;
+ const name = target.name;
if (!name) {
return;
}
- const newVal = ev.detail.value;
-
+ let newVal = ev.detail.value;
+ if (target.type === "number") {
+ newVal = Number(newVal);
+ }
if ((this._config![name] || "") === newVal) {
return;
}
@@ -453,6 +523,9 @@ export class HaAutomationEditor extends LitElement {
span[slot="introduction"] a {
color: var(--primary-color);
}
+ p {
+ margin-bottom: 0;
+ }
ha-entity-toggle {
margin-right: 8px;
}
diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts
index 85c35f7415..be5422de69 100644
--- a/src/panels/config/script/ha-script-editor.ts
+++ b/src/panels/config/script/ha-script-editor.ts
@@ -1,4 +1,5 @@
import "@polymer/app-layout/app-header/app-header";
+import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "../../../components/ha-icon-button";
import {
@@ -32,6 +33,10 @@ import "../ha-config-section";
import { configSections } from "../ha-panel-config";
import "../../../components/ha-svg-icon";
import { mdiContentSave } from "@mdi/js";
+import { PaperListboxElement } from "@polymer/paper-listbox";
+
+const MODES = ["single", "restart", "queued", "parallel"];
+const MODES_MAX = ["queued", "parallel"];
export class HaScriptEditor extends LitElement {
@property() public hass!: HomeAssistant;
@@ -105,6 +110,58 @@ export class HaScriptEditor extends LitElement {
@value-changed=${this._valueChanged}
>
+
+
+ ${this.hass.localize(
+ "ui.panel.config.script.editor.modes.description",
+ "documentation_link",
+ html`${this.hass.localize(
+ "ui.panel.config.script.editor.modes.documentation"
+ )}`
+ )}
+
+
+
+ ${MODES.map(
+ (mode) => html`
+
+ ${this.hass.localize(
+ `ui.panel.config.script.editor.modes.${mode}`
+ ) || mode}
+
+ `
+ )}
+
+
+ ${this._config.mode &&
+ MODES_MAX.includes(this._config.mode)
+ ? html`
+ `
+ : html``}
@@ -216,14 +273,28 @@ export class HaScriptEditor extends LitElement {
}
}
+ private _modeChanged(ev: CustomEvent) {
+ const mode = ((ev.target as PaperListboxElement)?.selectedItem as any)
+ ?.mode;
+
+ this._config = { ...this._config!, mode };
+ if (!MODES_MAX.includes(mode)) {
+ delete this._config.max;
+ }
+ this._dirty = true;
+ }
+
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
- const name = (ev.target as any)?.name;
+ const target = ev.target as any;
+ const name = target.name;
if (!name) {
return;
}
- const newVal = ev.detail.value;
-
+ let newVal = ev.detail.value;
+ if (target.type === "number") {
+ newVal = Number(newVal);
+ }
if ((this._config![name] || "") === newVal) {
return;
}
@@ -292,6 +363,9 @@ export class HaScriptEditor extends LitElement {
ha-card {
overflow: hidden;
}
+ p {
+ margin-bottom: 0;
+ }
.errors {
padding: 20px;
font-weight: bold;
diff --git a/src/resources/styles.ts b/src/resources/styles.ts
index 0b3c1b2678..9eeeb4821d 100644
--- a/src/resources/styles.ts
+++ b/src/resources/styles.ts
@@ -100,6 +100,10 @@ export const haStyle = css`
line-height: var(--paper-font-subhead_-_line-height);
}
+ a {
+ color: var(--primary-color);
+ }
+
.secondary {
color: var(--secondary-text-color);
}
diff --git a/src/translations/en.json b/src/translations/en.json
index 5004b80530..1f454dbe12 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -841,6 +841,19 @@
"label": "Description",
"placeholder": "Optional description"
},
+ "modes": {
+ "label": "Mode",
+ "description": "The mode controls what happens when the automation is triggered while the actions are still running from a previous trigger. Check the {documentation_link} for more info.",
+ "documentation": "automation documentation",
+ "single": "Single",
+ "restart": "Restart",
+ "queued": "Queued",
+ "parallel": "Parallel (default)"
+ },
+ "max": {
+ "queued": "Queue length",
+ "parallel": "Max number of parallel runs"
+ },
"edit_yaml": "Edit as YAML",
"edit_ui": "Edit with UI",
"triggers": {
@@ -1068,6 +1081,19 @@
"introduction": "Use scripts to execute a sequence of actions.",
"header": "Script: {name}",
"default_name": "New Script",
+ "modes": {
+ "label": "Mode",
+ "description": "The mode controls what happens when script is invoked while it is still running from one or more previous invocations. Check the {documentation_link} for more info.",
+ "documentation": "script documentation",
+ "single": "Single (default)",
+ "restart": "Restart",
+ "queued": "Queued",
+ "parallel": "Parallel"
+ },
+ "max": {
+ "queued": "Queue length",
+ "parallel": "Max number of parallel runs"
+ },
"load_error_not_editable": "Only scripts inside scripts.yaml are editable.",
"delete_confirm": "Are you sure you want to delete this script?",
"delete_script": "Delete script",