diff --git a/src/components/entity/ha-entity-attribute-picker.ts b/src/components/entity/ha-entity-attribute-picker.ts
new file mode 100644
index 0000000000..ee2958323b
--- /dev/null
+++ b/src/components/entity/ha-entity-attribute-picker.ts
@@ -0,0 +1,178 @@
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-item/paper-item";
+import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
+import { HassEntity } from "home-assistant-js-websocket";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ PropertyValues,
+ query,
+ TemplateResult,
+} from "lit-element";
+import { fireEvent } from "../../common/dom/fire_event";
+import { PolymerChangedEvent } from "../../polymer-types";
+import { HomeAssistant } from "../../types";
+import "../ha-icon-button";
+import "./state-badge";
+
+export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
+
+const rowRenderer = (root: HTMLElement, _owner, model: { item: string }) => {
+ if (!root.firstElementChild) {
+ root.innerHTML = `
+
+
+ `;
+ }
+ root.querySelector("paper-item")!.textContent = model.item;
+};
+
+@customElement("ha-entity-attribute-picker")
+class HaEntityAttributePicker extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property() public entityId?: string;
+
+ @property({ type: Boolean }) public autofocus = false;
+
+ @property({ type: Boolean }) public disabled = false;
+
+ @property({ type: Boolean, attribute: "allow-custom-value" })
+ public allowCustomValue;
+
+ @property() public label?: string;
+
+ @property() public value?: string;
+
+ @property({ type: Boolean }) private _opened = false;
+
+ @query("vaadin-combo-box-light") private _comboBox!: HTMLElement;
+
+ protected shouldUpdate(changedProps: PropertyValues) {
+ return !(!changedProps.has("_opened") && this._opened);
+ }
+
+ protected updated(changedProps: PropertyValues) {
+ if (changedProps.has("_opened") && this._opened) {
+ const state = this.entityId ? this.hass.states[this.entityId] : undefined;
+ (this._comboBox as any).items = state
+ ? Object.keys(state.attributes)
+ : [];
+ }
+ }
+
+ protected render(): TemplateResult {
+ if (!this.hass) {
+ return html``;
+ }
+
+ return html`
+
+
+ ${this.value
+ ? html`
+
+ Clear
+
+ `
+ : ""}
+
+
+ Toggle
+
+
+
+ `;
+ }
+
+ private _clearValue(ev: Event) {
+ ev.stopPropagation();
+ this._setValue("");
+ }
+
+ private get _value() {
+ return this.value || "";
+ }
+
+ private _openedChanged(ev: PolymerChangedEvent) {
+ this._opened = ev.detail.value;
+ }
+
+ private _valueChanged(ev: PolymerChangedEvent) {
+ const newValue = ev.detail.value;
+ if (newValue !== this._value) {
+ this._setValue(newValue);
+ }
+ }
+
+ private _setValue(value: string) {
+ this.value = value;
+ setTimeout(() => {
+ fireEvent(this, "value-changed", { value });
+ fireEvent(this, "change");
+ }, 0);
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ paper-input > ha-icon-button {
+ --mdc-icon-button-size: 24px;
+ padding: 0px 2px;
+ color: var(--secondary-text-color);
+ }
+ [hidden] {
+ display: none;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-entity-attribute-picker": HaEntityAttributePicker;
+ }
+}
diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts
index 300ae57a79..51ddf0d30f 100644
--- a/src/components/entity/ha-entity-picker.ts
+++ b/src/components/entity/ha-entity-picker.ts
@@ -1,4 +1,3 @@
-import "../ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-item/paper-item-body";
@@ -20,6 +19,7 @@ import { computeDomain } from "../../common/entity/compute_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
+import "../ha-icon-button";
import "./state-badge";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@@ -95,6 +95,8 @@ class HaEntityPicker extends LitElement {
@query("vaadin-combo-box-light") private _comboBox!: HTMLElement;
+ private _initedStates = false;
+
private _getStates = memoizeOne(
(
_opened: boolean,
@@ -148,11 +150,18 @@ class HaEntityPicker extends LitElement {
);
protected shouldUpdate(changedProps: PropertyValues) {
+ if (
+ changedProps.has("value") ||
+ changedProps.has("label") ||
+ changedProps.has("disabled")
+ ) {
+ return true;
+ }
return !(!changedProps.has("_opened") && this._opened);
}
protected updated(changedProps: PropertyValues) {
- if (changedProps.has("_opened") && this._opened) {
+ if (!this._initedStates || (changedProps.has("_opened") && this._opened)) {
const states = this._getStates(
this._opened,
this.hass,
@@ -162,6 +171,7 @@ class HaEntityPicker extends LitElement {
this.includeDeviceClasses
);
(this._comboBox as any).items = states;
+ this._initedStates = true;
}
}
@@ -169,7 +179,6 @@ class HaEntityPicker extends LitElement {
if (!this.hass) {
return html``;
}
-
return html`
-
+
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
@@ -261,6 +263,7 @@ export default class HaAutomationActionRow extends LitElement {
this._switchYamlMode();
break;
case 1:
+ fireEvent(this, "duplicate");
break;
case 2:
this._onDelete();
diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts
index d6e815ea63..77db982c3b 100644
--- a/src/panels/config/automation/action/ha-automation-action.ts
+++ b/src/panels/config/automation/action/ha-automation-action.ts
@@ -28,6 +28,7 @@ export default class HaAutomationAction extends LitElement {
.index=${idx}
.totalActions=${this.actions.length}
.action=${action}
+ @duplicate=${this._duplicateAction}
@move-action=${this._move}
@value-changed=${this._actionChanged}
.hass=${this.hass}
@@ -78,6 +79,14 @@ export default class HaAutomationAction extends LitElement {
fireEvent(this, "value-changed", { value: actions });
}
+ private _duplicateAction(ev: CustomEvent) {
+ ev.stopPropagation();
+ const index = (ev.target as any).index;
+ fireEvent(this, "value-changed", {
+ value: this.actions.concat(this.actions[index]),
+ });
+ }
+
static get styles(): CSSResult {
return css`
ha-automation-action-row,
diff --git a/src/panels/config/automation/action/types/ha-automation-action-choose.ts b/src/panels/config/automation/action/types/ha-automation-action-choose.ts
index 0bca89bf91..fa1f52f349 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-choose.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-choose.ts
@@ -1,22 +1,21 @@
+import { mdiDelete } from "@mdi/js";
import "@polymer/paper-input/paper-input";
+import "@polymer/paper-listbox/paper-listbox";
import {
+ css,
+ CSSResult,
customElement,
LitElement,
property,
- CSSResult,
- css,
} from "lit-element";
import { html } from "lit-html";
-import { Action, ChooseAction } from "../../../../../data/script";
-import { HomeAssistant } from "../../../../../types";
-import { ActionElement } from "../ha-automation-action-row";
-import "../../condition/ha-automation-condition-editor";
-import "@polymer/paper-listbox/paper-listbox";
import { fireEvent } from "../../../../../common/dom/fire_event";
-import "../ha-automation-action";
import { Condition } from "../../../../../data/automation";
+import { Action, ChooseAction } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
-import { mdiDelete } from "@mdi/js";
+import { HomeAssistant } from "../../../../../types";
+import "../ha-automation-action";
+import { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-choose")
export class HaChooseAction extends LitElement implements ActionElement {
diff --git a/src/panels/config/automation/action/types/ha-automation-action-repeat.ts b/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
index 3fd2cd95cb..13adc9aa74 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-repeat.ts
@@ -1,22 +1,21 @@
import "@polymer/paper-input/paper-input";
-import { customElement, LitElement, property, CSSResult } from "lit-element";
-import { html } from "lit-html";
-import {
- RepeatAction,
- Action,
- CountRepeat,
- WhileRepeat,
- UntilRepeat,
-} from "../../../../../data/script";
-import { HomeAssistant } from "../../../../../types";
-import { ActionElement } from "../ha-automation-action-row";
-import "../../condition/ha-automation-condition-editor";
import type { PaperListboxElement } from "@polymer/paper-listbox";
import "@polymer/paper-listbox/paper-listbox";
+import { CSSResult, customElement, LitElement, property } from "lit-element";
+import { html } from "lit-html";
import { fireEvent } from "../../../../../common/dom/fire_event";
-import "../ha-automation-action";
-import { Condition } from "../../../../lovelace/common/validate-condition";
+import {
+ Action,
+ CountRepeat,
+ RepeatAction,
+ UntilRepeat,
+ WhileRepeat,
+} from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
+import { HomeAssistant } from "../../../../../types";
+import { Condition } from "../../../../lovelace/common/validate-condition";
+import "../ha-automation-action";
+import { ActionElement } from "../ha-automation-action-row";
const OPTIONS = ["count", "while", "until"];
diff --git a/src/panels/config/automation/action/types/ha-automation-action-service.ts b/src/panels/config/automation/action/types/ha-automation-action-service.ts
index 5ef46a6875..c7e564bcb8 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-service.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-service.ts
@@ -8,6 +8,7 @@ import {
} from "lit-element";
import { html } from "lit-html";
import memoizeOne from "memoize-one";
+import { any, assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeDomain } from "../../../../../common/entity/compute_domain";
import { computeObjectId } from "../../../../../common/entity/compute_object_id";
@@ -18,14 +19,13 @@ import type { HaYamlEditor } from "../../../../../components/ha-yaml-editor";
import { ServiceAction } from "../../../../../data/script";
import type { PolymerChangedEvent } from "../../../../../polymer-types";
import type { HomeAssistant } from "../../../../../types";
-import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
-import { assert, optional, object, string } from "superstruct";
import { EntityId } from "../../../../lovelace/common/structs/is-entity-id";
+import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
const actionStruct = object({
service: optional(string()),
entity_id: optional(EntityId),
- data: optional(object()),
+ data: optional(any()),
});
@customElement("ha-automation-action-service")
diff --git a/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts
new file mode 100644
index 0000000000..2d99185f5a
--- /dev/null
+++ b/src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger.ts
@@ -0,0 +1,70 @@
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-input/paper-textarea";
+import { customElement, LitElement, property } from "lit-element";
+import { html } from "lit-html";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import "../../../../../components/ha-formfield";
+import { WaitForTriggerAction } from "../../../../../data/script";
+import { HomeAssistant } from "../../../../../types";
+import "../../trigger/ha-automation-trigger";
+import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
+
+@customElement("ha-automation-action-wait_for_trigger")
+export class HaWaitForTriggerAction extends LitElement
+ implements ActionElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property() public action!: WaitForTriggerAction;
+
+ public static get defaultConfig() {
+ return { wait_for_trigger: [], timeout: "" };
+ }
+
+ protected render() {
+ const { wait_for_trigger, continue_on_timeout, timeout } = this.action;
+
+ return html`
+
+
+
+
+
+
+ `;
+ }
+
+ private _continueChanged(ev) {
+ fireEvent(this, "value-changed", {
+ value: { ...this.action, continue_on_timeout: ev.target.checked },
+ });
+ }
+
+ private _valueChanged(ev: CustomEvent): void {
+ handleChangeEvent(this, ev);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-automation-action-wait_for_trigger": HaWaitForTriggerAction;
+ }
+}
diff --git a/src/panels/config/automation/action/types/ha-automation-action-wait_template.ts b/src/panels/config/automation/action/types/ha-automation-action-wait_template.ts
index e585d52032..2cf6a86299 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-wait_template.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-wait_template.ts
@@ -2,6 +2,7 @@ import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
import { customElement, LitElement, property } from "lit-element";
import { html } from "lit-html";
+import { fireEvent } from "../../../../../common/dom/fire_event";
import { WaitAction } from "../../../../../data/script";
import { HomeAssistant } from "../../../../../types";
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
@@ -13,11 +14,11 @@ export class HaWaitAction extends LitElement implements ActionElement {
@property() public action!: WaitAction;
public static get defaultConfig() {
- return { wait_template: "", timeout: "" };
+ return { wait_template: "" };
}
protected render() {
- const { wait_template, timeout } = this.action;
+ const { wait_template, timeout, continue_on_timeout } = this.action;
return html`
+
+
+
+
`;
}
+ private _continueChanged(ev) {
+ fireEvent(this, "value-changed", {
+ value: { ...this.action, continue_on_timeout: ev.target.checked },
+ });
+ }
+
private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev);
}
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 1afc44967c..1f51b47f13 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-row.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts
@@ -1,24 +1,24 @@
-import "../../../../components/ha-icon-button";
-import "@polymer/paper-item/paper-item";
+import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
-import "../../../../components/ha-button-menu";
import { mdiDotsVertical } from "@mdi/js";
+import "@polymer/paper-item/paper-item";
import {
css,
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
} from "lit-element";
import { fireEvent } from "../../../../common/dom/fire_event";
+import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
+import "../../../../components/ha-icon-button";
import { Condition } from "../../../../data/automation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { HomeAssistant } from "../../../../types";
import "./ha-automation-condition-editor";
-import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
export interface ConditionElement extends LitElement {
condition: Condition;
@@ -81,7 +81,7 @@ export default class HaAutomationConditionRow extends LitElement {
"ui.panel.config.automation.editor.edit_yaml"
)}
-
+
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
@@ -109,6 +109,7 @@ export default class HaAutomationConditionRow extends LitElement {
this._switchYamlMode();
break;
case 1:
+ fireEvent(this, "duplicate");
break;
case 2:
this._onDelete();
diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts
index 05367bd6a2..b4529c4650 100644
--- a/src/panels/config/automation/condition/ha-automation-condition.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition.ts
@@ -6,6 +6,7 @@ import {
html,
LitElement,
property,
+ PropertyValues,
} from "lit-element";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-card";
@@ -20,13 +21,43 @@ export default class HaAutomationCondition extends LitElement {
@property() public conditions!: Condition[];
+ protected updated(changedProperties: PropertyValues) {
+ if (!changedProperties.has("conditions")) {
+ return;
+ }
+ let updatedConditions: Condition[] | undefined;
+ if (!Array.isArray(this.conditions)) {
+ updatedConditions = [this.conditions];
+ }
+
+ (updatedConditions || this.conditions).forEach((condition, index) => {
+ if (typeof condition === "string") {
+ updatedConditions = updatedConditions || [...this.conditions];
+ updatedConditions[index] = {
+ condition: "template",
+ value_template: condition,
+ };
+ }
+ });
+
+ if (updatedConditions) {
+ fireEvent(this, "value-changed", {
+ value: updatedConditions,
+ });
+ }
+ }
+
protected render() {
+ if (!Array.isArray(this.conditions)) {
+ return html``;
+ }
return html`
${this.conditions.map(
(cond, idx) => html`
@@ -68,6 +99,14 @@ export default class HaAutomationCondition extends LitElement {
fireEvent(this, "value-changed", { value: conditions });
}
+ private _duplicateCondition(ev: CustomEvent) {
+ ev.stopPropagation();
+ const index = (ev.target as any).index;
+ fireEvent(this, "value-changed", {
+ value: this.conditions.concat(this.conditions[index]),
+ });
+ }
+
static get styles(): CSSResult {
return css`
ha-automation-condition-row,
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts
index 656fc999e6..71259efe5d 100644
--- a/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-numeric_state.ts
@@ -1,7 +1,6 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
import { customElement, html, LitElement, property } from "lit-element";
-import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import { NumericStateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
@@ -19,16 +18,34 @@ export default class HaNumericStateCondition extends LitElement {
};
}
- protected render() {
- const { value_template, entity_id, below, above } = this.condition;
+ public render() {
+ const {
+ value_template,
+ entity_id,
+ attribute,
+ below,
+ above,
+ } = this.condition;
return html`
+
+
) {
- ev.stopPropagation();
- fireEvent(this, "value-changed", {
- value: { ...this.condition, entity_id: ev.detail.value },
- });
- }
}
declare global {
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-time.ts b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts
index 7f790194fc..baac37cf35 100644
--- a/src/panels/config/automation/condition/types/ha-automation-condition-time.ts
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-time.ts
@@ -1,5 +1,14 @@
+import { Radio } from "@material/mwc-radio";
import "@polymer/paper-input/paper-input";
-import { customElement, html, LitElement, property } from "lit-element";
+import {
+ customElement,
+ html,
+ internalProperty,
+ LitElement,
+ property,
+} from "lit-element";
+import "../../../../../components/ha-formfield";
+import "../../../../../components/ha-radio";
import { TimeCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import {
@@ -7,38 +16,130 @@ import {
handleChangeEvent,
} from "../ha-automation-condition-row";
+const includeDomains = ["input_datetime"];
+
@customElement("ha-automation-condition-time")
export class HaTimeCondition extends LitElement implements ConditionElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition!: TimeCondition;
+ @internalProperty() private _inputModeBefore?: boolean;
+
+ @internalProperty() private _inputModeAfter?: boolean;
+
public static get defaultConfig() {
return {};
}
protected render() {
const { after, before } = this.condition;
+
+ const inputModeBefore =
+ this._inputModeBefore ?? before?.startsWith("input_datetime.");
+ const inputModeAfter =
+ this._inputModeAfter ?? after?.startsWith("input_datetime.");
+
return html`
-
-
+
+
+
+ >
+
+
+ ${inputModeAfter
+ ? html``
+ : html``}
+
+
+
+
+
+
+
+ ${inputModeBefore
+ ? html``
+ : html``}
`;
}
+ private _handleModeChanged(ev: Event) {
+ const target = ev.target as Radio;
+ if (target.getAttribute("name") === "mode_after") {
+ this._inputModeAfter = target.value === "input";
+ } else {
+ this._inputModeBefore = target.value === "input";
+ }
+ }
+
private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev);
}
diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts
index 255edefff7..1c6f0c125c 100644
--- a/src/panels/config/automation/ha-automation-editor.ts
+++ b/src/panels/config/automation/ha-automation-editor.ts
@@ -1,28 +1,32 @@
+import "@material/mwc-fab";
+import { mdiContentDuplicate, mdiContentSave, mdiDelete } from "@mdi/js";
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 { PaperListboxElement } from "@polymer/paper-listbox";
import {
css,
CSSResult,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
+import { classMap } from "lit-html/directives/class-map";
import { navigate } from "../../../common/navigate";
import "../../../components/ha-card";
+import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
-import "@material/mwc-fab";
import {
AutomationConfig,
AutomationEntity,
Condition,
deleteAutomation,
getAutomationEditorInitData,
+ showAutomationEditor,
Trigger,
triggerAutomation,
} from "../../../data/automation";
@@ -42,9 +46,6 @@ import { HaDeviceAction } from "./action/types/ha-automation-action-device_id";
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";
-import { classMap } from "lit-html/directives/class-map";
const MODES = ["single", "restart", "queued", "parallel"];
const MODES_MAX = ["queued", "parallel"];
@@ -53,6 +54,7 @@ declare global {
// for fire event
interface HASSDomEvents {
"ui-mode-not-available": Error;
+ duplicate: undefined;
}
}
@@ -92,14 +94,24 @@ export class HaAutomationEditor extends LitElement {
${!this.automationId
? ""
: html`
-
+
+
+
+ >
+
+
`}
${this._config
? html`
@@ -473,6 +485,31 @@ export class HaAutomationEditor extends LitElement {
}
}
+ private async _duplicate() {
+ if (this._dirty) {
+ if (
+ !(await showConfirmationDialog(this, {
+ text: this.hass!.localize(
+ "ui.panel.config.automation.editor.unsaved_confirm"
+ ),
+ confirmText: this.hass!.localize("ui.common.yes"),
+ dismissText: this.hass!.localize("ui.common.no"),
+ }))
+ ) {
+ return;
+ }
+ // Wait for dialog to complate closing
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ }
+ showAutomationEditor(this, {
+ ...this._config,
+ id: undefined,
+ alias: `${this._config?.alias} (${this.hass.localize(
+ "ui.panel.config.automation.picker.duplicate"
+ )})`,
+ });
+ }
+
private async _deleteConfirm() {
showConfirmationDialog(this, {
text: this.hass.localize(
diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
index a50373bbc8..feeda91773 100644
--- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
+++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts
@@ -1,25 +1,27 @@
+import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
+import "@material/mwc-list/mwc-list-item";
+import { mdiDotsVertical } from "@mdi/js";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
-import "../../../../components/ha-icon-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
-import "@material/mwc-list/mwc-list-item";
-import "../../../../components/ha-button-menu";
-import { mdiDotsVertical } from "@mdi/js";
import type { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
import {
css,
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
} from "lit-element";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
+import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
+import "../../../../components/ha-icon-button";
import type { Trigger } from "../../../../data/automation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
+import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "./types/ha-automation-trigger-device";
import "./types/ha-automation-trigger-event";
@@ -29,14 +31,12 @@ import "./types/ha-automation-trigger-mqtt";
import "./types/ha-automation-trigger-numeric_state";
import "./types/ha-automation-trigger-state";
import "./types/ha-automation-trigger-sun";
+import "./types/ha-automation-trigger-tag";
import "./types/ha-automation-trigger-template";
import "./types/ha-automation-trigger-time";
import "./types/ha-automation-trigger-time_pattern";
import "./types/ha-automation-trigger-webhook";
import "./types/ha-automation-trigger-zone";
-import "./types/ha-automation-trigger-tag";
-import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
-import { haStyle } from "../../../../resources/styles";
const OPTIONS = [
"device",
@@ -113,7 +113,7 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.edit_yaml"
)}
-
+
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
@@ -183,6 +183,7 @@ export default class HaAutomationTriggerRow extends LitElement {
this._switchYamlMode();
break;
case 1:
+ fireEvent(this, "duplicate");
break;
case 2:
this._onDelete();
diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts
index a074cbb881..953ec67037 100644
--- a/src/panels/config/automation/trigger/ha-automation-trigger.ts
+++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts
@@ -27,6 +27,7 @@ export default class HaAutomationTrigger extends LitElement {
@@ -68,6 +69,14 @@ export default class HaAutomationTrigger extends LitElement {
fireEvent(this, "value-changed", { value: triggers });
}
+ private _duplicateTrigger(ev: CustomEvent) {
+ ev.stopPropagation();
+ const index = (ev.target as any).index;
+ fireEvent(this, "value-changed", {
+ value: this.triggers.concat(this.triggers[index]),
+ });
+ }
+
static get styles(): CSSResult {
return css`
ha-automation-trigger-row,
diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts
index 068dbd6856..0c54205b2b 100644
--- a/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts
+++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state.ts
@@ -1,7 +1,6 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
import { customElement, html, LitElement, property } from "lit-element";
-import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import { ForDict, NumericStateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
@@ -19,8 +18,8 @@ export default class HaNumericStateTrigger extends LitElement {
};
}
- protected render() {
- const { value_template, entity_id, below, above } = this.trigger;
+ public render() {
+ const { value_template, entity_id, attribute, below, above } = this.trigger;
let trgFor = this.trigger.for;
if (
@@ -41,10 +40,22 @@ export default class HaNumericStateTrigger extends LitElement {
return html`
+
+
) {
- ev.stopPropagation();
- fireEvent(this, "value-changed", {
- value: { ...this.trigger, entity_id: ev.detail.value },
- });
- }
}
declare global {
diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts
index 7ea3043660..1d9e6fa677 100644
--- a/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts
+++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-time.ts
@@ -1,5 +1,14 @@
import "@polymer/paper-input/paper-input";
-import { customElement, html, LitElement, property } from "lit-element";
+import {
+ customElement,
+ html,
+ internalProperty,
+ LitElement,
+ property,
+} from "lit-element";
+import "../../../../../components/entity/ha-entity-picker";
+import "../../../../../components/ha-formfield";
+import "../../../../../components/ha-radio";
import { TimeTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import {
@@ -7,31 +16,81 @@ import {
TriggerElement,
} from "../ha-automation-trigger-row";
+const includeDomains = ["input_datetime"];
+
@customElement("ha-automation-trigger-time")
export class HaTimeTrigger extends LitElement implements TriggerElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public trigger!: TimeTrigger;
+ @internalProperty() private _inputMode?: boolean;
+
public static get defaultConfig() {
return { at: "" };
}
protected render() {
const { at } = this.trigger;
+ const inputMode = this._inputMode ?? at?.startsWith("input_datetime.");
return html`
-
+ >
+
+
+
+
+
+ ${inputMode
+ ? html``
+ : html``}
`;
}
+ private _handleModeChanged(ev: Event) {
+ this._inputMode = (ev.target as any).value === "input";
+ }
+
private _valueChanged(ev: CustomEvent): void {
handleChangeEvent(this, ev);
}
}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-automation-trigger-time": HaTimeTrigger;
+ }
+}
diff --git a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts
index cd230be535..49a9db601c 100644
--- a/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-entity-card-editor.ts
@@ -2,13 +2,15 @@ import "@polymer/paper-input/paper-input";
import {
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
} from "lit-element";
+import { assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stateIcon } from "../../../../common/entity/state_icon";
+import "../../../../components/entity/ha-entity-attribute-picker";
import "../../../../components/ha-icon-input";
import { HomeAssistant } from "../../../../types";
import { EntityCardConfig } from "../../cards/types";
@@ -19,7 +21,6 @@ import { headerFooterConfigStructs } from "../../header-footer/types";
import { LovelaceCardEditor } from "../../types";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
-import { string, object, optional, assert } from "superstruct";
const cardConfigStruct = object({
type: string(),
@@ -113,7 +114,9 @@ export class HuiEntityCardEditor extends LitElement
>