From 39a7b563e1849ccc1260fd9b33e88a3954ac209d Mon Sep 17 00:00:00 2001 From: Thomas Loven Date: Sat, 11 Nov 2023 22:17:15 +0000 Subject: [PATCH] Add timer support. Implement #192 --- README.md | 1 + src/controllers/controller.ts | 2 +- src/controllers/get-controller.ts | 2 + src/controllers/timer-controller.ts | 79 +++++++++++++++++++++++++++++ src/main.ts | 2 +- test/views/1_types.yaml | 3 ++ 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/controllers/timer-controller.ts diff --git a/README.md b/README.md index ca75d83..ca34980 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ entities: - `input_number` - set value (only if `mode: slider`) - `input_select` - select option - `number` - set value +- `timer` - set number of seconds remaining If you want to control more than one entity with the same slider, use [light group](https://www.home-assistant.io/integrations/light.group/), [cover group](https://www.home-assistant.io/integrations/cover.group/) or a custom made [template entity](https://www.home-assistant.io/integrations/#search/template). diff --git a/src/controllers/controller.ts b/src/controllers/controller.ts index 69dd5a2..7abb904 100644 --- a/src/controllers/controller.ts +++ b/src/controllers/controller.ts @@ -26,7 +26,7 @@ export abstract class Controller { abstract _max?: number; abstract _step?: number; - constructor(config: ControllerConfig) { + constructor(config: ControllerConfig, parent) { this._config = config; } diff --git a/src/controllers/get-controller.ts b/src/controllers/get-controller.ts index ed03e18..058084e 100644 --- a/src/controllers/get-controller.ts +++ b/src/controllers/get-controller.ts @@ -8,6 +8,7 @@ import { InputSelectController } from "./input-select-controller"; import { NumberController } from "./number-controller"; import { WaterHeaterController } from "./water-heater-controller"; import { HumidifierController } from "./humidifier-controller"; +import { TimerController } from "./timer-controller"; export function getController(domain: string) { return { @@ -21,5 +22,6 @@ export function getController(domain: string) { input_select: InputSelectController, number: NumberController, humidifier: HumidifierController, + timer: TimerController, }[domain]; } diff --git a/src/controllers/timer-controller.ts b/src/controllers/timer-controller.ts new file mode 100644 index 0000000..e99e4e5 --- /dev/null +++ b/src/controllers/timer-controller.ts @@ -0,0 +1,79 @@ +import { Controller } from "./controller"; + +export class TimerController extends Controller { + _calcvalue = 0; + _interval; + + constructor(config, parent) { + super(config, parent); + this._calcvalue = 0; + this._interval = window.setInterval(() => { + this._calcvalue = this.calculate_value(); + parent.requestUpdate(); + }, 1000); + } + + get _value() { + return this._calcvalue; + } + + calculate_value() { + let timeRemaining = this.stateObj.attributes.remaining; + if (!timeRemaining) return 0; + + const parts = timeRemaining.split(":").map(Number); + timeRemaining = parts[0] * 3600 + parts[1] * 60 + parts[2]; + + if (this.stateObj.state === "active") { + const now = new Date().getTime(); + const madeActive = new Date(this.stateObj.last_changed).getTime(); + timeRemaining = Math.max(timeRemaining - (now - madeActive) / 1000, 0); + } + + return timeRemaining; + } + + set _value(value) { + if (!value) { + this._hass.callService("timer", "finish", { + entity_id: this.stateObj.entity_id, + }); + } else { + this._hass.callService("timer", "start", { + entity_id: this.stateObj.entity_id, + duration: value, + }); + } + } + + get string() { + if (this.stateObj.state === "active") { + const leftpad = (num) => (num < 10 ? `0${num}` : `${num}`); + const h = Math.floor(this.value / 3600); + const m = Math.floor((this.value % 3600) / 60); + const s = Math.floor(this.value % 60); + if (h > 0) { + return `${h}:${leftpad(m)}:${leftpad(s)}`; + } + if (m > 0) { + return `${m}:${leftpad(s)}`; + } + return `${s}`; + } + return this._hass.localize("component.timer.entity_component._.state.idle"); + } + + get isOff() { + return this.stateObj.state !== "active"; + } + + get _min() { + return 0; + } + get _max() { + return 300; + } + get _step() { + return 1; + } +} diff --git a/src/main.ts b/src/main.ts index dd72d1e..b7a3d67 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,7 +18,7 @@ class SliderEntityRow extends LitElement { const domain = config.entity.split(".")[0]; const ctrlClass = getController(domain); if (!ctrlClass) throw new Error(`Unsupported entity type: ${domain}`); - this.ctrl = new ctrlClass(config); + this.ctrl = new ctrlClass(config, this); } async resized() { diff --git a/test/views/1_types.yaml b/test/views/1_types.yaml index 62dba77..787e489 100644 --- a/test/views/1_types.yaml +++ b/test/views/1_types.yaml @@ -64,3 +64,6 @@ cards: - type: custom:slider-entity-row entity: humidifier.hygrostat name: humidifier + - type: custom:slider-entity-row + entity: timer.timer + name: timer