mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Fix energy charts with leap years (#24059)
* Fix energy charts with leap years * handle quarters
This commit is contained in:
parent
ce0f02a45b
commit
31180e3a9e
@ -1,5 +1,16 @@
|
|||||||
import type { HassConfig } from "home-assistant-js-websocket";
|
import type { HassConfig } from "home-assistant-js-websocket";
|
||||||
import { addHours, subHours, differenceInDays } from "date-fns";
|
import {
|
||||||
|
differenceInMonths,
|
||||||
|
subHours,
|
||||||
|
differenceInDays,
|
||||||
|
differenceInYears,
|
||||||
|
startOfYear,
|
||||||
|
addMilliseconds,
|
||||||
|
startOfMonth,
|
||||||
|
addYears,
|
||||||
|
addMonths,
|
||||||
|
addHours,
|
||||||
|
} from "date-fns";
|
||||||
import type {
|
import type {
|
||||||
BarSeriesOption,
|
BarSeriesOption,
|
||||||
CallbackDataParams,
|
CallbackDataParams,
|
||||||
@ -7,7 +18,10 @@ import type {
|
|||||||
} from "echarts/types/dist/shared";
|
} from "echarts/types/dist/shared";
|
||||||
import type { FrontendLocaleData } from "../../../../../data/translation";
|
import type { FrontendLocaleData } from "../../../../../data/translation";
|
||||||
import { formatNumber } from "../../../../../common/number/format_number";
|
import { formatNumber } from "../../../../../common/number/format_number";
|
||||||
import { formatDateVeryShort } from "../../../../../common/datetime/format_date";
|
import {
|
||||||
|
formatDateMonthYear,
|
||||||
|
formatDateVeryShort,
|
||||||
|
} from "../../../../../common/datetime/format_date";
|
||||||
import { formatTime } from "../../../../../common/datetime/format_time";
|
import { formatTime } from "../../../../../common/datetime/format_time";
|
||||||
import type { ECOption } from "../../../../../resources/echarts";
|
import type { ECOption } from "../../../../../resources/echarts";
|
||||||
|
|
||||||
@ -53,7 +67,7 @@ export function getCommonOptions(
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
type: "time",
|
type: "time",
|
||||||
min: start,
|
min: start,
|
||||||
max: end,
|
max: getSuggestedMax(dayDifference, end),
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
@ -88,7 +102,6 @@ export function getCommonOptions(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return [mainItems, compareItems]
|
return [mainItems, compareItems]
|
||||||
.filter((items) => items.length > 0)
|
|
||||||
.map((items) =>
|
.map((items) =>
|
||||||
formatTooltip(
|
formatTooltip(
|
||||||
items,
|
items,
|
||||||
@ -100,6 +113,7 @@ export function getCommonOptions(
|
|||||||
formatTotal
|
formatTotal
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.filter(Boolean)
|
||||||
.join("<br><br>");
|
.join("<br><br>");
|
||||||
}
|
}
|
||||||
return formatTooltip(
|
return formatTooltip(
|
||||||
@ -126,14 +140,16 @@ function formatTooltip(
|
|||||||
unit?: string,
|
unit?: string,
|
||||||
formatTotal?: (total: number) => string
|
formatTotal?: (total: number) => string
|
||||||
) {
|
) {
|
||||||
if (!params[0].value) {
|
if (!params[0]?.value) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
// when comparing the first value is offset to match the main period
|
// when comparing the first value is offset to match the main period
|
||||||
// and the real date is in the third value
|
// and the real date is in the third value
|
||||||
const date = new Date(params[0].value?.[2] ?? params[0].value?.[0]);
|
const date = new Date(params[0].value?.[2] ?? params[0].value?.[0]);
|
||||||
let period: string;
|
let period: string;
|
||||||
if (dayDifference > 0) {
|
if (dayDifference > 89) {
|
||||||
|
period = `${formatDateMonthYear(date, locale, config)}`;
|
||||||
|
} else if (dayDifference > 0) {
|
||||||
period = `${formatDateVeryShort(date, locale, config)}`;
|
period = `${formatDateVeryShort(date, locale, config)}`;
|
||||||
} else {
|
} else {
|
||||||
period = `${
|
period = `${
|
||||||
@ -242,3 +258,25 @@ export function fillDataGapsAndRoundCaps(datasets: BarSeriesOption[]) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCompareTransform(start: Date, compareStart?: Date) {
|
||||||
|
if (!compareStart) {
|
||||||
|
return (ts: Date) => ts;
|
||||||
|
}
|
||||||
|
const compareYearDiff = differenceInYears(start, compareStart);
|
||||||
|
if (
|
||||||
|
compareYearDiff !== 0 &&
|
||||||
|
start.getTime() === startOfYear(start).getTime()
|
||||||
|
) {
|
||||||
|
return (ts: Date) => addYears(ts, compareYearDiff);
|
||||||
|
}
|
||||||
|
const compareMonthDiff = differenceInMonths(start, compareStart);
|
||||||
|
if (
|
||||||
|
compareMonthDiff !== 0 &&
|
||||||
|
start.getTime() === startOfMonth(start).getTime()
|
||||||
|
) {
|
||||||
|
return (ts: Date) => addMonths(ts, compareMonthDiff);
|
||||||
|
}
|
||||||
|
const compareOffset = start.getTime() - compareStart.getTime();
|
||||||
|
return (ts: Date) => addMilliseconds(ts, compareOffset);
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ import { hasConfigChanged } from "../../common/has-changed";
|
|||||||
import {
|
import {
|
||||||
fillDataGapsAndRoundCaps,
|
fillDataGapsAndRoundCaps,
|
||||||
getCommonOptions,
|
getCommonOptions,
|
||||||
|
getCompareTransform,
|
||||||
} from "./common/energy-chart-options";
|
} from "./common/energy-chart-options";
|
||||||
import { storage } from "../../../../common/decorators/storage";
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
import type { ECOption } from "../../../../resources/echarts";
|
import type { ECOption } from "../../../../resources/echarts";
|
||||||
@ -319,18 +320,19 @@ export class HuiEnergyDevicesDetailGraphCard
|
|||||||
datapoint[1];
|
datapoint[1];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const compareOffset = compare
|
const compareTransform = getCompareTransform(
|
||||||
? this._start.getTime() - this._compareStart!.getTime()
|
this._start,
|
||||||
: 0;
|
this._compareStart!
|
||||||
|
);
|
||||||
|
|
||||||
const untrackedConsumption: BarSeriesOption["data"] = [];
|
const untrackedConsumption: BarSeriesOption["data"] = [];
|
||||||
Object.keys(consumptionData.total).forEach((time) => {
|
Object.keys(consumptionData.total).forEach((time) => {
|
||||||
const value =
|
const value =
|
||||||
consumptionData.total[time] - (totalDeviceConsumption[time] || 0);
|
consumptionData.total[time] - (totalDeviceConsumption[time] || 0);
|
||||||
const dataPoint = [Number(time), value];
|
const dataPoint: (Date | string | number)[] = [time, value];
|
||||||
if (compare) {
|
if (compare) {
|
||||||
dataPoint[2] = dataPoint[0];
|
dataPoint[2] = dataPoint[0];
|
||||||
dataPoint[0] += compareOffset;
|
dataPoint[0] = compareTransform(new Date(time));
|
||||||
}
|
}
|
||||||
untrackedConsumption.push(dataPoint);
|
untrackedConsumption.push(dataPoint);
|
||||||
});
|
});
|
||||||
|
@ -29,6 +29,7 @@ import { hasConfigChanged } from "../../common/has-changed";
|
|||||||
import {
|
import {
|
||||||
fillDataGapsAndRoundCaps,
|
fillDataGapsAndRoundCaps,
|
||||||
getCommonOptions,
|
getCommonOptions,
|
||||||
|
getCompareTransform,
|
||||||
} from "./common/energy-chart-options";
|
} from "./common/energy-chart-options";
|
||||||
import type { ECOption } from "../../../../resources/echarts";
|
import type { ECOption } from "../../../../resources/echarts";
|
||||||
|
|
||||||
@ -213,9 +214,10 @@ export class HuiEnergyGasGraphCard
|
|||||||
compare = false
|
compare = false
|
||||||
) {
|
) {
|
||||||
const data: BarSeriesOption[] = [];
|
const data: BarSeriesOption[] = [];
|
||||||
const compareOffset = compare
|
const compareTransform = getCompareTransform(
|
||||||
? this._start.getTime() - this._compareStart!.getTime()
|
this._start,
|
||||||
: 0;
|
this._compareStart!
|
||||||
|
);
|
||||||
|
|
||||||
gasSources.forEach((source, idx) => {
|
gasSources.forEach((source, idx) => {
|
||||||
let prevStart: number | null = null;
|
let prevStart: number | null = null;
|
||||||
@ -236,10 +238,13 @@ export class HuiEnergyGasGraphCard
|
|||||||
if (prevStart === point.start) {
|
if (prevStart === point.start) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const dataPoint = [point.start, point.change];
|
const dataPoint: (Date | string | number)[] = [
|
||||||
|
point.start,
|
||||||
|
point.change,
|
||||||
|
];
|
||||||
if (compare) {
|
if (compare) {
|
||||||
dataPoint[2] = dataPoint[0];
|
dataPoint[2] = dataPoint[0];
|
||||||
dataPoint[0] += compareOffset;
|
dataPoint[0] = compareTransform(new Date(point.start));
|
||||||
}
|
}
|
||||||
gasConsumptionData.push(dataPoint);
|
gasConsumptionData.push(dataPoint);
|
||||||
prevStart = point.start;
|
prevStart = point.start;
|
||||||
|
@ -30,6 +30,7 @@ import { hasConfigChanged } from "../../common/has-changed";
|
|||||||
import {
|
import {
|
||||||
fillDataGapsAndRoundCaps,
|
fillDataGapsAndRoundCaps,
|
||||||
getCommonOptions,
|
getCommonOptions,
|
||||||
|
getCompareTransform,
|
||||||
} from "./common/energy-chart-options";
|
} from "./common/energy-chart-options";
|
||||||
import type { ECOption } from "../../../../resources/echarts";
|
import type { ECOption } from "../../../../resources/echarts";
|
||||||
|
|
||||||
@ -231,9 +232,10 @@ export class HuiEnergySolarGraphCard
|
|||||||
compare = false
|
compare = false
|
||||||
) {
|
) {
|
||||||
const data: BarSeriesOption[] = [];
|
const data: BarSeriesOption[] = [];
|
||||||
const compareOffset = compare
|
const compareTransform = getCompareTransform(
|
||||||
? this._start.getTime() - this._compareStart!.getTime()
|
this._start,
|
||||||
: 0;
|
this._compareStart!
|
||||||
|
);
|
||||||
|
|
||||||
solarSources.forEach((source, idx) => {
|
solarSources.forEach((source, idx) => {
|
||||||
let prevStart: number | null = null;
|
let prevStart: number | null = null;
|
||||||
@ -255,10 +257,13 @@ export class HuiEnergySolarGraphCard
|
|||||||
if (prevStart === point.start) {
|
if (prevStart === point.start) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const dataPoint = [point.start, point.change];
|
const dataPoint: (Date | string | number)[] = [
|
||||||
|
point.start,
|
||||||
|
point.change,
|
||||||
|
];
|
||||||
if (compare) {
|
if (compare) {
|
||||||
dataPoint[2] = dataPoint[0];
|
dataPoint[2] = dataPoint[0];
|
||||||
dataPoint[0] += compareOffset;
|
dataPoint[0] = compareTransform(new Date(point.start));
|
||||||
}
|
}
|
||||||
solarProductionData.push(dataPoint);
|
solarProductionData.push(dataPoint);
|
||||||
prevStart = point.start;
|
prevStart = point.start;
|
||||||
|
@ -27,6 +27,7 @@ import { hasConfigChanged } from "../../common/has-changed";
|
|||||||
import {
|
import {
|
||||||
fillDataGapsAndRoundCaps,
|
fillDataGapsAndRoundCaps,
|
||||||
getCommonOptions,
|
getCommonOptions,
|
||||||
|
getCompareTransform,
|
||||||
} from "./common/energy-chart-options";
|
} from "./common/energy-chart-options";
|
||||||
import type { ECOption } from "../../../../resources/echarts";
|
import type { ECOption } from "../../../../resources/echarts";
|
||||||
|
|
||||||
@ -476,9 +477,10 @@ export class HuiEnergyUsageGraphCard
|
|||||||
(a, b) => Number(a) - Number(b)
|
(a, b) => Number(a) - Number(b)
|
||||||
);
|
);
|
||||||
|
|
||||||
const compareOffset = compare
|
const compareTransform = getCompareTransform(
|
||||||
? this._start.getTime() - this._compareStart!.getTime()
|
this._start,
|
||||||
: 0;
|
this._compareStart!
|
||||||
|
);
|
||||||
|
|
||||||
Object.entries(combinedData).forEach(([type, sources]) => {
|
Object.entries(combinedData).forEach(([type, sources]) => {
|
||||||
Object.entries(sources).forEach(([statId, source]) => {
|
Object.entries(sources).forEach(([statId, source]) => {
|
||||||
@ -494,7 +496,7 @@ export class HuiEnergyUsageGraphCard
|
|||||||
];
|
];
|
||||||
if (compare) {
|
if (compare) {
|
||||||
dataPoint[2] = dataPoint[0];
|
dataPoint[2] = dataPoint[0];
|
||||||
dataPoint[0] += compareOffset;
|
dataPoint[0] = compareTransform(dataPoint[0]);
|
||||||
}
|
}
|
||||||
points.push(dataPoint);
|
points.push(dataPoint);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import { hasConfigChanged } from "../../common/has-changed";
|
|||||||
import {
|
import {
|
||||||
fillDataGapsAndRoundCaps,
|
fillDataGapsAndRoundCaps,
|
||||||
getCommonOptions,
|
getCommonOptions,
|
||||||
|
getCompareTransform,
|
||||||
} from "./common/energy-chart-options";
|
} from "./common/energy-chart-options";
|
||||||
import type { ECOption } from "../../../../resources/echarts";
|
import type { ECOption } from "../../../../resources/echarts";
|
||||||
import { formatNumber } from "../../../../common/number/format_number";
|
import { formatNumber } from "../../../../common/number/format_number";
|
||||||
@ -211,9 +212,10 @@ export class HuiEnergyWaterGraphCard
|
|||||||
compare = false
|
compare = false
|
||||||
) {
|
) {
|
||||||
const data: BarSeriesOption[] = [];
|
const data: BarSeriesOption[] = [];
|
||||||
const compareOffset = compare
|
const compareTransform = getCompareTransform(
|
||||||
? this._start.getTime() - this._compareStart!.getTime()
|
this._start,
|
||||||
: 0;
|
this._compareStart!
|
||||||
|
);
|
||||||
|
|
||||||
waterSources.forEach((source, idx) => {
|
waterSources.forEach((source, idx) => {
|
||||||
let prevStart: number | null = null;
|
let prevStart: number | null = null;
|
||||||
@ -234,10 +236,13 @@ export class HuiEnergyWaterGraphCard
|
|||||||
if (prevStart === point.start) {
|
if (prevStart === point.start) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const dataPoint = [point.start, point.change];
|
const dataPoint: (Date | string | number)[] = [
|
||||||
|
point.start,
|
||||||
|
point.change,
|
||||||
|
];
|
||||||
if (compare) {
|
if (compare) {
|
||||||
dataPoint[2] = dataPoint[0];
|
dataPoint[2] = dataPoint[0];
|
||||||
dataPoint[0] += compareOffset;
|
dataPoint[0] = compareTransform(new Date(point.start));
|
||||||
}
|
}
|
||||||
waterConsumptionData.push(dataPoint);
|
waterConsumptionData.push(dataPoint);
|
||||||
prevStart = point.start;
|
prevStart = point.start;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user