diff --git a/src/components/ha-form/ha-form-float.ts b/src/components/ha-form/ha-form-float.ts
index 0d2279ea72..d2d41990aa 100644
--- a/src/components/ha-form/ha-form-float.ts
+++ b/src/components/ha-form/ha-form-float.ts
@@ -32,7 +32,7 @@ export class HaFormFloat extends LitElement implements HaFormElement {
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 _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 = minutes + Math.floor(value / 60);
+ value %= 60;
+ }
+
+ if (unit === "minutes" && value > 59) {
+ hours = hours + Math.floor(value / 60);
+ value %= 60;
+ }
+
+ fireEvent(
+ this,
+ "value-changed",
+ {
+ value: {
+ hours,
+ minutes,
+ seconds: this._seconds,
+ ...{ [unit]: value },
+ },
+ },
+ { bubbles: false }
+ );
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-form-positive_time_period_dict": HaFormTimePeriod;
+ }
+}
diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts
index a8fe7d813d..48973c63b9 100644
--- a/src/components/ha-form/ha-form.ts
+++ b/src/components/ha-form/ha-form.ts
@@ -14,6 +14,7 @@ import "./ha-form-integer";
import "./ha-form-float";
import "./ha-form-boolean";
import "./ha-form-select";
+import "./ha-form-positive_time_period_dict";
import { fireEvent } from "../../common/dom/fire_event";
export type HaFormSchema =
@@ -21,7 +22,8 @@ export type HaFormSchema =
| HaFormIntegerSchema
| HaFormFloatSchema
| HaFormBooleanSchema
- | HaFormSelectSchema;
+ | HaFormSelectSchema
+ | HaFormTimeSchema;
export interface HaFormBaseSchema {
name: string;
@@ -55,6 +57,10 @@ export interface HaFormBooleanSchema extends HaFormBaseSchema {
type: "boolean";
}
+export interface HaFormTimeSchema extends HaFormBaseSchema {
+ type: "time";
+}
+
export interface HaFormDataContainer {
[key: string]: HaFormData;
}
@@ -64,13 +70,19 @@ export type HaFormData =
| HaFormIntegerData
| HaFormFloatData
| HaFormBooleanData
- | HaFormSelectData;
+ | HaFormSelectData
+ | HaFormTimeData;
export type HaFormStringData = string;
export type HaFormIntegerData = number;
export type HaFormFloatData = number;
export type HaFormBooleanData = boolean;
export type HaFormSelectData = string;
+export interface HaFormTimeData {
+ hours?: number;
+ minutes?: number;
+ seconds?: number;
+}
export interface HaFormElement extends LitElement {
schema: HaFormSchema;
diff --git a/src/components/paper-time-input.js b/src/components/paper-time-input.js
index 2a2d5bb52b..620da4d7b1 100644
--- a/src/components/paper-time-input.js
+++ b/src/components/paper-time-input.js
@@ -87,6 +87,10 @@ export class PaperTimeInput extends PolymerElement {
label {
@apply --paper-font-caption;
+ color: var(
+ --paper-input-container-color,
+ var(--secondary-text-color)
+ );
}
.time-input-wrap {
@@ -106,14 +110,17 @@ export class PaperTimeInput extends PolymerElement {
id="hour"
type="number"
value="{{hour}}"
+ label="[[hourLabel]]"
on-change="_shouldFormatHour"
- required=""
+ on-focus="_onFocus"
+ required
+ prevent-invalid-input
auto-validate="[[autoValidate]]"
- prevent-invalid-input=""
maxlength="2"
max="[[_computeHourMax(format)]]"
min="0"
- no-label-float=""
+ no-label-float$="[[!floatInputLabels]]"
+ always-float-label$="[[alwaysFloatInputLabels]]"
disabled="[[disabled]]"
>
:
@@ -124,15 +131,40 @@ export class PaperTimeInput extends PolymerElement {
id="min"
type="number"
value="{{min}}"
+ label="[[minLabel]]"
on-change="_formatMin"
- required=""
+ on-focus="_onFocus"
+ required
auto-validate="[[autoValidate]]"
- prevent-invalid-input=""
+ prevent-invalid-input
maxlength="2"
max="59"
min="0"
- no-label-float=""
+ no-label-float$="[[!floatInputLabels]]"
+ always-float-label$="[[alwaysFloatInputLabels]]"
disabled="[[disabled]]"
+ >
+ :
+
+
+
+
@@ -180,6 +212,20 @@ export class PaperTimeInput extends PolymerElement {
type: Boolean,
value: false,
},
+ /**
+ * float the input labels
+ */
+ floatInputLabels: {
+ type: Boolean,
+ value: false,
+ },
+ /**
+ * always float the input labels
+ */
+ alwaysFloatInputLabels: {
+ type: Boolean,
+ value: false,
+ },
/**
* 12 or 24 hr format
*/
@@ -208,6 +254,48 @@ export class PaperTimeInput extends PolymerElement {
type: String,
notify: true,
},
+ /**
+ * second
+ */
+ sec: {
+ type: String,
+ notify: true,
+ },
+ /**
+ * Suffix for the hour input
+ */
+ hourLabel: {
+ type: String,
+ value: "",
+ },
+ /**
+ * Suffix for the min input
+ */
+ minLabel: {
+ type: String,
+ value: ":",
+ },
+ /**
+ * Suffix for the sec input
+ */
+ secLabel: {
+ type: String,
+ value: "",
+ },
+ /**
+ * show the sec field
+ */
+ enableSecond: {
+ type: Boolean,
+ value: false,
+ },
+ /**
+ * limit hours input
+ */
+ noHoursLimit: {
+ type: Boolean,
+ value: false,
+ },
/**
* AM or PM
*/
@@ -223,7 +311,7 @@ export class PaperTimeInput extends PolymerElement {
type: String,
notify: true,
readOnly: true,
- computed: "_computeTime(min, hour, amPm)",
+ computed: "_computeTime(min, hour, sec, amPm)",
},
};
}
@@ -238,6 +326,10 @@ export class PaperTimeInput extends PolymerElement {
if (!this.$.hour.validate() | !this.$.min.validate()) {
valid = false;
}
+ // Validate second field
+ if (this.enableSecond && !this.$.sec.validate()) {
+ valid = false;
+ }
// Validate AM PM if 12 hour time
if (this.format === 12 && !this.$.dropdown.validate()) {
valid = false;
@@ -248,15 +340,37 @@ export class PaperTimeInput extends PolymerElement {
/**
* Create time string
*/
- _computeTime(min, hour, amPm) {
- if (hour && min) {
- // No ampm on 24 hr time
- if (this.format === 24) {
- amPm = "";
+ _computeTime(min, hour, sec, amPm) {
+ let str;
+ if (hour || min || (sec && this.enableSecond)) {
+ hour = hour || "00";
+ min = min || "00";
+ sec = sec || "00";
+ str = hour + ":" + min;
+ // add sec field
+ if (this.enableSecond && sec) {
+ str = str + ":" + sec;
+ }
+ // No ampm on 24 hr time
+ if (this.format === 12) {
+ str = str + " " + amPm;
}
- return hour + ":" + min + " " + amPm;
}
- return undefined;
+
+ return str;
+ }
+
+ _onFocus(ev) {
+ ev.target.inputElement.inputElement.select();
+ }
+
+ /**
+ * Format sec
+ */
+ _formatSec() {
+ if (this.sec.toString().length === 1) {
+ this.sec = this.sec.toString().padStart(2, "0");
+ }
}
/**
@@ -264,16 +378,16 @@ export class PaperTimeInput extends PolymerElement {
*/
_formatMin() {
if (this.min.toString().length === 1) {
- this.min = this.min < 10 ? "0" + this.min : this.min;
+ this.min = this.min.toString().padStart(2, "0");
}
}
/**
- * Hour needs a leading zero in 24hr format
+ * Format hour
*/
_shouldFormatHour() {
if (this.format === 24 && this.hour.toString().length === 1) {
- this.hour = this.hour < 10 ? "0" + this.hour : this.hour;
+ this.hour = this.hour.toString().padStart(2, "0");
}
}
@@ -281,6 +395,9 @@ export class PaperTimeInput extends PolymerElement {
* 24 hour format has a max hr of 23
*/
_computeHourMax(format) {
+ if (this.noHoursLimit) {
+ return null;
+ }
if (format === 12) {
return format;
}