Add support for delay values split into parts + millisecond time input (#7886)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Philip Allgaier 2021-01-27 10:46:15 +01:00 committed by GitHub
parent 8400e90e34
commit ed368ddd9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 272 additions and 100 deletions

View File

@ -6,8 +6,7 @@ import {
query, query,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../common/dom/fire_event"; import "../ha-time-input";
import "../paper-time-input";
import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form"; import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form";
@customElement("ha-form-positive_time_period_dict") @customElement("ha-form-positive_time_period_dict")
@ -20,7 +19,7 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
@property() public suffix!: string; @property() public suffix!: string;
@query("paper-time-input", true) private _input?: HTMLElement; @query("ha-time-input", true) private _input?: HTMLElement;
public focus() { public focus() {
if (this._input) { if (this._input) {
@ -30,86 +29,13 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<paper-time-input <ha-time-input
.label=${this.label} .label=${this.label}
.required=${this.schema.required} .required=${this.schema.required}
.autoValidate=${this.schema.required} .data=${this.data}
error-message="Required" ></ha-time-input>
enable-second
format="24"
.hour=${this._parseDuration(this._hours)}
.min=${this._parseDuration(this._minutes)}
.sec=${this._parseDuration(this._seconds)}
@hour-changed=${this._hourChanged}
@min-changed=${this._minChanged}
@sec-changed=${this._secChanged}
float-input-labels
no-hours-limit
always-float-input-labels
hour-label="hh"
min-label="mm"
sec-label="ss"
></paper-time-input>
`; `;
} }
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 { declare global {

View File

@ -8,6 +8,7 @@ import {
} from "lit-element"; } from "lit-element";
import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { HaTimeData } from "../ha-time-input";
import "./ha-form-boolean"; import "./ha-form-boolean";
import "./ha-form-constant"; import "./ha-form-constant";
import "./ha-form-float"; import "./ha-form-float";
@ -71,7 +72,7 @@ export interface HaFormBooleanSchema extends HaFormBaseSchema {
} }
export interface HaFormTimeSchema extends HaFormBaseSchema { export interface HaFormTimeSchema extends HaFormBaseSchema {
type: "time"; type: "positive_time_period_dict";
} }
export interface HaFormDataContainer { export interface HaFormDataContainer {
@ -93,11 +94,7 @@ export type HaFormFloatData = number;
export type HaFormBooleanData = boolean; export type HaFormBooleanData = boolean;
export type HaFormSelectData = string; export type HaFormSelectData = string;
export type HaFormMultiSelectData = string[]; export type HaFormMultiSelectData = string[];
export interface HaFormTimeData { export type HaFormTimeData = HaTimeData;
hours?: number;
minutes?: number;
seconds?: number;
}
export interface HaFormElement extends LitElement { export interface HaFormElement extends LitElement {
schema: HaFormSchema | HaFormSchema[]; schema: HaFormSchema | HaFormSchema[];

View File

@ -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`
<paper-time-input
.label=${this.label}
.required=${this.required}
.autoValidate=${this.required}
error-message="Required"
enable-second
.enableMillisecond=${this.enableMillisecond}
format="24"
.hour=${this._parseDuration(this._hours)}
.min=${this._parseDuration(this._minutes)}
.sec=${this._parseDuration(this._seconds)}
.millisec=${this._parseDurationMillisec(this._milliseconds)}
@hour-changed=${this._hourChanged}
@min-changed=${this._minChanged}
@sec-changed=${this._secChanged}
@millisec-changed=${this._millisecChanged}
float-input-labels
no-hours-limit
always-float-input-labels
hour-label="hh"
min-label="mm"
sec-label="ss"
millisec-label="ms"
></paper-time-input>
`;
}
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;
}
}

View File

@ -103,6 +103,10 @@ export class PaperTimeInput extends PolymerElement {
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
#millisec {
width: 38px;
}
</style> </style>
<label hidden$="[[hideLabel]]">[[label]]</label> <label hidden$="[[hideLabel]]">[[label]]</label>
@ -167,6 +171,28 @@ export class PaperTimeInput extends PolymerElement {
always-float-label$="[[alwaysFloatInputLabels]]" always-float-label$="[[alwaysFloatInputLabels]]"
disabled="[[disabled]]" disabled="[[disabled]]"
hidden$="[[!enableSecond]]" hidden$="[[!enableSecond]]"
>
<span hidden$="[[!enableMillisecond]]" suffix slot="suffix">:</span>
</paper-input>
<!-- Millisec Input -->
<paper-input
id="millisec"
type="number"
value="{{millisec}}"
label="[[millisecLabel]]"
on-change="_formatMillisec"
on-focus="_onFocus"
required
auto-validate="[[autoValidate]]"
prevent-invalid-input
maxlength="3"
max="999"
min="0"
no-label-float$="[[!floatInputLabels]]"
always-float-label$="[[alwaysFloatInputLabels]]"
disabled="[[disabled]]"
hidden$="[[!enableMillisecond]]"
> >
</paper-input> </paper-input>
@ -263,6 +289,13 @@ export class PaperTimeInput extends PolymerElement {
type: String, type: String,
notify: true, notify: true,
}, },
/**
* milli second
*/
millisec: {
type: String,
notify: true,
},
/** /**
* Suffix for the hour input * Suffix for the hour input
*/ */
@ -284,6 +317,13 @@ export class PaperTimeInput extends PolymerElement {
type: String, type: String,
value: "", value: "",
}, },
/**
* Suffix for the milli sec input
*/
millisecLabel: {
type: String,
value: "",
},
/** /**
* show the sec field * show the sec field
*/ */
@ -291,6 +331,13 @@ export class PaperTimeInput extends PolymerElement {
type: Boolean, type: Boolean,
value: false, value: false,
}, },
/**
* show the milli sec field
*/
enableMillisecond: {
type: Boolean,
value: false,
},
/** /**
* limit hours input * limit hours input
*/ */
@ -313,7 +360,7 @@ export class PaperTimeInput extends PolymerElement {
type: String, type: String,
notify: true, notify: true,
readOnly: 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()) { if (this.enableSecond && !this.$.sec.validate()) {
valid = false; valid = false;
} }
// Validate milli second field
if (this.enableMillisecond && !this.$.millisec.validate()) {
valid = false;
}
// Validate AM PM if 12 hour time // Validate AM PM if 12 hour time
if (this.format === 12 && !this.$.dropdown.validate()) { if (this.format === 12 && !this.$.dropdown.validate()) {
valid = false; valid = false;
@ -342,17 +393,27 @@ export class PaperTimeInput extends PolymerElement {
/** /**
* Create time string * Create time string
*/ */
_computeTime(min, hour, sec, amPm) { _computeTime(min, hour, sec, millisec, amPm) {
let str; let str;
if (hour || min || (sec && this.enableSecond)) { if (
hour ||
min ||
(sec && this.enableSecond) ||
(millisec && this.enableMillisecond)
) {
hour = hour || "00"; hour = hour || "00";
min = min || "00"; min = min || "00";
sec = sec || "00"; sec = sec || "00";
millisec = millisec || "000";
str = hour + ":" + min; str = hour + ":" + min;
// add sec field // add sec field
if (this.enableSecond && sec) { if (this.enableSecond && sec) {
str = str + ":" + sec; str = str + ":" + sec;
} }
// add milli sec field
if (this.enableMillisecond && millisec) {
str = str + ":" + millisec;
}
// No ampm on 24 hr time // No ampm on 24 hr time
if (this.format === 12) { if (this.format === 12) {
str = str + " " + amPm; str = str + " " + amPm;
@ -366,6 +427,15 @@ export class PaperTimeInput extends PolymerElement {
ev.target.inputElement.inputElement.select(); 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 * Format sec
*/ */

View File

@ -45,8 +45,15 @@ export interface DeviceAction {
entity_id: string; entity_id: string;
} }
export interface DelayActionParts {
milliseconds?: number;
seconds?: number;
minutes?: number;
hours?: number;
days?: number;
}
export interface DelayAction { export interface DelayAction {
delay: number; delay: number | Partial<DelayActionParts>;
} }
export interface SceneAction { export interface SceneAction {

View File

@ -1,10 +1,12 @@
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { customElement, html, LitElement, property } from "lit-element"; import { customElement, html, LitElement, property } from "lit-element";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker"; import "../../../../../components/entity/ha-entity-picker";
import { HaFormTimeData } from "../../../../../components/ha-form/ha-form";
import "../../../../../components/ha-service-picker"; import "../../../../../components/ha-service-picker";
import { DelayAction } from "../../../../../data/script"; import { DelayAction } from "../../../../../data/script";
import { HomeAssistant } from "../../../../../types"; import { HomeAssistant } from "../../../../../types";
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row"; import { ActionElement } from "../ha-automation-action-row";
@customElement("ha-automation-action-delay") @customElement("ha-automation-action-delay")
export class HaDelayAction extends LitElement implements ActionElement { export class HaDelayAction extends LitElement implements ActionElement {
@ -17,22 +19,46 @@ export class HaDelayAction extends LitElement implements ActionElement {
} }
protected render() { 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` return html`
<paper-input <ha-time-input
.label=${this.hass.localize( .data=${data}
"ui.panel.config.automation.editor.actions.type.delay.delay" enableMillisecond
)}
name="delay"
.value=${delay}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></ha-time-input>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent) {
handleChangeEvent(this, ev); ev.stopPropagation();
const value = ev.detail.value;
if (!value) {
return;
}
fireEvent(this, "value-changed", {
value: { ...this.action, delay: value },
});
} }
} }