Add optional weather API key support

This commit is contained in:
2026-01-22 23:00:41 +01:00
parent 144c813acf
commit 20c0569731
2 changed files with 90 additions and 18 deletions
+78 -17
View File
@@ -27,9 +27,24 @@ class WeatherManager {
return $cached; return $cached;
} }
// Hole frische Daten von API (Open-Meteo)
$coords = $this->settingsManager->getWeatherCoords(); $coords = $this->settingsManager->getWeatherCoords();
$apiKey = trim($this->settingsManager->getWeatherApiKey());
$weather = $apiKey !== ''
? $this->fetchOpenWeather($coords, $apiKey)
: $this->fetchOpenMeteo($coords);
if (isset($weather['error'])) {
return $weather;
}
// Cache speichern
$this->saveCache($weather);
return $weather;
}
private function fetchOpenMeteo($coords) {
// Open-Meteo API URL - komplett kostenlos, kein API Key! // Open-Meteo API URL - komplett kostenlos, kein API Key!
$url = "https://api.open-meteo.com/v1/forecast?" . http_build_query([ $url = "https://api.open-meteo.com/v1/forecast?" . http_build_query([
'latitude' => $coords['lat'], 'latitude' => $coords['lat'],
@@ -38,17 +53,8 @@ class WeatherManager {
'timezone' => 'Europe/Zurich' 'timezone' => 'Europe/Zurich'
]); ]);
$ch = curl_init(); $response = $this->fetchUrl($url);
curl_setopt($ch, CURLOPT_URL, $url); if ($response === null) {
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']; return ['error' => 'API Fehler'];
} }
@@ -59,8 +65,7 @@ class WeatherManager {
$current = $data['current']; $current = $data['current'];
// Formatiere Daten return [
$weather = [
'temp' => round($current['temperature_2m'], 1), 'temp' => round($current['temperature_2m'], 1),
'feels_like' => round($current['temperature_2m'], 1), // Open-Meteo hat keine "feels like" 'feels_like' => round($current['temperature_2m'], 1), // Open-Meteo hat keine "feels like"
'humidity' => $current['relative_humidity_2m'], 'humidity' => $current['relative_humidity_2m'],
@@ -76,11 +81,67 @@ class WeatherManager {
'location' => $this->settingsManager->getWeatherLocation(), 'location' => $this->settingsManager->getWeatherLocation(),
'timestamp' => time() 'timestamp' => time()
]; ];
}
// Cache speichern private function fetchOpenWeather($coords, $apiKey) {
$this->saveCache($weather); $units = $this->settingsManager->getWeatherUnits();
$url = "https://api.openweathermap.org/data/2.5/weather?" . http_build_query([
'lat' => $coords['lat'],
'lon' => $coords['lon'],
'appid' => $apiKey,
'units' => $units,
'lang' => 'de'
]);
return $weather; $response = $this->fetchUrl($url);
if ($response === null) {
return ['error' => 'API Fehler'];
}
$data = json_decode($response, true);
if (!$data || !isset($data['main'], $data['weather'][0], $data['wind'])) {
return ['error' => 'Ungültige API Antwort'];
}
$windSpeed = $data['wind']['speed'];
if ($units === 'metric') {
$windSpeed = $windSpeed * 3.6; // m/s -> km/h
}
return [
'temp' => round($data['main']['temp'], 1),
'feels_like' => round($data['main']['feels_like'], 1),
'humidity' => $data['main']['humidity'],
'pressure' => round($data['main']['pressure'], 0),
'wind_speed' => round($windSpeed, 1),
'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']),
'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()
];
}
private function fetchUrl($url) {
$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 null;
}
return $response;
} }
/** /**
+12 -1
View File
@@ -1271,7 +1271,7 @@ class AdminManager {
// Weather Settings // Weather Settings
$output .= '<div class="settings-group">'; $output .= '<div class="settings-group">';
$output .= '<h4>🌤️ Wetter-Widget <span style="font-size:12px; color:#4CAF50;">(Open-Meteo - 100% kostenlos!)</span></h4>'; $output .= '<h4>🌤️ Wetter-Widget <span style="font-size:12px; color:#4CAF50;">(Open-Meteo kostenlos, OpenWeatherMap optional)</span></h4>';
$output .= '<div class="setting-row">'; $output .= '<div class="setting-row">';
$output .= '<span class="setting-label">Wetter-Widget anzeigen</span>'; $output .= '<span class="setting-label">Wetter-Widget anzeigen</span>';
@@ -1283,6 +1283,13 @@ class AdminManager {
$output .= '</div>'; $output .= '</div>';
$output .= '</div>'; $output .= '</div>';
$output .= '<div class="setting-row">';
$output .= '<span class="setting-label">API Key (OpenWeatherMap, optional)</span>';
$output .= '<div class="setting-input">';
$output .= '<input type="text" id="setting-weather-api-key" class="text-input" placeholder="OWM API Key" value="' . htmlspecialchars($settingsManager->get('weather.api_key')) . '">';
$output .= '</div>';
$output .= '</div>';
$output .= '<div class="setting-row">'; $output .= '<div class="setting-row">';
$output .= '<span class="setting-label">Standort (Stadt,Land)</span>'; $output .= '<span class="setting-label">Standort (Stadt,Land)</span>';
$output .= '<div class="setting-input">'; $output .= '<div class="setting-input">';
@@ -3578,6 +3585,10 @@ const AdminSettings = {
this.updateSetting('weather.enabled', e.target.checked); 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) => { document.getElementById('setting-weather-location')?.addEventListener('change', (e) => {
this.updateSetting('weather.location', e.target.value); this.updateSetting('weather.location', e.target.value);
}); });