from datetime import datetime, timedelta, timezone import json import os import urllib.error import urllib.parse import urllib.request from zoneinfo import ZoneInfo class TomorrowAPI: def __init__(self, apikey=None, gps=None, opts={"units": "metric"}): self.apikey = apikey or os.environ["TOMORROWAPIKEY"] self.baseurl = "https://api.tomorrow.io/v4" self.default_gps = gps or ("GPS" in os.environ and os.environ["GPS"]) or "48.8127,2.3437" self.opts = opts self._cached_file = ".weather-%s-%s.cache" def get_weather(self, apitype="forecast", gps=None): if gps is None: gps = self.default_gps # Read the mod time statinfo = None try: statinfo = os.stat(self._cached_file % (apitype, gps)) except: pass if statinfo is None or datetime.fromtimestamp(statinfo.st_mtime, tz=timezone.utc) + timedelta(hours=1) < datetime.now(tz=timezone.utc): # Do the request and save it try: with urllib.request.urlopen(self.baseurl + "/weather/" + apitype + "?location=" + gps + "&apikey=" + str(self.apikey) + "&" + ("&".join([opt+"="+self.opts[opt] for opt in self.opts]))) as f: with open(self._cached_file % (apitype, gps), 'wb') as fd: fd.write(f.read()) except ConnectionResetError: pass except urllib.error.URLError: pass # Retrieve cached data res = {} with open(self._cached_file % (apitype, gps)) as f: res = json.load(f) return res def get_icon(icon, night=False, current=False): if icon == 1000: if not night: return "wi-day-sunny.png" else: return "wi-night-clear.png" elif icon == 1100: if not night: return "wi-day-sunny-overcast.png" else: return "wi-night-alt-partly-cloudy.png" elif icon == 1101: if not night: return "wi-day-cloudy-high.png" else: return "wi-night-partly-cloudy.png" elif icon == 1102: return "wi-cloud.png" elif icon == 1001: return "wi-cloudy.png" elif icon == 1103: if not night: return "wi-day-cloudy-high.png" else: return "night-cloud-high.png" elif icon == 2000: return "wi-fog.png" elif icon == 2100 or icon == 2101 or icon == 2102 or icon == 2103 or icon == 2106 or icon == 2107 or icon == 2108: if not night: return "wi-day-fog.png" else: return "wi-night-fog.png" elif icon == 4000: return "wi-sprinkle.png" elif icon == 4000: return "wi-sprinkle.png" elif icon == 4200: return "wi-showers.png" elif icon == 4001: return "wi-rain.png" elif icon == 4201: return "wi-showers.png" elif icon == 4203 or icon == 4204 or icon == 4205 or icon == 4213 or icon == 4214 or icon == 4215 or icon == 4209 or icon == 4208 or icon == 4210 or icon == 4211 or icon == 4202 or icon == 4212: if not night: return "wi-day-rain.png" else: return "wi-night-alt-rain.png" elif icon == 5001 or icon == 5100 or icon == 5000 or icon == 5101: return "wi-snow.png" elif icon == 5115 or icon == 5116: if not night: return "wi-day-sleet.png" else: return "wi-night-sleet.png" elif icon == 5122 or icon == 5110 or icon == 5108 or icon == 5114 or icon == 5112: return "wi-sleet.png" elif icon == 5103 or icon == 5104 or icon == 5105 or icon == 5106 or icon == 5107 or icon == 5119 or icon == 5120 or icon == 5121: if not night: return "wi-day-snow.png" else: return "wi-night-snow.png" elif icon == 6000 or icon == 6200 or icon == 6001 or icon == 6201 or icon == 6204 or icon == 6206 or icon == 6212 or icon == 6220 or icon == 6222: return "wi-sprinkle.png" elif icon == 6003 or icon == 6002 or icon == 6004 or icon == 6205 or icon == 6203 or icon == 6209 or icon == 6213 or icon == 6214 or icon == 6215 or icon == 6207 or icon == 6202 or icon == 6208: if not night: return "wi-day-sprinkle.png" else: return "wi-night-sprinkle.png" elif icon == 7102 or icon == 7000 or icon == 7101 or icon == 7105 or icon == 7115 or icon == 7117 or icon == 7106 or icon == 7103: return "wi-hail.png" elif icon == 7110 or icon == 7111 or icon == 7112 or icon == 7108 or icon == 7107 or icon == 7109 or icon == 7114 or icon == 7116: if not night: return "wi-day-hail.png" else: return "wi-night-hail.png" elif icon == 8000: return "wi-thunderstorm.png" elif icon == 8001 or icon == 8003 or icon == 8002: if not night: return "wi-day-thunderstorm.png" else: return "wi-night-thunderstorm.png" else: return "wi-alien.png" def get_description(icon, night=False): if icon == 1000: if not night: return "Ensoleillé" else: return "Temps clair" elif icon == 1100: return "Dégagé" elif icon == 1101: return "Partiellement nuageux" elif icon == 1102: return "Plutôt nuageux" elif icon == 1001: return "Nuageux" elif icon == 1103: return "Dégagé et nuageux" elif icon == 2000: return "Brouillard" elif icon == 2100: return "Brouillard léger" elif icon == 2101: return "Brouillard léger et ciel dégagé" elif icon == 2102: return "Brouillard léger et ciel partiellement nuageux" elif icon == 2103: return "Brouillard léger et ciel plutôt nuageux" elif icon == 2106: return "Brouillard et ciel dégagé" elif icon == 2107: return "Brouillard et ciel partiellement nuageux" elif icon == 2108: return "Brouillard et ciel plutôt nuageux" elif icon == 4000: return "Bruine" elif icon == 4200: return "Pluie légère" elif icon == 4001: return "Pluie" elif icon == 4201: return "Forte pluie" elif icon == 4203: return "Bruine et ciel dégagé" elif icon == 4204: return "Bruine et ciel partiellement nuageux" elif icon == 4205: return "Bruine et ciel plutôt nuageux" elif icon == 4213: return "Averse et ciel dégagé" elif icon == 4214: return "Averse et ciel partiellement nuageux" elif icon == 4215: return "Averse et ciel plutôt nuageux" elif icon == 4209: return "Pluie et ciel dégagé" elif icon == 4208: return "Pluie et ciel partiellement nuageux" elif icon == 4210: return "Pluie et ciel plutôt nuageux" elif icon == 4211: return "Pluie forte et ciel dégagé" elif icon == 4202: return "Pluie forte et ciel partiellement nuageux" elif icon == 4212: return "Pluie forte et ciel plutôt nuageux" elif icon == 5001: return "Quelques flocons" elif icon == 5100: return "Neige légère" elif icon == 5000: return "Neige" elif icon == 5101: return "Tempête de neige" elif icon == 5115: return "Quelques flocons et ciel dégagé" elif icon == 5116: return "Quelques flocons et ciel partiellement nuageux" elif icon == 5117: return "Quelques flocons et ciel plutôt nuageux" elif icon == 5122: return "Bruine et quelques flocons" elif icon == 5102: return "Neige légère et ciel dégagé" elif icon == 5103: return "Neige légère et ciel partiellement nuageux" elif icon == 5104: return "Neige légère et ciel plutôt nuageux" elif icon == 5105: return "Neige et ciel dégagé" elif icon == 5106: return "Neige et ciel partiellement nuageux" elif icon == 5107: return "Neige et ciel plutôt nuageux" elif icon == 5119: return "Neige abondante et ciel dégagé" elif icon == 5120: return "Neige abondante et ciel partiellement nuageux" elif icon == 5121: return "Neige abondante et ciel plutôt nuageux" elif icon == 5110: return "Bruine et neige" elif icon == 5108: return "Pluie et neige" elif icon == 5114: return "Neige et pluie verglaçante" elif icon == 5112: return "Neige et grèle" elif icon == 6000: return "Bruine verglaçante" elif icon == 6200: return "Légère bruine verglaçante" elif icon == 6001: return "Pluie verglaçante" elif icon == 6201: return "Pluie forte verglaçante" elif icon == 6003: return "Bruine verglaçante et ciel dégagé" elif icon == 6002: return "Bruine verglaçante et ciel partiellement nuageux" elif icon == 6004: return "Bruine verglaçante et ciel plutôt nuageux" elif icon == 6204: return "Bruine verglaçante et bruine" elif icon == 6206: return "Bruine verglaçante et pluie légère" elif icon == 6205: return "Averse verglaçante et ciel dégagé" elif icon == 6203: return "Averse verglaçante et ciel partiellement nuageux" elif icon == 6209: return "Averse verglaçante et ciel plutôt nuageux" elif icon == 6213: return "Pluie verglaçante et ciel dégagé" elif icon == 6214: return "Pluie verglaçante et ciel partiellement nuageux" elif icon == 6215: return "Pluie verglaçante et ciel plutôt nuageux" elif icon == 6212: return "Pluie verglaçante et bruine" elif icon == 6220: return "Pluie verglaçante et pluie légère" elif icon == 6222: return "Averses et pluie verglaçante" elif icon == 6207: return "Pluie forte verglaçante et ciel dégagé" elif icon == 6202: return "Pluie forte verglaçante et ciel partiellement nuageux" elif icon == 6208: return "Pluie forte verglaçante et ciel plutôt nuageux" elif icon == 7000: return "Grèle" elif icon == 7102: return "Grèle légère" elif icon == 7101: return "Forte grèle" elif icon == 7110: return "Grèle légère et ciel dégagé" elif icon == 7111: return "Grèle légère et ciel partiellement nuageux" elif icon == 7112: return "Grèle légère et ciel plutôt nuageux" elif icon == 7108: return "Grèle et ciel dégagé" elif icon == 7107: return "Grèle et ciel partiellement nuageux" elif icon == 7109: return "Grèle et ciel plutôt nuageux" elif icon == 7113: return "Grèle forte et ciel dégagé" elif icon == 7114: return "Grèle forte et ciel partiellement nuageux" elif icon == 7116: return "Grèle forte et ciel plutôt nuageux" elif icon == 7105: return "Bruine et grèle" elif icon == 7115: return "Pluie légère et grèle" elif icon == 7117: return "Pluie et grèle" elif icon == 7106: return "Pluie verglaçante et grèle" elif icon == 7103: return "Pluie verglaçante et grèle forte" elif icon == 8000: return "Orageux" elif icon == 8001: return "Orage et ciel dégagé" elif icon == 8003: return "Orage et ciel partiellement nuageux" elif icon == 8002: return "Orage et ciel plutôt nuageux" else: return "Invasion d'aliens ?" def get_moon_icon(self, day=0): moon_phase = self.get_daily()["data"][day]["moonPhase"] if moon_phase < 0.035: return "wi-moon-alt-new.png" elif moon_phase < 0.071: return "wi-moon-alt-waxing-crescent-1.png" elif moon_phase < 0.107: return "wi-moon-alt-waxing-crescent-2.png" elif moon_phase < 0.142: return "wi-moon-alt-waxing-crescent-3.png" elif moon_phase < 0.178: return "wi-moon-alt-waxing-crescent-4.png" elif moon_phase < 0.214: return "wi-moon-alt-waxing-crescent-5.png" elif moon_phase < 0.25: return "wi-moon-alt-waxing-crescent-6.png" elif moon_phase < 0.285: return "wi-moon-alt-first-quarter.png" elif moon_phase < 0.321: return "wi-moon-alt-waxing-gibbous-1.png" elif moon_phase < 0.357: return "wi-moon-alt-waxing-gibbous-2.png" elif moon_phase < 0.392: return "wi-moon-alt-waxing-gibbous-3.png" elif moon_phase < 0.428: return "wi-moon-alt-waxing-gibbous-4.png" elif moon_phase < 0.464: return "wi-moon-alt-waxing-gibbous-5.png" elif moon_phase < 0.5: return "wi-moon-alt-waxing-gibbous-6.png" elif moon_phase < 0.535: return "wi-moon-alt-full.png" elif moon_phase < 0.571: return "wi-moon-alt-waning-gibbous-1.png" elif moon_phase < 0.607: return "wi-moon-alt-waning-gibbous-2.png" elif moon_phase < 0.642: return "wi-moon-alt-waning-gibbous-3.png" elif moon_phase < 0.678: return "wi-moon-alt-waning-gibbous-4.png" elif moon_phase < 0.714: return "wi-moon-alt-waning-gibbous-5.png" elif moon_phase < 0.75: return "wi-moon-alt-waning-gibbous-6.png" elif moon_phase < 0.785: return "wi-moon-alt-third-quarter.png" elif moon_phase < 0.821: return "wi-moon-alt-waning-crescent-1.png" elif moon_phase < 0.857: return "wi-moon-alt-waning-crescent-2.png" elif moon_phase < 0.892: return "wi-moon-alt-waning-crescent-3.png" elif moon_phase < 0.928: return "wi-moon-alt-waning-crescent-4.png" elif moon_phase < 0.964: return "wi-moon-alt-waning-crescent-5.png" else: return "wi-moon-alt-waning-crescent-6.png" def get_currently(self, *args, **kwargs): return self.get_weather("realtime", *args, **kwargs)["data"]["values"] def get_hourly(self, *args, **kwargs): return self.get_weather(*args, **kwargs)["timelines"]["hourly"] def get_daily(self, *args, **kwargs): return self.get_weather(*args, **kwargs)["timelines"]["daily"] def has_alerts(self, *args, **kwargs): return "alerts" in self.get_weather(*args, **kwargs) and len(self.get_weather(*args, **kwargs)["alerts"]) > 0 def get_alerts(self, *args, **kwargs): return self.get_weather(*args, **kwargs)["alerts"] def read_timestamp(self, timestamp, *args, **kwargs): PARIS = ZoneInfo("Europe/Paris") return datetime.fromisoformat(timestamp.replace("Z", "+00:00")).astimezone(PARIS) WeatherAPI = TomorrowAPI if __name__ == '__main__': dsa = TomorrowAPI() print(dsa.get_currently()) print(dsa.get_daily())