diff --git a/src/common/datetime/create_duration_data.ts b/src/common/datetime/create_duration_data.ts
new file mode 100644
index 0000000000..92b3d01021
--- /dev/null
+++ b/src/common/datetime/create_duration_data.ts
@@ -0,0 +1,31 @@
+import { HaDurationData } from "../../components/ha-duration-input";
+import { ForDict } from "../../data/automation";
+
+export const createDurationData = (
+ duration: string | number | ForDict | undefined
+): HaDurationData => {
+ if (duration === undefined) {
+ return {};
+ }
+ if (typeof duration !== "object") {
+ if (typeof duration === "string" || isNaN(duration)) {
+ const parts = duration?.toString().split(":") || [];
+ return {
+ hours: Number(parts[0]) || 0,
+ minutes: Number(parts[1]) || 0,
+ seconds: Number(parts[2]) || 0,
+ milliseconds: Number(parts[3]) || 0,
+ };
+ }
+ return { seconds: duration };
+ }
+ const { days, minutes, seconds, milliseconds } = duration;
+ let hours = duration.hours || 0;
+ hours = (hours || 0) + (days || 0) * 24;
+ return {
+ hours,
+ minutes,
+ seconds,
+ milliseconds,
+ };
+};
diff --git a/src/components/ha-duration-input.ts b/src/components/ha-duration-input.ts
new file mode 100644
index 0000000000..32651cb2c2
--- /dev/null
+++ b/src/components/ha-duration-input.ts
@@ -0,0 +1,140 @@
+import { html, LitElement, TemplateResult } from "lit";
+import { customElement, property, query } from "lit/decorators";
+import { fireEvent } from "../common/dom/fire_event";
+import "./paper-time-input";
+
+export interface HaDurationData {
+ hours?: number;
+ minutes?: number;
+ seconds?: number;
+ milliseconds?: number;
+}
+
+@customElement("ha-duration-input")
+class HaDurationInput extends LitElement {
+ @property({ attribute: false }) public data!: HaDurationData;
+
+ @property() public label?: string;
+
+ @property() public suffix?: string;
+
+ @property({ type: Boolean }) public required?: boolean;
+
+ @property({ type: Boolean }) public enableMillisecond?: boolean;
+
+ @query("paper-time-input", true) private _input?: HTMLElement;
+
+ public focus() {
+ if (this._input) {
+ this._input.focus();
+ }
+ }
+
+ protected render(): TemplateResult {
+ return html`
+
+ `;
+ }
+
+ private get _hours() {
+ return this.data && this.data.hours ? Number(this.data.hours) : 0;
+ }
+
+ private get _minutes() {
+ return this.data && this.data.minutes ? Number(this.data.minutes) : 0;
+ }
+
+ private get _seconds() {
+ return this.data && this.data.seconds ? Number(this.data.seconds) : 0;
+ }
+
+ private get _milliseconds() {
+ return this.data && this.data.milliseconds
+ ? Number(this.data.milliseconds)
+ : 0;
+ }
+
+ private _parseDuration(value) {
+ return value.toString().padStart(2, "0");
+ }
+
+ private _parseDurationMillisec(value) {
+ return value.toString().padStart(3, "0");
+ }
+
+ private _hourChanged(ev) {
+ this._durationChanged(ev, "hours");
+ }
+
+ private _minChanged(ev) {
+ this._durationChanged(ev, "minutes");
+ }
+
+ private _secChanged(ev) {
+ this._durationChanged(ev, "seconds");
+ }
+
+ private _millisecChanged(ev) {
+ this._durationChanged(ev, "milliseconds");
+ }
+
+ private _durationChanged(ev, unit) {
+ let value = Number(ev.detail.value);
+
+ if (value === this[`_${unit}`]) {
+ return;
+ }
+
+ let hours = this._hours;
+ let minutes = this._minutes;
+
+ if (unit === "seconds" && value > 59) {
+ minutes += Math.floor(value / 60);
+ value %= 60;
+ }
+
+ if (unit === "minutes" && value > 59) {
+ hours += Math.floor(value / 60);
+ value %= 60;
+ }
+
+ fireEvent(this, "value-changed", {
+ value: {
+ hours,
+ minutes,
+ seconds: this._seconds,
+ milliseconds: this._milliseconds,
+ ...{ [unit]: value },
+ },
+ });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-duration-input": HaDurationInput;
+ }
+}
diff --git a/src/components/ha-form/ha-form-positive_time_period_dict.ts b/src/components/ha-form/ha-form-positive_time_period_dict.ts
index 04ec575981..7806e5c685 100644
--- a/src/components/ha-form/ha-form-positive_time_period_dict.ts
+++ b/src/components/ha-form/ha-form-positive_time_period_dict.ts
@@ -1,6 +1,6 @@
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
-import "../ha-time-input";
+import "../ha-duration-input";
import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form";
@customElement("ha-form-positive_time_period_dict")
@@ -23,11 +23,11 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
protected render(): TemplateResult {
return html`
-
+ >
`;
}
}
diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts
index 1a4c02e319..49b19a8328 100644
--- a/src/components/ha-form/ha-form.ts
+++ b/src/components/ha-form/ha-form.ts
@@ -2,7 +2,7 @@ import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
-import { HaTimeData } from "../ha-time-input";
+import { HaDurationData } from "../ha-duration-input";
import "./ha-form-boolean";
import "./ha-form-constant";
import "./ha-form-float";
@@ -88,7 +88,7 @@ export type HaFormFloatData = number;
export type HaFormBooleanData = boolean;
export type HaFormSelectData = string;
export type HaFormMultiSelectData = string[];
-export type HaFormTimeData = HaTimeData;
+export type HaFormTimeData = HaDurationData;
export interface HaFormElement extends LitElement {
schema: HaFormSchema | HaFormSchema[];
diff --git a/src/components/ha-selector/ha-selector-time.ts b/src/components/ha-selector/ha-selector-time.ts
index 9ac9ea8e51..f1a116d371 100644
--- a/src/components/ha-selector/ha-selector-time.ts
+++ b/src/components/ha-selector/ha-selector-time.ts
@@ -1,10 +1,8 @@
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
-import { useAmPm } from "../../common/datetime/use_am_pm";
-import { fireEvent } from "../../common/dom/fire_event";
import { TimeSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
-import "../paper-time-input";
+import "../ha-time-input";
@customElement("ha-selector-time")
export class HaTimeSelector extends LitElement {
@@ -19,46 +17,16 @@ export class HaTimeSelector extends LitElement {
@property({ type: Boolean }) public disabled = false;
protected render() {
- const useAMPM = useAmPm(this.hass.locale);
-
- const parts = this.value?.split(":") || [];
- const hours = parts[0];
-
return html`
- 12 ? Number(hours) - 12 : hours)}
- .min=${parts[1]}
- .sec=${parts[2]}
- .format=${useAMPM ? 12 : 24}
- .amPm=${useAMPM && (Number(hours) > 12 ? "PM" : "AM")}
+
+ >
`;
}
-
- private _timeChanged(ev) {
- let value = ev.target.value;
- const useAMPM = useAmPm(this.hass.locale);
- let hours = Number(ev.target.hour || 0);
- if (value && useAMPM) {
- if (ev.target.amPm === "PM") {
- hours += 12;
- }
- value = `${hours}:${ev.target.min || "00"}:${ev.target.sec || "00"}`;
- }
- if (value === this.value) {
- return;
- }
- fireEvent(this, "value-changed", {
- value,
- });
- }
}
declare global {
diff --git a/src/components/ha-time-input.ts b/src/components/ha-time-input.ts
index 382ea4d569..c4ca542801 100644
--- a/src/components/ha-time-input.ts
+++ b/src/components/ha-time-input.ts
@@ -1,134 +1,78 @@
-import { html, LitElement, TemplateResult } from "lit";
-import { customElement, property, query } from "lit/decorators";
+import { html, LitElement } from "lit";
+import { customElement, property } from "lit/decorators";
+import { useAmPm } from "../common/datetime/use_am_pm";
import { fireEvent } from "../common/dom/fire_event";
import "./paper-time-input";
-
-export interface HaTimeData {
- hours?: number;
- minutes?: number;
- seconds?: number;
- milliseconds?: number;
-}
+import { FrontendLocaleData } from "../data/translation";
@customElement("ha-time-input")
-class HaTimeInput extends LitElement {
- @property() public data!: HaTimeData;
+export class HaTimeInput extends LitElement {
+ @property() public locale!: FrontendLocaleData;
+
+ @property() public value?: string;
@property() public label?: string;
- @property() public suffix?: string;
+ @property({ type: Boolean }) public disabled = false;
- @property({ type: Boolean }) public required?: boolean;
+ @property({ type: Boolean, attribute: "hide-label" }) public hideLabel =
+ false;
- @property({ type: Boolean }) public enableMillisecond?: boolean;
+ @property({ type: Boolean, attribute: "enable-second" })
+ public enableSecond = false;
- @query("paper-time-input", true) private _input?: HTMLElement;
+ protected render() {
+ const useAMPM = useAmPm(this.locale);
- public focus() {
- if (this._input) {
- this._input.focus();
+ const parts = this.value?.split(":") || [];
+ let hours = parts[0];
+ const numberHours = Number(parts[0]);
+ if (numberHours && useAMPM && numberHours > 12) {
+ hours = String(numberHours - 12).padStart(2, "0");
+ }
+ if (useAMPM && numberHours === 0) {
+ hours = "12";
}
- }
- protected render(): TemplateResult {
return html`
= 12 ? "PM" : "AM")}
+ .disabled=${this.disabled}
+ @change=${this._timeChanged}
+ @am-pm-changed=${this._timeChanged}
+ .hideLabel=${this.hideLabel}
+ .enableSecond=${this.enableSecond}
>
`;
}
- private get _hours() {
- return this.data && this.data.hours ? Number(this.data.hours) : 0;
- }
-
- private get _minutes() {
- return this.data && this.data.minutes ? Number(this.data.minutes) : 0;
- }
-
- private get _seconds() {
- return this.data && this.data.seconds ? Number(this.data.seconds) : 0;
- }
-
- private get _milliseconds() {
- return this.data && this.data.milliseconds
- ? Number(this.data.milliseconds)
- : 0;
- }
-
- private _parseDuration(value) {
- return value.toString().padStart(2, "0");
- }
-
- private _parseDurationMillisec(value) {
- return value.toString().padStart(3, "0");
- }
-
- private _hourChanged(ev) {
- this._durationChanged(ev, "hours");
- }
-
- private _minChanged(ev) {
- this._durationChanged(ev, "minutes");
- }
-
- private _secChanged(ev) {
- this._durationChanged(ev, "seconds");
- }
-
- private _millisecChanged(ev) {
- this._durationChanged(ev, "milliseconds");
- }
-
- private _durationChanged(ev, unit) {
- let value = Number(ev.detail.value);
-
- if (value === this[`_${unit}`]) {
+ private _timeChanged(ev) {
+ let value = ev.target.value;
+ const useAMPM = useAmPm(this.locale);
+ let hours = Number(ev.target.hour || 0);
+ if (value && useAMPM) {
+ if (ev.target.amPm === "PM" && hours < 12) {
+ hours += 12;
+ }
+ if (ev.target.amPm === "AM" && hours === 12) {
+ hours = 0;
+ }
+ value = `${hours.toString().padStart(2, "0")}:${ev.target.min || "00"}:${
+ ev.target.sec || "00"
+ }`;
+ }
+ if (value === this.value) {
return;
}
-
- let hours = this._hours;
- let minutes = this._minutes;
-
- if (unit === "seconds" && value > 59) {
- minutes += Math.floor(value / 60);
- value %= 60;
- }
-
- if (unit === "minutes" && value > 59) {
- hours += Math.floor(value / 60);
- value %= 60;
- }
-
+ this.value = value;
+ fireEvent(this, "change");
fireEvent(this, "value-changed", {
- value: {
- hours,
- minutes,
- seconds: this._seconds,
- milliseconds: this._milliseconds,
- ...{ [unit]: value },
- },
+ value,
});
}
}
diff --git a/src/data/automation.ts b/src/data/automation.ts
index da6a12ddba..b6ea4c90c6 100644
--- a/src/data/automation.ts
+++ b/src/data/automation.ts
@@ -46,9 +46,11 @@ export interface BlueprintAutomationConfig extends ManualAutomationConfig {
}
export interface ForDict {
- hours?: number | string;
- minutes?: number | string;
- seconds?: number | string;
+ days?: number;
+ hours?: number;
+ minutes?: number;
+ seconds?: number;
+ milliseconds?: number;
}
export interface ContextConstraint {
diff --git a/src/dialogs/more-info/controls/more-info-input_datetime.js b/src/dialogs/more-info/controls/more-info-input_datetime.js
deleted file mode 100644
index cff1b5233b..0000000000
--- a/src/dialogs/more-info/controls/more-info-input_datetime.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes";
-import "@polymer/paper-input/paper-input";
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-/* eslint-plugin-disable lit */
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-import { attributeClassNames } from "../../../common/entity/attribute_class_names";
-import "../../../components/ha-date-input";
-import "../../../components/ha-relative-time";
-import "../../../components/paper-time-input";
-
-class DatetimeInput extends PolymerElement {
- static get template() {
- return html`
-
- `;
- }
-
- constructor() {
- super();
- this.is_ready = false;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- },
-
- stateObj: {
- type: Object,
- observer: "stateObjChanged",
- },
-
- selectedDate: {
- type: String,
- observer: "dateTimeChanged",
- },
-
- selectedHour: {
- type: Number,
- observer: "dateTimeChanged",
- },
-
- selectedMinute: {
- type: Number,
- observer: "dateTimeChanged",
- },
- };
- }
-
- ready() {
- super.ready();
- this.is_ready = true;
- }
-
- /* Convert the date in the stateObj into a string useable by vaadin-date-picker */
- getDateString(stateObj) {
- if (stateObj.state === "unknown") {
- return "";
- }
- let monthFiller;
- if (stateObj.attributes.month < 10) {
- monthFiller = "0";
- } else {
- monthFiller = "";
- }
-
- let dayFiller;
- if (stateObj.attributes.day < 10) {
- dayFiller = "0";
- } else {
- dayFiller = "";
- }
-
- return (
- stateObj.attributes.year +
- "-" +
- monthFiller +
- stateObj.attributes.month +
- "-" +
- dayFiller +
- stateObj.attributes.day
- );
- }
-
- /* Should fire when any value was changed *by the user*, not b/c of setting
- * initial values. */
- dateTimeChanged() {
- // Check if the change is really coming from the user
- if (!this.is_ready) {
- return;
- }
-
- let changed = false;
- let minuteFiller;
-
- const serviceData = {
- entity_id: this.stateObj.entity_id,
- };
-
- if (this.stateObj.attributes.has_time) {
- changed =
- changed ||
- parseInt(this.selectedMinute) !== this.stateObj.attributes.minute;
- changed =
- changed ||
- parseInt(this.selectedHour) !== this.stateObj.attributes.hour;
- if (this.selectedMinute < 10) {
- minuteFiller = "0";
- } else {
- minuteFiller = "";
- }
- const timeStr =
- this.selectedHour + ":" + minuteFiller + this.selectedMinute;
- serviceData.time = timeStr;
- }
-
- if (this.stateObj.attributes.has_date) {
- if (this.selectedDate.length === 0) {
- return; // Date was not set
- }
-
- const dateValInput = new Date(this.selectedDate);
- const dateValState = new Date(
- this.stateObj.attributes.year,
- this.stateObj.attributes.month - 1,
- this.stateObj.attributes.day
- );
-
- changed = changed || dateValState !== dateValInput;
-
- serviceData.date = this.selectedDate;
- }
-
- if (changed) {
- this.hass.callService("input_datetime", "set_datetime", serviceData);
- }
- }
-
- stateObjChanged(newVal) {
- // Set to non-ready s.t. dateTimeChanged does not fire
- this.is_ready = false;
-
- if (newVal.attributes.has_time) {
- this.selectedHour = newVal.attributes.hour;
- this.selectedMinute = newVal.attributes.minute;
- }
-
- if (newVal.attributes.has_date) {
- this.selectedDate = this.getDateString(newVal);
- }
-
- this.is_ready = true;
- }
-
- doesHaveDate(stateObj) {
- return stateObj.attributes.has_date;
- }
-
- doesHaveTime(stateObj) {
- return stateObj.attributes.has_time;
- }
-
- computeClassNames(stateObj) {
- return (
- "more-info-input_datetime " +
- attributeClassNames(stateObj, ["has_time", "has_date"])
- );
- }
-}
-
-customElements.define("more-info-input_datetime", DatetimeInput);
diff --git a/src/dialogs/more-info/controls/more-info-input_datetime.ts b/src/dialogs/more-info/controls/more-info-input_datetime.ts
new file mode 100644
index 0000000000..af1e9aa6a1
--- /dev/null
+++ b/src/dialogs/more-info/controls/more-info-input_datetime.ts
@@ -0,0 +1,103 @@
+import { HassEntity } from "home-assistant-js-websocket";
+import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
+import { customElement, property } from "lit/decorators";
+import "../../../components/ha-date-input";
+import "../../../components/ha-time-input";
+import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
+import { setInputDateTimeValue } from "../../../data/input_datetime";
+import type { HomeAssistant } from "../../../types";
+
+@customElement("more-info-input_datetime")
+class MoreInfoInputDatetime extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public stateObj?: HassEntity;
+
+ protected render(): TemplateResult {
+ if (!this.stateObj) {
+ return html``;
+ }
+
+ return html`
+ ${
+ this.stateObj.attributes.has_date
+ ? html`
+
+
+ `
+ : ``
+ }
+ ${
+ this.stateObj.attributes.has_time
+ ? html`
+
+ `
+ : ``
+ }
+
+ `;
+ }
+
+ private _stopEventPropagation(ev: Event): void {
+ ev.stopPropagation();
+ }
+
+ private _timeChanged(ev): void {
+ setInputDateTimeValue(
+ this.hass!,
+ this.stateObj!.entity_id,
+ ev.detail.value,
+ this.stateObj!.attributes.has_date
+ ? this.stateObj!.state.split(" ")[0]
+ : undefined
+ );
+ ev.target.blur();
+ }
+
+ private _dateChanged(ev): void {
+ setInputDateTimeValue(
+ this.hass!,
+ this.stateObj!.entity_id,
+ this.stateObj!.attributes.has_time
+ ? this.stateObj!.state.split(" ")[1]
+ : undefined,
+ ev.detail.value
+ );
+
+ ev.target.blur();
+ }
+
+ static get styles(): CSSResultGroup {
+ return css`
+ :host {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ }
+ ha-date-input + ha-time-input {
+ margin-left: 4px;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "more-info-input_datetime": MoreInfoInputDatetime;
+ }
+}
diff --git a/src/panels/config/automation/action/types/ha-automation-action-delay.ts b/src/panels/config/automation/action/types/ha-automation-action-delay.ts
index ab9a3ed8ab..e03a533be6 100644
--- a/src/panels/config/automation/action/types/ha-automation-action-delay.ts
+++ b/src/panels/config/automation/action/types/ha-automation-action-delay.ts
@@ -1,14 +1,13 @@
-import "@polymer/paper-input/paper-input";
import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { hasTemplate } from "../../../../../common/string/has-template";
-import "../../../../../components/entity/ha-entity-picker";
-import { HaFormTimeData } from "../../../../../components/ha-form/ha-form";
-import "../../../../../components/ha-service-picker";
+import type { HaDurationData } from "../../../../../components/ha-duration-input";
+import "../../../../../components/ha-duration-input";
import { DelayAction } from "../../../../../data/script";
import { HomeAssistant } from "../../../../../types";
import { ActionElement } from "../ha-automation-action-row";
+import { createDurationData } from "../../../../../common/datetime/create_duration_data";
@customElement("ha-automation-action-delay")
export class HaDelayAction extends LitElement implements ActionElement {
@@ -16,13 +15,13 @@ export class HaDelayAction extends LitElement implements ActionElement {
@property() public action!: DelayAction;
- @property() public _timeData!: HaFormTimeData;
+ @property() public _timeData!: HaDurationData;
public static get defaultConfig() {
return { delay: "" };
}
- protected updated(changedProperties: PropertyValues) {
+ public willUpdate(changedProperties: PropertyValues) {
if (!changedProperties.has("action")) {
return;
}
@@ -36,37 +35,15 @@ export class HaDelayAction extends LitElement implements ActionElement {
return;
}
- if (typeof this.action.delay !== "object") {
- if (typeof this.action.delay === "string" || isNaN(this.action.delay)) {
- const parts = this.action.delay?.toString().split(":") || [];
- this._timeData = {
- hours: Number(parts[0]) || 0,
- minutes: Number(parts[1]) || 0,
- seconds: Number(parts[2]) || 0,
- milliseconds: Number(parts[3]) || 0,
- };
- } else {
- this._timeData = { seconds: this.action.delay };
- }
- return;
- }
- const { days, minutes, seconds, milliseconds } = this.action.delay;
- let { hours } = this.action.delay || 0;
- hours = (hours || 0) + (days || 0) * 24;
- this._timeData = {
- hours: hours,
- minutes: minutes,
- seconds: seconds,
- milliseconds: milliseconds,
- };
+ this._timeData = createDurationData(this.action.delay);
}
protected render() {
- return html``;
+ >`;
}
private _valueChanged(ev: CustomEvent) {
diff --git a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts
index 1b841f0af9..22103067f8 100644
--- a/src/panels/config/automation/condition/types/ha-automation-condition-state.ts
+++ b/src/panels/config/automation/condition/types/ha-automation-condition-state.ts
@@ -1,14 +1,16 @@
import "@polymer/paper-input/paper-input";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
+import { createDurationData } from "../../../../../common/datetime/create_duration_data";
import "../../../../../components/entity/ha-entity-attribute-picker";
import "../../../../../components/entity/ha-entity-picker";
-import { ForDict, StateCondition } from "../../../../../data/automation";
+import { StateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import {
ConditionElement,
handleChangeEvent,
} from "../ha-automation-condition-row";
+import "../../../../../components/ha-duration-input";
@customElement("ha-automation-condition-state")
export class HaStateCondition extends LitElement implements ConditionElement {
@@ -22,23 +24,7 @@ export class HaStateCondition extends LitElement implements ConditionElement {
protected render() {
const { entity_id, attribute, state } = this.condition;
- let forTime = this.condition.for;
-
- if (
- forTime &&
- ((forTime as ForDict).hours ||
- (forTime as ForDict).minutes ||
- (forTime as ForDict).seconds)
- ) {
- // If the trigger was defined using the yaml dict syntax, convert it to
- // the equivalent string format
- let { hours = 0, minutes = 0, seconds = 0 } = forTime as ForDict;
- hours = hours.toString().padStart(2, "0");
- minutes = minutes.toString().padStart(2, "0");
- seconds = seconds.toString().padStart(2, "0");
-
- forTime = `${hours}:${minutes}:${seconds}`;
- }
+ const forTime = createDurationData(this.condition.for);
return html`
-
+ >
`;
}
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 e606e31eac..f227847a3f 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,4 @@
import { Radio } from "@material/mwc-radio";
-import "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
@@ -13,6 +12,7 @@ import {
ConditionElement,
handleChangeEvent,
} from "../ha-automation-condition-row";
+import "../../../../../components/ha-time-input";
const includeDomains = ["input_datetime"];
@@ -89,14 +89,15 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
.hass=${this.hass}
allow-custom-entity
>`
- : html``}
+ >`}
`
- : html``}
+ >`}
${Object.keys(DAYS).map(
(day) => html`
+ ${this._warnings
+ ? html`
+ ${this.hass.localize("ui.errors.config.editor_not_supported")}:
+
+ ${this._warnings.length && this._warnings[0] !== undefined
+ ? html`
+ ${this._warnings.map(
+ (warning) => html`- ${warning}
`
+ )}
+
`
+ : ""}
+ ${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
+
`
+ : ""}
${yamlMode
? html`
${selected === -1
@@ -170,7 +187,7 @@ export default class HaAutomationTriggerRow extends LitElement {
@value-changed=${this._idChanged}
>
-
+
${dynamicElement(
`ha-automation-trigger-${this.trigger.platform}`,
{ hass: this.hass, trigger: this.trigger }
@@ -182,6 +199,13 @@ export default class HaAutomationTriggerRow extends LitElement {
`;
}
+ private _handleUiModeNotAvailable(ev: CustomEvent) {
+ this._warnings = handleStructError(this.hass, ev.detail).warnings;
+ if (!this._yamlMode) {
+ this._yamlMode = true;
+ }
+ }
+
private _handleAction(ev: CustomEvent
) {
switch (ev.detail.index) {
case 0:
@@ -258,6 +282,7 @@ export default class HaAutomationTriggerRow extends LitElement {
}
private _switchYamlMode() {
+ this._warnings = undefined;
this._yamlMode = !this._yamlMode;
}
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 cf7cf64115..27c2c6d708 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,11 +1,15 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
-import { html, LitElement } from "lit";
+import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
+import { createDurationData } from "../../../../../common/datetime/create_duration_data";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import { hasTemplate } from "../../../../../common/string/has-template";
import "../../../../../components/entity/ha-entity-picker";
-import { ForDict, NumericStateTrigger } from "../../../../../data/automation";
+import { NumericStateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-trigger-row";
+import "../../../../../components/ha-duration-input";
@customElement("ha-automation-trigger-numeric_state")
export default class HaNumericStateTrigger extends LitElement {
@@ -13,6 +17,20 @@ export default class HaNumericStateTrigger extends LitElement {
@property() public trigger!: NumericStateTrigger;
+ public willUpdate(changedProperties: PropertyValues) {
+ if (!changedProperties.has("trigger")) {
+ return;
+ }
+ // Check for templates in trigger. If found, revert to YAML mode.
+ if (this.trigger && hasTemplate(this.trigger)) {
+ fireEvent(
+ this,
+ "ui-mode-not-available",
+ Error(this.hass.localize("ui.errors.config.no_template_editor_support"))
+ );
+ }
+ }
+
public static get defaultConfig() {
return {
entity_id: "",
@@ -21,23 +39,8 @@ export default class HaNumericStateTrigger extends LitElement {
public render() {
const { value_template, entity_id, attribute, below, above } = this.trigger;
- let trgFor = this.trigger.for;
+ const trgFor = createDurationData(this.trigger.for);
- if (
- trgFor &&
- ((trgFor as ForDict).hours ||
- (trgFor as ForDict).minutes ||
- (trgFor as ForDict).seconds)
- ) {
- // If the trigger was defined using the yaml dict syntax, convert it to
- // the equivalent string format
- let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict;
- hours = hours.toString();
- minutes = minutes.toString().padStart(2, "0");
- seconds = seconds.toString().padStart(2, "0");
-
- trgFor = `${hours}:${minutes}:${seconds}`;
- }
return html`
-
+ >
`;
}
diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts
index 1ad3b18555..6be2d30fab 100644
--- a/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts
+++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-state.ts
@@ -1,10 +1,14 @@
import "@polymer/paper-input/paper-input";
-import { html, LitElement } from "lit";
+import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
+import { createDurationData } from "../../../../../common/datetime/create_duration_data";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import { hasTemplate } from "../../../../../common/string/has-template";
import "../../../../../components/entity/ha-entity-attribute-picker";
import "../../../../../components/entity/ha-entity-picker";
-import { ForDict, StateTrigger } from "../../../../../data/automation";
+import { StateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
+import "../../../../../components/ha-duration-input";
import {
handleChangeEvent,
TriggerElement,
@@ -20,25 +24,23 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
return { entity_id: "" };
}
+ public willUpdate(changedProperties: PropertyValues) {
+ if (!changedProperties.has("trigger")) {
+ return;
+ }
+ // Check for templates in trigger. If found, revert to YAML mode.
+ if (this.trigger && hasTemplate(this.trigger)) {
+ fireEvent(
+ this,
+ "ui-mode-not-available",
+ Error(this.hass.localize("ui.errors.config.no_template_editor_support"))
+ );
+ }
+ }
+
protected render() {
const { entity_id, attribute, to, from } = this.trigger;
- let trgFor = this.trigger.for;
-
- if (
- trgFor &&
- ((trgFor as ForDict).hours ||
- (trgFor as ForDict).minutes ||
- (trgFor as ForDict).seconds)
- ) {
- // If the trigger was defined using the yaml dict syntax, convert it to
- // the equivalent string format
- let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict;
- hours = hours.toString();
- minutes = minutes.toString().padStart(2, "0");
- seconds = seconds.toString().padStart(2, "0");
-
- trgFor = `${hours}:${minutes}:${seconds}`;
- }
+ const trgFor = createDurationData(this.trigger.for);
return html`
-
+ >
`;
}
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 ddb0404907..a72c5a9749 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,4 @@
-import "@polymer/paper-input/paper-input";
-import { html, LitElement } from "lit";
+import { html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-formfield";
@@ -10,9 +9,10 @@ import {
handleChangeEvent,
TriggerElement,
} from "../ha-automation-trigger-row";
+import "../../../../../components/ha-time-input";
+import { fireEvent } from "../../../../../common/dom/fire_event";
const includeDomains = ["input_datetime"];
-
@customElement("ha-automation-trigger-time")
export class HaTimeTrigger extends LitElement implements TriggerElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -25,11 +25,32 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
return { at: "" };
}
+ public willUpdate(changedProperties: PropertyValues) {
+ if (!changedProperties.has("trigger")) {
+ return;
+ }
+ // We dont support multiple times atm.
+ if (this.trigger && Array.isArray(this.trigger.at)) {
+ fireEvent(
+ this,
+ "ui-mode-not-available",
+ Error(this.hass.localize("ui.errors.config.editor_not_supported"))
+ );
+ }
+ }
+
protected render() {
- const { at } = this.trigger;
- const inputMode = this._inputMode ?? at?.startsWith("input_datetime.");
- return html`
-
+
${inputMode
? html``
- : html``}
- `;
+ >`} `;
}
private _handleModeChanged(ev: Event) {
diff --git a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts
index 0567009b12..c3441ac769 100644
--- a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts
@@ -1,9 +1,13 @@
-import { html, LitElement, PropertyValues, TemplateResult } from "lit";
-import { customElement, property, state, query } from "lit/decorators";
+import {
+ css,
+ CSSResultGroup,
+ html,
+ LitElement,
+ PropertyValues,
+ TemplateResult,
+} from "lit";
+import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-date-input";
-import type { HaDateInput } from "../../../components/ha-date-input";
-import "../../../components/paper-time-input";
-import type { PaperTimeInput } from "../../../components/paper-time-input";
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
import { setInputDateTimeValue } from "../../../data/input_datetime";
import type { HomeAssistant } from "../../../types";
@@ -11,6 +15,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { EntityConfig, LovelaceRow } from "./types";
+import "../../../components/ha-time-input";
@customElement("hui-input-datetime-entity-row")
class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
@@ -18,10 +23,6 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
@state() private _config?: EntityConfig;
- @query("paper-time-input") private _timeInputEl?: PaperTimeInput;
-
- @query("ha-date-input") private _dateInputEl?: HaDateInput;
-
public setConfig(config: EntityConfig): void {
if (!config) {
throw new Error("Invalid configuration");
@@ -55,27 +56,25 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
- ${stateObj.attributes.has_time ? "," : ""}
`
: ``}
${stateObj.attributes.has_time
? html`
-
+ @value-changed=${this._timeChanged}
+ @click=${this._stopEventPropagation}
+ >
`
: ``}
@@ -86,19 +85,37 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
ev.stopPropagation();
}
- private _selectedValueChanged(ev): void {
+ private _timeChanged(ev): void {
+ const stateObj = this.hass!.states[this._config!.entity];
+ setInputDateTimeValue(
+ this.hass!,
+ stateObj.entity_id,
+ ev.detail.value,
+ stateObj.attributes.has_date ? stateObj.state.split(" ")[0] : undefined
+ );
+ ev.target.blur();
+ }
+
+ private _dateChanged(ev): void {
const stateObj = this.hass!.states[this._config!.entity];
- const time = this._timeInputEl
- ? this._timeInputEl.value?.trim()
- : undefined;
-
- const date = this._dateInputEl ? this._dateInputEl.value : undefined;
-
- setInputDateTimeValue(this.hass!, stateObj.entity_id, time, date);
+ setInputDateTimeValue(
+ this.hass!,
+ stateObj.entity_id,
+ stateObj.attributes.has_time ? stateObj.state.split(" ")[1] : undefined,
+ ev.detail.value
+ );
ev.target.blur();
}
+
+ static get styles(): CSSResultGroup {
+ return css`
+ ha-date-input + ha-time-input {
+ margin-left: 4px;
+ }
+ `;
+ }
}
declare global {