Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display live remaining time for timer on tile card #21290

Merged
merged 1 commit into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/data/timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)})`;
Expand Down
17 changes: 13 additions & 4 deletions src/panels/lovelace/cards/hui-tile-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!)}
`;
}
piitaya marked this conversation as resolved.
Show resolved Hide resolved

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");
Expand Down
87 changes: 6 additions & 81 deletions src/panels/lovelace/entity-rows/hui-timer-entity-row.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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() {
Expand All @@ -70,61 +38,18 @@ class HuiTimerEntityRow extends LitElement {
return html`
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<div class="text-content">
${computeDisplayTimer(this.hass, stateObj, this._timeRemaining)}
<state-display-timer
.hass=${this.hass}
.stateObj=${stateObj}
></state-display-timer>
</div>
</hui-generic-entity-row>
`;
}

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 {
Expand Down
74 changes: 74 additions & 0 deletions src/state-display/state-display-timer.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Comment on lines +17 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no shadowroot?


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;
}
}
67 changes: 7 additions & 60 deletions src/state-summary/state-card-timer.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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`
<div class="horizontal justified layout">
Expand All @@ -34,63 +23,21 @@ class StateCardTimer extends LitElement {
.inDialog=${this.inDialog}
></state-info>
<div class="state">
${this._displayState(this.timeRemaining, this.stateObj)}
<state-display-timer
.hass=${this.hass}
.stateObj=${this.stateObj}
></state-display-timer>
</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 {
return [
haStyle,
css`
.state {
color: var(--primary-text-color);

margin-left: 16px;
margin-inline-start: 16px;
margin-inline-end: initial;
Expand Down
Loading