""" Weather.py ────────── Fetches hourly weather from Open-Meteo (free, no API key). Runs in a background thread and caches the last result. """ import threading import time import urllib.request import json from datetime import datetime _lock = threading.Lock() _cache = {} _cfg = {} WMO_CODES = { 0: ("Klar", "☀️"), 1: ("Überwiegend klar", "🌤️"), 2: ("Teilweise bewölkt", "⛅"), 3: ("Bedeckt", "☁️"), 45: ("Nebel", "🌫️"), 48: ("Reifnebel", "🌫️"), 51: ("Leichter Nieselregen","🌦️"), 53: ("Nieselregen", "🌦️"), 55: ("Dichter Nieselregen", "🌦️"), 61: ("Leichter Regen", "🌧️"), 63: ("Regen", "🌧️"), 65: ("Starker Regen", "🌧️"), 71: ("Leichter Schnee", "🌨️"), 73: ("Schnee", "🌨️"), 75: ("Starker Schnee", "❄️"), 80: ("Regenschauer", "🌦️"), 81: ("Regenschauer", "🌦️"), 82: ("Starke Schauer", "⛈️"), 95: ("Gewitter", "⛈️"), 96: ("Gewitter mit Hagel", "⛈️"), 99: ("Heftiges Gewitter", "⛈️"), } def init(location_cfg: dict) -> None: global _cfg _cfg = location_cfg def get_weather() -> dict: with _lock: return dict(_cache) def start_fetcher() -> threading.Thread: t = threading.Thread(target=_fetch_loop, daemon=True, name="weather-fetch") t.start() return t def _fetch_loop() -> None: interval = _cfg.get("weather_refresh_s", 600) while True: try: _do_fetch() except Exception as exc: print(f"[Weather] fetch error: {exc}") time.sleep(interval) def _do_fetch() -> None: lat = _cfg["latitude"] lon = _cfg["longitude"] url = ( f"https://api.open-meteo.com/v1/forecast" f"?latitude={lat}&longitude={lon}" f"&hourly=precipitation_probability" f"¤t=temperature_2m,relative_humidity_2m," f"apparent_temperature,weather_code,wind_speed_10m" f"&wind_speed_unit=kmh" f"&timezone=Europe%2FBerlin" f"&forecast_days=1" ) with urllib.request.urlopen(url, timeout=10) as resp: raw = json.loads(resp.read()) cur = raw.get("current", {}) hrly = raw.get("hourly", {}) # Pick precipitation probability for current hour now_h = datetime.now().hour prec_prob = 0 times = hrly.get("time", []) precs = hrly.get("precipitation_probability", []) for i, t in enumerate(times): if f"T{now_h:02d}:" in t and i < len(precs): prec_prob = precs[i] break code = cur.get("weather_code", 0) desc, icon = WMO_CODES.get(code, ("Unbekannt", "❓")) result = { "location": _cfg.get("name", ""), "temp": cur.get("temperature_2m"), "feels_like": cur.get("apparent_temperature"), "humidity": cur.get("relative_humidity_2m"), "wind_kmh": cur.get("wind_speed_10m"), "weather_code": code, "description": desc, "icon": icon, "rain_prob": prec_prob, "fetched_at": datetime.now().isoformat(timespec="seconds"), } with _lock: _cache.update(result) print(f"[Weather] {desc} {cur.get('temperature_2m')}°C, Regen {prec_prob}%")