From ed368ddd9d214efd9cacf98e861e2cb729c197f4 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 27 Jan 2021 10:46:15 +0100 Subject: [PATCH] Add support for delay values split into parts + millisecond time input (#7886) Co-authored-by: Bram Kragten --- .../ha-form-positive_time_period_dict.ts | 84 +--------- src/components/ha-form/ha-form.ts | 9 +- src/components/ha-time-input.ts | 146 ++++++++++++++++++ src/components/paper-time-input.js | 76 ++++++++- src/data/script.ts | 9 +- .../types/ha-automation-action-delay.ts | 48 ++++-- 6 files changed, 272 insertions(+), 100 deletions(-) create mode 100644 src/components/ha-time-input.ts 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 d5a0db5975..453eaf2a68 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 @@ -6,8 +6,7 @@ import { query, TemplateResult, } from "lit-element"; -import { fireEvent } from "../../common/dom/fire_event"; -import "../paper-time-input"; +import "../ha-time-input"; import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form"; @customElement("ha-form-positive_time_period_dict") @@ -20,7 +19,7 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement { @property() public suffix!: string; - @query("paper-time-input", true) private _input?: HTMLElement; + @query("ha-time-input", true) private _input?: HTMLElement; public focus() { if (this._input) { @@ -30,86 +29,13 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement { protected render(): TemplateResult { return html` - + .data=${this.data} + > `; } - - 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 _parseDuration(value) { - return value.toString().padStart(2, "0"); - } - - private _hourChanged(ev) { - this._durationChanged(ev, "hours"); - } - - private _minChanged(ev) { - this._durationChanged(ev, "minutes"); - } - - private _secChanged(ev) { - this._durationChanged(ev, "seconds"); - } - - 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, - ...{ [unit]: value }, - }, - }); - } } declare global { diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index bab97d6a22..25cf8cd18d 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -8,6 +8,7 @@ import { } from "lit-element"; import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; +import { HaTimeData } from "../ha-time-input"; import "./ha-form-boolean"; import "./ha-form-constant"; import "./ha-form-float"; @@ -71,7 +72,7 @@ export interface HaFormBooleanSchema extends HaFormBaseSchema { } export interface HaFormTimeSchema extends HaFormBaseSchema { - type: "time"; + type: "positive_time_period_dict"; } export interface HaFormDataContainer { @@ -93,11 +94,7 @@ export type HaFormFloatData = number; export type HaFormBooleanData = boolean; export type HaFormSelectData = string; export type HaFormMultiSelectData = string[]; -export interface HaFormTimeData { - hours?: number; - minutes?: number; - seconds?: number; -} +export type HaFormTimeData = HaTimeData; export interface HaFormElement extends LitElement { schema: HaFormSchema | HaFormSchema[]; diff --git a/src/components/ha-time-input.ts b/src/components/ha-time-input.ts new file mode 100644 index 0000000000..e72e1a09d9 --- /dev/null +++ b/src/components/ha-time-input.ts @@ -0,0 +1,146 @@ +import { + customElement, + html, + LitElement, + property, + query, + TemplateResult, +} from "lit-element"; +import { fireEvent } from "../common/dom/fire_event"; +import "./paper-time-input"; + +export interface HaTimeData { + hours?: number; + minutes?: number; + seconds?: number; + milliseconds?: number; +} + +@customElement("ha-time-input") +class HaTimeInput extends LitElement { + @property() public data!: HaTimeData; + + @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-time-input": HaTimeInput; + } +} diff --git a/src/components/paper-time-input.js b/src/components/paper-time-input.js index 7ec7cc64ad..944360227d 100644 --- a/src/components/paper-time-input.js +++ b/src/components/paper-time-input.js @@ -103,6 +103,10 @@ export class PaperTimeInput extends PolymerElement { [hidden] { display: none !important; } + + #millisec { + width: 38px; + } @@ -167,6 +171,28 @@ export class PaperTimeInput extends PolymerElement { always-float-label$="[[alwaysFloatInputLabels]]" disabled="[[disabled]]" hidden$="[[!enableSecond]]" + > + : + + + + @@ -263,6 +289,13 @@ export class PaperTimeInput extends PolymerElement { type: String, notify: true, }, + /** + * milli second + */ + millisec: { + type: String, + notify: true, + }, /** * Suffix for the hour input */ @@ -284,6 +317,13 @@ export class PaperTimeInput extends PolymerElement { type: String, value: "", }, + /** + * Suffix for the milli sec input + */ + millisecLabel: { + type: String, + value: "", + }, /** * show the sec field */ @@ -291,6 +331,13 @@ export class PaperTimeInput extends PolymerElement { type: Boolean, value: false, }, + /** + * show the milli sec field + */ + enableMillisecond: { + type: Boolean, + value: false, + }, /** * limit hours input */ @@ -313,7 +360,7 @@ export class PaperTimeInput extends PolymerElement { type: String, notify: true, readOnly: true, - computed: "_computeTime(min, hour, sec, amPm)", + computed: "_computeTime(min, hour, sec, millisec, amPm)", }, }; } @@ -332,6 +379,10 @@ export class PaperTimeInput extends PolymerElement { if (this.enableSecond && !this.$.sec.validate()) { valid = false; } + // Validate milli second field + if (this.enableMillisecond && !this.$.millisec.validate()) { + valid = false; + } // Validate AM PM if 12 hour time if (this.format === 12 && !this.$.dropdown.validate()) { valid = false; @@ -342,17 +393,27 @@ export class PaperTimeInput extends PolymerElement { /** * Create time string */ - _computeTime(min, hour, sec, amPm) { + _computeTime(min, hour, sec, millisec, amPm) { let str; - if (hour || min || (sec && this.enableSecond)) { + if ( + hour || + min || + (sec && this.enableSecond) || + (millisec && this.enableMillisecond) + ) { hour = hour || "00"; min = min || "00"; sec = sec || "00"; + millisec = millisec || "000"; str = hour + ":" + min; // add sec field if (this.enableSecond && sec) { str = str + ":" + sec; } + // add milli sec field + if (this.enableMillisecond && millisec) { + str = str + ":" + millisec; + } // No ampm on 24 hr time if (this.format === 12) { str = str + " " + amPm; @@ -366,6 +427,15 @@ export class PaperTimeInput extends PolymerElement { ev.target.inputElement.inputElement.select(); } + /** + * Format milli sec + */ + _formatMillisec() { + if (this.millisec.toString().length === 1) { + this.millisec = this.millisec.toString().padStart(3, "0"); + } + } + /** * Format sec */ diff --git a/src/data/script.ts b/src/data/script.ts index a7cfa7506a..e528754f5e 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -45,8 +45,15 @@ export interface DeviceAction { entity_id: string; } +export interface DelayActionParts { + milliseconds?: number; + seconds?: number; + minutes?: number; + hours?: number; + days?: number; +} export interface DelayAction { - delay: number; + delay: number | Partial; } export interface SceneAction { 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 9b44078768..b8e7cee0a5 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,10 +1,12 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, LitElement, property } from "lit-element"; +import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/entity/ha-entity-picker"; +import { HaFormTimeData } from "../../../../../components/ha-form/ha-form"; import "../../../../../components/ha-service-picker"; import { DelayAction } from "../../../../../data/script"; import { HomeAssistant } from "../../../../../types"; -import { ActionElement, handleChangeEvent } from "../ha-automation-action-row"; +import { ActionElement } from "../ha-automation-action-row"; @customElement("ha-automation-action-delay") export class HaDelayAction extends LitElement implements ActionElement { @@ -17,22 +19,46 @@ export class HaDelayAction extends LitElement implements ActionElement { } protected render() { - const { delay } = this.action; + let data: HaFormTimeData = {}; + + if (typeof this.action.delay !== "object") { + const parts = this.action.delay?.toString().split(":") || []; + data = { + hours: Number(parts[0]), + minutes: Number(parts[1]), + seconds: Number(parts[2]), + milliseconds: Number(parts[3]), + }; + } else { + const { days, minutes, seconds, milliseconds } = this.action.delay; + let { hours } = this.action.delay || 0; + hours = (hours || 0) + (days || 0) * 24; + data = { + hours: hours, + minutes: minutes, + seconds: seconds, + milliseconds: milliseconds, + }; + } return html` - + > `; } - private _valueChanged(ev: CustomEvent): void { - handleChangeEvent(this, ev); + private _valueChanged(ev: CustomEvent) { + ev.stopPropagation(); + const value = ev.detail.value; + if (!value) { + return; + } + fireEvent(this, "value-changed", { + value: { ...this.action, delay: value }, + }); } }