Display live remaining time for timer on tile card (#21290)

Display timer by default on tile card
This commit is contained in:
Paul Bottein 2024-07-11 11:09:14 +02:00 committed by GitHub
parent d9583582e6
commit e59c04c685
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 101 additions and 146 deletions

View File

@ -92,7 +92,7 @@ export const computeDisplayTimer = (
return hass.formatEntityState(stateObj); return hass.formatEntityState(stateObj);
} }
let display = secondsToDuration(timeRemaining || 0); let display = secondsToDuration(timeRemaining || 0) || "0";
if (stateObj.state === "paused") { if (stateObj.state === "paused") {
display = `${display} (${hass.formatEntityState(stateObj)})`; display = `${display} (${hass.formatEntityState(stateObj)})`;

View File

@ -311,10 +311,19 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
} }
if (domain === "update") { if (domain === "update") {
return html`${computeUpdateStateDisplay( return html`
stateObj as UpdateEntity, ${computeUpdateStateDisplay(stateObj as UpdateEntity, this.hass!)}
this.hass! `;
)}`; }
if (domain === "timer") {
import("../../../state-display/state-display-timer");
return html`
<state-display-timer
.hass=${this.hass}
.stateObj=${stateObj}
></state-display-timer>
`;
} }
return this._renderStateContent(stateObj, "state"); return this._renderStateContent(stateObj, "state");

View File

@ -1,7 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket"; import { LitElement, PropertyValues, html, nothing } from "lit";
import { html, LitElement, PropertyValues, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { computeDisplayTimer, timerTimeRemaining } from "../../../data/timer"; import "../../../state-display/state-display-timer";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row"; import "../components/hui-generic-entity-row";
@ -14,42 +13,11 @@ class HuiTimerEntityRow extends LitElement {
@state() private _config?: EntityConfig; @state() private _config?: EntityConfig;
@state() private _timeRemaining?: number;
private _interval?: number;
public setConfig(config: EntityConfig): void { public setConfig(config: EntityConfig): void {
if (!config) { if (!config) {
throw new Error("Invalid configuration"); throw new Error("Invalid configuration");
} }
this._config = config; this._config = config;
if (!this.hass) {
return;
}
const stateObj = this.hass!.states[this._config.entity];
if (stateObj) {
this._startInterval(stateObj);
} else {
this._clearInterval();
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
this._clearInterval();
}
public connectedCallback(): void {
super.connectedCallback();
if (this._config && this._config.entity) {
const stateObj = this.hass?.states[this._config!.entity];
if (stateObj) {
this._startInterval(stateObj);
}
}
} }
protected render() { protected render() {
@ -70,61 +38,18 @@ class HuiTimerEntityRow extends LitElement {
return html` return html`
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}> <hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<div class="text-content"> <div class="text-content">
${computeDisplayTimer(this.hass, stateObj, this._timeRemaining)} <state-display-timer
.hass=${this.hass}
.stateObj=${stateObj}
></state-display-timer>
</div> </div>
</hui-generic-entity-row> </hui-generic-entity-row>
`; `;
} }
protected shouldUpdate(changedProps: PropertyValues): boolean { protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_timeRemaining")) {
return true;
}
return hasConfigOrEntityChanged(this, changedProps); return hasConfigOrEntityChanged(this, changedProps);
} }
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (!this._config || !changedProps.has("hass")) {
return;
}
const stateObj = this.hass!.states[this._config!.entity];
const oldHass = changedProps.get("hass") as this["hass"];
const oldStateObj = oldHass
? oldHass.states[this._config!.entity]
: undefined;
if (oldStateObj !== stateObj) {
this._startInterval(stateObj);
} else if (!stateObj) {
this._clearInterval();
}
}
private _clearInterval(): void {
if (this._interval) {
window.clearInterval(this._interval);
this._interval = undefined;
}
}
private _startInterval(stateObj: HassEntity): void {
this._clearInterval();
this._calculateRemaining(stateObj);
if (stateObj.state === "active") {
this._interval = window.setInterval(
() => this._calculateRemaining(stateObj),
1000
);
}
}
private _calculateRemaining(stateObj: HassEntity): void {
this._timeRemaining = timerTimeRemaining(stateObj);
}
} }
declare global { declare global {

View File

@ -0,0 +1,74 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDisplayTimer, timerTimeRemaining } from "../data/timer";
import type { HomeAssistant } from "../types";
@customElement("state-display-timer")
class StateDisplayTimer extends ReactiveElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj!: HassEntity;
@state() private timeRemaining?: number;
private _updateRemaining: any;
protected createRenderRoot() {
return this;
}
protected update(changedProps: PropertyValues) {
super.update(changedProps);
this.innerHTML =
computeDisplayTimer(this.hass, this.stateObj, this.timeRemaining) ?? "-";
}
connectedCallback() {
super.connectedCallback();
if (this.stateObj) {
this._startInterval(this.stateObj);
}
}
disconnectedCallback() {
super.disconnectedCallback();
this._clearInterval();
}
protected willUpdate(changedProp: PropertyValues): void {
super.willUpdate(changedProp);
if (changedProp.has("stateObj")) {
this._startInterval(this.stateObj);
}
}
private _clearInterval() {
if (this._updateRemaining) {
clearInterval(this._updateRemaining);
this._updateRemaining = null;
}
}
private _startInterval(stateObj: HassEntity) {
this._clearInterval();
this._calculateRemaining(stateObj);
if (stateObj.state === "active") {
this._updateRemaining = setInterval(
() => this._calculateRemaining(this.stateObj),
1000
);
}
}
private _calculateRemaining(stateObj: HassEntity) {
this.timeRemaining = timerTimeRemaining(stateObj);
}
}
declare global {
interface HTMLElementTagNameMap {
"state-display-timer": StateDisplayTimer;
}
}

View File

@ -1,17 +1,10 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import "../components/entity/state-info"; import "../components/entity/state-info";
import { computeDisplayTimer, timerTimeRemaining } from "../data/timer";
import { HomeAssistant } from "../types";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
import "../state-display/state-display-timer";
import { HomeAssistant } from "../types";
@customElement("state-card-timer") @customElement("state-card-timer")
class StateCardTimer extends LitElement { class StateCardTimer extends LitElement {
@ -21,10 +14,6 @@ class StateCardTimer extends LitElement {
@property({ type: Boolean }) public inDialog = false; @property({ type: Boolean }) public inDialog = false;
@property({ type: Number }) public timeRemaining?: number;
private _updateRemaining: any;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="horizontal justified layout"> <div class="horizontal justified layout">
@ -34,63 +23,21 @@ class StateCardTimer extends LitElement {
.inDialog=${this.inDialog} .inDialog=${this.inDialog}
></state-info> ></state-info>
<div class="state"> <div class="state">
${this._displayState(this.timeRemaining, this.stateObj)} <state-display-timer
.hass=${this.hass}
.stateObj=${this.stateObj}
></state-display-timer>
</div> </div>
</div> </div>
`; `;
} }
connectedCallback() {
super.connectedCallback();
this._startInterval(this.stateObj);
}
disconnectedCallback() {
super.disconnectedCallback();
this._clearInterval();
}
protected willUpdate(changedProp: PropertyValues): void {
super.willUpdate(changedProp);
if (changedProp.has("stateObj")) {
this._startInterval(this.stateObj);
}
}
private _clearInterval() {
if (this._updateRemaining) {
clearInterval(this._updateRemaining);
this._updateRemaining = null;
}
}
private _startInterval(stateObj) {
this._clearInterval();
this._calculateRemaining(stateObj);
if (stateObj.state === "active") {
this._updateRemaining = setInterval(
() => this._calculateRemaining(this.stateObj),
1000
);
}
}
private _calculateRemaining(stateObj) {
this.timeRemaining = timerTimeRemaining(stateObj);
}
private _displayState(timeRemaining, stateObj) {
return computeDisplayTimer(this.hass, stateObj, timeRemaining);
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
css` css`
.state { .state {
color: var(--primary-text-color); color: var(--primary-text-color);
margin-left: 16px; margin-left: 16px;
margin-inline-start: 16px; margin-inline-start: 16px;
margin-inline-end: initial; margin-inline-end: initial;