Skip to content

Commit 6b361d3

Browse files
committed
chore: unify water chart
1 parent fff58ac commit 6b361d3

File tree

4 files changed

+65
-50
lines changed

4 files changed

+65
-50
lines changed

apps/client/src/Components/Pages/Energy/charts/WaterChart.tsx

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,53 @@
11
import RefreshIcon from "@mui/icons-material/Refresh";
22
import { Button, Chip, IconButton, Stack } from "@mui/material";
33
import { FC, useState } from "react";
4-
import {
5-
useGetWaterQuery,
6-
type GetWaterResponse,
7-
} from "../../../../Services/generated/energyUsageApiWithRetry";
4+
import { BarProps } from "recharts";
5+
import { useGetWaterQuery } from "../../../../Services/generated/energyUsageApiWithRetry";
86
import EnergyChart, {
7+
SensorItem,
98
axisDateTimeFormatDayHour,
109
} from "../../../Molecules/EnergyChart/EnergyChart";
1110

12-
type WaterSensorItem = GetWaterResponse[0][0] & {
13-
delta?: number;
14-
liters?: number;
15-
};
16-
17-
const INTERVAL_24_HOURS = 1000 * 60 * 60 * 24;
18-
const INTERVAL_5_MIN = 1000 * 60 * 5;
19-
20-
const aggregateByInterval =
21-
(interval: number) =>
22-
(acc: WaterSensorItem[], next: WaterSensorItem): WaterSensorItem[] => {
23-
const prevEntry = acc.at(-1) ?? next;
24-
const prevTime = new Date(prevEntry.last_changed ?? "0").getTime();
25-
const nextTime = new Date(next.last_changed ?? "0").getTime();
26-
if (nextTime <= prevTime + interval) {
27-
return [
28-
...acc.slice(0, -1),
29-
{ ...prevEntry, liters: (prevEntry.liters ?? 0) + 1 },
30-
];
31-
}
32-
return [...acc, { ...next, liters: (next.liters ?? 0) + 1 }];
33-
};
11+
const COLORS = ["#66bb6a"];
3412

3513
export const WaterChart: FC = () => {
3614
const [mode, setMode] = useState<"day" | "month">("day");
3715
const { data, isLoading, isFetching, refetch } = useGetWaterQuery({
3816
range: mode,
3917
});
4018

41-
const sensorEntries: WaterSensorItem[] = data?.[0] ?? [];
19+
const sensors = data?.flatMap((sensor) => sensor[0]) ?? [];
4220

43-
const interval = mode === "month" ? INTERVAL_24_HOURS : INTERVAL_5_MIN;
21+
const entriesBySensor = (
22+
data?.flatMap((sensor) =>
23+
sensor
24+
.map((item) => ({
25+
time: new Date(item?.last_changed ?? 0).getTime(),
26+
[`${item.attributes?.friendly_name ?? item?.entity_id}`]:
27+
parseFloat(item.state ?? ""),
28+
}))
29+
.slice(1)
30+
) ?? []
31+
).toSorted((a, b) => {
32+
return a.time - b.time;
33+
});
34+
const entriesByTimestamp = entriesBySensor.reduce<
35+
Record<number, SensorItem>
36+
>((acc: Record<number, SensorItem>, item: SensorItem) => {
37+
acc[item.time] = { ...acc[item.time], ...item };
38+
return acc;
39+
}, {});
40+
const entries = Object.values(entriesByTimestamp);
4441

45-
const aggregatedByInterval = sensorEntries.reduce<WaterSensorItem[]>(
46-
aggregateByInterval(interval),
47-
[]
48-
);
42+
const bars: Omit<BarProps, "ref">[] = sensors.map((sensor, i) => ({
43+
dataKey: `${sensor.attributes?.friendly_name ?? sensor?.entity_id}`,
44+
fill: COLORS[i],
45+
unit: "l",
46+
}));
47+
48+
if (!data) {
49+
return <div>Loading...</div>;
50+
}
4951

5052
return (
5153
<>
@@ -84,19 +86,9 @@ export const WaterChart: FC = () => {
8486
))}
8587
</Stack>
8688
<EnergyChart
87-
data={aggregatedByInterval.map((item) => ({
88-
time: new Date(item.last_changed ?? "0").getTime() ?? 1,
89-
liters: item.liters,
90-
state: item.state,
91-
}))}
89+
data={entries}
9290
config={{
93-
bars: [
94-
{
95-
dataKey: "liters",
96-
fill: "#66bb6a",
97-
unit: "l",
98-
},
99-
],
91+
bars,
10092
rightYAxis: {
10193
unit: "l",
10294
},

apps/server/src/energyusage/energyusage.controller.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe("EnergyUsage Controller", () => {
4343
entity_id: "water_id",
4444
state: "1",
4545
attributes: {},
46-
last_changed: "123",
46+
last_changed: "2025-04-30T06:00:00.000Z",
4747
last_updated: "124",
4848
},
4949
],
@@ -62,14 +62,14 @@ describe("EnergyUsage Controller", () => {
6262
entity_id: "water_id",
6363
state: "1",
6464
attributes: {},
65-
last_changed: "123",
65+
last_changed: "2025-04-30T06:00:00.000Z",
6666
last_updated: "124",
6767
},
6868
],
6969
]);
7070
expect(mockGot).toBeCalledTimes(1);
7171
expect(mockGot).toBeCalledWith(
72-
"/api/history/period/2024-04-24T00:00:00.000Z?end_time=2024-04-25T00:00:00.000Z&filter_entity_id=&minimal_response",
72+
"/api/history/period/2024-04-24T00:00:00.000Z?end_time=2024-04-25T00:00:00.000Z&filter_entity_id=",
7373
{
7474
headers: {
7575
Authorization: "Bearer ",

apps/server/src/energyusage/energyusage.controller.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,15 +514,38 @@ export class EnergyUsageController {
514514
const startTime = new Date(time - startOffset).toISOString();
515515
const endTime = new Date(time).toISOString();
516516

517-
const url = `${this.haApiConfig.baseUrl}/api/history/period/${startTime}?end_time=${endTime}&filter_entity_id=${this.haApiConfig.waterSensorId}&minimal_response`;
517+
const url = `${this.haApiConfig.baseUrl}/api/history/period/${startTime}?end_time=${endTime}&filter_entity_id=${this.haApiConfig.waterSensorId}`;
518518

519519
const result = await got(url, {
520520
headers: {
521521
Authorization: `Bearer ${this.haApiConfig.token}`,
522522
},
523523
}).json<GetHaSensorHistoryResponse>();
524524

525-
return result;
525+
const reduced = result
526+
.map((sensorEntries) =>
527+
// Reduce by timeslot, timeslot is RANGE, from 00:00
528+
sensorEntries.reduce(
529+
reduceSensorEntriesByTimeslot(
530+
range === "day" ? DAY_IN_MS / 24 : DAY_IN_MS
531+
),
532+
[]
533+
)
534+
)
535+
.map((sensorEntries) => {
536+
if (
537+
sensorEntries.length > 0 &&
538+
sensorEntries[0].attributes.device_class === "water"
539+
) {
540+
return sensorEntries.reduce(
541+
reduceSensorEntriesToDeltas,
542+
[]
543+
);
544+
}
545+
return sensorEntries;
546+
});
547+
548+
return reduced;
526549
} catch (err) {
527550
this.logger.error(`[${req.user.name}] ${err}`);
528551
throw new HttpException(

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "homeremote",
3-
"version": "3.7.14",
3+
"version": "3.7.15",
44
"license": "MIT",
55
"scripts": {
66
"writeGitInfo": "ts-node --project ./tsconfig.node.json writeGitInfo.ts",

0 commit comments

Comments
 (0)