mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-04 01:10:33 -06:00
Smooth sensor card more when "Show more detail" is disabled (#27891)
* Smooth sensor card more when "Show more detail" is disabled * Set minimum sample points to 10
This commit is contained in:
parent
7179bb2d26
commit
1c05afebd7
@ -6,7 +6,8 @@ export function downSampleLineData<
|
||||
data: T[] | undefined,
|
||||
maxDetails: number,
|
||||
minX?: number,
|
||||
maxX?: number
|
||||
maxX?: number,
|
||||
useMean = false
|
||||
): T[] {
|
||||
if (!data) {
|
||||
return [];
|
||||
@ -17,15 +18,13 @@ export function downSampleLineData<
|
||||
const min = minX ?? getPointData(data[0]!)[0];
|
||||
const max = maxX ?? getPointData(data[data.length - 1]!)[0];
|
||||
const step = Math.ceil((max - min) / Math.floor(maxDetails));
|
||||
const frames = new Map<
|
||||
number,
|
||||
{
|
||||
min: { point: (typeof data)[number]; x: number; y: number };
|
||||
max: { point: (typeof data)[number]; x: number; y: number };
|
||||
}
|
||||
>();
|
||||
|
||||
// Group points into frames
|
||||
const frames = new Map<
|
||||
number,
|
||||
{ point: (typeof data)[number]; x: number; y: number }[]
|
||||
>();
|
||||
|
||||
for (const point of data) {
|
||||
const pointData = getPointData(point);
|
||||
if (!Array.isArray(pointData)) continue;
|
||||
@ -36,28 +35,53 @@ export function downSampleLineData<
|
||||
const frameIndex = Math.floor((x - min) / step);
|
||||
const frame = frames.get(frameIndex);
|
||||
if (!frame) {
|
||||
frames.set(frameIndex, { min: { point, x, y }, max: { point, x, y } });
|
||||
frames.set(frameIndex, [{ point, x, y }]);
|
||||
} else {
|
||||
if (frame.min.y > y) {
|
||||
frame.min = { point, x, y };
|
||||
}
|
||||
if (frame.max.y < y) {
|
||||
frame.max = { point, x, y };
|
||||
}
|
||||
frame.push({ point, x, y });
|
||||
}
|
||||
}
|
||||
|
||||
// Convert frames back to points
|
||||
const result: T[] = [];
|
||||
for (const [_i, frame] of frames) {
|
||||
// Use min/max points to preserve visual accuracy
|
||||
// The order of the data must be preserved so max may be before min
|
||||
if (frame.min.x > frame.max.x) {
|
||||
result.push(frame.max.point);
|
||||
|
||||
if (useMean) {
|
||||
// Use mean values for each frame
|
||||
for (const [_i, framePoints] of frames) {
|
||||
const sumY = framePoints.reduce((acc, p) => acc + p.y, 0);
|
||||
const meanY = sumY / framePoints.length;
|
||||
const sumX = framePoints.reduce((acc, p) => acc + p.x, 0);
|
||||
const meanX = sumX / framePoints.length;
|
||||
|
||||
const firstPoint = framePoints[0].point;
|
||||
const pointData = getPointData(firstPoint);
|
||||
const meanPoint = (
|
||||
Array.isArray(pointData) ? [meanX, meanY] : { value: [meanX, meanY] }
|
||||
) as T;
|
||||
result.push(meanPoint);
|
||||
}
|
||||
result.push(frame.min.point);
|
||||
if (frame.min.x < frame.max.x) {
|
||||
result.push(frame.max.point);
|
||||
} else {
|
||||
// Use min/max values for each frame
|
||||
for (const [_i, framePoints] of frames) {
|
||||
let minPoint = framePoints[0];
|
||||
let maxPoint = framePoints[0];
|
||||
|
||||
for (const p of framePoints) {
|
||||
if (p.y < minPoint.y) {
|
||||
minPoint = p;
|
||||
}
|
||||
if (p.y > maxPoint.y) {
|
||||
maxPoint = p;
|
||||
}
|
||||
}
|
||||
|
||||
// The order of the data must be preserved so max may be before min
|
||||
if (minPoint.x > maxPoint.x) {
|
||||
result.push(maxPoint.point);
|
||||
}
|
||||
result.push(minPoint.point);
|
||||
if (minPoint.x < maxPoint.x) {
|
||||
result.push(maxPoint.point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,8 @@ export const coordinates = (
|
||||
width: number,
|
||||
height: number,
|
||||
maxDetails: number,
|
||||
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number }
|
||||
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number },
|
||||
useMean = false
|
||||
) => {
|
||||
history = history.filter((item) => !Number.isNaN(item[1]));
|
||||
|
||||
@ -58,7 +59,8 @@ export const coordinates = (
|
||||
history,
|
||||
maxDetails,
|
||||
limits?.minX,
|
||||
limits?.maxX
|
||||
limits?.maxX,
|
||||
useMean
|
||||
);
|
||||
return calcPoints(sampledData, width, height, limits);
|
||||
};
|
||||
@ -68,7 +70,8 @@ export const coordinatesMinimalResponseCompressedState = (
|
||||
width: number,
|
||||
height: number,
|
||||
maxDetails: number,
|
||||
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number }
|
||||
limits?: { minX?: number; maxX?: number; minY?: number; maxY?: number },
|
||||
useMean = false
|
||||
) => {
|
||||
if (!history?.length) {
|
||||
return { points: [], yAxisOrigin: 0 };
|
||||
@ -80,5 +83,5 @@ export const coordinatesMinimalResponseCompressedState = (
|
||||
item.lu * 1000,
|
||||
Number(item.s),
|
||||
]);
|
||||
return coordinates(mappedHistory, width, height, maxDetails, limits);
|
||||
return coordinates(mappedHistory, width, height, maxDetails, limits, useMean);
|
||||
};
|
||||
|
||||
@ -155,16 +155,20 @@ export class HuiGraphHeaderFooter
|
||||
}
|
||||
const width = this.clientWidth || this.offsetWidth;
|
||||
// sample to 1 point per hour or 1 point per 5 pixels
|
||||
const maxDetails =
|
||||
const maxDetails = Math.max(
|
||||
10,
|
||||
this._config.detail! > 1
|
||||
? Math.max(width / 5, this._config.hours_to_show!)
|
||||
: this._config.hours_to_show!;
|
||||
: this._config.hours_to_show!
|
||||
);
|
||||
const useMean = this._config.detail !== 2;
|
||||
const { points } = coordinatesMinimalResponseCompressedState(
|
||||
combinedHistory[this._config.entity],
|
||||
width,
|
||||
width / 5,
|
||||
maxDetails,
|
||||
{ minY: this._config.limits?.min, maxY: this._config.limits?.max }
|
||||
{ minY: this._config.limits?.min, maxY: this._config.limits?.max },
|
||||
useMean
|
||||
);
|
||||
this._coordinates = points;
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user