Skip to content

Commit 122d1fa

Browse files
committed
better error reporting on auto periods
1 parent 254ae58 commit 122d1fa

File tree

4 files changed

+37
-37
lines changed

4 files changed

+37
-37
lines changed

src/duration/duration.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ export type TimeDurationStr = `${number}${TimeUnit}` | `0`;
1717
* @returns duration in milliseconds
1818
*/
1919
export const parseTimeDuration = (str: TimeDurationStr | undefined): number => {
20-
if (!str) return 0;
21-
if (str === "0") return 0;
22-
if (!str.match) return 0;
20+
if (!str || !str.match)
21+
throw new Error(`Cannot parse "${str}" as a duration`);
2322
const match = str.match(
2423
/^(?<sign>[+-])?(?<number>\d*(\.\d)?)(?<unit>(ms|s|m|h|d|w|M|y))$/
2524
);

src/plotly-graph-card.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@ import { TimestampRange } from "./types";
1616
import Cache from "./cache/Cache";
1717
import getThemedLayout from "./themed-layout";
1818
import isProduction from "./is-production";
19-
import { sleep } from "./utils";
19+
import { getIsPureObject, sleep } from "./utils";
2020
import { Datum } from "plotly.js";
2121
import colorSchemes, { isColorSchemeArray } from "./color-schemes";
2222
import { parseISO } from "date-fns";
2323
import {
2424
STATISTIC_PERIODS,
2525
STATISTIC_TYPES,
2626
StatisticPeriod,
27-
getIsAutoPeriodConfig,
2827
} from "./recorder-types";
2928
import { parseTimeDuration } from "./duration/duration";
3029

@@ -229,6 +228,7 @@ export class PlotlyGraph extends HTMLElement {
229228
return await this._setConfig(config);
230229
} catch (e: any) {
231230
console.error(e);
231+
clearTimeout(this.handles.refreshTimeout!);
232232
if (typeof e.message === "string") {
233233
this.msgEl.innerText = e.message;
234234
} else {
@@ -291,7 +291,7 @@ export class PlotlyGraph extends HTMLElement {
291291
);
292292
// @TODO: cleanup how this is done
293293
if (entity.period === "auto") {
294-
entity.autoPeriod = {
294+
entity.period = {
295295
"0": "5minute",
296296
"1d": "hour",
297297
"7d": "day",
@@ -300,11 +300,25 @@ export class PlotlyGraph extends HTMLElement {
300300
};
301301
entity.period = undefined;
302302
}
303-
const isAutoPeriodConfig = getIsAutoPeriodConfig(entity.period);
304-
305-
if (isAutoPeriodConfig) {
303+
if (getIsPureObject(entity.period)) {
306304
entity.autoPeriod = entity.period;
307305
entity.period = undefined;
306+
let lastDuration = -1;
307+
for (const durationStr in entity.autoPeriod) {
308+
const period = entity.autoPeriod[durationStr];
309+
const duration = parseTimeDuration(durationStr as any); // will throw if not a valud duration
310+
if (!STATISTIC_PERIODS.includes(period as any)) {
311+
throw new Error(
312+
`Error parsing automatic period config: "${period}" not expected. Must be ${STATISTIC_PERIODS}`
313+
);
314+
}
315+
if (duration <= lastDuration) {
316+
throw new Error(
317+
`Error parsing automatic period config: ranges must be sorted in ascending order, "${durationStr}" not expected`
318+
);
319+
}
320+
lastDuration = duration;
321+
}
308322
}
309323
const validPeriod = STATISTIC_PERIODS.includes(entity.period);
310324
if (entity.period === undefined) entity.period = "hour";
@@ -379,17 +393,19 @@ export class PlotlyGraph extends HTMLElement {
379393
if (isEntityIdStatisticsConfig(entity) && entity.autoPeriod) {
380394
entity.period = "5minute";
381395
const timeSpan = range[1] - range[0];
382-
const mapping = Object.entries(entity.autoPeriod)
383-
.map(
384-
([duration, period]) =>
385-
[parseTimeDuration(duration as any), period] as [
386-
number,
387-
StatisticPeriod
388-
]
389-
)
390-
.sort(([durationA], [durationB]) => durationA - durationB);
396+
const mapping = Object.entries(entity.autoPeriod).map(
397+
([duration, period]) =>
398+
[parseTimeDuration(duration as any), period] as [
399+
number,
400+
StatisticPeriod
401+
]
402+
);
391403

392404
for (const [fromMS, aPeriod] of mapping) {
405+
/*
406+
the durations are validated to be sorted in ascendinig order
407+
when the config is parsed
408+
*/
393409
if (timeSpan >= fromMS) entity.period = aPeriod;
394410
}
395411
this.parsed_config.layout = merge(this.parsed_config.layout, {

src/recorder-types.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// https://github.com/home-assistant/frontend/blob/dev/src/data/recorder.ts
2-
import { keys } from "lodash";
3-
import { parseTimeDuration, TimeDurationStr } from "./duration/duration";
2+
import { TimeDurationStr } from "./duration/duration";
43

54
export interface StatisticValue {
65
statistic_id: string;
@@ -29,20 +28,3 @@ export const STATISTIC_PERIODS = [
2928
] as const;
3029
export type StatisticPeriod = typeof STATISTIC_PERIODS[number];
3130
export type AutoPeriodConfig = Record<TimeDurationStr, StatisticPeriod>;
32-
33-
export function getIsAutoPeriodConfig(val: any): val is AutoPeriodConfig {
34-
const isObject =
35-
typeof val === "object" && val !== null && !Array.isArray(val);
36-
if (!isObject) return false;
37-
const entries = Object.entries(val);
38-
if (entries.length === 0) return false;
39-
return entries.every(([duration, period]) => {
40-
if (!STATISTIC_PERIODS.includes(period as any)) return false;
41-
try {
42-
parseTimeDuration(duration as any);
43-
} catch (e) {
44-
return false;
45-
}
46-
return true;
47-
});
48-
}

src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
export const sleep = (ms: number) =>
22
new Promise((resolve) => setTimeout(resolve, ms));
3+
export function getIsPureObject(val: any) {
4+
return typeof val === "object" && val !== null && !Array.isArray(val);
5+
}

0 commit comments

Comments
 (0)