"""Client Open-Meteo (gratuit, sans clé API).""" import json import os from datetime import datetime, timezone from pathlib import Path from typing import Any import httpx CACHE_PATH = Path(os.environ.get("UPLOAD_DIR", "/data")).parent / "meteo_cache.json" CACHE_TTL_SECONDS = 3 * 3600 # 3h WMO_LABELS = { 0: "Ensoleillé", 1: "Principalement ensoleillé", 2: "Partiellement nuageux", 3: "Couvert", 45: "Brouillard", 48: "Brouillard givrant", 51: "Bruine légère", 53: "Bruine modérée", 55: "Bruine dense", 61: "Pluie légère", 63: "Pluie modérée", 65: "Pluie forte", 71: "Neige légère", 73: "Neige modérée", 75: "Neige forte", 80: "Averses légères", 81: "Averses modérées", 82: "Averses violentes", 85: "Averses de neige", 95: "Orage", 96: "Orage avec grêle", 99: "Orage violent", } WMO_ICONS = { 0: "☀️", 1: "🌤", 2: "⛅", 3: "☁️", 45: "🌫", 48: "🌫", 51: "🌦", 53: "🌦", 55: "🌧", 61: "🌦", 63: "🌧", 65: "🌧", 71: "🌨", 73: "🌨", 75: "❄️", 80: "🌦", 81: "🌧", 82: "⛈", 85: "🌨", 95: "⛈", 96: "⛈", 99: "⛈", } def _cache_fresh() -> dict | None: if not CACHE_PATH.exists(): return None try: data = json.loads(CACHE_PATH.read_text()) cached_at = datetime.fromisoformat( data.get("cached_at", "2000-01-01T00:00:00+00:00") ) if (datetime.now(timezone.utc) - cached_at).total_seconds() < CACHE_TTL_SECONDS: return data except Exception: pass return None def fetch_forecast( lat: float = 45.14, lon: float = 4.12, days: int = 14 ) -> dict[str, Any]: cached = _cache_fresh() if cached: return cached url = "https://api.open-meteo.com/v1/forecast" params = { "latitude": lat, "longitude": lon, "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,weathercode", "timezone": "Europe/Paris", "forecast_days": min(days, 16), } try: r = httpx.get(url, params=params, timeout=10) r.raise_for_status() raw = r.json() except Exception as e: return {"error": str(e), "days": []} daily = raw.get("daily", {}) dates = daily.get("time", []) result_days = [] for i, d in enumerate(dates): code = int(daily.get("weathercode", [0] * len(dates))[i] or 0) result_days.append( { "date": d, "t_max": daily.get("temperature_2m_max", [None] * len(dates))[i], "t_min": daily.get("temperature_2m_min", [None] * len(dates))[i], "pluie_mm": daily.get("precipitation_sum", [0] * len(dates))[i] or 0, "vent_kmh": daily.get("windspeed_10m_max", [0] * len(dates))[i] or 0, "code": code, "label": WMO_LABELS.get(code, "Inconnu"), "icone": WMO_ICONS.get(code, "🌡"), } ) data = { "cached_at": datetime.now(timezone.utc).isoformat(), "days": result_days, } try: CACHE_PATH.parent.mkdir(parents=True, exist_ok=True) CACHE_PATH.write_text(json.dumps(data, ensure_ascii=False)) except Exception: pass return data