add software

This commit is contained in:
dustinbrun
2026-07-05 10:48:19 +02:00
parent 79f5830bbc
commit 7b6edc542d
6 changed files with 672 additions and 0 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+70
View File
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<title>Powermeter Dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="icon" type="image/png" href="icon.png">
<script src="chart.js"></script>
</head>
<body>
<div class="topnav">
<h1>Powermeter Dashboard</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<p class="card-title">Power</p>
<p class="reading"><span id="P_W"></span> W</p>
</div>
<div class="card">
<p class="card-title">Current</p>
<p class="reading"><span id="I_A"></span> A</p>
</div>
<div class="card">
<p class="card-title">Voltage</p>
<p class="reading"><span id="U_V"></span> V</p>
</div>
<div class="card">
<p class="card-title">Configuration</p>
<p>Update Interval: <input type="number" id="interval" min="1" value="500"></p>
<p>Plot Diagrams?: <input type="checkbox" id="plot_diagrams" name="plot_diagrams" checked></p>
<p>Diagram max. Number of Values: <input type="number" id="num_values" min="1" value="100"></p>
<button onclick="button_export()">Export Data</button>
<button onclick="button_clear()">Clear Diagrams</button>
<button onclick="button_apply()">Apply</button>
<p id="output_last_update"></p>
<p id="output_online"></p>
</div>
</div>
</div>
<div class="content">
<div class="card-grid_graph">
<div class="card">
<p class="card-title">Power Graph</p>
<canvas id="graph_P_W" width="8" height="6"></canvas>
</div>
<div class="card">
<p class="card-title">Current Graph</p>
<canvas id="graph_I_A" width="8" height="6"></canvas>
</div>
<div class="card">
<p class="card-title">Voltage Graph</p>
<canvas id="graph_U_V" width="8" height="6"></canvas>
</div>
</div>
</div>
<script src="updater_script.js"></script>
</body>
</html>
+55
View File
@@ -0,0 +1,55 @@
html {
font-family: Arial, Helvetica, sans-serif;
display: inline-block;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
.topnav {
overflow: hidden;
background-color: #0A1128;
}
body {
margin: 0;
}
.content {
padding: 50px;
}
.card-grid {
max-width: 800px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card-grid_graph {
max-width: 2000px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140, 140, 140, .5);
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
color: #034078
}
.reading {
font-size: 1.2rem;
color: #1282A2;
}
+226
View File
@@ -0,0 +1,226 @@
window.addEventListener('load', button_apply); // Virtually press apply button when webpage loads the first time
var myObj = 0;
var was_error;
function get_readings() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState == 4) { // request finished, response ready
if (this.status == 200) {
myObj = JSON.parse(this.responseText);
was_error = false;
console.log("Received JSON");
console.log(myObj);
}
else {
was_error = true;
console.log("JSON response error or timeout");
}
}
};
xhr.open("GET", "/readings", true);
xhr.timeout = 500;
xhr.send();
}
function update_values() {
get_readings();
if (was_error == true) {
document.getElementById("output_online").innerText = "Offline";
document.getElementById("output_online").style.color = "#FF0000";
document.getElementById('P_W').innerHTML = "nan";
document.getElementById('I_A').innerHTML = "nan";
document.getElementById('U_V').innerHTML = "nan";
}
else {
document.getElementById("output_online").innerText = "Online";
document.getElementById("output_online").style.color = "#008000";
document.getElementById('P_W').innerHTML = myObj.P_W;
document.getElementById('I_A').innerHTML = myObj.I_A;
document.getElementById('U_V').innerHTML = myObj.U_V;
var timestamp = new Date().toLocaleTimeString();
var plot_diagrams = document.getElementById("plot_diagrams");
if (plot_diagrams.checked == true) {
chart_P_W.data.datasets[0].data.push(myObj.P_W);
chart_I_A.data.datasets[0].data.push(myObj.I_A);
chart_U_V.data.datasets[0].data.push(myObj.U_V);
chart_P_W.data.labels.push(timestamp);
chart_I_A.data.labels.push(timestamp);
chart_U_V.data.labels.push(timestamp);
if (chart_P_W.data.labels.length >= MAX_DATA_SET_LENGTH) {
chart_P_W.data.datasets[0].data.shift();
chart_I_A.data.datasets[0].data.shift();
chart_U_V.data.datasets[0].data.shift();
chart_P_W.data.labels.shift();
chart_I_A.data.labels.shift();
chart_U_V.data.labels.shift();
}
chart_P_W.update();
chart_I_A.update();
chart_U_V.update();
}
}
document.getElementById("output_last_update").innerText = "Last update: " + new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", fractionalSecondDigits: "3" });
}
let intervalId;
function button_apply() {
let mseconds = parseInt(document.getElementById("interval").value);
if (isNaN(mseconds) || mseconds < 100) {
alert("Please enter a valid number 100ms or larger");
return;
}
clearInterval(intervalId);
intervalId = setInterval(update_values, mseconds);
MAX_DATA_SET_LENGTH = parseInt(document.getElementById("num_values").value);
}
function button_clear() {
chart_P_W.data.datasets[0].data = [];
chart_I_A.data.datasets[0].data = [];
chart_U_V.data.datasets[0].data = [];
chart_P_W.data.labels = [];
chart_I_A.data.labels = [];
chart_U_V.data.labels = [];
chart_P_W.update();
chart_I_A.update();
chart_U_V.update();
}
var MAX_DATA_SET_LENGTH = 100;
let data_P_W = [];
let labels_P_W = [];
let labels_I_A = [];
let labels_U_V = [];
let data_I_A = [];
let data_U_V = [];
var options = {
scales: {
yAxes: [{
type: 'linear',
ticks: {
maxTicksLimit: 5,
},
}],
xAxes: [{
type: 'time',
ticks: {
maxTicksLimit: 3,
},
time: {
unit: 'second',
displayFormats: {
'second': 'HH:mm:ss',
},
tooltipFormat: 'HH:mm:ss',
}
}]
},
scales: {
x: { title: { display: true, text: "Time" } },
y: { title: { display: false, text: "Value" } }
},
animation: {
duration: 50,
},
pointRadius: 2,
responsive: true,
showLines: true
};
const graph_P_W = document.getElementById("graph_P_W").getContext("2d");
const chart_P_W = new Chart(graph_P_W, {
type: "line",
data: {
labels: labels_P_W,
datasets: [{
label: "Power [W]",
data: data_P_W,
borderColor: "blue",
borderWidth: 2,
fill: false,
}]
},
options: options
});
const graph_I_A = document.getElementById("graph_I_A").getContext("2d");
const chart_I_A = new Chart(graph_I_A, {
type: "line",
data: {
labels: labels_I_A,
datasets: [{
label: "Current [A]",
data: data_I_A,
borderColor: "red",
borderWidth: 2,
fill: false,
}]
},
options: options
});
const graph_U_V = document.getElementById("graph_U_V").getContext("2d");
const chart_U_V = new Chart(graph_U_V, {
type: "line",
data: {
labels: labels_U_V,
datasets: [{
label: "Voltage [V]",
data: data_U_V,
borderColor: "green",
borderWidth: 2,
fill: false,
}]
},
options: options
});
function exportToCSV(filename, data) {
}
function button_export() {
const csvContent = chart_P_W.data.labels + "\n" +
chart_P_W.data.datasets[0].data + "\n" +
chart_I_A.data.datasets[0].data + "\n" +
chart_U_V.data.datasets[0].data;
const blob = new Blob([csvContent], { type: "text/csv" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "data.csv.txt";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}