420 lines
16 KiB
Python
420 lines
16 KiB
Python
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())
|