Add data plot to sauna route
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
This commit is contained in:
parent
59cb6d1380
commit
5822328d9d
914
webui/package-lock.json
generated
914
webui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hamburger-menu-svelte": "^2.2.0"
|
"hamburger-menu-svelte": "^2.2.0",
|
||||||
|
"uplot": "^1.6.30"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
113
webui/src/lib/Uplot.svelte
Normal file
113
webui/src/lib/Uplot.svelte
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import 'uplot/dist/uPlot.min.css';
|
||||||
|
|
||||||
|
export let data_label = ""; // data label in legend
|
||||||
|
export let unit = ""; // unit in legend
|
||||||
|
|
||||||
|
export let last_for_seconds = 20; // Period of time [s] the data are shifted out on the left
|
||||||
|
let plotContainer;
|
||||||
|
let data = [];
|
||||||
|
data[0] = [Date.now()];
|
||||||
|
data[1] = [null];
|
||||||
|
|
||||||
|
const PRECISION = 1;
|
||||||
|
|
||||||
|
export async function create(legend_label, legend_unit, duration) {
|
||||||
|
console.log(legend_label);
|
||||||
|
console.log(legend_unit);
|
||||||
|
console.log(duration);
|
||||||
|
data_label = legend_label;
|
||||||
|
unit = legend_unit;
|
||||||
|
last_for_seconds = duration;
|
||||||
|
const uplotModule = await import('uplot');
|
||||||
|
const uPlot = uplotModule.default;
|
||||||
|
redraw(uPlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function add_data(new_data) {
|
||||||
|
data[0].push(Date.now());
|
||||||
|
data[1].push(new_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function redraw(uPlot) {
|
||||||
|
function getSize() {
|
||||||
|
let max_height = window.innerHeight;
|
||||||
|
let max_width = window.innerWidth;
|
||||||
|
let current_posY = plotContainer.getBoundingClientRect().top;
|
||||||
|
let height = Math.max(max_height - current_posY - 200, 250);
|
||||||
|
let width = Math.max(max_width - (2 * 50), 600);
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const opts = {
|
||||||
|
...getSize(),
|
||||||
|
ms: 1,
|
||||||
|
cursor: {drag: {setScale: false}},
|
||||||
|
points: {show: false},
|
||||||
|
pxSnap: false,
|
||||||
|
pxAlign: 0,
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
label: "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: data_label,
|
||||||
|
stroke: "red",
|
||||||
|
paths: uPlot.paths.spline(),
|
||||||
|
value: (u, v, sidx, didx) => {
|
||||||
|
if (didx == null) {
|
||||||
|
let d = u.data[sidx];
|
||||||
|
v = d[d.length - 1];
|
||||||
|
}
|
||||||
|
let value = v;
|
||||||
|
if ( value != null) {
|
||||||
|
value = value.toFixed(PRECISION) + unit;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
pxAlign: 0,
|
||||||
|
points: { show: false },
|
||||||
|
}
|
||||||
|
],
|
||||||
|
axes: [{grid: { show: false}}, {grid: { show: false}}],
|
||||||
|
// hooks are used here to filter out the time series from the legend
|
||||||
|
hooks: {
|
||||||
|
init: [
|
||||||
|
u => {
|
||||||
|
[...u.root.querySelectorAll('.u-legend .u-series')].forEach((el, i) => {
|
||||||
|
if (u.series[i].label ==="time") {
|
||||||
|
el.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let uplot = new uPlot(opts, data, plotContainer);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const now = Date.now();
|
||||||
|
const scale = { min: now - (last_for_seconds * 1000), max: now };
|
||||||
|
|
||||||
|
// shift out data earlier than chart origin
|
||||||
|
let last = Date.now();
|
||||||
|
let first = last - (last_for_seconds * 1000);
|
||||||
|
while(data[0][0] < first) {
|
||||||
|
for(let i = 0; i < data.length; i++) {
|
||||||
|
data[i].shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uplot.setData(data, false);
|
||||||
|
uplot.setScale('x', scale);
|
||||||
|
uplot.setLegend({idx: data[0].length - 1}, false);
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={plotContainer}></div>
|
@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from "../../../node_modules/svelte/internal";
|
import { onMount } from "../../../node_modules/svelte/internal";
|
||||||
|
import Plot from "$lib/Uplot.svelte"
|
||||||
import icon from "$lib/images/sauna.svg"
|
import icon from "$lib/images/sauna.svg"
|
||||||
|
|
||||||
let backend_url = "https://home.blackfinn.de/api/sauna";
|
let backend_url = "https://home.blackfinn.de/api/sauna";
|
||||||
@ -8,6 +9,7 @@
|
|||||||
let temperature_unit = "°C"
|
let temperature_unit = "°C"
|
||||||
let time = "00:00:00";
|
let time = "00:00:00";
|
||||||
let updated = false;
|
let updated = false;
|
||||||
|
let plot;
|
||||||
|
|
||||||
function get_temperature() {
|
function get_temperature() {
|
||||||
fetch(backend_url)
|
fetch(backend_url)
|
||||||
@ -16,6 +18,9 @@
|
|||||||
temperature_unit = data.unit
|
temperature_unit = data.unit
|
||||||
temperature_value = data.value
|
temperature_value = data.value
|
||||||
time = data.time.slice(11, 19)
|
time = data.time.slice(11, 19)
|
||||||
|
if(plot) {
|
||||||
|
plot.add_data(Math.floor(Math.random() * 30) - 15);
|
||||||
|
}
|
||||||
updated = JSON.parse(data.valid);
|
updated = JSON.parse(data.valid);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@ -28,6 +33,9 @@
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
if(plot) {
|
||||||
|
plot.create("Temperature", "°C", 4 * 60 * 60 /* 4h */);
|
||||||
|
}
|
||||||
get_temperature();
|
get_temperature();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -45,5 +53,8 @@
|
|||||||
{#if updated}
|
{#if updated}
|
||||||
<div>{time} Uhr:</div>
|
<div>{time} Uhr:</div>
|
||||||
<h1>{temperature_value} {temperature_unit}</h1>
|
<h1>{temperature_value} {temperature_unit}</h1>
|
||||||
|
<div><Plot/></div>
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<Plot bind:this={plot}/>
|
||||||
|
Loading…
Reference in New Issue
Block a user