96 lines
3.3 KiB
PHP
96 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class WeatherService
|
|
{
|
|
/**
|
|
* Open-Meteo Weather Code Mapping → [description, icon-key]
|
|
* Icons werden im Blade als inline SVG anhand des Keys gerendert.
|
|
*/
|
|
protected const CODES = [
|
|
0 => ['Klar', 'sun'],
|
|
1 => ['Überwiegend klar', 'sun'],
|
|
2 => ['Teilweise bewölkt', 'cloud-sun'],
|
|
3 => ['Bedeckt', 'cloud'],
|
|
45 => ['Nebel', 'fog'],
|
|
48 => ['Reifnebel', 'fog'],
|
|
51 => ['Leichter Nieselregen', 'drizzle'],
|
|
53 => ['Nieselregen', 'drizzle'],
|
|
55 => ['Starker Nieselregen', 'drizzle'],
|
|
61 => ['Leichter Regen', 'rain'],
|
|
63 => ['Regen', 'rain'],
|
|
65 => ['Starker Regen', 'rain'],
|
|
71 => ['Leichter Schnee', 'snow'],
|
|
73 => ['Schnee', 'snow'],
|
|
75 => ['Starker Schnee', 'snow'],
|
|
77 => ['Schneegriesel', 'snow'],
|
|
80 => ['Regenschauer', 'rain'],
|
|
81 => ['Regenschauer', 'rain'],
|
|
82 => ['Heftige Regenschauer', 'rain'],
|
|
85 => ['Schneeschauer', 'snow'],
|
|
86 => ['Heftige Schneeschauer', 'snow'],
|
|
95 => ['Gewitter', 'storm'],
|
|
96 => ['Gewitter mit Hagel', 'storm'],
|
|
99 => ['Schweres Gewitter', 'storm'],
|
|
];
|
|
|
|
/**
|
|
* @return array{temperature: float|null, city: string, icon: string, description: string, fetched_at: ?string}
|
|
*/
|
|
public function fetch(): array
|
|
{
|
|
$lat = (float) config('services.weather.lat', env('FOX_LAT', 48.2082));
|
|
$lon = (float) config('services.weather.lon', env('FOX_LON', 16.3738));
|
|
$city = (string) config('services.weather.city', env('FOX_CITY', 'Wien'));
|
|
|
|
$cacheKey = sprintf('fox.weather.%s.%s', $lat, $lon);
|
|
|
|
return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($lat, $lon, $city) {
|
|
try {
|
|
$response = Http::timeout(8)->get('https://api.open-meteo.com/v1/forecast', [
|
|
'latitude' => $lat,
|
|
'longitude' => $lon,
|
|
'current' => 'temperature_2m,weathercode',
|
|
'timezone' => 'auto',
|
|
]);
|
|
|
|
if (! $response->successful()) {
|
|
return $this->fallback($city);
|
|
}
|
|
|
|
$temp = $response->json('current.temperature_2m');
|
|
$code = (int) $response->json('current.weathercode', 0);
|
|
[$description, $icon] = self::CODES[$code] ?? ['Unbekannt', 'cloud'];
|
|
|
|
return [
|
|
'temperature' => $temp !== null ? round((float) $temp, 1) : null,
|
|
'city' => $city,
|
|
'icon' => $icon,
|
|
'description' => $description,
|
|
'fetched_at' => now()->toIso8601String(),
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::warning('WeatherService failed', ['error' => $e->getMessage()]);
|
|
|
|
return $this->fallback($city);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected function fallback(string $city): array
|
|
{
|
|
return [
|
|
'temperature' => null,
|
|
'city' => $city,
|
|
'icon' => 'cloud',
|
|
'description' => 'Keine Daten',
|
|
'fetched_at' => null,
|
|
];
|
|
}
|
|
}
|