diff --git a/src/common/datetime/duration.ts b/src/common/datetime/duration.ts new file mode 100644 index 0000000000..2d6c46ff87 --- /dev/null +++ b/src/common/datetime/duration.ts @@ -0,0 +1,16 @@ +import secondsToDuration from "./seconds_to_duration"; + +const DAY_IN_SECONDS = 86400; +const HOUR_IN_SECONDS = 3600; +const MINUTE_IN_SECONDS = 60; + +export const UNIT_TO_SECOND_CONVERT = { + s: 1, + min: MINUTE_IN_SECONDS, + h: HOUR_IN_SECONDS, + d: DAY_IN_SECONDS, +}; + +export const formatDuration = (duration: string, units: string): string => + secondsToDuration(parseFloat(duration) * UNIT_TO_SECOND_CONVERT[units]) || + "0"; diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index ed890603a9..d796a80e64 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -13,6 +13,7 @@ import { formatNumber, isNumericState } from "../number/format_number"; import { LocalizeFunc } from "../translations/localize"; import { computeStateDomain } from "./compute_state_domain"; import { supportsFeature } from "./supports-feature"; +import { formatDuration, UNIT_TO_SECOND_CONVERT } from "../datetime/duration"; export const computeStateDisplay = ( localize: LocalizeFunc, @@ -28,6 +29,21 @@ export const computeStateDisplay = ( // Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber` if (isNumericState(stateObj)) { + // state is duration + if ( + stateObj.attributes.device_class === "duration" && + stateObj.attributes.unit_of_measurement && + UNIT_TO_SECOND_CONVERT[stateObj.attributes.unit_of_measurement] + ) { + try { + return formatDuration( + compareState, + stateObj.attributes.unit_of_measurement + ); + } catch (_err) { + // fallback to default + } + } if (stateObj.attributes.device_class === "monetary") { try { return formatNumber(compareState, locale, { diff --git a/test/common/datetime/duration.ts b/test/common/datetime/duration.ts new file mode 100644 index 0000000000..3e3a037451 --- /dev/null +++ b/test/common/datetime/duration.ts @@ -0,0 +1,34 @@ +import { assert } from "chai"; + +import { formatDuration } from "../../../src/common/datetime/duration"; + +describe("formatDuration", () => { + it("works", () => { + assert.strictEqual(formatDuration("0", "s"), "0"); + assert.strictEqual(formatDuration("65", "s"), "1:05"); + assert.strictEqual(formatDuration("3665", "s"), "1:01:05"); + assert.strictEqual(formatDuration("39665", "s"), "11:01:05"); + assert.strictEqual(formatDuration("932093", "s"), "258:54:53"); + + assert.strictEqual(formatDuration("0", "min"), "0"); + assert.strictEqual(formatDuration("65", "min"), "1:05:00"); + assert.strictEqual(formatDuration("3665", "min"), "61:05:00"); + assert.strictEqual(formatDuration("39665", "min"), "661:05:00"); + assert.strictEqual(formatDuration("932093", "min"), "15534:53:00"); + assert.strictEqual(formatDuration("12.4", "min"), "12:24"); + + assert.strictEqual(formatDuration("0", "h"), "0"); + assert.strictEqual(formatDuration("65", "h"), "65:00:00"); + assert.strictEqual(formatDuration("3665", "h"), "3665:00:00"); + assert.strictEqual(formatDuration("39665", "h"), "39665:00:00"); + assert.strictEqual(formatDuration("932093", "h"), "932093:00:00"); + assert.strictEqual(formatDuration("24.3", "h"), "24:18:00"); + assert.strictEqual(formatDuration("24.32423", "h"), "24:19:27"); + + assert.strictEqual(formatDuration("0", "d"), "0"); + assert.strictEqual(formatDuration("65", "d"), "1560:00:00"); + assert.strictEqual(formatDuration("3665", "d"), "87960:00:00"); + assert.strictEqual(formatDuration("39665", "d"), "951960:00:00"); + assert.strictEqual(formatDuration("932093", "d"), "22370232:00:00"); + }); +}); diff --git a/test/common/datetime/seconds_to_duration_test.ts b/test/common/datetime/seconds_to_duration_test.ts index 33f7a9a9a5..3b361a8635 100644 --- a/test/common/datetime/seconds_to_duration_test.ts +++ b/test/common/datetime/seconds_to_duration_test.ts @@ -8,5 +8,6 @@ describe("secondsToDuration", () => { assert.strictEqual(secondsToDuration(65), "1:05"); assert.strictEqual(secondsToDuration(3665), "1:01:05"); assert.strictEqual(secondsToDuration(39665), "11:01:05"); + assert.strictEqual(secondsToDuration(932093), "258:54:53"); }); });