# -*- coding: utf-8 -*- """ OpenWeatherMap Wetter-Provider. Erfordert einen kostenlosen API-Key. https://openweathermap.org/api """ from datetime import date, datetime import httpx from plugins.weather.models import ForecastDay, WeatherData, WeatherResult from plugins.weather.providers.base import WeatherProvider from trixy_core.utils.debug import pdebug, perror class OpenWeatherMapProvider(WeatherProvider): """Wetter-Provider ueber die OpenWeatherMap API.""" BASE_URL = "https://api.openweathermap.org/data/2.5" def __init__(self, config: dict, http=None) -> None: super().__init__(name="openweathermap", config=config, http=http) self._api_key = config.get("api_key", "") async def get_weather( self, lat: float, lon: float, location_name: str = "" ) -> WeatherResult | None: """Ruft Wetterdaten von OpenWeatherMap ab.""" if not self._api_key: perror("OpenWeatherMap: Kein API-Key konfiguriert") return None base_params = { "lat": lat, "lon": lon, "appid": self._api_key, "lang": "de", "units": "metric", } try: async with httpx.AsyncClient(timeout=10.0) as client: # Aktuelles Wetter current_resp = await client.get( f"{self.BASE_URL}/weather", params=base_params ) current_resp.raise_for_status() current_data = current_resp.json() # Vorhersage (5 Tage / 3 Stunden) forecast_resp = await client.get( f"{self.BASE_URL}/forecast", params=base_params ) forecast_resp.raise_for_status() forecast_data = forecast_resp.json() return self._parse_response(current_data, forecast_data, location_name) except Exception as e: perror(f"OpenWeatherMap: Fehler beim Abruf: {e}") return None async def health_check(self) -> bool: """Prueft ob die OpenWeatherMap API erreichbar ist.""" if not self._api_key: return False try: async with httpx.AsyncClient(timeout=5.0) as client: response = await client.get( f"{self.BASE_URL}/weather", params={ "lat": 49.25, "lon": 8.88, "appid": self._api_key, "units": "metric", }, ) return response.status_code == 200 except Exception: return False def _parse_response( self, current_data: dict, forecast_data: dict, location_name: str, ) -> WeatherResult | None: """Parst die OpenWeatherMap API-Antworten.""" try: main = current_data.get("main", {}) wind = current_data.get("wind", {}) weather = current_data.get("weather", [{}])[0] clouds = current_data.get("clouds", {}) rain = current_data.get("rain", {}) # Windgeschwindigkeit: m/s -> km/h wind_speed_ms = wind.get("speed", 0.0) wind_speed_kmh = wind_speed_ms * 3.6 name = location_name or current_data.get("name", "Unbekannt") current_weather = WeatherData( location=name, temperature=main.get("temp", 0.0), feels_like=main.get("feels_like"), humidity=int(main.get("humidity", 0)), description=weather.get("description", "").capitalize(), wind_speed=round(wind_speed_kmh, 1), wind_direction=int(wind.get("deg", 0)), precipitation=rain.get("1h", 0.0), cloud_cover=clouds.get("all"), pressure=main.get("pressure"), provider_name=self.name, timestamp=datetime.now(), raw_data=current_data, ) # Vorhersage parsen forecast = self._parse_forecast(forecast_data) return WeatherResult( current=current_weather, forecast=forecast, fetched_at=datetime.now(), ) except Exception as e: perror(f"OpenWeatherMap: Fehler beim Parsen: {e}") return None def _parse_forecast(self, forecast_data: dict) -> list[ForecastDay]: """ Parst die 5-Tage/3-Stunden-Vorhersage zu Tageswerten. Aggregiert die 3-Stunden-Intervalle zu Min/Max pro Tag. """ daily: dict[date, dict] = {} for entry in forecast_data.get("list", []): dt = datetime.fromtimestamp(entry.get("dt", 0)) day = dt.date() main = entry.get("main", {}) wind = entry.get("wind", {}) weather = entry.get("weather", [{}])[0] rain = entry.get("rain", {}) temp = main.get("temp", 0.0) wind_speed = wind.get("speed", 0.0) * 3.6 precip = rain.get("3h", 0.0) if day not in daily: daily[day] = { "temp_min": temp, "temp_max": temp, "precipitation": 0.0, "wind_speed_max": wind_speed, "description": weather.get("description", "").capitalize(), } else: d = daily[day] d["temp_min"] = min(d["temp_min"], temp) d["temp_max"] = max(d["temp_max"], temp) d["precipitation"] += precip d["wind_speed_max"] = max(d["wind_speed_max"], wind_speed) forecast: list[ForecastDay] = [] for day_date in sorted(daily.keys()): d = daily[day_date] forecast.append(ForecastDay( date=day_date, temp_min=d["temp_min"], temp_max=d["temp_max"], description=d["description"], precipitation=round(d["precipitation"], 1), wind_speed_max=round(d["wind_speed_max"], 1), )) return forecast