release-2.0
This commit is contained in:
117
raspi/Weather.py
Normal file
117
raspi/Weather.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
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}%")
|
||||
Reference in New Issue
Block a user