Files
advanced-camera-card/src/condition-trigger/common/time-period.ts
T

64 lines
2.5 KiB
TypeScript

import type { TemplateRenderer } from '../../card-controller/templates';
import type { TimePeriod } from '../../config/schema/common/time-period';
import { isRecord } from '../../utils/basic';
import type { ConditionState } from '../conditions/types';
// Parses a Home Assistant time-period value (a condition/trigger `for:`) to
// seconds, matching HA's `cv.time_period`:
// - a number, or a bare numeric string, is a count of seconds;
// - a colon string is `HH:MM` or `HH:MM:SS` -- HA reads TWO parts as
// hours:minutes (not minutes:seconds);
// - a `{days, hours, minutes, seconds, milliseconds}` dict (each field a
// number or a numeric string, e.g. once a template field has been rendered).
// Accepts `unknown` so a freshly-rendered value can be parsed directly; returns
// null when unparseable or negative (HA `for:` requires a positive period).
const parseTimePeriodToSeconds = (value: unknown): number | null => {
const num = (field: unknown): number => Number(field ?? 0);
let seconds: number;
if (typeof value === 'number') {
seconds = value;
} else if (typeof value === 'string') {
if (value.includes(':')) {
const parts = value.split(':');
if (parts.length < 2 || parts.length > 3 || parts.some((part) => !part.trim())) {
return null;
}
const [hours, minutes, secs = 0] = parts.map(Number);
seconds = hours * 3600 + minutes * 60 + secs;
} else if (!value.trim()) {
return null;
} else {
seconds = Number(value);
}
} else if (isRecord(value)) {
seconds =
num(value.days) * 86400 +
num(value.hours) * 3600 +
num(value.minutes) * 60 +
num(value.seconds) +
num(value.milliseconds) / 1000;
} else {
return null;
}
return Number.isFinite(seconds) && seconds >= 0 ? seconds : null;
};
// Renders any templates within a `for:` time period (HA's
// `cv.positive_time_period_template`) against the current state, then parses it
// to seconds. The whole value or any dict field may be a template; a
// template-free value renders to itself. Without `hass` the value cannot be
// rendered, so it is parsed as-is (a literal duration still works; a template
// yields null).
export const renderTimePeriodToSeconds = (
templateRenderer: TemplateRenderer,
value: TimePeriod,
conditionState?: ConditionState,
): number | null => {
if (!conditionState?.hass) {
return parseTimePeriodToSeconds(value);
}
return parseTimePeriodToSeconds(
templateRenderer.renderRecursively(conditionState.hass, value, { conditionState }),
);
};