diff --git a/main.py b/main.py index f6b0853..4af6f3a 100644 --- a/main.py +++ b/main.py @@ -104,7 +104,11 @@ def main(only_on_coming_evt=False, ignore_module=[], force_coming_event=True, ex if occuped_space < 250: # weekly weather from modules.weather import WeeklyWeatherModule - shape.append(WidgetPlacement(WeeklyWeatherModule, size=(int(480/1.6), 265), position=(480-int(480/1.6), NEXT_STOP_Y + occuped_space + (5 if occuped_space else 0)))) + shape.append(WidgetPlacement(WeeklyWeatherModule, size=(int(480/1.6), 165), position=(480-int(480/1.6), NEXT_STOP_Y + occuped_space + (5 if occuped_space else 0)))) + + # air quality + from modules.weather import WeatherAirQualityModule + shape.append(WidgetPlacement(WeatherAirQualityModule, size=(int(480/1.6), 100), position=(480-int(480/1.6), NEXT_STOP_Y + 165 + occuped_space + (5 if occuped_space else 0)))) # RATP weather major_lines = ["M7", "M14", "RB", "TT3A"] diff --git a/modules/ratp.py b/modules/ratp.py index 3c75ac9..d5e992e 100644 --- a/modules/ratp.py +++ b/modules/ratp.py @@ -224,52 +224,20 @@ class IDFMAPI: elif mode == "R" or mode == "rers": icon_mode = "RER" - img_line = None - if mode == "rers" and fill != "white": - img = Image.new('RGBA', (size * 2, int(size * 2 * 1.25)), '#fff0') - draw = ImageDraw.Draw(img) - - fnt_icon = ImageFont.truetype(os.path.join(config.fonts_dir, IDFMAPI.fnt_RB_path), size*2-9) - draw.rounded_rectangle((0, 0, size*2-1, size*2-1), 7, outline=fill, width=2) - draw.text((size, size), line, fill=fill, anchor="mm", font=fnt_icon) - - elif mode == "buses": - width = int(size * 1.38) - img = Image.new('RGBA', (width, width), '#fff0') - draw = ImageDraw.Draw(img) - - fnt_icon = ImageFont.truetype(os.path.join(config.fonts_dir, IDFMAPI.fnt_RB_path), size-3) - draw.rectangle((0, 0, width, size), fill) - draw.text((int(width / 2), int(size / 2)), line, fill="white", anchor="mm", font=fnt_icon) - - elif fill == "white": + if mode != "buses": img = Image.open(os.path.join(config.icons_dir, "ratp", "lignes", icon_mode + "_" + line + "_NB" + state + "_RVB.png")) return img.resize((int(size), int(img.height * size / img.width))) - else: - img_line = Image.open(os.path.join(config.icons_dir, "ratp", "lignes", icon_mode + "_" + line + "_NB_RVB.png")) - img = Image.new(img_line.mode, (img_line.width, int(img_line.height * 1.4))) - if mode != "rers": - img.paste(Image.new("RGB", (img_line.width, img_line.height), fill), (0,0), img_line) - else: - img.paste(img_line, (0,0)) - if state != "": - if state.endswith("petit"): - coeff = 2.5 - elif state.endswith("moyen"): - coeff = 2 - else: - coeff = 1.5 + width = int(size * 1.38) if mode == "buses" or mode == "tramways" else size + image = Image.new('RGBA', (width, size), '#fff0') + draw = ImageDraw.Draw(image) - img_perturb = Image.open(os.path.join(config.icons_dir, "ratp", "Perturbation_travaux_NB_RVB.png" if state.startswith("_travaux") else "Perturbation_trafic_NB_RVB.png")) - img_perturb = img_perturb.resize((int(img.width / coeff), int(img_perturb.height * img.width / (coeff * img_perturb.width)))) - if img_line is not None: - img.paste(img_perturb, (img.width-img_perturb.width,img_line.height-int(img_perturb.height/coeff))) - else: - img.paste(img_perturb, (img.width-img_perturb.width,img.height-img_perturb.height)) + fnt_icon = ImageFont.truetype(os.path.join(config.fonts_dir, IDFMAPI.fnt_RB_path), size-3) + draw.rectangle((0, 0, width, size), fill=fill) + draw.text((int(width / 2), int(size / 2)), line, fill="white" if (fill == "black" and mode == "tramways") or (fill != "white" and mode != "tramways") or (fill == "white" and mode == "tramways") else "black", anchor="mm", font=fnt_icon) - return img.resize((int(size), int(img.height * size / img.width))) + return image diff --git a/modules/weather.py b/modules/weather.py index 24627f3..40b8f65 100644 --- a/modules/weather.py +++ b/modules/weather.py @@ -51,6 +51,7 @@ def draw_format_array(infos, width, height, fnt_R, fnt_B, label_margin, align_he title_hsize = 0 text_hsize = 0 for k,v in infos.items(): + print(fnt_R.getbbox(k)) _,_,ksize,ksizeH = fnt_R.getbbox(k) _,_,vsize,vsizeH = fnt_B.getbbox(v) if title_hsize < ksizeH: @@ -95,10 +96,10 @@ class WeatherToolbarModule: weather = WeatherAPI().get_currently() infos = { - "Vent": "%d km/h" % weather["wind_speed"], + "Vent": "%d km/h" % weather["wind_kph"], "Humidité": "%d%%" % (weather["humidity"]), - "Indice UV": str(int(weather["uvi"])), - "Pression": "%s hPa" % weather["pressure"], + "Indice UV": str(int(weather["uv"])), + "Pression": "%d hPa" % weather["pressure_mb"], } txt = draw_format_infos(infos, width, height, fnt_R, fnt_B, self.label_margin, align_height=height/2, anchor="lm") @@ -122,21 +123,21 @@ class WeatherJumboCurrentModule: # current curweather = WeatherAPI().get_currently() - icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(curweather["weather"][0]["id"], night=curweather["weather"][0]["icon"].endswith("n"), current=True))).resize((height, height)) + icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(curweather["condition"]["code"], night=not curweather["is_day"], current=True))).resize((height, height)) image.paste(icon, (int(width*self.middle_align - height), 0), icon) draw.text( (width*self.middle_align, height/3), - "%d˚ %s." % (math.trunc(curweather["temp"]), curweather["weather"][0]["description"]), + "%d˚ %s." % (math.trunc(curweather["temp_c"]), curweather["condition"]["text"]), fill="black", anchor="ld", font=fnt_Big ) thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0] infos = { - "Ressentie": "%d˚" % curweather["feels_like"], - "Minimale": "%d˚" % thisdayweather["temp"]["min"], - "Maximale": "%d˚" % thisdayweather["temp"]["max"], + "Ressentie": "%d˚" % curweather["feelslike_c"], + "Minimale": "%d˚" % thisdayweather["mintemp_c"], + "Maximale": "%d˚" % thisdayweather["maxtemp_c"], } txt = draw_format_infos(infos, (1-self.middle_align) * width, 20, fnt_R, fnt_B, 5, margin_bf_first=False, fill="black", anchor="lt") @@ -147,10 +148,10 @@ class WeatherJumboCurrentModule: dayweather = list(itertools.islice(WeatherAPI().get_hourly(), 2)) - if dayweather[0]["weather"][0]["description"] != dayweather[1]["weather"][0]["description"]: - display_longtext(draw, (width*self.middle_align, height/1.28), dayweather[1]["weather"][0]["description"] + " la prochaine heure.\n" + thisdayweather["summary"] + ".", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width) + if dayweather[0]["condition"]["text"] != dayweather[1]["condition"]["text"]: + display_longtext(draw, (width*self.middle_align, height/1.28), dayweather[1]["condition"]["text"] + " la prochaine heure.\n" + thisdayweather["condition"]["text"] + " aujourd'hui.", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width) else: - display_longtext(draw, (width*self.middle_align, height/1.28), thisdayweather["summary"] + ".", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width) + display_longtext(draw, (width*self.middle_align, height/1.28), thisdayweather["condition"]["text"] + " aujourd'hui", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width) return image @@ -176,8 +177,8 @@ class WeatherSunModule: import time infos = { - "sunrise": WeatherAPI().read_timestamp(thisdayweather["sunrise"]), - "sunset": WeatherAPI().read_timestamp(thisdayweather["sunset"]), + "sunrise": thisdayweather["sunrise"], + "sunset": thisdayweather["sunset"], } align = start_align @@ -189,10 +190,10 @@ class WeatherSunModule: align += height + 2 draw.text( (align, height / 2), - str(info), + info, fill="black", anchor="lm", font=fnt_R ) - align += fnt_R.getlength(str(info)) + align += fnt_R.getlength(info) align += 10 return image @@ -210,18 +211,24 @@ class WeatherRainModule: percent_formatter = lambda x: '{:.10g}%'.format(x) gauge.value_formatter = percent_formatter - if thisdayweather["pop"] > 0: - gauge.add('Pluie', [{'value': thisdayweather["pop"] * 100}]) - if "wind_gust" in thisdayweather and thisdayweather["wind_gust"] > 45: + if thisdayweather["daily_chance_of_snow"] > 0: + gauge.add('Neige', [{'value': thisdayweather["daily_chance_of_snow"] + 1}]) + icon_path = "wi-snowflake-cold.png" + elif thisdayweather["daily_chance_of_rain"] > 0: + gauge.add('Pluie', [{'value': thisdayweather["daily_chance_of_rain"] + 1}]) + if "gust_kph" in thisdayweather and thisdayweather["gust_kph"] > 45: icon_path = "wi-wind-beaufort-6.png" else: icon_path = "wi-umbrella.png" - elif thisdayweather["uvi"] > 4: - gauge.add('Index UV', [{'value': thisdayweather["uvi"] * 10}]) + elif thisdayweather["uv"] > 4: + gauge.add('Index UV', [{'value': thisdayweather["uv"] * 10}]) icon_path = "wi-hot.png" - elif thisdayweather["wind_gust"] > 40: - gauge.add('Vent', [{'value': thisdayweather["wind_gust"], 'color': '#999'}]) + elif thisdayweather["maxwind_kph"] > 40: + gauge.add('Vent', [{'value': thisdayweather["maxwind_kph"], 'color': '#999'}]) icon_path = "wi-strong-wind.png" + elif thisdayweather["avgvis_km"] < 10: + gauge.add('Visibilité', [{'value': thisdayweather["avgvis_km"] * 10, 'color': '#999'}]) + icon_path = "wi-fog.png" elif "air_quality" in thisdayweather: gauge.add("Qualité de l'air", [{'value': thisdayweather["air_quality"]["gb-defra-index"] * 10}]) if thisdayweather["air_quality"]["gb-defra-index"] >= 5: @@ -229,7 +236,7 @@ class WeatherRainModule: else: icon_path = "wi-smoke.png" else: - gauge.add('Vent', [{'value': thisdayweather["wind_speed"], 'color': '#999'}]) + gauge.add('Vent', [{'value': thisdayweather["maxwind_kph"], 'color': '#999'}]) icon_path = "wi-wind-beaufort-1.png" image = Image.open(io.BytesIO(gauge.render_to_png())) @@ -247,7 +254,7 @@ class WeatherTemperatureModule: def draw_module(self, config, width, height): thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 2)) - if datetime.now().hour >= 19 and thisdayweather[1]["pop"] > thisdayweather[0]["pop"]: + if datetime.now().hour >= 19 and thisdayweather[1]["totalprecip_mm"] > thisdayweather[0]["totalprecip_mm"]: thisdayweather = thisdayweather[1] else: thisdayweather = thisdayweather[0] @@ -257,22 +264,22 @@ class WeatherTemperatureModule: hourly_min = 0 hourly_max = 0 for h in hours_weather: - if hourly_min > h["temp"]: - hourly_min = h["temp"] - if hourly_max < h["temp"]: - hourly_max = h["temp"] + if hourly_min > h["temp_c"]: + hourly_min = h["temp_c"] + if hourly_max < h["temp_c"]: + hourly_max = h["temp_c"] - line_chart = pygal.Line(config.pygal_config, interpolate='hermite', interpolation_parameters={'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1}, width=width+10, height=height, inverse_y_axis=False, x_label_rotation=45, range=(hourly_min, hourly_max), secondary_range=(0,100) if thisdayweather["pop"] > 0 else (0,10), **config.charts_opts) + line_chart = pygal.Line(config.pygal_config, interpolate='hermite', interpolation_parameters={'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1}, width=width+10, height=height, inverse_y_axis=False, x_label_rotation=45, range=(hourly_min, hourly_max), secondary_range=(0,100) if thisdayweather["totalprecip_mm"] + thisdayweather["totalsnow_cm"] > 0 else (0,10), **config.charts_opts) line_chart.value_formatter = lambda x: "%d" % x - line_chart.x_labels = [WeatherAPI().read_timestamp(d["dt"]).strftime("%Hh") if WeatherAPI().read_timestamp(d["dt"]).hour % 2 == 0 else "" for d in hours_weather[:self.limit_futur]] + line_chart.x_labels = [WeatherAPI().read_timestamp(d["time_epoch"]).strftime("%Hh") if WeatherAPI().read_timestamp(d["time_epoch"]).hour % 2 == 0 else "" for d in hours_weather[:self.limit_futur]] - line_chart.add('Températures', [d["temp"] for d in hours_weather[:self.limit_futur]], show_dots=False) + line_chart.add('Températures', [d["temp_c"] for d in hours_weather[:self.limit_futur]], show_dots=False) - if thisdayweather["pop"] > 0: - line_chart.add('Précipitations', [d["pop"]*100 for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False, fill=True if hourly_min == 0 else False) + if thisdayweather["totalprecip_mm"] + thisdayweather["totalsnow_cm"] > 0: + line_chart.add('Précipitations', [d["chance_of_rain"] + d["chance_of_snow"] for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False, fill=True if hourly_min == 0 else False) else: - line_chart.add('Index UV', [d["uvi"] for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False) + line_chart.add('Index UV', [d["uv"] for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False) img = Image.open(io.BytesIO(line_chart.render_to_png())) draw = ImageDraw.Draw(img) @@ -289,7 +296,7 @@ class WeeklyWeatherModule: def __init__(self): self.first_day = 1 - self.limit_futur = 5 + self.limit_futur = 7 def draw_module(self, config, width, height): image = Image.new('RGB', (width, height), 'white') @@ -310,24 +317,24 @@ class WeeklyWeatherModule: ) temp = [] - for day in weekweather[max(0,self.first_day-1):self.first_day+self.limit_futur]: - temp.append(day["temp"]["min"]) - temp.append(day["temp"]["max"]) + for day in weekweather[self.first_day:self.first_day+self.limit_futur]: + temp.append(day["mintemp_c"]) + temp.append(day["maxtemp_c"]) t_min = min(temp) t_max = max(temp) t_scale = (width - day_size - 30) / (t_max - t_min) i = 1 for day in weekweather[self.first_day:self.first_day+self.limit_futur]: - icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(day["weather"][0]["id"]))).resize((day_size, day_size)) + icon = Image.open(os.path.join(config.icons_dir, "weather", WeatherAPI.get_icon(day["condition"]["code"]))).resize((day_size, day_size)) image.paste(icon, (0, i * day_size), icon) draw.text( - (15 + 2 + day_size + int((day["temp"]["min"]-t_min)*t_scale), i*day_size + 4), - "%d˚" % math.trunc(day["temp"]["min"]), + (15 + 2 + day_size + int((day["mintemp_c"]-t_min)*t_scale), i*day_size + 4), + "%d˚" % math.trunc(day["mintemp_c"]), fill="black", anchor="rt", font=fnt_R ) - summary_size = fnt_R.getlength(day["date"].strftime("%a") + " : " + day["weather"][0]["description"]) + summary_size = fnt_R.getlength(day["date"].strftime("%a") + " : " + day["condition"]["text"]) draw.text( (day_size + (width - day_size - summary_size) / 2, (i + 1) * day_size - 6), day["date"].strftime("%a") + " : ", @@ -335,22 +342,22 @@ class WeeklyWeatherModule: ) draw.text( (day_size + (width - day_size + summary_size) / 2, (i + 1) * day_size - 6), - day["weather"][0]["description"], + day["condition"]["text"], fill="black", anchor="rs", font=fnt_R ) draw.text( - (day_size + int((day["temp"]["max"]-t_min)*t_scale) + 2, i * day_size + 4), - "%d˚" % math.trunc(day["temp"]["max"]), + (day_size + int((day["maxtemp_c"]-t_min)*t_scale) + 2, i * day_size + 4), + "%d˚" % math.trunc(day["maxtemp_c"]), fill="black", anchor="lt", font=fnt_R ) try: draw.rounded_rectangle( - (15 + day_size + int((day["temp"]["min"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["temp"]["max"]-t_min)*t_scale),i*day_size + 14), + (15 + day_size + int((day["mintemp_c"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["maxtemp_c"]-t_min)*t_scale),i*day_size + 14), radius=5, fill="black") except AttributeError: draw.rectangle( - (15 + day_size + int((day["temp"]["min"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["temp"]["max"]-t_min)*t_scale),i*day_size + 14), + (15 + day_size + int((day["mintemp_c"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["maxtemp_c"]-t_min)*t_scale),i*day_size + 14), fill="black") i += 1 diff --git a/modules/weather_api.py b/modules/weather_api.py index 86d7dbe..4a1b345 100644 --- a/modules/weather_api.py +++ b/modules/weather_api.py @@ -5,285 +5,211 @@ import os import urllib.error import urllib.parse import urllib.request +from zoneinfo import ZoneInfo -class DarkSkyAPI: +class WeatherAPI: def __init__(self, apikey=None, gps=None, opts={"lang": "fr", "units": "metric"}): self.apikey = apikey or os.environ["WEATHERAPIKEY"] - self.baseurl = "https://api.openweathermap.org/data/3.0/onecall" + self.baseurl = "http://api.weatherapi.com/v1" self.default_gps = gps or ("GPS" in os.environ and os.environ["GPS"]) or "48.8127,2.3437" self.opts = opts - self._cached_file = ".darksky-%s.cache" + self._cached_file = ".weather-%s-%s.cache" - def get_weather(self, gps=None): + def get_weather(self, apitype="forecast", gps=None, **params): if gps is None: gps = self.default_gps + cached_filename = self._cached_file % (apitype + ("" if "dt" not in params else "-{}".format(params["dt"])), gps) # Read the mod time statinfo = None try: - statinfo = os.stat(self._cached_file % gps) + statinfo = os.stat(cached_filename) except: pass + params["q"] = gps + params["key"] = str(self.apikey) + params["alerts"] = "yes" + params["aqi"] = "yes" + for k in self.opts: + params[k] = self.opts[k] + 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: - (lat,lon) = gps.split(",") - with urllib.request.urlopen(self.baseurl + "?appid=" + str(self.apikey) + "&lat=" + str(lat) + "&lon=" + str(lon) + "&" + ("&".join([opt+"="+self.opts[opt] for opt in self.opts]))) as f: - with open(self._cached_file % gps, 'wb') as fd: + with urllib.request.urlopen(self.baseurl + "/" + apitype + ".json?" + urllib.parse.urlencode(params)) as f: + with open(cached_filename, 'wb') as fd: fd.write(f.read()) except ConnectionResetError: pass - #except urllib.error.URLError: - # pass + except urllib.error.URLError: + pass # Retrieve cached data res = {} - with open(self._cached_file % gps) as f: + with open(cached_filename) as f: res = json.load(f) return res def get_icon(icon, night=False, current=False): - # 200: Thunderstorm - if icon < 300: - if icon == 200 or icon == 201 or icon == 202: - if not night: - return "wi-day-thunderstorm.png" - else: - return "wi-night-thunderstorm.png" - elif icon == 210 or icon == 211: - return "wi-thunderstorm.png" - elif icon == 212 or icon == 221: - return "wi-lightning.png" - elif icon == 230 or icon == 231 or icon == 232: - if not night: - return "wi-day-storm-showers.png" - else: - return "wi-night-storm-showers.png" - else: - return "wi-thunderstorm.png" - - # 300: Drizzle - elif icon < 400: - if icon < 311: - if not night: - return "wi-day-showers.png" - else: - return "wi-night-showers.png" - else: - return "wi-showers.png" - - # 500: Rain - elif icon < 600: - if icon == 500: - if not night: - return "wi-day-rain.png" - else: - return "wi-night-rain.png" - if icon < 510: - return "wi-rain.png" - elif icon < 520: - if current: - return "wi-hail.png" - elif not night: - return "wi-day-hail.png" - else: - return "wi-night-hail.png" - elif icon == 520: - if not night: - return "wi-day-showers.png" - else: - return "wi-night-showers.png" - elif icon > 520: - return "wi-showers.png" - else: - return "wi-rain.png" - - # 600: Snow - elif icon < 700: - if icon == 600: - if not night: - return "wi-day-snow.png" - else: - return "wi-night-alt-snow.png" - elif icon < 610: - return "wi-snow.png" - elif icon < 620: - if current: - return "wi-sleet.png" - if not night: - return "wi-day-sleet.png" - else: - return "wi-night-alt-sleet.png" - elif icon == 620: - if not night: - return "wi-day-snow-wind.png" - else: - return "wi-night-snow-wind.png" - elif icon < 630: - return "wi-snow-wind.png" - else: - return "wi-snow.png" - - # Atmosphere - elif icon < 800: - if icon == 701: - if not night: - return "wi-day-haze.png" - else: - return "wi-night-fog.png" - elif icon < 720: - return "wi-smoke.png" - elif icon < 730: - if not night: - return "wi-day-haze.png" - else: - return "wi-night-fog.png" - elif icon < 740: - return "wi-dust.png" - elif icon < 750: - if not night: - return "wi-day-fog.png" - else: - return "wi-night-fog.png" - elif icon == 751: - return "wi-sandstorm.png" - elif icon < 762: - return "wi-dust.png" - elif icon == 762: - return "wi-volcano.png" - elif icon == 771: - return "wi-strong-wind.png" - elif icon == 781: - return "wi-tornado.png" - - # 800: Clear - if icon == 800: + if icon == 1000: if not night: return "wi-day-sunny.png" else: return "wi-night-clear.png" - - # 800: Clouds - elif icon == 801: - if not night: - return "wi-day-sunny-overcast.png" - else: - return "wi-night-alt-partly-cloudy.png" - elif icon == 802: + elif icon == 1003: if not night: return "wi-day-cloudy.png" else: return "wi-night-alt-cloudy.png" - elif icon == 804: + elif icon == 1006: return "wi-cloudy.png" - elif icon < 900: - return "wi-cloud.png" - - return "wi-alien.png" + elif icon == 1009: + if not night: + return "wi-day-sunny-overcast.png" + else: + return "wi-night-alt-partly-cloudy.png" + elif icon == 1030 or icon == 1072: + if not night: + return "wi-day-haze.png" + else: + return "wi-night-fog.png" + elif icon == 1063: + if not night: + return "wi-day-rain.png" + else: + return "wi-night-alt-rain.png" + elif icon == 1066: + if not night: + return "wi-day-snow.png" + else: + return "wi-night-alt-snow.png" + elif icon == 1069 or icon == 1204: + if not night: + return "wi-day-sleet.png" + else: + return "wi-night-alt-sleet.png" + elif icon == 1087: + if not night: + return "wi-day-lightning.png" + else: + return "wi-night-alt-lightning.png" + elif icon == 1114 or icon == 1219 or icon == 1222 or icon == 1258: + return "wi-snow.png" + elif icon == 1117 or icon == 1225: + return "wi-snow-wind.png" + elif icon == 1135 or icon == 1147: + return "wi-fog.png" + elif icon == 1150 or icon == 1153: + return "wi-sprinkle.png" + elif icon == 1168 or icon == 1171 or icon == 1198 or icon == 1201: + return "wi-snowflake-cold.png" + elif icon == 1180 or icon == 1183 or icon == 1186: + if not night: + return "wi-day-rain.png" + else: + return "wi-night-alt-rain.png" + elif icon == 1189 or icon == 1192 or icon == 1195: + return "wi-rain.png" + elif icon == 1240 or icon == 1243: + return "wi-showers.png" + elif icon == 1246: + return "wi-tsunami.png" + elif icon == 1207 or icon == 1249 or icon == 1252: + return "wi-sleet.png" + elif icon == 1210 or icon == 1213 or icon == 1216 or icon == 1255: + if not night: + return "wi-day-snow.png" + else: + return "wi-night-snow.png" + elif icon == 1237: + return "wi-hail.png" + elif icon == 1261 or icon == 1264: + if not night: + return "wi-day-hail.png" + else: + return "wi-night-hail.png" + elif icon == 1273 or icon == 1276: + if not night: + return "wi-day-thunderstorm.png" + else: + return "wi-night-thunderstorm.png" + elif icon == 1279 or icon == 1282: + if not night: + return "wi-day-snow-thunderstorm.png" + else: + return "wi-night-snow-thunderstorm.png" + else: + return "wi-alien.png" def get_moon_icon(self, day=0): - thisdayweather = list(itertools.islice(self.get_daily(), 1))[0] - moon_phase = thisdayweather["moon_phase"] + moon_phase = list(itertools.islice(self.get_daily(), 1))[day]["moon_phase"] - if moon_phase < 0.035: + if moon_phase == "New Moon": 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: + elif moon_phase == "Waxing Crescent": 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: + elif moon_phase == "First Quarter": 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: + elif moon_phase == "Waxing Gibbous": 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: + elif moon_phase == "Full Moon": 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: + elif moon_phase == "Waning Gibbous": 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: + elif moon_phase == "Last Quarter": 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" + return "wi-moon-alt-waning-crescent-3.png" def get_currently(self, *args, **kwargs): - cur = self.get_weather(*args, **kwargs)["current"] - cur["is_day"] = True - return cur + return self.get_weather("current", *args, **kwargs)["current"] + def get_forecast(self, *args, **kwargs): + for i in [0, 1, 2, 3]: + enddt = datetime.now() + timedelta(days=i) + v = self.get_weather(*args, **kwargs, dt=enddt.strftime("%Y-%m-%d"), alerts="yes")["forecast"]["forecastday"][0] + v["day"]["date"] = enddt + yield v + def get_hourly(self, *args, **kwargs): - return self.get_weather(*args, **kwargs)["hourly"] - + now = datetime.now().astimezone(ZoneInfo("Europe/Paris")) + for d in self.get_forecast(*args, **kwargs): + for h in d["hour"]: + if d["day"]["date"].day == now.day and now > self.read_timestamp(h["time_epoch"]) + timedelta(hours=1): + continue + yield h def get_daily(self, *args, **kwargs): - for d in self.get_weather(*args, **kwargs)["daily"]: - d["date"] = datetime.fromtimestamp(d["dt"]) - yield d - - def has_alerts(self, *args, **kwargs): - return "alerts" in self.get_weather(*args, **kwargs) and len(self.get_weather(*args, **kwargs)["alerts"]) > 0 + for d in self.get_forecast(*args, **kwargs): + yield { + **d["day"], + **d["astro"] + } def get_alerts(self, *args, **kwargs): - w = self.get_weather(*args, **kwargs) - if "alerts" in w: - return w["alerts"] - return [] + for i in [0, 1, 2, 3]: + enddt = datetime.now() + timedelta(days=i) + for a in self.get_weather(*args, **kwargs, dt=enddt.strftime("%Y-%m-%d"))["alerts"]["alert"]: + yield a def read_timestamp(self, timestamp, *args, **kwargs): - wth = self.get_weather(*args, **kwargs) - - tz = timezone(timedelta(seconds=wth["timezone_offset"]), wth["timezone"]) - return datetime.fromtimestamp(timestamp, tz=tz) + PARIS = ZoneInfo("Europe/Paris") + return datetime.fromtimestamp(int(timestamp), tz=timezone.utc).astimezone(PARIS) -WeatherAPI = DarkSkyAPI +#WeatherAPI = TomorrowAPI if __name__ == '__main__': - dsa = DarkSkyAPI() - print(dsa.get_currently()) + dsa = WeatherAPI() + print(json.dumps(dsa.get_currently())) + #print(json.dumps([d for d in dsa.get_daily()][0]))