Compare commits
2 Commits
d6b718a8ef
...
5f516f5dd4
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f516f5dd4 | |||
| 2907f5de18 |
89
static/css/style.css
Normal file
89
static/css/style.css
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 15px 40px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover {
|
||||||
|
background: #e0e0e0;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charts-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
background: #f9f9f9;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
/* feste Mindesthöhe sorgt für konsistente Layouts */
|
||||||
|
min-height: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stellt sicher, dass Charts nicht breiter als ihre Container werden */
|
||||||
|
#charts { width: 100%; }
|
||||||
|
.chart-container > div { width: 100%; max-width: 100%; }
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.charts-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
210
static/js/app.js
Normal file
210
static/js/app.js
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
let currentPeriod = 'day';
|
||||||
|
const DEFAULT_CHART_HEIGHT = 360;
|
||||||
|
const POLAR_CHART_HEIGHT = 420;
|
||||||
|
|
||||||
|
// HighCharts globale Einstellungen
|
||||||
|
Highcharts.setOptions({
|
||||||
|
lang: {
|
||||||
|
months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
||||||
|
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||||
|
shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun',
|
||||||
|
'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||||
|
weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
|
||||||
|
shortWeekdays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function switchPeriod(period) {
|
||||||
|
currentPeriod = period;
|
||||||
|
|
||||||
|
// Tab-Status aktualisieren
|
||||||
|
document.querySelectorAll('.tab').forEach(tab => {
|
||||||
|
tab.classList.remove('active');
|
||||||
|
});
|
||||||
|
event.target.classList.add('active');
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadData() {
|
||||||
|
document.getElementById('loading').style.display = 'block';
|
||||||
|
document.getElementById('charts').style.display = 'none';
|
||||||
|
|
||||||
|
fetch(`/api/data/${currentPeriod}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
renderCharts(data);
|
||||||
|
document.getElementById('loading').style.display = 'none';
|
||||||
|
document.getElementById('charts').style.display = 'grid';
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Laden:', error);
|
||||||
|
document.getElementById('loading').innerHTML = 'Fehler beim Laden der Daten';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCharts(apiData) {
|
||||||
|
const data = apiData.data;
|
||||||
|
const rainData = apiData.rain_hourly;
|
||||||
|
|
||||||
|
// Konvertiere Timestamps in Millisekunden
|
||||||
|
const timestamps = data.map(d => {
|
||||||
|
const [date, time] = d.datetime.split(' ');
|
||||||
|
return new Date(date + 'T' + time).getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
const rainTimestamps = rainData.map(d => {
|
||||||
|
const [date, time] = d.hour.split(' ');
|
||||||
|
return new Date(date + 'T' + time).getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Berechne Zeitbereich für die Achsen
|
||||||
|
const minTime = Math.min(...timestamps, ...rainTimestamps);
|
||||||
|
const maxTime = Math.max(...timestamps, ...rainTimestamps);
|
||||||
|
|
||||||
|
// 1-Stunden-Intervalle für die Achsen-Labels (3600000 ms = 1 Stunde)
|
||||||
|
const ONE_HOUR = 3600000;
|
||||||
|
const FOUR_HOURS = ONE_HOUR * 4;
|
||||||
|
|
||||||
|
// Temperatur
|
||||||
|
Highcharts.chart('temp-chart', {
|
||||||
|
chart: { type: 'line', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '🌡️ Temperatur (°C)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: { title: { text: '°C' } },
|
||||||
|
legend: { enabled: true },
|
||||||
|
series: [{
|
||||||
|
name: 'Temperatur',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.temperature]),
|
||||||
|
color: '#ff6b6b',
|
||||||
|
lineWidth: 2
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Luftfeuchtigkeit
|
||||||
|
Highcharts.chart('humidity-chart', {
|
||||||
|
chart: { type: 'line', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '💧 Luftfeuchtigkeit (%)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: { title: { text: '%' } },
|
||||||
|
legend: { enabled: true },
|
||||||
|
series: [{
|
||||||
|
name: 'Luftfeuchtigkeit',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.humidity]),
|
||||||
|
color: '#4ecdc4',
|
||||||
|
lineWidth: 2
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Luftdruck
|
||||||
|
Highcharts.chart('pressure-chart', {
|
||||||
|
chart: { type: 'line', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '🎈 Luftdruck (hPa)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: { title: { text: 'hPa' } },
|
||||||
|
legend: { enabled: true },
|
||||||
|
series: [{
|
||||||
|
name: 'Luftdruck',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.pressure]),
|
||||||
|
color: '#95e1d3',
|
||||||
|
lineWidth: 2
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regenmenge pro Stunde
|
||||||
|
Highcharts.chart('rain-chart', {
|
||||||
|
chart: { type: 'column', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '🌧️ Regenmenge pro Stunde (mm)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: { title: { text: 'mm' } },
|
||||||
|
legend: { enabled: false },
|
||||||
|
series: [{
|
||||||
|
name: 'Regen',
|
||||||
|
data: rainData.map((d, i) => [rainTimestamps[i], d.rain]),
|
||||||
|
color: '#3498db'
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Windgeschwindigkeit
|
||||||
|
Highcharts.chart('wind-speed-chart', {
|
||||||
|
chart: { type: 'line', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '💨 Windgeschwindigkeit (m/s)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: { title: { text: 'm/s' } },
|
||||||
|
legend: { enabled: true },
|
||||||
|
series: [{
|
||||||
|
name: 'Windgeschwindigkeit',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.wind_speed]),
|
||||||
|
color: '#f38181',
|
||||||
|
lineWidth: 2
|
||||||
|
}, {
|
||||||
|
name: 'Böen',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.wind_gust]),
|
||||||
|
color: '#aa96da',
|
||||||
|
lineWidth: 2,
|
||||||
|
dashStyle: 'dash'
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Windrichtung
|
||||||
|
Highcharts.chart('wind-dir-chart', {
|
||||||
|
chart: { type: 'line', height: DEFAULT_CHART_HEIGHT, spacingRight: 20 },
|
||||||
|
title: { text: '🧭 Windrichtung (°)' },
|
||||||
|
xAxis: {
|
||||||
|
type: 'datetime',
|
||||||
|
title: { text: 'Zeit' },
|
||||||
|
labels: { format: '{value:%H}' },
|
||||||
|
tickInterval: FOUR_HOURS
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
title: { text: 'Richtung (°)' },
|
||||||
|
min: 0,
|
||||||
|
max: 360,
|
||||||
|
tickPositions: [0, 90, 180, 270, 360]
|
||||||
|
},
|
||||||
|
legend: { enabled: true },
|
||||||
|
series: [{
|
||||||
|
name: 'Windrichtung',
|
||||||
|
data: data.map((d, i) => [timestamps[i], d.wind_dir || 0]),
|
||||||
|
color: '#f39c12',
|
||||||
|
lineWidth: 2
|
||||||
|
}],
|
||||||
|
credits: { enabled: false }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiales Laden
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
// Auto-Refresh alle 5 Minuten
|
||||||
|
setInterval(loadData, 5 * 60 * 1000);
|
||||||
309
wetterstation.py
309
wetterstation.py
@@ -241,322 +241,13 @@ def get_data(period):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def create_html_template():
|
|
||||||
"""HTML Template erstellen"""
|
|
||||||
import os
|
|
||||||
os.makedirs('templates', exist_ok=True)
|
|
||||||
|
|
||||||
html_content = '''<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Wetterstation</title>
|
|
||||||
<script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1400px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: white;
|
|
||||||
border-radius: 20px;
|
|
||||||
padding: 30px;
|
|
||||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
font-size: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
|
||||||
padding: 15px 40px;
|
|
||||||
background: #f0f0f0;
|
|
||||||
border: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 18px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab:hover {
|
|
||||||
background: #e0e0e0;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab.active {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.charts-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-container {
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 15px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
||||||
/* feste Mindesthöhe sorgt für konsistente Layouts */
|
|
||||||
min-height: 420px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stellt sicher, dass Charts nicht breiter als ihre Container werden */
|
|
||||||
#charts { width: 100%; }
|
|
||||||
.chart-container > div { width: 100%; max-width: 100%; }
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
text-align: center;
|
|
||||||
padding: 50px;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.charts-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>🌤️ Wetterstation Dashboard</h1>
|
|
||||||
|
|
||||||
<div class="tabs">
|
|
||||||
<button class="tab active" onclick="switchPeriod('day')">Tag (24h)</button>
|
|
||||||
<button class="tab" onclick="switchPeriod('week')">Woche (7 Tage)</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" id="loading">Lade Daten...</div>
|
|
||||||
|
|
||||||
<div class="charts-grid" id="charts" style="display: none;">
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="temp-chart"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="humidity-chart"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="pressure-chart"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="rain-chart"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="wind-speed-chart"></div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container">
|
|
||||||
<div id="wind-dir-chart"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let currentPeriod = 'day';
|
|
||||||
const DEFAULT_CHART_HEIGHT = 360;
|
|
||||||
const POLAR_CHART_HEIGHT = 420;
|
|
||||||
const plotConfig = { responsive: true, displayModeBar: false };
|
|
||||||
|
|
||||||
function switchPeriod(period) {
|
|
||||||
currentPeriod = period;
|
|
||||||
|
|
||||||
// Tab-Status aktualisieren
|
|
||||||
document.querySelectorAll('.tab').forEach(tab => {
|
|
||||||
tab.classList.remove('active');
|
|
||||||
});
|
|
||||||
event.target.classList.add('active');
|
|
||||||
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadData() {
|
|
||||||
document.getElementById('loading').style.display = 'block';
|
|
||||||
document.getElementById('charts').style.display = 'none';
|
|
||||||
|
|
||||||
fetch(`/api/data/${currentPeriod}`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
renderCharts(data);
|
|
||||||
document.getElementById('loading').style.display = 'none';
|
|
||||||
document.getElementById('charts').style.display = 'grid';
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Fehler beim Laden:', error);
|
|
||||||
document.getElementById('loading').innerHTML = 'Fehler beim Laden der Daten';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderCharts(apiData) {
|
|
||||||
const data = apiData.data;
|
|
||||||
const rainData = apiData.rain_hourly;
|
|
||||||
|
|
||||||
const timestamps = data.map(d => d.datetime);
|
|
||||||
|
|
||||||
// Temperatur
|
|
||||||
Plotly.newPlot('temp-chart', [{
|
|
||||||
x: timestamps,
|
|
||||||
y: data.map(d => d.temperature),
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines',
|
|
||||||
name: 'Temperatur',
|
|
||||||
line: {color: '#ff6b6b', width: 3}
|
|
||||||
}], {
|
|
||||||
title: '🌡️ Temperatur (°C)',
|
|
||||||
xaxis: {title: 'Zeit'},
|
|
||||||
yaxis: {title: '°C'},
|
|
||||||
margin: {t: 50, b: 60, l: 60, r: 30, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: DEFAULT_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
|
|
||||||
// Luftfeuchtigkeit
|
|
||||||
Plotly.newPlot('humidity-chart', [{
|
|
||||||
x: timestamps,
|
|
||||||
y: data.map(d => d.humidity),
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines',
|
|
||||||
name: 'Luftfeuchtigkeit',
|
|
||||||
line: {color: '#4ecdc4', width: 3}
|
|
||||||
}], {
|
|
||||||
title: '💧 Luftfeuchtigkeit (%)',
|
|
||||||
xaxis: {title: 'Zeit'},
|
|
||||||
yaxis: {title: '%'},
|
|
||||||
margin: {t: 50, b: 60, l: 60, r: 30, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: DEFAULT_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
|
|
||||||
// Luftdruck
|
|
||||||
Plotly.newPlot('pressure-chart', [{
|
|
||||||
x: timestamps,
|
|
||||||
y: data.map(d => d.pressure),
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines',
|
|
||||||
name: 'Luftdruck',
|
|
||||||
line: {color: '#95e1d3', width: 3}
|
|
||||||
}], {
|
|
||||||
title: '🎈 Luftdruck (hPa)',
|
|
||||||
xaxis: {title: 'Zeit'},
|
|
||||||
yaxis: {title: 'hPa'},
|
|
||||||
margin: {t: 50, b: 60, l: 60, r: 30, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: DEFAULT_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
|
|
||||||
// Regenmenge pro Stunde
|
|
||||||
Plotly.newPlot('rain-chart', [{
|
|
||||||
x: rainData.map(d => d.hour),
|
|
||||||
y: rainData.map(d => d.rain),
|
|
||||||
type: 'bar',
|
|
||||||
name: 'Regen',
|
|
||||||
marker: {color: '#3498db'}
|
|
||||||
}], {
|
|
||||||
title: '🌧️ Regenmenge pro Stunde (mm)',
|
|
||||||
xaxis: {title: 'Zeit'},
|
|
||||||
yaxis: {title: 'mm'},
|
|
||||||
margin: {t: 50, b: 60, l: 60, r: 30, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: DEFAULT_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
|
|
||||||
// Windgeschwindigkeit
|
|
||||||
Plotly.newPlot('wind-speed-chart', [{
|
|
||||||
x: timestamps,
|
|
||||||
y: data.map(d => d.wind_speed),
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines',
|
|
||||||
name: 'Windgeschwindigkeit',
|
|
||||||
line: {color: '#f38181', width: 3}
|
|
||||||
}, {
|
|
||||||
x: timestamps,
|
|
||||||
y: data.map(d => d.wind_gust),
|
|
||||||
type: 'scatter',
|
|
||||||
mode: 'lines',
|
|
||||||
name: 'Böen',
|
|
||||||
line: {color: '#aa96da', width: 2, dash: 'dash'}
|
|
||||||
}], {
|
|
||||||
title: '💨 Windgeschwindigkeit (m/s)',
|
|
||||||
xaxis: {title: 'Zeit'},
|
|
||||||
yaxis: {title: 'm/s'},
|
|
||||||
margin: {t: 50, b: 60, l: 60, r: 30, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: DEFAULT_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
|
|
||||||
// Windrichtung (Polarplot)
|
|
||||||
Plotly.newPlot('wind-dir-chart', [{
|
|
||||||
r: data.map(d => d.wind_speed),
|
|
||||||
theta: data.map(d => d.wind_dir),
|
|
||||||
mode: 'markers',
|
|
||||||
type: 'scatterpolar',
|
|
||||||
marker: {
|
|
||||||
size: 8,
|
|
||||||
color: data.map(d => d.wind_speed),
|
|
||||||
colorscale: 'Viridis',
|
|
||||||
showscale: true,
|
|
||||||
colorbar: {title: 'm/s', orientation: 'h', y: -0.25}
|
|
||||||
}
|
|
||||||
}], {
|
|
||||||
title: '🧭 Windrichtung',
|
|
||||||
polar: {
|
|
||||||
radialaxis: {title: 'Geschwindigkeit (m/s)'},
|
|
||||||
angularaxis: {direction: 'clockwise'}
|
|
||||||
},
|
|
||||||
margin: {t: 50, b: 80, l: 60, r: 60, pad: 0},
|
|
||||||
legend: {orientation: 'h', y: -0.2},
|
|
||||||
height: POLAR_CHART_HEIGHT
|
|
||||||
}, plotConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initiales Laden
|
|
||||||
loadData();
|
|
||||||
|
|
||||||
// Auto-Refresh alle 5 Minuten
|
|
||||||
setInterval(loadData, 5 * 60 * 1000);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>'''
|
|
||||||
|
|
||||||
with open('templates/index.html', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(html_content)
|
|
||||||
|
|
||||||
print("HTML Template erstellt: templates/index.html")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Hauptprogramm"""
|
"""Hauptprogramm"""
|
||||||
print("Wetterstation wird gestartet...")
|
print("Wetterstation wird gestartet...")
|
||||||
|
|
||||||
# HTML Template erstellen
|
|
||||||
create_html_template()
|
|
||||||
|
|
||||||
# MQTT Client starten
|
# MQTT Client starten
|
||||||
mqtt_client = MQTTClient()
|
mqtt_client = MQTTClient()
|
||||||
mqtt_client.start()
|
mqtt_client.start()
|
||||||
|
|||||||
Reference in New Issue
Block a user