Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

Add real-time summary queries and calculations (#16) #54

Merged
merged 8 commits into from
Dec 9, 2023
5 changes: 4 additions & 1 deletion src/api/measurements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios, { AxiosError } from 'axios'

export function measurements(
device_name: string,
sensor: string,
sensor: string[],
starttime: string,
endtime: string
) {
Expand All @@ -25,6 +25,9 @@ export function measurements(
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
Authorization: `Bearer ${JSON.parse(token)}`
},
paramsSerializer: {
indexes: null
}
})
.then((response) => {
Expand Down
2 changes: 1 addition & 1 deletion src/api/queries/battery_status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const useBatteryStatus = () => {
queries: queries.map(({ device, sensor, start, end }) => {
return {
queryKey: ['measurements', sensor, start, end],
queryFn: () => measurements(device, sensor, start, end)
queryFn: () => measurements(device, [sensor], start, end)
}
}),
combine: (results) => {
Expand Down
30 changes: 27 additions & 3 deletions src/api/queries/measurements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,34 @@ export const useMeasurements = (
const start = moment(startTime).format()
const end = moment(endTime).format()

const queries = sensors.reduce(
(acc: { [key: string]: Sensor[] }, sensor: Sensor) => {
acc[sensor.device] = acc[sensor.device] ?? []
acc[sensor.device].push(sensor)

return acc
},
{}
)

const { isSuccess, isError, data } = useQueries({
queries: sensors.map((sensor) => ({
queryKey: ['measurements', sensor.name, start, end],
queryFn: () => measurements(sensor.device, sensor.sensor, start, end)
queries: Object.entries(queries).map(([device, sensors]) => ({
queryKey: [
'measurements',
sensors.reduce(
(acc: string, sensor: Sensor) => `${acc},${sensor.name}`,
''
),
start,
end
],
queryFn: () =>
measurements(
device,
sensors.map((sensor) => sensor.sensor),
start,
end
)
})),
combine: (results) => {
return {
Expand Down
93 changes: 93 additions & 0 deletions src/api/queries/realtime_summary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useQueries } from '@tanstack/react-query'
import moment from 'moment'

import { predictions } from '../predictions'
import { measurements } from '../measurements'

import { RealtimeSummary } from '../../types'

export const useRealtimeSummary = () => {
const startTimeToday = moment().startOf('day').format()
const endTimeToday = moment().endOf('day').format()

const startTimeNow = moment().startOf('hour').format()
const endTimeNow = moment().endOf('hour').format()

const { isSuccess, data } = useQueries({
queries: [
{
queryKey: ['measurements', 'summary', startTimeNow, endTimeNow],
queryFn: () =>
measurements(
'E_HUB',
[
'pload_L1',
'pload_L2',
'pload_L3',
'ul_L1',
'ul_L2',
'ul_L3',
'il_L1',
'il_L2',
'il_L3'
],
startTimeNow,
endTimeNow
)
},
{
queryKey: ['predictions', 'Spot_price', startTimeToday, endTimeToday],
queryFn: () => predictions('Spot_price', startTimeToday, endTimeToday)
}
],
combine: (results) => {
return {
data: Object.assign({}, ...results.map((result) => result.data)),
isSuccess: results.every((result) => result.isSuccess)
}
}
})

if (!isSuccess) {
return {
isSuccess,
summary: {
totalPower: 0,
voltage: 0,
current: 0,
energyPrice: 0,
energyCost: 0
}
}
}

const totalPower =
data.pload_L1.value[data.pload_L1.value.length - 1] +
data.pload_L2.value[data.pload_L2.value.length - 1] +
data.pload_L3.value[data.pload_L3.value.length - 1]

const energyPrice = Math.avg(data.Spot_price.value)

// Calculate energy cost over 24 hours
const energyCost = (totalPower / 1000) * energyPrice

const summary: RealtimeSummary = {
totalPower,
voltage: Math.avg([
data.ul_L1.value[data.ul_L1.value.length - 1],
data.ul_L2.value[data.ul_L2.value.length - 1],
data.ul_L3.value[data.ul_L3.value.length - 1]
]),
current:
data.il_L1.value[data.il_L1.value.length - 1] +
data.il_L2.value[data.il_L2.value.length - 1] +
data.il_L3.value[data.il_L3.value.length - 1],
energyPrice,
energyCost
}

return {
isSuccess,
summary
}
}
26 changes: 14 additions & 12 deletions src/components/cards/realtime_summary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ReactElement } from 'react'

import { useRealtimeSummary } from '../../api/queries/realtime_summary'

export default function RealtimeSummary(): ReactElement {
const { isSuccess, summary } = useRealtimeSummary()

return (
<div className="flex flex-col gap-8 bg-white rounded-lg shadow p-7 dark:bg-slate-700 transition-all duration-500">
<div>
Expand All @@ -17,7 +21,7 @@ export default function RealtimeSummary(): ReactElement {
Total power
</h4>
<p className="mt-2 text-center text-5xl text-sky-700 font-bold dark:text-gray-200">
2000 W
{isSuccess && Math.round(summary.totalPower)} W
</p>
</div>

Expand All @@ -27,15 +31,17 @@ export default function RealtimeSummary(): ReactElement {
Voltage
</h5>
<p className="mt-1 text-3xl font-bold dark:text-slate-300">
237 <span className="text-xl">V</span>
{isSuccess && summary.voltage.toFixed(1)}{' '}
<span className="text-xl">V</span>
</p>
</div>
<div className="text-right">
<h5 className="text-sm text-gray-500 font-semibold dark:text-slate-400">
Current
</h5>
<p className="mt-1 text-3xl font-bold dark:text-slate-300">
8.44 <span className="text-xl">A</span>
{isSuccess && summary.current.toFixed(1)}{' '}
<span className="text-xl">A</span>
</p>
</div>
</div>
Expand All @@ -46,30 +52,26 @@ export default function RealtimeSummary(): ReactElement {
Predicted energy price
</h5>
<p className="mt-1 text-3xl font-bold flex items-center dark:text-slate-300">
15.71&nbsp;
{isSuccess && (summary.energyPrice / 10).toFixed(2)}&nbsp;
<span className="text-xl">c&euro;/kWh</span>
<span className="mx-2 px-1.5 py-1 rounded-sm text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300">
&#9650; 8%
</span>
</p>
<p className="text-xs text-gray-400 dark:text-slate-400">
Since yesterday
</p>
<p className="text-xs text-gray-400 dark:text-slate-400">Today</p>
</div>
<div className="text-right">
<h5 className="text-sm text-gray-500 font-semibold dark:text-slate-400">
Predicted energy cost
</h5>
<p className="mt-1 text-3xl font-bold flex justify-end items-center dark:text-slate-300">
0.31&nbsp;
<span className="text-xl">&euro;/h</span>
{isSuccess && (summary.energyCost / 100).toFixed(2)}&nbsp;
<span className="text-xl">c&euro;/h</span>
<span className="mx-2 px-1.5 py-1 rounded-sm text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300">
&#9660; 75%
</span>
</p>
<p className="text-xs text-gray-400 dark:text-slate-400">
Since yesterday
</p>
<p className="text-xs text-gray-400 dark:text-slate-400">Today</p>
</div>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ export interface BatteryStatus {
charged: number
discharged: number
}

export interface RealtimeSummary {
totalPower: number
voltage: number
current: number
energyPrice: number
energyCost: number
}