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,
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`
<paper-time-input
<ha-time-input
.label=${this.label}
.required=${this.schema.required}
.autoValidate=${this.schema.required}
error-message="Required"
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>
.data=${this.data}
></ha-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 {

View File

@ -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[];

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] {
display: none !important;
}
#millisec {
width: 38px;
}
</style>
<label hidden$="[[hideLabel]]">[[label]]</label>
@ -167,6 +171,28 @@ export class PaperTimeInput extends PolymerElement {
always-float-label$="[[alwaysFloatInputLabels]]"
disabled="[[disabled]]"
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>
@ -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
*/

View File

@ -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<DelayActionParts>;
}
export interface SceneAction {

View File

@ -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`
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.delay.delay"
)}
name="delay"
.value=${delay}
<ha-time-input
.data=${data}
enableMillisecond
@value-changed=${this._valueChanged}
></paper-input>
></ha-time-input>
`;
}
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 },
});
}
}