Skip to content

Commit 90496aa

Browse files
committed
PoC: add echarts for sensor readings
Directly read sensors from Prometheus instead of embedding Grafana in an iframe. This must be cleaned up a bit before merging.
1 parent 25842b2 commit 90496aa

8 files changed

Lines changed: 106 additions & 23 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
"react-router-dom": "^7.11.0",
3131
"sass": "^1.97.0",
3232
"swr": "^2.3.8",
33-
"vite-plugin-compression": "^0.5.1"
33+
"vite-plugin-compression": "^0.5.1",
34+
"echarts": "^6.0.0",
35+
"echarts-for-react": "^3.0.5"
3436
},
3537
"devDependencies": {
3638
"typescript":">=4.0.0",

public/locales/bg.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ network_devices:
109109
new_network_device: Ново мрежово устройство
110110
edit:
111111
edit_network_device: Редакция на мрежово устройство
112+
rooms:
113+
big-room: Голяма зала
114+
small-room: Малка зала
115+
kitchen: Кухня
116+
outside: Навън
112117
views:
113118
dashboard:
114119
sensor_readings: Температура и влажност на въздуха
@@ -227,6 +232,8 @@ views:
227232
are_you_sure: 'Сигурни ли сте?'
228233
sensors:
229234
title: Показания на сензорите
235+
temperature: Температура
236+
humidity: Влажност
230237
action_log:
231238
title: Лог на действията
232239
columns:

public/locales/en.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ network_devices:
103103
new_network_device: New network device
104104
edit:
105105
edit_network_device: Edit network device
106+
rooms:
107+
big-room: Big Room
108+
small-room: Small Room
109+
kitchen: Kitchen
110+
outside: Outside
106111
views:
107112
dashboard:
108113
sensor_readings: Air temperature and humidity
@@ -219,6 +224,8 @@ views:
219224
are_you_sure: 'Are you shnur?'
220225
sensors:
221226
title: Sensor readings
227+
temperature: Temperature
228+
humidity: Humidity
222229
action_log:
223230
title: Action logs
224231
columns:

src/config.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ export const oidc = {
1313

1414
export type MqttConfig = {[topic: string]: {label: string}}
1515
export const sensors: MqttConfig = {
16-
'sensors/big-room': {label: 'Big room',},
17-
'sensors/small-room': {label: 'Small room',},
18-
'sensors/kitchen': {label: 'Kitchen',},
19-
'sensors/outside': {label: 'Outside',},
16+
'sensors/big-room/test': {label: 'big-room'},
17+
'sensors/small-room/test': {label: 'small-room',},
18+
'sensors/kitchen/test': {label: 'kitchen',},
19+
'sensors/outside/test': {label: 'outside',},
2020
};
2121

2222
export const variantHosts: {[hostname: string]: string} = {
@@ -45,12 +45,4 @@ export const variants: {[variant: string]: VariantConfig} = {
4545
logo: {url: colibriLogo, alt: 'colibri logo',},
4646
title: 'Casa Libri',
4747
},
48-
}
49-
50-
export const grafana = {
51-
urls: [
52-
'https://stats.initlab.org/d-solo/SGAb0ZXMk/temperature-and-humidity?orgId=1&refresh=1m&panelId=4',
53-
'https://stats.initlab.org/d-solo/SGAb0ZXMk/temperature-and-humidity?orgId=1&refresh=1m&panelId=5',
54-
'https://stats.initlab.org/d-solo/SGAb0ZXMk/temperature-and-humidity?orgId=1&refresh=1m&panelId=10',
55-
]
56-
};
48+
}

src/hooks/useEndpoints.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export function useMqttStatus(config?: SWRConfiguration) {
3333
return useCheckSWR<{ [topic: string]: RawMqttReading }>(import.meta.env.MQTT_PROXY_URL.concat('status'), config);
3434
}
3535

36+
export function useMqttHistory(config?: SWRConfiguration) {
37+
return useCheckSWR<{ [sensor: string]: {[metric: string]: [[number, number]]} }>(import.meta.env.MQTT_PROXY_URL.concat('sensors/'), config);
38+
}
39+
3640
export function useActionLog(config?: SWRConfiguration) {
3741
return useAuthSWR<PortierActionLogEntry[]>(import.meta.env.PORTIER_URL.concat('api/actionLog/0/0'), config);
3842
}

src/hooks/useLocale.ts

Whitespace-only changes.

src/pages/Sensors.tsx

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,92 @@
11
import {Col, Row} from 'react-bootstrap';
22
import {useTranslation} from 'react-i18next';
3-
import {grafana} from '../config.ts';
3+
import {sensors} from '../config.ts';
4+
import {useMqttHistory} from "../hooks/useEndpoints.ts";
5+
import EChartsReact from "echarts-for-react";
46

57
const Sensors = () => {
68
const {t} = useTranslation();
9+
const {data} = useMqttHistory();
710

8-
return (<>
11+
if(!data) return <></>;
12+
13+
return <>
914
<Row>
1015
<Col>
1116
<h2>{t('views.sensors.title')}</h2>
1217
</Col>
1318
</Row>
14-
<Row className="row-cols row-cols-1 row-cols-xxl-3">
15-
{grafana.urls.map(url => <Col key={url}>
16-
<iframe src={url} className="w-100" height={300}/>
17-
</Col>)}
19+
<Row className="row-cols row-cols-1 row-cols-xxl-2">
20+
21+
{Object.entries(data).map(([sensor, metrics]) => {
22+
23+
const temperature = metrics['sensor_temperature'];
24+
const humidity = metrics['sensor_humidity'];
25+
26+
const matchedSensor = Object.keys(sensors).find(n => n.includes(sensor));
27+
const label = sensors[matchedSensor??''].label ?? sensor;
28+
29+
const yAxis = [];
30+
const series = [];
31+
32+
const parse = (d: [number, number]): [number, number] => [d[0]*1000, d[1]];
33+
34+
if(temperature) {
35+
yAxis.push({
36+
name: t('views.sensors.temperature'),
37+
type: 'value',
38+
interval: 2.5,
39+
axisLabel: {formatter: '{value} °C'},
40+
min: (v: any) => Math.floor(v.min / 5) * 5,
41+
max: (v: any) => Math.ceil(v.max / 5) * 5,
42+
});
43+
series.push({
44+
name: t('views.sensors.temperature'),
45+
type: 'line',
46+
smooth: true,
47+
symbol: 'none',
48+
data: temperature.map(parse),
49+
yAxisIndex: yAxis.length - 1,
50+
tooltip: {valueFormatter: (v: any) => `${v} °C`}
51+
});
52+
}
53+
if(humidity) {
54+
yAxis.push({
55+
name: t('views.sensors.humidity'),
56+
type: 'value',
57+
interval: 2.5,
58+
axisLabel: {formatter: '{value} %'},
59+
min: (v: any) => Math.floor(v.min / 5) * 5,
60+
max: (v: any) => Math.ceil(v.max / 5) * 5,
61+
});
62+
series.push({
63+
name: t('views.sensors.humidity'),
64+
type: 'line',
65+
smooth: true,
66+
symbol: 'none',
67+
data: humidity.map(parse),
68+
yAxisIndex: yAxis.length - 1,
69+
tooltip: {valueFormatter: (v: any) => `${v} %`}
70+
});
71+
}
72+
73+
74+
return <Col key={sensor}><EChartsReact option={{
75+
title: {text: t(`rooms.${label}`), left: 'left'},
76+
tooltip: {
77+
trigger: 'axis'
78+
},
79+
legend: {type: 'plain', left: 'left'},
80+
xAxis: {type: 'time',max:Date.now()},
81+
grid: { left: 0, right: 0, top: 70, bottom: 70 },
82+
yAxis: yAxis,
83+
series: series,
84+
animation: false,
85+
}}/></Col>;
86+
}
87+
)}
1888
</Row>
19-
</>);
89+
</>;
2090
};
2191

2292
export default Sensors;

src/widgets/SensorReadings/SensorPanel.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import './SensorPanel.css';
66
import {useMemo, useState} from 'react';
77
import type {MqttReading} from "../../types";
88
import type {RawMqttReading} from "../../portier-types";
9+
import {useTranslation} from "react-i18next";
910

1011
const temperatureThresholds = [18, 24, 26, 32];
1112

@@ -52,7 +53,7 @@ const SensorReadingValue = ({reading, valueFormat}: {
5253
}
5354

5455
const SensorPanel = ({label, readings}: MqttReading) => {
55-
56+
const {t} = useTranslation();
5657
const formatTemperature = (t: number) => Math.round(t) + '°C'
5758
const formatHumidity = (h: number) => Math.round(h) + '%'
5859
const [loadTime] = useState<number>(Date.now);
@@ -104,7 +105,7 @@ const SensorPanel = ({label, readings}: MqttReading) => {
104105
<i className={'fa-solid fa-battery-' + batteryState(state.readings.battery.value)}
105106
title={formatTimestamp(state.readings.battery.timestamp)}
106107
/>} {' '}
107-
{label}
108+
{t(`rooms.${label}`)}
108109
</div>
109110
</Col>
110111
</Row>

0 commit comments

Comments
 (0)