diff --git a/src/data/timer.ts b/src/data/timer.ts
index 7dc4f8e41dba..4b0ab1ce2768 100644
--- a/src/data/timer.ts
+++ b/src/data/timer.ts
@@ -92,7 +92,7 @@ export const computeDisplayTimer = (
return hass.formatEntityState(stateObj);
}
- let display = secondsToDuration(timeRemaining || 0);
+ let display = secondsToDuration(timeRemaining || 0) || "0";
if (stateObj.state === "paused") {
display = `${display} (${hass.formatEntityState(stateObj)})`;
diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts
index ae8c8a99c0b8..3208928d0273 100644
--- a/src/panels/lovelace/cards/hui-tile-card.ts
+++ b/src/panels/lovelace/cards/hui-tile-card.ts
@@ -311,10 +311,19 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
}
if (domain === "update") {
- return html`${computeUpdateStateDisplay(
- stateObj as UpdateEntity,
- this.hass!
- )}`;
+ return html`
+ ${computeUpdateStateDisplay(stateObj as UpdateEntity, this.hass!)}
+ `;
+ }
+
+ if (domain === "timer") {
+ import("../../../state-display/state-display-timer");
+ return html`
+
+ `;
}
return this._renderStateContent(stateObj, "state");
diff --git a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts
index bd58e41534b2..8cb3cac9a185 100644
--- a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts
@@ -1,7 +1,6 @@
-import { HassEntity } from "home-assistant-js-websocket";
-import { html, LitElement, PropertyValues, nothing } from "lit";
+import { LitElement, PropertyValues, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
-import { computeDisplayTimer, timerTimeRemaining } from "../../../data/timer";
+import "../../../state-display/state-display-timer";
import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
@@ -14,42 +13,11 @@ class HuiTimerEntityRow extends LitElement {
@state() private _config?: EntityConfig;
- @state() private _timeRemaining?: number;
-
- private _interval?: number;
-
public setConfig(config: EntityConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
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() {
@@ -70,61 +38,18 @@ class HuiTimerEntityRow extends LitElement {
return html`
- ${computeDisplayTimer(this.hass, stateObj, this._timeRemaining)}
+
`;
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
- if (changedProps.has("_timeRemaining")) {
- return true;
- }
-
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 {
diff --git a/src/state-display/state-display-timer.ts b/src/state-display/state-display-timer.ts
new file mode 100644
index 000000000000..13960c088cc4
--- /dev/null
+++ b/src/state-display/state-display-timer.ts
@@ -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;
+ }
+}
diff --git a/src/state-summary/state-card-timer.ts b/src/state-summary/state-card-timer.ts
index 57bb83e8e7be..134ebdb89c61 100644
--- a/src/state-summary/state-card-timer.ts
+++ b/src/state-summary/state-card-timer.ts
@@ -1,17 +1,10 @@
import type { HassEntity } from "home-assistant-js-websocket";
-import {
- css,
- CSSResultGroup,
- html,
- LitElement,
- PropertyValues,
- TemplateResult,
-} from "lit";
+import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../components/entity/state-info";
-import { computeDisplayTimer, timerTimeRemaining } from "../data/timer";
-import { HomeAssistant } from "../types";
import { haStyle } from "../resources/styles";
+import "../state-display/state-display-timer";
+import { HomeAssistant } from "../types";
@customElement("state-card-timer")
class StateCardTimer extends LitElement {
@@ -21,10 +14,6 @@ class StateCardTimer extends LitElement {
@property({ type: Boolean }) public inDialog = false;
- @property({ type: Number }) public timeRemaining?: number;
-
- private _updateRemaining: any;
-
protected render(): TemplateResult {
return html`
@@ -34,63 +23,21 @@ class StateCardTimer extends LitElement {
.inDialog=${this.inDialog}
>
- ${this._displayState(this.timeRemaining, this.stateObj)}
+
`;
}
- 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 {
return [
haStyle,
css`
.state {
color: var(--primary-text-color);
-
margin-left: 16px;
margin-inline-start: 16px;
margin-inline-end: initial;