Add weather widget with OpenWeatherMap integration
Implementiert vollständiges Wetter-Widget oberhalb des Webcam-Videos: **Features:** 🌤️ **Wetter-Anzeige:** - Temperatur (°C) - Windgeschwindigkeit & Richtung (km/h) - Luftdruck (hPa) - Luftfeuchtigkeit (%) - Wetterbeschreibung mit Emoji-Icons - Niederschlag (Regen/Schnee) wenn vorhanden ⚙️ **Technisch:** - OpenWeatherMap API Integration - 5 Minuten Cache (konfigurierbar) - Auto-Update alle X Minuten (Frontend) - WeatherManager Klasse für Backend - Schönes Gradient-Design mit Hover-Effekten - Responsive für Mobile 🎛️ **Admin-Settings:** - Wetter-Widget ein/aus - API Key Eingabefeld + Registrierungs-Link - Standort konfigurierbar (Stadt,Land) - GPS-Koordinaten (Lat/Lon) - Update-Intervall (5-60 Minuten) - Einheiten (Metrisch/Imperial) **Dateien:** - WeatherManager.php: Neue Klasse für API-Calls & Caching - SettingsManager.php: Weather Settings Defaults & Helper - index.php: Widget HTML, CSS, JavaScript Auto-Update - settings.json: Weather Defaults initialisiert **Koordinaten Oberdürnten:** Lat: 47.2833, Lon: 8.7167 **Setup für User:** 1. Gratis Account auf openweathermap.org erstellen 2. API Key im Admin-Panel einfügen 3. Fertig! Widget zeigt Live-Wetter an
This commit is contained in:
@@ -73,6 +73,16 @@ class SettingsManager {
|
|||||||
'meta_description' => '',
|
'meta_description' => '',
|
||||||
'meta_keywords' => ''
|
'meta_keywords' => ''
|
||||||
],
|
],
|
||||||
|
// Weather Widget
|
||||||
|
'weather' => [
|
||||||
|
'enabled' => true,
|
||||||
|
'api_key' => '',
|
||||||
|
'location' => 'Oberdürnten,CH',
|
||||||
|
'lat' => '47.2833',
|
||||||
|
'lon' => '8.7167',
|
||||||
|
'update_interval' => 5, // Minuten
|
||||||
|
'units' => 'metric' // metric (Celsius) oder imperial (Fahrenheit)
|
||||||
|
],
|
||||||
'last_updated' => null,
|
'last_updated' => null,
|
||||||
'updated_by' => null
|
'updated_by' => null
|
||||||
];
|
];
|
||||||
@@ -239,4 +249,32 @@ class SettingsManager {
|
|||||||
public function getMetaKeywords() {
|
public function getMetaKeywords() {
|
||||||
return $this->get('seo.meta_keywords') ?? '';
|
return $this->get('seo.meta_keywords') ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Weather Helper
|
||||||
|
public function isWeatherEnabled() {
|
||||||
|
return $this->get('weather.enabled') === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeatherApiKey() {
|
||||||
|
return $this->get('weather.api_key') ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeatherLocation() {
|
||||||
|
return $this->get('weather.location') ?? 'Oberdürnten,CH';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeatherCoords() {
|
||||||
|
return [
|
||||||
|
'lat' => $this->get('weather.lat') ?? '47.2833',
|
||||||
|
'lon' => $this->get('weather.lon') ?? '8.7167'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeatherUpdateInterval() {
|
||||||
|
return $this->get('weather.update_interval') ?? 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeatherUnits() {
|
||||||
|
return $this->get('weather.units') ?? 'metric';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WeatherManager - Holt und cached Wetterdaten von OpenWeatherMap
|
||||||
|
*/
|
||||||
|
class WeatherManager {
|
||||||
|
private $settingsManager;
|
||||||
|
private $cacheFile = 'weather_cache.json';
|
||||||
|
private $cacheTime = 300; // 5 Minuten in Sekunden
|
||||||
|
|
||||||
|
public function __construct($settingsManager) {
|
||||||
|
$this->settingsManager = $settingsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holt aktuelle Wetterdaten (cached)
|
||||||
|
*/
|
||||||
|
public function getCurrentWeather() {
|
||||||
|
// Prüfe ob Weather aktiviert ist
|
||||||
|
if (!$this->settingsManager->isWeatherEnabled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe API Key
|
||||||
|
$apiKey = $this->settingsManager->getWeatherApiKey();
|
||||||
|
if (empty($apiKey)) {
|
||||||
|
return ['error' => 'API Key fehlt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe Cache
|
||||||
|
$cached = $this->getCache();
|
||||||
|
if ($cached !== null) {
|
||||||
|
return $cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole frische Daten von API
|
||||||
|
$coords = $this->settingsManager->getWeatherCoords();
|
||||||
|
$units = $this->settingsManager->getWeatherUnits();
|
||||||
|
|
||||||
|
$url = "https://api.openweathermap.org/data/2.5/weather?lat={$coords['lat']}&lon={$coords['lon']}&units={$units}&appid={$apiKey}&lang=de";
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200 || !$response) {
|
||||||
|
return ['error' => 'API Fehler'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
if (!$data || !isset($data['main'])) {
|
||||||
|
return ['error' => 'Ungültige API Antwort'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatiere Daten
|
||||||
|
$weather = [
|
||||||
|
'temp' => round($data['main']['temp'], 1),
|
||||||
|
'feels_like' => round($data['main']['feels_like'], 1),
|
||||||
|
'humidity' => $data['main']['humidity'],
|
||||||
|
'pressure' => $data['main']['pressure'],
|
||||||
|
'wind_speed' => round($data['wind']['speed'] * 3.6, 1), // m/s -> km/h
|
||||||
|
'wind_deg' => $data['wind']['deg'] ?? 0,
|
||||||
|
'wind_direction' => $this->getWindDirection($data['wind']['deg'] ?? 0),
|
||||||
|
'clouds' => $data['clouds']['all'] ?? 0,
|
||||||
|
'description' => ucfirst($data['weather'][0]['description'] ?? 'Unbekannt'),
|
||||||
|
'icon' => $data['weather'][0]['icon'] ?? '01d',
|
||||||
|
'rain_1h' => $data['rain']['1h'] ?? 0,
|
||||||
|
'snow_1h' => $data['snow']['1h'] ?? 0,
|
||||||
|
'location' => $data['name'] ?? $this->settingsManager->getWeatherLocation(),
|
||||||
|
'timestamp' => time()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Cache speichern
|
||||||
|
$this->saveCache($weather);
|
||||||
|
|
||||||
|
return $weather;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wandelt Windrichtung (Grad) in Kompassrichtung um
|
||||||
|
*/
|
||||||
|
private function getWindDirection($deg) {
|
||||||
|
$directions = ['N', 'NNO', 'NO', 'ONO', 'O', 'OSO', 'SO', 'SSO', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];
|
||||||
|
$index = round($deg / 22.5) % 16;
|
||||||
|
return $directions[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holt Daten aus Cache (wenn noch gültig)
|
||||||
|
*/
|
||||||
|
private function getCache() {
|
||||||
|
if (!file_exists($this->cacheFile)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($this->cacheFile);
|
||||||
|
$data = json_decode($content, true);
|
||||||
|
|
||||||
|
if (!$data || !isset($data['timestamp'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update-Intervall aus Settings holen (in Minuten)
|
||||||
|
$updateInterval = $this->settingsManager->getWeatherUpdateInterval() * 60; // Minuten -> Sekunden
|
||||||
|
|
||||||
|
// Prüfe ob Cache noch gültig
|
||||||
|
if (time() - $data['timestamp'] < $updateInterval) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert Daten im Cache
|
||||||
|
*/
|
||||||
|
private function saveCache($data) {
|
||||||
|
$json = json_encode($data, JSON_PRETTY_PRINT);
|
||||||
|
file_put_contents($this->cacheFile, $json, LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt Wetter-Icon-Emoji zurück
|
||||||
|
*/
|
||||||
|
public function getWeatherEmoji($iconCode) {
|
||||||
|
$map = [
|
||||||
|
'01d' => '☀️', '01n' => '🌙',
|
||||||
|
'02d' => '⛅', '02n' => '☁️',
|
||||||
|
'03d' => '☁️', '03n' => '☁️',
|
||||||
|
'04d' => '☁️', '04n' => '☁️',
|
||||||
|
'09d' => '🌧️', '09n' => '🌧️',
|
||||||
|
'10d' => '🌦️', '10n' => '🌧️',
|
||||||
|
'11d' => '⛈️', '11n' => '⛈️',
|
||||||
|
'13d' => '❄️', '13n' => '❄️',
|
||||||
|
'50d' => '🌫️', '50n' => '🌫️'
|
||||||
|
];
|
||||||
|
return $map[$iconCode] ?? '🌤️';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX Handler für Wetter-Updates
|
||||||
|
*/
|
||||||
|
public function handleAjax() {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') return;
|
||||||
|
if (!isset($_GET['weather_action'])) return;
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if ($_GET['weather_action'] === 'get') {
|
||||||
|
$weather = $this->getCurrentWeather();
|
||||||
|
echo json_encode(['success' => true, 'data' => $weather]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+337
-2
@@ -4,12 +4,17 @@ use PHPMailer\PHPMailer\Exception;
|
|||||||
|
|
||||||
require __DIR__ . '/vendor/autoload.php';
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
require_once 'SettingsManager.php';
|
require_once 'SettingsManager.php';
|
||||||
|
require_once 'WeatherManager.php';
|
||||||
|
|
||||||
// SettingsManager initialisieren
|
// SettingsManager initialisieren
|
||||||
$settingsManager = new SettingsManager();
|
$settingsManager = new SettingsManager();
|
||||||
|
|
||||||
// AJAX-Handler für Settings (VOR anderen Ausgaben!)
|
// WeatherManager initialisieren
|
||||||
|
$weatherManager = new WeatherManager($settingsManager);
|
||||||
|
|
||||||
|
// AJAX-Handler für Settings und Weather (VOR anderen Ausgaben!)
|
||||||
$settingsManager->handleAjax();
|
$settingsManager->handleAjax();
|
||||||
|
$weatherManager->handleAjax();
|
||||||
|
|
||||||
if (isset($_GET['download_video'])) {
|
if (isset($_GET['download_video'])) {
|
||||||
$videoDir = './videos/';
|
$videoDir = './videos/';
|
||||||
@@ -1264,6 +1269,67 @@ class AdminManager {
|
|||||||
$output .= '</div>';
|
$output .= '</div>';
|
||||||
$output .= '</div>'; // settings-group
|
$output .= '</div>'; // settings-group
|
||||||
|
|
||||||
|
// Weather Settings
|
||||||
|
$output .= '<div class="settings-group">';
|
||||||
|
$output .= '<h4>🌤️ Wetter-Widget</h4>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Wetter-Widget anzeigen</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<label class="toggle-switch">';
|
||||||
|
$output .= '<input type="checkbox" id="setting-weather-enabled" ' . ($settingsManager->get('weather.enabled') ? 'checked' : '') . '>';
|
||||||
|
$output .= '<span class="toggle-slider"></span>';
|
||||||
|
$output .= '</label>';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">OpenWeatherMap API Key <a href="https://openweathermap.org/api" target="_blank" style="font-size:11px;">(kostenlos registrieren)</a></span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<input type="text" id="setting-weather-api-key" class="text-input" placeholder="Dein API Key hier einfügen..." value="' . htmlspecialchars($settingsManager->get('weather.api_key')) . '">';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Standort (Stadt,Land)</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<input type="text" id="setting-weather-location" class="text-input" placeholder="Oberdürnten,CH" value="' . htmlspecialchars($settingsManager->get('weather.location')) . '">';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Latitude (Breitengrad)</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<input type="text" id="setting-weather-lat" class="text-input" placeholder="47.2833" value="' . htmlspecialchars($settingsManager->get('weather.lat')) . '">';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Longitude (Längengrad)</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<input type="text" id="setting-weather-lon" class="text-input" placeholder="8.7167" value="' . htmlspecialchars($settingsManager->get('weather.lon')) . '">';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Update-Intervall (Minuten)</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<input type="number" id="setting-weather-interval" class="number-input" min="5" max="60" value="' . $settingsManager->get('weather.update_interval') . '">';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
$output .= '<div class="setting-row">';
|
||||||
|
$output .= '<span class="setting-label">Einheit</span>';
|
||||||
|
$output .= '<div class="setting-input">';
|
||||||
|
$output .= '<select id="setting-weather-units" class="select-input">';
|
||||||
|
$currentUnits = $settingsManager->get('weather.units');
|
||||||
|
$output .= '<option value="metric" ' . ($currentUnits === 'metric' ? 'selected' : '') . '>Metrisch (°C, km/h)</option>';
|
||||||
|
$output .= '<option value="imperial" ' . ($currentUnits === 'imperial' ? 'selected' : '') . '>Imperial (°F, mph)</option>';
|
||||||
|
$output .= '</select>';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>';
|
||||||
|
$output .= '</div>'; // settings-group
|
||||||
|
|
||||||
$output .= '</div>'; // admin-settings-panel
|
$output .= '</div>'; // admin-settings-panel
|
||||||
|
|
||||||
// Bestehender Admin-Content
|
// Bestehender Admin-Content
|
||||||
@@ -2065,6 +2131,90 @@ button[type="submit"]:hover { background-color: #45a049; }
|
|||||||
|
|
||||||
.delete-btn { background-color: #ff4136; color: white; border: none; padding: 5px 10px; cursor: pointer; font-size: 0.8em; margin-left: 10px; border-radius: 3px; }
|
.delete-btn { background-color: #ff4136; color: white; border: none; padding: 5px 10px; cursor: pointer; font-size: 0.8em; margin-left: 10px; border-radius: 3px; }
|
||||||
|
|
||||||
|
/* Weather Widget */
|
||||||
|
.weather-widget {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
||||||
|
animation: weatherFadeIn 0.5s ease;
|
||||||
|
}
|
||||||
|
.weather-widget.weather-error {
|
||||||
|
background: linear-gradient(135deg, #f44336 0%, #e91e63 100%);
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.weather-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 8px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
min-width: 120px;
|
||||||
|
transition: transform 0.3s ease, background 0.3s ease;
|
||||||
|
}
|
||||||
|
.weather-item:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
.weather-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.weather-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.weather-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
.weather-description {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
.weather-description .weather-value {
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@keyframes weatherFadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.weather-widget {
|
||||||
|
gap: 10px;
|
||||||
|
padding: 15px 10px;
|
||||||
|
}
|
||||||
|
.weather-item {
|
||||||
|
min-width: 90px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
.weather-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.weather-value {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.weather-label {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Guestbook */
|
/* Guestbook */
|
||||||
.guestbook-entry { background-color: #f9f9f9; border-left: 5px solid #4CAF50; margin-bottom: 20px; padding: 15px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
.guestbook-entry { background-color: #f9f9f9; border-left: 5px solid #4CAF50; margin-bottom: 20px; padding: 15px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
||||||
|
|
||||||
@@ -2498,6 +2648,53 @@ body.theme-neo footer {
|
|||||||
<!-- WEBCAM SECTION -->
|
<!-- WEBCAM SECTION -->
|
||||||
<section id="webcams" class="section">
|
<section id="webcams" class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<!-- WEATHER WIDGET -->
|
||||||
|
<?php if ($settingsManager->isWeatherEnabled()): ?>
|
||||||
|
<?php
|
||||||
|
$weather = $weatherManager->getCurrentWeather();
|
||||||
|
if ($weather && !isset($weather['error'])):
|
||||||
|
?>
|
||||||
|
<div id="weather-widget" class="weather-widget">
|
||||||
|
<div class="weather-item weather-temp">
|
||||||
|
<span class="weather-icon">🌡️</span>
|
||||||
|
<span class="weather-value"><?php echo $weather['temp']; ?>°C</span>
|
||||||
|
<span class="weather-label">Temperatur</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-wind">
|
||||||
|
<span class="weather-icon">💨</span>
|
||||||
|
<span class="weather-value"><?php echo $weather['wind_speed']; ?> km/h <?php echo $weather['wind_direction']; ?></span>
|
||||||
|
<span class="weather-label">Wind</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-pressure">
|
||||||
|
<span class="weather-icon">🔽</span>
|
||||||
|
<span class="weather-value"><?php echo $weather['pressure']; ?> hPa</span>
|
||||||
|
<span class="weather-label">Luftdruck</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-humidity">
|
||||||
|
<span class="weather-icon">💧</span>
|
||||||
|
<span class="weather-value"><?php echo $weather['humidity']; ?>%</span>
|
||||||
|
<span class="weather-label">Luftfeuchtigkeit</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-description">
|
||||||
|
<span class="weather-icon"><?php echo $weatherManager->getWeatherEmoji($weather['icon']); ?></span>
|
||||||
|
<span class="weather-value"><?php echo $weather['description']; ?></span>
|
||||||
|
<span class="weather-label"><?php echo $weather['location']; ?></span>
|
||||||
|
</div>
|
||||||
|
<?php if ($weather['rain_1h'] > 0 || $weather['snow_1h'] > 0): ?>
|
||||||
|
<div class="weather-item weather-precipitation">
|
||||||
|
<span class="weather-icon"><?php echo $weather['rain_1h'] > 0 ? '🌧️' : '❄️'; ?></span>
|
||||||
|
<span class="weather-value"><?php echo $weather['rain_1h'] > 0 ? $weather['rain_1h'] : $weather['snow_1h']; ?> mm</span>
|
||||||
|
<span class="weather-label">Niederschlag</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($weather && isset($weather['error'])): ?>
|
||||||
|
<div class="weather-widget weather-error">
|
||||||
|
<span>⚠️ Wetterdaten nicht verfügbar: <?php echo $weather['error']; ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- VIDEO PLAYER -->
|
<!-- VIDEO PLAYER -->
|
||||||
<div class="video-container" style="border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.15);">
|
<div class="video-container" style="border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.15);">
|
||||||
<?php echo $webcamManager->displayWebcam(); ?>
|
<?php echo $webcamManager->displayWebcam(); ?>
|
||||||
@@ -3262,6 +3459,25 @@ const AdminSettings = {
|
|||||||
case 'seo.meta_keywords':
|
case 'seo.meta_keywords':
|
||||||
this.showNotification('SEO Meta gespeichert (wirksam bei Reload)', 'success');
|
this.showNotification('SEO Meta gespeichert (wirksam bei Reload)', 'success');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Weather Settings
|
||||||
|
case 'weather.enabled':
|
||||||
|
const weatherWidget = document.getElementById('weather-widget');
|
||||||
|
if (weatherWidget) {
|
||||||
|
weatherWidget.style.display = boolValue ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
this.showNotification('Wetter-Widget ' + (boolValue ? 'aktiviert' : 'deaktiviert'), 'success');
|
||||||
|
break;
|
||||||
|
case 'weather.api_key':
|
||||||
|
case 'weather.location':
|
||||||
|
case 'weather.lat':
|
||||||
|
case 'weather.lon':
|
||||||
|
case 'weather.units':
|
||||||
|
this.showNotification('Wetter-Einstellung gespeichert (Reload empfohlen)', 'success');
|
||||||
|
break;
|
||||||
|
case 'weather.update_interval':
|
||||||
|
this.showNotification('Update-Intervall: ' + value + ' Minuten (Reload empfohlen)', 'success');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -3359,6 +3575,35 @@ const AdminSettings = {
|
|||||||
document.getElementById('setting-meta-keywords')?.addEventListener('change', (e) => {
|
document.getElementById('setting-meta-keywords')?.addEventListener('change', (e) => {
|
||||||
this.updateSetting('seo.meta_keywords', e.target.value);
|
this.updateSetting('seo.meta_keywords', e.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Weather Settings
|
||||||
|
document.getElementById('setting-weather-enabled')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.enabled', e.target.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-api-key')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.api_key', e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-location')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.location', e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-lat')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.lat', e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-lon')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.lon', e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-interval')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.update_interval', parseInt(e.target.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('setting-weather-units')?.addEventListener('change', (e) => {
|
||||||
|
this.updateSetting('weather.units', e.target.value);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
showNotification: function(message, type) {
|
showNotification: function(message, type) {
|
||||||
@@ -3597,7 +3842,97 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- WEATHER AUTO-UPDATE -->
|
||||||
|
<script>
|
||||||
|
const WeatherUpdater = {
|
||||||
|
updateInterval: <?php echo $settingsManager->getWeatherUpdateInterval() * 60 * 1000; ?>, // Minuten -> Millisekunden
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
if (!document.getElementById('weather-widget')) return;
|
||||||
|
|
||||||
|
// Update alle X Minuten
|
||||||
|
setInterval(() => this.updateWeather(), this.updateInterval);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWeather: function() {
|
||||||
|
fetch(window.location.href + '?weather_action=get')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.data && !data.data.error) {
|
||||||
|
this.renderWeather(data.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => console.error('Weather update error:', err));
|
||||||
|
},
|
||||||
|
|
||||||
|
renderWeather: function(weather) {
|
||||||
|
const widget = document.getElementById('weather-widget');
|
||||||
|
if (!widget) return;
|
||||||
|
|
||||||
|
const rainSnow = weather.rain_1h > 0 || weather.snow_1h > 0;
|
||||||
|
const precipIcon = weather.rain_1h > 0 ? '🌧️' : '❄️';
|
||||||
|
const precipValue = weather.rain_1h > 0 ? weather.rain_1h : weather.snow_1h;
|
||||||
|
|
||||||
|
widget.innerHTML = `
|
||||||
|
<div class="weather-item weather-temp">
|
||||||
|
<span class="weather-icon">🌡️</span>
|
||||||
|
<span class="weather-value">${weather.temp}°C</span>
|
||||||
|
<span class="weather-label">Temperatur</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-wind">
|
||||||
|
<span class="weather-icon">💨</span>
|
||||||
|
<span class="weather-value">${weather.wind_speed} km/h ${weather.wind_direction}</span>
|
||||||
|
<span class="weather-label">Wind</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-pressure">
|
||||||
|
<span class="weather-icon">🔽</span>
|
||||||
|
<span class="weather-value">${weather.pressure} hPa</span>
|
||||||
|
<span class="weather-label">Luftdruck</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-humidity">
|
||||||
|
<span class="weather-icon">💧</span>
|
||||||
|
<span class="weather-value">${weather.humidity}%</span>
|
||||||
|
<span class="weather-label">Luftfeuchtigkeit</span>
|
||||||
|
</div>
|
||||||
|
<div class="weather-item weather-description">
|
||||||
|
<span class="weather-icon">${this.getWeatherEmoji(weather.icon)}</span>
|
||||||
|
<span class="weather-value">${weather.description}</span>
|
||||||
|
<span class="weather-label">${weather.location}</span>
|
||||||
|
</div>
|
||||||
|
${rainSnow ? `
|
||||||
|
<div class="weather-item weather-precipitation">
|
||||||
|
<span class="weather-icon">${precipIcon}</span>
|
||||||
|
<span class="weather-value">${precipValue} mm</span>
|
||||||
|
<span class="weather-label">Niederschlag</span>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Fade-in Animation
|
||||||
|
widget.style.animation = 'weatherFadeIn 0.5s ease';
|
||||||
|
},
|
||||||
|
|
||||||
|
getWeatherEmoji: function(iconCode) {
|
||||||
|
const map = {
|
||||||
|
'01d': '☀️', '01n': '🌙',
|
||||||
|
'02d': '⛅', '02n': '☁️',
|
||||||
|
'03d': '☁️', '03n': '☁️',
|
||||||
|
'04d': '☁️', '04n': '☁️',
|
||||||
|
'09d': '🌧️', '09n': '🌧️',
|
||||||
|
'10d': '🌦️', '10n': '🌧️',
|
||||||
|
'11d': '⛈️', '11n': '⛈️',
|
||||||
|
'13d': '❄️', '13n': '❄️',
|
||||||
|
'50d': '🌫️', '50n': '🌫️'
|
||||||
|
};
|
||||||
|
return map[iconCode] || '🌤️';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
WeatherUpdater.init();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -46,6 +46,15 @@
|
|||||||
"meta_description": "",
|
"meta_description": "",
|
||||||
"meta_keywords": ""
|
"meta_keywords": ""
|
||||||
},
|
},
|
||||||
|
"weather": {
|
||||||
|
"enabled": true,
|
||||||
|
"api_key": "",
|
||||||
|
"location": "Oberdürnten,CH",
|
||||||
|
"lat": "47.2833",
|
||||||
|
"lon": "8.7167",
|
||||||
|
"update_interval": 5,
|
||||||
|
"units": "metric"
|
||||||
|
},
|
||||||
"last_updated": null,
|
"last_updated": null,
|
||||||
"updated_by": null
|
"updated_by": null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user