"""Keyless current-weather lookup via Open-Meteo (no API key required). FR24's API does not expose airport weather, so we fetch real conditions from Open-Meteo for the airport's coordinates. Cached briefly to stay polite. """ from __future__ import annotations import time import requests _CACHE: dict[tuple, tuple[float, dict]] = {} _TTL = 600 # seconds _ARROWS = ["↑", "↗", "→", "↘", "↓", "↙", "←", "↖"] def wind_arrow(deg) -> str: if deg is None: return "·" # Arrow points in the direction the wind blows TOWARD (deg is "from"). idx = int(((float(deg) + 180) % 360) / 45 + 0.5) % 8 return _ARROWS[idx] def current(lat: float, lon: float) -> dict | None: """Return {temp_c, wind_kts, wind_dir, code} or None.""" if lat is None or lon is None: return None key = (round(lat, 2), round(lon, 2)) now = time.time() hit = _CACHE.get(key) if hit and now - hit[0] < _TTL: return hit[1] try: resp = requests.get( "https://api.open-meteo.com/v1/forecast", params={ "latitude": lat, "longitude": lon, "current": "temperature_2m,wind_speed_10m,wind_direction_10m,weather_code", "wind_speed_unit": "kn", }, timeout=10, ) if resp.status_code >= 400: return None cur = resp.json().get("current", {}) out = { "temp_c": cur.get("temperature_2m"), "wind_kts": cur.get("wind_speed_10m"), "wind_dir": cur.get("wind_direction_10m"), "code": cur.get("weather_code"), } _CACHE[key] = (now, out) return out except Exception: return None # WMO weather codes -> compact glyph for the hacker readout. def code_glyph(code) -> str: if code is None: return "·" code = int(code) if code == 0: return "☀" if code in (1, 2): return "⛅" if code == 3: return "☁" if code in (45, 48): return "≋" if 51 <= code <= 67: return "☂" if 71 <= code <= 77: return "❄" if 80 <= code <= 82: return "☔" if 95 <= code <= 99: return "⚡" return "·"