diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..cada201 --- /dev/null +++ b/static/css/style.css @@ -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; + } +} diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..423bde1 --- /dev/null +++ b/static/js/app.js @@ -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); diff --git a/wetterstation.py b/wetterstation.py index 687c8c4..7804deb 100644 --- a/wetterstation.py +++ b/wetterstation.py @@ -241,333 +241,13 @@ def get_data(period): }) -def create_html_template(): - """HTML Template erstellen""" - import os - os.makedirs('templates', exist_ok=True) - - html_content = ''' - -
- - -