From 5014736e72c4cf919eb81bf8c853f61e3052cb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Wed, 24 Apr 2019 21:13:09 +0200 Subject: [PATCH 1/3] Basic input-datetime entity row --- src/components/ha-date-input.ts | 124 ++++++++++++++++++ src/data/input_datetime.ts | 15 +++ .../lovelace/common/create-row-element.ts | 2 + .../hui-input-datetime-entity-row.ts | 119 +++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 src/components/ha-date-input.ts create mode 100644 src/data/input_datetime.ts create mode 100644 src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts diff --git a/src/components/ha-date-input.ts b/src/components/ha-date-input.ts new file mode 100644 index 000000000000..0363304c2762 --- /dev/null +++ b/src/components/ha-date-input.ts @@ -0,0 +1,124 @@ +import { + html, + css, + LitElement, + TemplateResult, + property, + customElement, +} from "lit-element"; + +import { PaperInputElement } from "@polymer/paper-input/paper-input"; + +@customElement("ha-date-input") +class HaDateInput extends LitElement { + @property() public year?: string; + @property() public month?: string; + @property() public day?: string; + @property() public disabled?: boolean = false; + + static get styles() { + return css` + :host { + display: block; + font-family: var(--paper-font-common-base_-_font-family); + -webkit-font-smoothing: var( + --paper-font-common-base_-_-webkit-font-smoothing + ); + } + + paper-input { + width: 30px; + text-align: center; + --paper-input-container-input_-_-moz-appearance: textfield; + --paper-input-container-input-webkit-spinner_-_-webkit-appearance: none; + --paper-input-container-input-webkit-spinner_-_margin: 0; + --paper-input-container-input-webkit-spinner_-_display: none; + } + + paper-input#year { + width: 50px; + } + + .date-input-wrap { + display: flex; + flex-direction: row; + } + `; + } + + protected render(): TemplateResult { + return html` +
+ + - + + + - + + + +
+ `; + } + + private _formatYear() { + const yearElement = this.shadowRoot!.querySelector( + "#year" + ) as PaperInputElement; + this.year = yearElement.value!; + } + + private _formatMonth() { + const monthElement = this.shadowRoot!.querySelector( + "#month" + ) as PaperInputElement; + this.month = ("0" + monthElement.value!).slice(-2); + } + + private _formatDay() { + const dayElement = this.shadowRoot!.querySelector( + "#day" + ) as PaperInputElement; + this.day = ("0" + dayElement.value!).slice(-2); + } + + get value() { + return `${this.year}-${this.month}-${this.day}`; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-date-input": HaDateInput; + } +} diff --git a/src/data/input_datetime.ts b/src/data/input_datetime.ts new file mode 100644 index 000000000000..3cfb579b4a28 --- /dev/null +++ b/src/data/input_datetime.ts @@ -0,0 +1,15 @@ +import { HomeAssistant } from "../types"; + +export const setValue = ( + hass: HomeAssistant, + entity: string, + time: string | null = null, + date: string | null = null +) => { + const param = { + entity_id: entity, + ...(time !== null && { time }), + ...(date !== null && { date }), + }; + hass.callService(entity.split(".", 1)[0], "set_datetime", param); +}; diff --git a/src/panels/lovelace/common/create-row-element.ts b/src/panels/lovelace/common/create-row-element.ts index 510d52a53af5..7269247bb60a 100644 --- a/src/panels/lovelace/common/create-row-element.ts +++ b/src/panels/lovelace/common/create-row-element.ts @@ -10,6 +10,7 @@ import { import "../entity-rows/hui-climate-entity-row"; import "../entity-rows/hui-cover-entity-row"; import "../entity-rows/hui-group-entity-row"; +import "../entity-rows/hui-input-datetime-entity-row"; import "../entity-rows/hui-input-number-entity-row"; import "../entity-rows/hui-input-select-entity-row"; import "../entity-rows/hui-input-text-entity-row"; @@ -58,6 +59,7 @@ const DOMAIN_TO_ELEMENT_TYPE = { // Temporary. Once climate is rewritten, // water heater should get it's own row. water_heater: "climate", + input_datetime: "input-datetime", }; const TIMEOUT = 2000; 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 new file mode 100644 index 000000000000..f95f20ceefa2 --- /dev/null +++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts @@ -0,0 +1,119 @@ +import { + html, + LitElement, + TemplateResult, + property, + PropertyValues, + customElement, +} from "lit-element"; + +import "../components/hui-generic-entity-row"; +import "../../../components/paper-time-input.js"; +import "../../../components/ha-date-input"; + +import { HomeAssistant } from "../../../types"; +import { EntityRow, EntityConfig } from "./types"; +import { setValue } from "../../../data/input_datetime"; +import { hasConfigOrEntityChanged } from "../common/has-changed"; + +@customElement("hui-input-datetime-entity-row") +class HuiInputDatetimeEntityRow extends LitElement implements EntityRow { + @property() public hass?: HomeAssistant; + @property() private _config?: EntityConfig; + + public setConfig(config: EntityConfig): void { + if (!config) { + throw new Error("Configuration error"); + } + this._config = config; + } + + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + + protected render(): TemplateResult | void { + if (!this._config || !this.hass) { + return html``; + } + + const stateObj = this.hass.states[this._config.entity]; + + if (!stateObj) { + return html` + ${this.hass.localize( + "ui.panel.lovelace.warning.entity_not_found", + "entity", + this._config.entity + )} + `; + } + + return html` + + ${stateObj.attributes.has_date + ? html` + + ${stateObj.attributes.has_time ? "," : ""} + ` + : ``} + ${stateObj.attributes.has_time + ? html` + + ` + : ``} + + `; + } + + private get _timeInputEl(): any { + return this.shadowRoot!.querySelector("paper-time-input"); + } + + private get _dateInputEl() { + return this.shadowRoot!.querySelector("ha-date-input"); + } + + private _selectedValueChanged(ev): void { + const stateObj = this.hass!.states[this._config!.entity]; + + const time = + this._timeInputEl !== null + ? this._timeInputEl.value.trim() + ":00" + : null; + + const date = this._dateInputEl !== null ? this._dateInputEl.value : null; + + if (time !== stateObj.state) { + setValue(this.hass!, stateObj.entity_id, time, date); + } + + ev.target.blur(); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-input-datetime-entity-row": HuiInputDatetimeEntityRow; + } +} From b8b229b04bfee9648daf2c340a5470c12b835128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Thu, 25 Apr 2019 09:18:11 +0200 Subject: [PATCH 2/3] Address review comments --- src/components/ha-date-input.ts | 40 +++++++-------- src/components/paper-time-input.js | 2 +- src/data/input_datetime.ts | 16 +++--- .../hui-input-datetime-entity-row.ts | 51 +++++++++++-------- 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/components/ha-date-input.ts b/src/components/ha-date-input.ts index 0363304c2762..44b419fbc569 100644 --- a/src/components/ha-date-input.ts +++ b/src/components/ha-date-input.ts @@ -10,11 +10,11 @@ import { import { PaperInputElement } from "@polymer/paper-input/paper-input"; @customElement("ha-date-input") -class HaDateInput extends LitElement { +export class HaDateInput extends LitElement { @property() public year?: string; @property() public month?: string; @property() public day?: string; - @property() public disabled?: boolean = false; + @property({ type: Boolean }) public disabled = false; static get styles() { return css` @@ -52,39 +52,39 @@ class HaDateInput extends LitElement { - - @@ -92,22 +92,22 @@ class HaDateInput extends LitElement { } private _formatYear() { - const yearElement = this.shadowRoot!.querySelector( - "#year" + const yearElement = this.shadowRoot!.getElementById( + "year" ) as PaperInputElement; this.year = yearElement.value!; } private _formatMonth() { - const monthElement = this.shadowRoot!.querySelector( - "#month" + const monthElement = this.shadowRoot!.getElementById( + "month" ) as PaperInputElement; this.month = ("0" + monthElement.value!).slice(-2); } private _formatDay() { - const dayElement = this.shadowRoot!.querySelector( - "#day" + const dayElement = this.shadowRoot!.getElementById( + "day" ) as PaperInputElement; this.day = ("0" + dayElement.value!).slice(-2); } diff --git a/src/components/paper-time-input.js b/src/components/paper-time-input.js index 59c819858afb..3fec74281ab9 100644 --- a/src/components/paper-time-input.js +++ b/src/components/paper-time-input.js @@ -23,7 +23,7 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; -class PaperTimeInput extends PolymerElement { +export class PaperTimeInput extends PolymerElement { static get template() { return html`