mirror of
https://github.com/home-assistant/frontend
synced 2024-09-30 15:52:53 +02:00
Fix trailing energy gaps, refactor chart options (#19117)
* Fix trailing gap on energy graph cards * Use date methods instead of unix time calculation * Fix trailing energy gaps, refactor chart options --------- Co-authored-by: Till Fleisch <till@fleisch.dev>
This commit is contained in:
parent
bded31b311
commit
df54687de1
168
src/panels/lovelace/cards/energy/common/energy-chart-options.ts
Normal file
168
src/panels/lovelace/cards/energy/common/energy-chart-options.ts
Normal file
@ -0,0 +1,168 @@
|
||||
import { ChartOptions } from "chart.js";
|
||||
import { HassConfig } from "home-assistant-js-websocket";
|
||||
import {
|
||||
addHours,
|
||||
subHours,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
} from "date-fns/esm";
|
||||
import { FrontendLocaleData } from "../../../../../data/translation";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../../common/number/format_number";
|
||||
import { formatDateVeryShort } from "../../../../../common/datetime/format_date";
|
||||
import { formatTime } from "../../../../../common/datetime/format_time";
|
||||
|
||||
export function getSuggestedMax(dayDifference: number, end: Date): number {
|
||||
let suggestedMax = new Date(end);
|
||||
|
||||
// Sometimes around DST we get a time of 0:59 instead of 23:59 as expected.
|
||||
// Correct for this when showing days/months so we don't get an extra day.
|
||||
if (dayDifference > 2 && suggestedMax.getHours() === 0) {
|
||||
suggestedMax = subHours(suggestedMax, 1);
|
||||
}
|
||||
|
||||
suggestedMax.setMinutes(0, 0, 0);
|
||||
if (dayDifference > 35) {
|
||||
suggestedMax.setDate(1);
|
||||
}
|
||||
if (dayDifference > 2) {
|
||||
suggestedMax.setHours(0);
|
||||
}
|
||||
return suggestedMax.getTime();
|
||||
}
|
||||
|
||||
export function getCommonOptions(
|
||||
start: Date,
|
||||
end: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig,
|
||||
unit?: string,
|
||||
compareStart?: Date,
|
||||
compareEnd?: Date
|
||||
): ChartOptions {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
const compare = compareStart !== undefined && compareEnd !== undefined;
|
||||
if (compare && dayDifference <= 35) {
|
||||
const difference = differenceInHours(end, start);
|
||||
const differenceCompare = differenceInHours(compareEnd!, compareStart!);
|
||||
// If the compare period doesn't match the main period, adjust them to match
|
||||
if (differenceCompare > difference) {
|
||||
end = addHours(end, differenceCompare - difference);
|
||||
} else if (difference > differenceCompare) {
|
||||
compareEnd = addHours(compareEnd!, difference - differenceCompare);
|
||||
}
|
||||
}
|
||||
|
||||
const options: ChartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
mode: "x",
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
max: getSuggestedMax(dayDifference, end),
|
||||
adapters: {
|
||||
date: {
|
||||
locale,
|
||||
config,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: unit,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: (value) => formatNumber(Math.abs(value), locale),
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
callbacks: {
|
||||
title: (datasets) => {
|
||||
if (dayDifference > 0) {
|
||||
return datasets[0].label;
|
||||
}
|
||||
const date = new Date(datasets[0].parsed.x);
|
||||
return `${
|
||||
compare ? `${formatDateVeryShort(date, locale, config)}: ` : ""
|
||||
}${formatTime(date, locale, config)} – ${formatTime(
|
||||
addHours(date, 1),
|
||||
locale,
|
||||
config
|
||||
)}`;
|
||||
},
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} ${unit}`,
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 50,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
if (compare) {
|
||||
options.scales!.xAxisCompare = {
|
||||
...(options.scales!.x as Record<string, any>),
|
||||
suggestedMin: compareStart!.getTime(),
|
||||
max: getSuggestedMax(dayDifference, compareEnd!),
|
||||
display: false,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
}
|
@ -4,14 +4,7 @@ import {
|
||||
ChartOptions,
|
||||
ScatterDataPoint,
|
||||
} from "chart.js";
|
||||
import {
|
||||
addHours,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
endOfToday,
|
||||
isToday,
|
||||
startOfToday,
|
||||
} from "date-fns";
|
||||
import { endOfToday, isToday, startOfToday } from "date-fns";
|
||||
import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@ -31,12 +24,7 @@ import {
|
||||
rgb2lab,
|
||||
} from "../../../../common/color/convert-color";
|
||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
||||
import { formatDateVeryShort } from "../../../../common/datetime/format_date";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/number/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import {
|
||||
@ -56,6 +44,7 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergyGasGraphCardConfig } from "../types";
|
||||
import { hasConfigChanged } from "../../common/has-changed";
|
||||
import { getCommonOptions } from "./common/energy-chart-options";
|
||||
|
||||
@customElement("hui-energy-gas-graph-card")
|
||||
export class HuiEnergyGasGraphCard
|
||||
@ -159,105 +148,23 @@ export class HuiEnergyGasGraphCard
|
||||
compareStart?: Date,
|
||||
compareEnd?: Date
|
||||
): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
const compare = compareStart !== undefined && compareEnd !== undefined;
|
||||
if (compare) {
|
||||
const difference = differenceInHours(end, start);
|
||||
const differenceCompare = differenceInHours(compareEnd!, compareStart!);
|
||||
// If the compare period doesn't match the main period, adjust them to match
|
||||
if (differenceCompare > difference) {
|
||||
end = addHours(end, differenceCompare - difference);
|
||||
} else if (difference > differenceCompare) {
|
||||
compareEnd = addHours(compareEnd!, difference - differenceCompare);
|
||||
}
|
||||
}
|
||||
|
||||
const commonOptions = getCommonOptions(
|
||||
start,
|
||||
end,
|
||||
locale,
|
||||
config,
|
||||
unit,
|
||||
compareStart,
|
||||
compareEnd
|
||||
);
|
||||
const options: ChartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
mode: "x",
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale,
|
||||
config,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: unit,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
...commonOptions.plugins,
|
||||
tooltip: {
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
...commonOptions.plugins!.tooltip,
|
||||
callbacks: {
|
||||
title: (datasets) => {
|
||||
if (dayDifference > 0) {
|
||||
return datasets[0].label;
|
||||
}
|
||||
const date = new Date(datasets[0].parsed.x);
|
||||
return `${
|
||||
compare
|
||||
? `${formatDateVeryShort(date, locale, config)}: `
|
||||
: ""
|
||||
}${formatTime(date, locale, config)} – ${formatTime(
|
||||
addHours(date, 1),
|
||||
locale,
|
||||
config
|
||||
)}`;
|
||||
},
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} ${unit}`,
|
||||
...commonOptions.plugins!.tooltip!.callbacks,
|
||||
footer: (contexts) => {
|
||||
if (contexts.length < 2) {
|
||||
return [];
|
||||
@ -278,33 +185,8 @@ export class HuiEnergyGasGraphCard
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 50,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
if (compare) {
|
||||
options.scales!.xAxisCompare = {
|
||||
...(options.scales!.x as Record<string, any>),
|
||||
suggestedMin: compareStart!.getTime(),
|
||||
suggestedMax: compareEnd!.getTime(),
|
||||
display: false,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
}
|
||||
);
|
||||
|
@ -5,9 +5,7 @@ import {
|
||||
ScatterDataPoint,
|
||||
} from "chart.js";
|
||||
import {
|
||||
addHours,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
endOfToday,
|
||||
isToday,
|
||||
startOfToday,
|
||||
@ -31,12 +29,7 @@ import {
|
||||
rgb2lab,
|
||||
} from "../../../../common/color/convert-color";
|
||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
||||
import { formatDateVeryShort } from "../../../../common/datetime/format_date";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/number/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import {
|
||||
@ -57,6 +50,7 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergySolarGraphCardConfig } from "../types";
|
||||
import { hasConfigChanged } from "../../common/has-changed";
|
||||
import { getCommonOptions } from "./common/energy-chart-options";
|
||||
|
||||
@customElement("hui-energy-solar-graph-card")
|
||||
export class HuiEnergySolarGraphCard
|
||||
@ -156,104 +150,23 @@ export class HuiEnergySolarGraphCard
|
||||
compareStart?: Date,
|
||||
compareEnd?: Date
|
||||
): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
const compare = compareStart !== undefined && compareEnd !== undefined;
|
||||
if (compare) {
|
||||
const difference = differenceInHours(end, start);
|
||||
const differenceCompare = differenceInHours(compareEnd!, compareStart!);
|
||||
// If the compare period doesn't match the main period, adjust them to match
|
||||
if (differenceCompare > difference) {
|
||||
end = addHours(end, differenceCompare - difference);
|
||||
} else if (difference > differenceCompare) {
|
||||
compareEnd = addHours(compareEnd!, difference - differenceCompare);
|
||||
}
|
||||
}
|
||||
|
||||
const commonOptions = getCommonOptions(
|
||||
start,
|
||||
end,
|
||||
locale,
|
||||
config,
|
||||
"kWh",
|
||||
compareStart,
|
||||
compareEnd
|
||||
);
|
||||
const options: ChartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
mode: "x",
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale,
|
||||
config,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
...commonOptions.plugins,
|
||||
tooltip: {
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
...commonOptions.plugins!.tooltip,
|
||||
callbacks: {
|
||||
title: (datasets) => {
|
||||
if (dayDifference > 0) {
|
||||
return datasets[0].label;
|
||||
}
|
||||
const date = new Date(datasets[0].parsed.x);
|
||||
return `${
|
||||
compare
|
||||
? `${formatDateVeryShort(date, locale, config)}: `
|
||||
: ""
|
||||
}${formatTime(date, locale, config)} – ${formatTime(
|
||||
addHours(date, 1),
|
||||
locale,
|
||||
config
|
||||
)}`;
|
||||
},
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} kWh`,
|
||||
...commonOptions.plugins!.tooltip!.callbacks,
|
||||
footer: (contexts) => {
|
||||
const production_contexts = contexts.filter(
|
||||
(c) => c.dataset?.stack === "solar"
|
||||
@ -277,15 +190,6 @@ export class HuiEnergySolarGraphCard
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
@ -297,17 +201,7 @@ export class HuiEnergySolarGraphCard
|
||||
hitRadius: 5,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
if (compare) {
|
||||
options.scales!.xAxisCompare = {
|
||||
...(options.scales!.x as Record<string, any>),
|
||||
suggestedMin: compareStart!.getTime(),
|
||||
suggestedMax: compareEnd!.getTime(),
|
||||
display: false,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
}
|
||||
);
|
||||
|
@ -4,14 +4,7 @@ import {
|
||||
ChartOptions,
|
||||
ScatterDataPoint,
|
||||
} from "chart.js";
|
||||
import {
|
||||
addHours,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
endOfToday,
|
||||
isToday,
|
||||
startOfToday,
|
||||
} from "date-fns/esm";
|
||||
import { endOfToday, isToday, startOfToday } from "date-fns/esm";
|
||||
import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@ -31,12 +24,7 @@ import {
|
||||
rgb2lab,
|
||||
} from "../../../../common/color/convert-color";
|
||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
||||
import { formatDateVeryShort } from "../../../../common/datetime/format_date";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/number/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
||||
@ -51,6 +39,7 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergyUsageGraphCardConfig } from "../types";
|
||||
import { hasConfigChanged } from "../../common/has-changed";
|
||||
import { getCommonOptions } from "./common/energy-chart-options";
|
||||
|
||||
interface ColorSet {
|
||||
base: string;
|
||||
@ -155,81 +144,21 @@ export class HuiEnergyUsageGraphCard
|
||||
compareStart?: Date,
|
||||
compareEnd?: Date
|
||||
): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
const compare = compareStart !== undefined && compareEnd !== undefined;
|
||||
if (compare) {
|
||||
const difference = differenceInHours(end, start);
|
||||
const differenceCompare = differenceInHours(compareEnd!, compareStart!);
|
||||
// If the compare period doesn't match the main period, adjust them to match
|
||||
if (differenceCompare > difference) {
|
||||
end = addHours(end, differenceCompare - difference);
|
||||
} else if (difference > differenceCompare) {
|
||||
compareEnd = addHours(compareEnd!, difference - differenceCompare);
|
||||
}
|
||||
}
|
||||
|
||||
const commonOptions = getCommonOptions(
|
||||
start,
|
||||
end,
|
||||
locale,
|
||||
config,
|
||||
"kWh",
|
||||
compareStart,
|
||||
compareEnd
|
||||
);
|
||||
const options: ChartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
mode: "x",
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale,
|
||||
config,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: "kWh",
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: (value) => formatNumber(Math.abs(value), locale),
|
||||
},
|
||||
},
|
||||
},
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
...commonOptions.plugins,
|
||||
tooltip: {
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
...commonOptions.plugins!.tooltip,
|
||||
itemSort: function (a: any, b: any) {
|
||||
if (a.raw?.y > 0 && b.raw?.y < 0) {
|
||||
return -1;
|
||||
@ -243,26 +172,7 @@ export class HuiEnergyUsageGraphCard
|
||||
return a.datasetIndex - b.datasetIndex;
|
||||
},
|
||||
callbacks: {
|
||||
title: (datasets) => {
|
||||
if (dayDifference > 0) {
|
||||
return datasets[0].label;
|
||||
}
|
||||
const date = new Date(datasets[0].parsed.x);
|
||||
return `${
|
||||
compare
|
||||
? `${formatDateVeryShort(date, locale, config)}: `
|
||||
: ""
|
||||
}${formatTime(date, locale, config)} – ${formatTime(
|
||||
addHours(date, 1),
|
||||
locale,
|
||||
config
|
||||
)}`;
|
||||
},
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
Math.abs(context.parsed.y),
|
||||
locale
|
||||
)} kWh`,
|
||||
...commonOptions.plugins!.tooltip!.callbacks,
|
||||
footer: (contexts) => {
|
||||
let totalConsumed = 0;
|
||||
let totalReturned = 0;
|
||||
@ -292,33 +202,8 @@ export class HuiEnergyUsageGraphCard
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 50,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
if (compare) {
|
||||
options.scales!.xAxisCompare = {
|
||||
...(options.scales!.x as Record<string, any>),
|
||||
suggestedMin: compareStart!.getTime(),
|
||||
suggestedMax: compareEnd!.getTime(),
|
||||
display: false,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
}
|
||||
);
|
||||
|
@ -4,14 +4,7 @@ import {
|
||||
ChartOptions,
|
||||
ScatterDataPoint,
|
||||
} from "chart.js";
|
||||
import {
|
||||
addHours,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
endOfToday,
|
||||
isToday,
|
||||
startOfToday,
|
||||
} from "date-fns";
|
||||
import { endOfToday, isToday, startOfToday } from "date-fns";
|
||||
import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@ -31,12 +24,7 @@ import {
|
||||
rgb2lab,
|
||||
} from "../../../../common/color/convert-color";
|
||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
||||
import { formatDateVeryShort } from "../../../../common/datetime/format_date";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/number/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import {
|
||||
@ -56,6 +44,7 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCard } from "../../types";
|
||||
import { EnergyWaterGraphCardConfig } from "../types";
|
||||
import { hasConfigChanged } from "../../common/has-changed";
|
||||
import { getCommonOptions } from "./common/energy-chart-options";
|
||||
|
||||
@customElement("hui-energy-water-graph-card")
|
||||
export class HuiEnergyWaterGraphCard
|
||||
@ -159,105 +148,23 @@ export class HuiEnergyWaterGraphCard
|
||||
compareStart?: Date,
|
||||
compareEnd?: Date
|
||||
): ChartOptions => {
|
||||
const dayDifference = differenceInDays(end, start);
|
||||
const compare = compareStart !== undefined && compareEnd !== undefined;
|
||||
if (compare) {
|
||||
const difference = differenceInHours(end, start);
|
||||
const differenceCompare = differenceInHours(compareEnd!, compareStart!);
|
||||
// If the compare period doesn't match the main period, adjust them to match
|
||||
if (differenceCompare > difference) {
|
||||
end = addHours(end, differenceCompare - difference);
|
||||
} else if (difference > differenceCompare) {
|
||||
compareEnd = addHours(compareEnd!, difference - differenceCompare);
|
||||
}
|
||||
}
|
||||
|
||||
const commonOptions = getCommonOptions(
|
||||
start,
|
||||
end,
|
||||
locale,
|
||||
config,
|
||||
unit,
|
||||
compareStart,
|
||||
compareEnd
|
||||
);
|
||||
const options: ChartOptions = {
|
||||
parsing: false,
|
||||
animation: false,
|
||||
interaction: {
|
||||
mode: "x",
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
suggestedMin: start.getTime(),
|
||||
suggestedMax: end.getTime(),
|
||||
adapters: {
|
||||
date: {
|
||||
locale,
|
||||
config,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
font: (context) =>
|
||||
context.tick && context.tick.major
|
||||
? ({ weight: "bold" } as any)
|
||||
: {},
|
||||
},
|
||||
time: {
|
||||
tooltipFormat:
|
||||
dayDifference > 35
|
||||
? "monthyear"
|
||||
: dayDifference > 7
|
||||
? "date"
|
||||
: dayDifference > 2
|
||||
? "weekday"
|
||||
: dayDifference > 0
|
||||
? "datetime"
|
||||
: "hour",
|
||||
minUnit:
|
||||
dayDifference > 35
|
||||
? "month"
|
||||
: dayDifference > 2
|
||||
? "day"
|
||||
: "hour",
|
||||
},
|
||||
offset: true,
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
type: "linear",
|
||||
title: {
|
||||
display: true,
|
||||
text: unit,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
...commonOptions.plugins,
|
||||
tooltip: {
|
||||
position: "nearest",
|
||||
filter: (val) => val.formattedValue !== "0",
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
...commonOptions.plugins!.tooltip,
|
||||
callbacks: {
|
||||
title: (datasets) => {
|
||||
if (dayDifference > 0) {
|
||||
return datasets[0].label;
|
||||
}
|
||||
const date = new Date(datasets[0].parsed.x);
|
||||
return `${
|
||||
compare
|
||||
? `${formatDateVeryShort(date, locale, config)}: `
|
||||
: ""
|
||||
}${formatTime(date, locale, config)} – ${formatTime(
|
||||
addHours(date, 1),
|
||||
locale,
|
||||
config
|
||||
)}`;
|
||||
},
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
locale
|
||||
)} ${unit}`,
|
||||
...commonOptions.plugins!.tooltip!.callbacks,
|
||||
footer: (contexts) => {
|
||||
if (contexts.length < 2) {
|
||||
return [];
|
||||
@ -278,33 +185,8 @@ export class HuiEnergyWaterGraphCard
|
||||
},
|
||||
},
|
||||
},
|
||||
filler: {
|
||||
propagate: false,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
bar: { borderWidth: 1.5, borderRadius: 4 },
|
||||
point: {
|
||||
hitRadius: 50,
|
||||
},
|
||||
},
|
||||
// @ts-expect-error
|
||||
locale: numberFormatToLocale(locale),
|
||||
};
|
||||
if (compare) {
|
||||
options.scales!.xAxisCompare = {
|
||||
...(options.scales!.x as Record<string, any>),
|
||||
suggestedMin: compareStart!.getTime(),
|
||||
suggestedMax: compareEnd!.getTime(),
|
||||
display: false,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user