118 lines
3.4 KiB
Python
118 lines
3.4 KiB
Python
"""
|
|
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}%")
|