initial commit

This commit is contained in:
V
2026-03-09 01:26:52 +01:00
commit 2198c20790
18 changed files with 1942 additions and 0 deletions

BIN
public/favicon.ico Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

45
public/index.css Normal file
View File

@@ -0,0 +1,45 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
}
body {
font-size: 16px;
}
.container {
padding: 1rem;
margin: 0 auto;
max-width: 1200px;
}
.select-form {
display: flex;
flex-direction: row;
justify-content: space-between;
border: 1px lightgray solid;
gap: 2rem;
margin: 1rem 0;
padding: 1rem;
& > .pickers {
display: flex;
flex-direction: row;
gap: 1rem;
& > label {
padding: 0.5rem 0rem;
& > select {
padding: 0.5rem 1rem;
margin: 0.5rem;
font-size: 1rem;
}
}
}
& > input {
padding: 0.5rem 1rem;
font-size: 1rem;
}
}

73
public/index.html Normal file
View File

@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/moment-timezone@0.6.0/moment-timezone.min.js"></script>-->
<script src="https://momentjs.com/downloads/moment-timezone-with-data.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
<script defer src="index.js"></script>
<!-- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>-->
<!-- <script defer src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>-->
<!-- <script defer src="https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes"></script>-->
<link rel="stylesheet" href="index.css">
</head>
<body>
<main class="container">
<h1>Luftfeuchtigkeitsdaten</h1>
<form class="select-form">
<div class="pickers">
<label for="tag-select">Daten von
<select name="tag-select" id="tag-select">
<option value="">--Lade--</option>
</select>
</label>
<label for="range-select">Wie lange?
<select name="range-select" id="range-select">
<option value="3" selected>3 Stunden</option>
<option value="6">6 Stunden</option>
<option value="12">12 Stunden</option>
<option value="24">1 Tag</option>
<option value="72">3 Tage</option>
<option value="72">3 Tage</option>
<option value="168">1 Woche</option>
</select>
</div>
</label>
<input type="submit" value="Anwenden" id="submit-select">
</form>
<div class="chart-container">
<canvas id="humChart" class="chart"> Lade...</canvas>
<canvas id="pwrChart" class="chart"> Lade...</canvas>
</div>
</main>
<!--<form>-->
<!-- <label for="range-select">Choose a range:</label>-->
<!-- <select name="range-select" id="table-select">-->
<!-- <option value="">&#45;&#45;Please choose an option&#45;&#45;</option>-->
<!-- <option value="humidity">Luftfeuchtigkeit</option>-->
<!-- <option value="power">Stromverbrauch</option>-->
<!-- </select>-->
<!-- <input type="submit" value="Submit" id="submit-table-select">-->
<!--</form>-->
</body>
</html>

244
public/index.js Normal file
View File

@@ -0,0 +1,244 @@
moment.tz('Europe/Berlin')
Chart.defaults.font.size = 24;
let humChart, pwrChart
function toMoment(time) {
let m = moment(time);
m = m.subtract({hours: 1});
return m
}
async function fetchTags() {
let res = await fetch("/tags");
let tags = await res.json();
return tags;
}
async function fetchChartData(series, tag, from) {
const queryParams = new URLSearchParams({tag, from});
const url = `/${series}?${queryParams.toString()}`
let res = await fetch(url);
let data = await res.json();
return data;
}
async function fetchTagData(tag, from) {
let humidityDataPromise = fetchChartData("humidity",tag, from);
let powerDataPromise = fetchChartData("power",tag, from);
let stateDataPromise = fetchChartData("state",tag, from);
let [humidityData, powerData, stateData] = await Promise.all([humidityDataPromise, powerDataPromise,stateDataPromise])
return {
humidityData, powerData, stateData
}
}
function initHumChart({humidityData, powerData, stateData}) {
// let humidityData = await fetchChartData("humidity",tag, new Date().getTime());
// let powerData = await fetchChartData("power",tag, new Date().getTime());
// let stateData = await fetchChartData("state",tag, new Date().getTime());
let humidityDataSet = {
label: 'Luftfeuchtigkeit',
yAxisID: 'humidity',
data: humidityData.map(v => ({x: toMoment(v.Time), y: v.Humidity})),
backgroundColor: 'rgba(8,62,236,0.2)',
borderColor: 'rgb(101,119,234)',
borderWidth: 1
}
let powerDataSet = {
label: 'Apparent Power',
yAxisID: 'power',
data: powerData.map(v => ({x: toMoment(v.Time), y: v.ApparentPower})),
backgroundColor: 'rgba(193,151,91,0.2)',
borderColor: 'rgb(207,179,106)',
borderWidth: 1
}
// let stateDataSet = {
// label: 'An/Aus',
// data: stateData.map(v => ({x: v.Time, y: v.SwitchState})),
// backgroundColor: 'rgba(125,158,124,0.2)',
// borderColor: 'rgb(8,255,0)',
// borderWidth: 1
// }
chart = new Chart(document.getElementById('humChart').getContext('2d'), {
type: 'line',
data: {
datasets: [humidityDataSet, powerDataSet]
},
options: {
scales:{
x:{
type: 'time',
ticks: {
autoSkip: true,
maxTicksLimit: 20
},
time: {
displayFormats: {minute: 'HH:mm'}
}
},
humidity: {
type: 'linear',
position: 'left',
ticks:
{
beginAtZero: true,
},
grid: { display: false }
},
power: {
type: 'linear',
position: 'right',
ticks:
{
beginAtZero: true,
},
grid: { display: false }
},
}
}
});
return chart
}
function initPwrChart({humidityData, powerData, stateData}) {
//
// let humidityDataSet = {
// label: 'Luftfeuchtigkeit',
// yAxisID: 'humidity',
// data: humidityData.map(v => ({x: toMoment(v.Time), y: v.Humidity})),
// backgroundColor: 'rgba(8,62,236,0.2)',
// borderColor: 'rgb(101,119,234)',
// borderWidth: 1
// }
let totalDataset = {
label: 'Total KWh',
yAxisID: 'total',
data: powerData.map(v => ({x: toMoment(v.Time), y: v.Total})),
backgroundColor: 'rgba(193,151,91,0.2)',
borderColor: 'rgb(207,179,106)',
borderWidth: 1
}
let yesterdayDataset = {
label: 'Gestern KWh',
yAxisID: 'yesterday',
data: powerData.map(v => ({x: toMoment(v.Time), y: v.Yesterday})),
backgroundColor: 'rgba(110,70,70,0.2)',
borderColor: 'rgb(207,106,114)',
borderWidth: 1
}
chart = new Chart(document.getElementById('pwrChart').getContext('2d'), {
type: 'line',
data: {
datasets: [totalDataset, yesterdayDataset]
},
options: {
scales:{
x:{
type: 'time',
ticks: {
autoSkip: true,
maxTicksLimit: 20
},
time: {
displayFormats: {minute: 'HH:mm'}
}
},
total: {
type: 'linear',
position: 'left',
ticks:
{
beginAtZero: true,
},
grid: { display: false }
},
yesterday: {
type: 'linear',
position: 'right',
ticks:
{
beginAtZero: true,
},
grid: { display: false }
},
}
}
});
return chart
}
async function loadChartData(tag, date) {
console.log(date)
let data = await fetchTagData(tag, date);
humChart = initHumChart(data);
pwrChart = initPwrChart(data);
}
async function populateOptions() {
let tagSelectElement = document.getElementById("tag-select")
tagSelectElement.active = false
let tags = await fetchTags();
tagSelectElement.innerHTML = '';
for (let tag of tags) {
let option = document.createElement("option");
option.value = tag;
option.text = tag;
tagSelectElement.appendChild(option);
}
tagSelectElement.active = true
let rangeSelectElement = document.getElementById("range-select")
document.getElementById("submit-select").addEventListener("click", function(event){
event.preventDefault();
console.log("submit clicked", rangeSelectElement.value, tagSelectElement.value);
let m = moment().subtract({hours: rangeSelectElement.value});
console.log(m);
var fromDate = m.toDate();
console.log(fromDate.toISOString());
if (humChart !== undefined) {
humChart.destroy();
}
if (pwrChart !== undefined) {
pwrChart.destroy();
}
loadChartData(tagSelectElement.value, fromDate.toISOString());
});
}
populateOptions();
let m = moment().subtract({hours: 3});
var fromDate = m.toDate();
loadChartData("Bad Unten", fromDate.toISOString());