Add frontend support for datetime platform (#14391)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Raman Gupta 2023-05-29 17:32:57 -04:00 committed by GitHub
parent 38cf774a0d
commit 3df7c50690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 166 additions and 51 deletions

View File

@ -211,6 +211,7 @@ export const DOMAINS_INPUT_ROW = [
"button",
"cover",
"date",
"datetime",
"fan",
"group",
"humidifier",

View File

@ -117,59 +117,39 @@ export const computeStateDisplayFromEntityAttributes = (
const domain = computeDomain(entityId);
if (domain === "datetime") {
const time = new Date(state);
return formatDateTime(time, locale);
}
if (["date", "input_datetime", "time"].includes(domain)) {
if (state !== undefined) {
// If trying to display an explicit state, need to parse the explicit state to `Date` then format.
// Attributes aren't available, we have to use `state`.
try {
const components = state.split(" ");
if (components.length === 2) {
// Date and time.
return formatDateTime(new Date(components.join("T")), locale);
// If trying to display an explicit state, need to parse the explicit state to `Date` then format.
// Attributes aren't available, we have to use `state`.
try {
const components = state.split(" ");
if (components.length === 2) {
// Date and time.
return formatDateTime(new Date(components.join("T")), locale);
}
if (components.length === 1) {
if (state.includes("-")) {
// Date only.
return formatDate(new Date(`${state}T00:00`), locale);
}
if (components.length === 1) {
if (state.includes("-")) {
// Date only.
return formatDate(new Date(`${state}T00:00`), locale);
}
if (state.includes(":")) {
// Time only.
const now = new Date();
return formatTime(
new Date(`${now.toISOString().split("T")[0]}T${state}`),
locale
);
}
if (state.includes(":")) {
// Time only.
const now = new Date();
return formatTime(
new Date(`${now.toISOString().split("T")[0]}T${state}`),
locale
);
}
return state;
} catch (_e) {
// Formatting methods may throw error if date parsing doesn't go well,
// just return the state string in that case.
return state;
}
} else {
// If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format.
let date: Date;
if (attributes.has_date && attributes.has_time) {
date = new Date(
attributes.year,
attributes.month - 1,
attributes.day,
attributes.hour,
attributes.minute
);
return formatDateTime(date, locale);
}
if (attributes.has_date) {
date = new Date(attributes.year, attributes.month - 1, attributes.day);
return formatDate(date, locale);
}
if (attributes.has_time) {
date = new Date();
date.setHours(attributes.hour, attributes.minute);
return formatTime(date, locale);
}
return state;
} catch (_e) {
// Formatting methods may throw error if date parsing doesn't go well,
// just return the state string in that case.
return state;
}
}

View File

@ -10,5 +10,5 @@ export const setDateValue = (
date: string | undefined = undefined
) => {
const param = { entity_id: entityId, date };
hass.callService(entityId.split(".", 1)[0], "set_value", param);
hass.callService("date", "set_value", param);
};

12
src/data/datetime.ts Normal file
View File

@ -0,0 +1,12 @@
import { HomeAssistant } from "../types";
export const setDateTimeValue = (
hass: HomeAssistant,
entityId: string,
datetime: Date
) => {
hass.callService("datetime", "set_value", {
entity_id: entityId,
datetime: datetime.toISOString(),
});
};

View File

@ -38,7 +38,7 @@ export const setInputDateTimeValue = (
date: string | undefined = undefined
) => {
const param = { entity_id: entityId, time, date };
hass.callService(entityId.split(".", 1)[0], "set_datetime", param);
hass.callService("input_datetime", "set_datetime", param);
};
export const fetchInputDateTime = (hass: HomeAssistant) =>

View File

@ -6,5 +6,5 @@ export const setTimeValue = (
time: string | undefined = undefined
) => {
const param = { entity_id: entityId, time: time };
hass.callService(entityId.split(".", 1)[0], "set_value", param);
hass.callService("time", "set_value", param);
};

View File

@ -28,6 +28,7 @@ const LAZY_LOAD_TYPES = {
"climate-entity": () => import("../entity-rows/hui-climate-entity-row"),
"cover-entity": () => import("../entity-rows/hui-cover-entity-row"),
"date-entity": () => import("../entity-rows/hui-date-entity-row"),
"datetime-entity": () => import("../entity-rows/hui-datetime-entity-row"),
"group-entity": () => import("../entity-rows/hui-group-entity-row"),
"input-button-entity": () =>
import("../entity-rows/hui-input-button-entity-row"),
@ -63,6 +64,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
climate: "climate",
cover: "cover",
date: "date",
datetime: "datetime",
fan: "toggle",
group: "group",
humidifier: "humidifier",

View File

@ -0,0 +1,120 @@
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-date-input";
import { format } from "date-fns";
import { isUnavailableState } from "../../../data/entity";
import { setDateTimeValue } from "../../../data/datetime";
import type { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { EntityConfig, LovelaceRow } from "./types";
import "../../../components/ha-time-input";
@customElement("hui-datetime-entity-row")
class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: EntityConfig;
public setConfig(config: EntityConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
return hasConfigOrEntityChanged(this, changedProps);
}
protected render(): TemplateResult | typeof nothing {
if (!this._config || !this.hass) {
return nothing;
}
const stateObj = this.hass.states[this._config.entity];
if (!stateObj) {
return html`
<hui-warning>
${createEntityNotFoundWarning(this.hass, this._config.entity)}
</hui-warning>
`;
}
const dateObj = new Date(stateObj.state);
const time = format(dateObj, "HH:mm:ss");
const date = format(dateObj, "yyyy-MM-dd");
return html`
<hui-generic-entity-row
.hass=${this.hass}
.config=${this._config}
hideName="true"
>
<ha-date-input
.locale=${this.hass.locale}
.value=${date}
.disabled=${isUnavailableState(stateObj.state)}
@value-changed=${this._dateChanged}
>
</ha-date-input>
<ha-time-input
.value=${time}
.disabled=${isUnavailableState(stateObj.state)}
.locale=${this.hass.locale}
@value-changed=${this._timeChanged}
@click=${this._stopEventPropagation}
></ha-time-input>
</hui-generic-entity-row>
`;
}
private _stopEventPropagation(ev: Event): void {
ev.stopPropagation();
}
private _timeChanged(ev: CustomEvent<{ value: string }>): void {
const stateObj = this.hass!.states[this._config!.entity];
const dateObj = new Date(stateObj.state);
const newTime = ev.detail.value.split(":").map(Number);
dateObj.setHours(newTime[0], newTime[1], newTime[2]);
setDateTimeValue(this.hass!, stateObj.entity_id, dateObj);
}
private _dateChanged(ev: CustomEvent<{ value: string }>): void {
const stateObj = this.hass!.states[this._config!.entity];
const dateObj = new Date(stateObj.state);
const newDate = ev.detail.value.split("-").map(Number);
dateObj.setFullYear(newDate[0], newDate[1] - 1, newDate[2]);
setDateTimeValue(this.hass!, stateObj.entity_id, dateObj);
}
static get styles(): CSSResultGroup {
return css`
ha-date-input + ha-time-input {
margin-left: 4px;
margin-inline-start: 4px;
margin-inline-end: initial;
direction: var(--direction);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-datetime-entity-row": HuiInputDatetimeEntityRow;
}
}