1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Only center bars for sub-daily periods using getSuggestedPeriod

This commit is contained in:
Petar Petrov
2026-03-31 12:50:23 +03:00
parent 3363347200
commit ec43671550
6 changed files with 48 additions and 14 deletions

View File

@@ -39,7 +39,7 @@ import { getSuggestedPeriod } from "../../../../../data/energy";
/**
* Energy chart data point tuple:
* [0] displayX - midpoint of the period, used for bar positioning
* [0] displayX - bar position (midpoint for sub-daily periods, start otherwise)
* [1] value - the energy value
* [2] originalStart - original period start timestamp, used for tooltips
*/
@@ -228,8 +228,8 @@ function formatTooltip(
if (!params[0]?.value) {
return "";
}
// displayX is the period midpoint; originalStart has the real date for display.
// Gap-filled entries lack originalStart, so find the first real one.
// displayX may be shifted from the period start (see EnergyDataPoint);
// originalStart has the real date for display. Gap-filled entries lack it.
const origDate = params.find((p) => p.value?.[2] != null)?.value?.[2];
const date = new Date(origDate ?? params[0].value?.[0]);
let period: string;
@@ -393,11 +393,24 @@ export function fillLineGaps(datasets: LineSeriesOption[]) {
return datasets;
}
/**
* Compute the display x-position for an energy bar chart data point.
* For sub-daily periods (hour/5minute), returns the midpoint to center bars
* between ticks. For daily or longer periods, returns the start timestamp.
*/
export function computeStatMidpoint(
start: number,
end: number,
period: string,
compareTransform?: (ts: Date) => Date
): number {
const center = period === "hour" || period === "5minute";
if (!center) {
if (compareTransform) {
return compareTransform(new Date(start)).getTime();
}
return start;
}
if (compareTransform) {
return (
(compareTransform(new Date(start)).getTime() +

View File

@@ -16,6 +16,7 @@ import type {
} from "../../../../data/energy";
import {
getEnergyDataCollection,
getSuggestedPeriod,
getSummedData,
computeConsumptionData,
validateEnergyCollectionKey,
@@ -399,6 +400,7 @@ export class HuiEnergyDevicesDetailGraphCard
this._start,
this._compareStart!
);
const period = getSuggestedPeriod(this._start, this._end);
const untrackedConsumption: BarSeriesOption["data"] = [];
const sortedTimes = Object.keys(consumptionData.used_total).sort(
@@ -407,7 +409,7 @@ export class HuiEnergyDevicesDetailGraphCard
// Only start timestamps available here, so estimate midpoint from the gap
// between the first two entries. Assumes uniform period spacing.
const periodOffset =
sortedTimes.length >= 2
(period === "hour" || period === "5minute") && sortedTimes.length >= 2
? (Number(sortedTimes[1]) - Number(sortedTimes[0])) / 2
: 0;
sortedTimes.forEach((time) => {
@@ -466,6 +468,7 @@ export class HuiEnergyDevicesDetailGraphCard
this._start,
this._compareStart!
);
const period = getSuggestedPeriod(this._start, this._end);
devices.forEach((source, idx) => {
const order = sorted_devices.indexOf(source.stat_consumption);
@@ -509,6 +512,7 @@ export class HuiEnergyDevicesDetailGraphCard
computeStatMidpoint(
point.start,
point.end,
period,
compare ? compareTransform : undefined
),
point.change - sumChildren,

View File

@@ -16,6 +16,7 @@ import type {
} from "../../../../data/energy";
import {
getEnergyDataCollection,
getSuggestedPeriod,
validateEnergyCollectionKey,
} from "../../../../data/energy";
import type { Statistics, StatisticsMetaData } from "../../../../data/recorder";
@@ -267,6 +268,7 @@ export class HuiEnergyGasGraphCard
this._start,
this._compareStart!
);
const period = getSuggestedPeriod(this._start, this._end);
gasSources.forEach((source, idx) => {
let prevStart: number | null = null;
@@ -291,6 +293,7 @@ export class HuiEnergyGasGraphCard
computeStatMidpoint(
point.start,
point.end,
period,
compare ? compareTransform : undefined
),
point.change,

View File

@@ -289,6 +289,7 @@ export class HuiEnergySolarGraphCard
this._start,
this._compareStart!
);
const period = getSuggestedPeriod(this._start, this._end);
solarSources.forEach((source, idx) => {
let prevStart: number | null = null;
@@ -314,6 +315,7 @@ export class HuiEnergySolarGraphCard
computeStatMidpoint(
point.start,
point.end,
period,
compare ? compareTransform : undefined
),
point.change,
@@ -413,15 +415,19 @@ export class HuiEnergySolarGraphCard
if (forecastsData) {
const solarForecastData: LineSeriesOption["data"] = [];
// Only start timestamps available for forecasts, so estimate midpoint
// from the gap between the first two entries. Assumes uniform spacing.
const forecastTimes = Object.keys(forecastsData)
.map(Number)
.sort((a, b) => a - b);
const forecastOffset =
forecastTimes.length >= 2
? (forecastTimes[1] - forecastTimes[0]) / 2
: 0;
// Only center forecast points for sub-daily periods to align with bars.
// Only start timestamps available, so estimate midpoint from the gap
// between the first two entries. Assumes uniform spacing.
let forecastOffset = 0;
if (period === "hour" || period === "5minute") {
const forecastTimes = Object.keys(forecastsData)
.map(Number)
.sort((a, b) => a - b);
forecastOffset =
forecastTimes.length >= 2
? (forecastTimes[1] - forecastTimes[0]) / 2
: 0;
}
for (const [time, value] of Object.entries(forecastsData)) {
solarForecastData.push([
Number(time) + forecastOffset,

View File

@@ -24,6 +24,7 @@ import type {
import {
computeConsumptionData,
getEnergyDataCollection,
getSuggestedPeriod,
getSummedData,
validateEnergyCollectionKey,
} from "../../../../data/energy";
@@ -483,10 +484,14 @@ export class HuiEnergyUsageGraphCard
const uniqueKeys = summedData.timestamps;
// Only center bars for sub-daily periods (hour/5min).
// Only start timestamps available here, so estimate midpoint from the gap
// between the first two entries. Assumes uniform period spacing.
const period = getSuggestedPeriod(this._start, this._end);
const periodOffset =
uniqueKeys.length >= 2 ? (uniqueKeys[1] - uniqueKeys[0]) / 2 : 0;
(period === "hour" || period === "5minute") && uniqueKeys.length >= 2
? (uniqueKeys[1] - uniqueKeys[0]) / 2
: 0;
const compareTransform = getCompareTransform(
this._start,

View File

@@ -15,6 +15,7 @@ import type {
} from "../../../../data/energy";
import {
getEnergyDataCollection,
getSuggestedPeriod,
validateEnergyCollectionKey,
} from "../../../../data/energy";
import type { Statistics, StatisticsMetaData } from "../../../../data/recorder";
@@ -267,6 +268,7 @@ export class HuiEnergyWaterGraphCard
this._start,
this._compareStart!
);
const period = getSuggestedPeriod(this._start, this._end);
waterSources.forEach((source, idx) => {
let prevStart: number | null = null;
@@ -291,6 +293,7 @@ export class HuiEnergyWaterGraphCard
computeStatMidpoint(
point.start,
point.end,
period,
compare ? compareTransform : undefined
),
point.change,