Merge pull request #42 from metacube2/codex/fix-missing-api-key-in-index.php
Add optional OpenWeatherMap API key support for weather widget
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user