From 5ea523e6c48d6712abca10bef83d15605cae1ea2 Mon Sep 17 00:00:00 2001 From: Richard Zink Date: Wed, 7 Oct 2026 08:26:48 +0200 Subject: [PATCH] Add weather report from Open-Meteo for Passau --- index.html | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index c0abf9a..48c4284 100644 --- a/index.html +++ b/index.html @@ -71,6 +71,77 @@ font-size: 11px; margin-left: 4px; } + .weather-divider { + height: 1px; + background: rgba(230, 223, 209, 0.2); + margin: 8px 0; + } + .weather-current { + display: flex; + align-items: center; + gap: 12px; + padding: 4px 0 8px 0; + border-bottom: 1px solid rgba(230, 223, 209, 0.2); + margin-bottom: 8px; + } + .weather-current-icon { + width: 36px; + height: 36px; + flex-shrink: 0; + } + .weather-current-icon svg { width: 100%; height: 100%; } + .weather-current-temp { + font-size: 22px; + font-weight: bold; + } + .weather-current-detail { + font-size: 11px; + color: #b0a89a; + line-height: 1.4; + } + .weather-day { + margin-bottom: 6px; + } + .weather-day-header { + display: flex; + justify-content: space-between; + align-items: baseline; + padding: 2px 0; + font-size: 12px; + } + .weather-day-name { + color: #e6dfd1; + font-weight: bold; + } + .weather-day-minmax { + color: #999187; + font-size: 11px; + } + .weather-day-slots { + display: flex; + justify-content: space-between; + gap: 4px; + font-size: 11px; + } + .weather-slot { + display: flex; + flex-direction: column; + align-items: center; + flex: 1; + gap: 2px; + } + .weather-slot-icon { + width: 22px; + height: 22px; + } + .weather-slot-icon svg { width: 100%; height: 100%; } + .weather-slot-label { + color: #999187; + font-size: 9px; + } + .weather-slot-temp { + color: #e6dfd1; + } @@ -160,10 +231,13 @@
- +
Mond unter--:--
+
+
+
@@ -602,6 +676,177 @@ setTimeout(tick, 1000); } + // ============ Weather ============ + var WEATHER_LAT = 48.5667; + var WEATHER_LON = 13.4319; + var weatherData = null; + + var WMO_ICONS = { + 0: 'sun', + 1: 'sun-cloud', + 2: 'cloud-sun', + 3: 'cloud', + 45: 'fog', + 48: 'fog', + 51: 'drizzle', + 53: 'drizzle', + 55: 'drizzle', + 56: 'drizzle', + 57: 'drizzle', + 61: 'rain', + 63: 'rain', + 65: 'rain', + 66: 'rain', + 67: 'rain', + 71: 'snow', + 73: 'snow', + 75: 'snow', + 77: 'snow', + 80: 'rain', + 81: 'rain', + 82: 'rain', + 85: 'snow', + 86: 'snow', + 95: 'thunder', + 96: 'thunder', + 99: 'thunder' + }; + + function weatherSVG(code) { + var c = '#e6dfd1'; + switch (code) { + case 0: + return ''; + case 1: + case 2: + return ''; + case 3: + return ''; + case 45: + case 48: + return ''; + case 51: + case 55: + return ''; + case 53: + return ''; + case 61: + case 80: + return ''; + case 63: + case 81: + return ''; + case 65: + case 82: + return ''; + case 71: + case 73: + case 75: + case 77: + case 85: + case 86: + return ''; + case 95: + case 96: + case 99: + return ''; + default: + return ''; + } + } + + function fetchWeather() { + var url = 'https://api.open-meteo.com/v1/forecast?latitude=' + WEATHER_LAT + + '&longitude=' + WEATHER_LON + + '¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m' + + '&hourly=temperature_2m,weather_code' + + '&daily=weather_code,temperature_2m_max,temperature_2m_min' + + '&timezone=Europe/Berlin&forecast_days=5'; + + fetch(url) + .then(function(response) { return response.json(); }) + .then(function(data) { + weatherData = data; + updateWeatherDisplay(); + }) + .catch(function(err) { + console.log('Weather fetch failed:', err); + }); + } + + function findHourlyIndex(targetDate, hourlyTimeArray) { + var bestIdx = 0; + var bestDiff = Infinity; + for (var i = 0; i < hourlyTimeArray.length; i++) { + var t = new Date(hourlyTimeArray[i]); + var diff = Math.abs(t.getTime() - targetDate.getTime()); + if (diff < bestDiff) { + bestDiff = diff; + bestIdx = i; + } + } + return bestIdx; + } + + function updateWeatherDisplay() { + if (!weatherData) return; + + var now = new Date(); + var current = weatherData.current; + var hourly = weatherData.hourly; + var daily = weatherData.daily; + + // Current + var currentHtml = '
' + weatherSVG(current.weather_code) + '
' + + '
' + Math.round(current.temperature_2m) + '°C
' + + '
' + + 'Feuchte ' + Math.round(current.relative_humidity_2m) + '% · Wind ' + Math.round(current.wind_speed_10m) + ' km/h
'; + document.getElementById('weather-current').innerHTML = currentHtml; + + // Forecast + var forecastHtml = ''; + var dayNames = ['Heute', 'Morgen']; + for (var d = 0; d < 4; d++) { + var dayDate = new Date(daily.time[d]); + var dayLabel = d === 0 ? 'Heute' : dayNames[1] || dayDate.toLocaleDateString('de-DE', { weekday: 'short' }); + if (d > 1) { + dayLabel = dayDate.toLocaleDateString('de-DE', { weekday: 'short' }) + ' ' + + (dayDate.getDate() < 10 ? '0' : '') + dayDate.getDate() + '.' + + (dayDate.getMonth() + 1 < 10 ? '0' : '') + (dayDate.getMonth() + 1) + '.'; + } + + // Morning (8), Noon (14), Evening (20) + var slots = [8, 14, 20]; + var slotsHtml = ''; + for (var i = 0; i < slots.length; i++) { + var slotDate = new Date(dayDate); + slotDate.setHours(slots[i], 0, 0, 0); + var slotIdx = findHourlyIndex(slotDate, hourly.time); + var slotCode = hourly.weather_code[slotIdx]; + var slotTemp = hourly.temperature_2m[slotIdx]; + var slotLabels = ['M', 'Mi', 'A']; + slotsHtml += '
' + + '
' + weatherSVG(slotCode) + '
' + + '
' + slotLabels[i] + '
' + + '
' + Math.round(slotTemp) + '°
' + + '
'; + } + + forecastHtml += '
' + + '
' + + '' + dayLabel + '' + + '' + Math.round(daily.temperature_2m_min[d]) + '° / ' + Math.round(daily.temperature_2m_max[d]) + '°' + + '
' + + '
' + slotsHtml + '
' + + '
'; + } + document.getElementById('weather-forecast').innerHTML = forecastHtml; + } + + // Update weather every 15 minutes + fetchWeather(); + setInterval(fetchWeather, 15 * 60 * 1000); + animate(); tick();