Compare commits
2 Commits
ae73f68444
...
e8fd423e5f
Author | SHA1 | Date | |
---|---|---|---|
e8fd423e5f | |||
7b0a3dd5ac |
6
main.py
6
main.py
@ -98,7 +98,11 @@ def main(only_on_coming_evt=False, ignore_module=[], force_coming_event=True, ex
|
|||||||
if occuped_space < 250:
|
if occuped_space < 250:
|
||||||
# weekly weather
|
# weekly weather
|
||||||
from modules.weather import WeeklyWeatherModule
|
from modules.weather import WeeklyWeatherModule
|
||||||
shape.append(WidgetPlacement(WeeklyWeatherModule, size=(int(480/1.6), 275), 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
|
# RATP weather
|
||||||
major_lines = ["M7", "M14", "RB", "TT3A"]
|
major_lines = ["M7", "M14", "RB", "TT3A"]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
import io
|
import io
|
||||||
|
import itertools
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
@ -40,6 +41,44 @@ def draw_format_infos(infos, width, height, fnt_R, fnt_B, label_margin, align_he
|
|||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
def draw_format_array(infos, width, height, fnt_R, fnt_B, label_margin, align_height=0, margin_bf_first=True, **kwargs):
|
||||||
|
image = Image.new('RGBA', (int(width), int(height)))
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
|
nb_infos = len(infos.keys())
|
||||||
|
size = 0
|
||||||
|
title_hsize = 0
|
||||||
|
text_hsize = 0
|
||||||
|
for k,v in infos.items():
|
||||||
|
ksize,ksizeH = fnt_R.getsize(k)
|
||||||
|
vsize,vsizeH = fnt_B.getsize(v)
|
||||||
|
if title_hsize < ksizeH:
|
||||||
|
title_hsize = ksizeH
|
||||||
|
if text_hsize < vsizeH:
|
||||||
|
text_hsize = vsizeH
|
||||||
|
size += max(ksize,vsize)
|
||||||
|
size += label_margin
|
||||||
|
|
||||||
|
margin = (width - size) / nb_infos
|
||||||
|
align = 0
|
||||||
|
if margin_bf_first:
|
||||||
|
align += margin / 2
|
||||||
|
for k,v in infos.items():
|
||||||
|
size = max(fnt_R.getsize(k)[0],fnt_B.getsize(v)[0])
|
||||||
|
draw.text(
|
||||||
|
(align + (0 if "anchor" not in kwargs or kwargs["anchor"][0] != "m" else size/2), align_height-title_hsize/2),
|
||||||
|
k,
|
||||||
|
font=fnt_R, **kwargs
|
||||||
|
)
|
||||||
|
draw.text(
|
||||||
|
(align + (0 if "anchor" not in kwargs or kwargs["anchor"][0] != "m" else size/2), align_height+text_hsize/2),
|
||||||
|
v,
|
||||||
|
font=fnt_B, **kwargs
|
||||||
|
)
|
||||||
|
align += size + margin
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
class WeatherToolbarModule:
|
class WeatherToolbarModule:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -55,10 +94,10 @@ class WeatherToolbarModule:
|
|||||||
weather = WeatherAPI().get_currently()
|
weather = WeatherAPI().get_currently()
|
||||||
|
|
||||||
infos = {
|
infos = {
|
||||||
"Vent": "%d km/h" % weather["windSpeed"],
|
"Vent": "%d km/h" % weather["wind_kph"],
|
||||||
"Humidité": "%d%%" % (weather["humidity"]),
|
"Humidité": "%d%%" % (weather["humidity"]),
|
||||||
"Indice UV": str(weather["uvIndex"]),
|
"Indice UV": str(int(weather["uv"])),
|
||||||
"Pression": "%d hPa" % weather["pressureSurfaceLevel"],
|
"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")
|
txt = draw_format_infos(infos, width, height, fnt_R, fnt_B, self.label_margin, align_height=height/2, anchor="lm")
|
||||||
@ -82,21 +121,21 @@ class WeatherJumboCurrentModule:
|
|||||||
# current
|
# current
|
||||||
curweather = WeatherAPI().get_currently()
|
curweather = WeatherAPI().get_currently()
|
||||||
|
|
||||||
icon = Image.open("icons/" + WeatherAPI.get_icon(curweather["weatherCode"], current=True)).resize((height, height))
|
icon = Image.open("icons/" + 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)
|
image.paste(icon, (int(width*self.middle_align - height), 0), icon)
|
||||||
|
|
||||||
draw.text(
|
draw.text(
|
||||||
(width*self.middle_align, height/3),
|
(width*self.middle_align, height/3),
|
||||||
"%d˚ %s." % (math.trunc(curweather["temperature"]), curweather["summary"] if "summary" in curweather else WeatherAPI.get_description(curweather["weatherCode"])),
|
"%d˚ %s." % (math.trunc(curweather["temp_c"]), curweather["condition"]["text"]),
|
||||||
fill="black", anchor="ld", font=fnt_Big
|
fill="black", anchor="ld", font=fnt_Big
|
||||||
)
|
)
|
||||||
|
|
||||||
thisdayweather = WeatherAPI().get_daily()[0]["values"]
|
thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]
|
||||||
|
|
||||||
infos = {
|
infos = {
|
||||||
"Ressentie": "%d˚" % curweather["temperatureApparent"],
|
"Ressentie": "%d˚" % curweather["feelslike_c"],
|
||||||
"Minimale": "%d˚" % thisdayweather["temperatureMin"],
|
"Minimale": "%d˚" % thisdayweather["mintemp_c"],
|
||||||
"Maximale": "%d˚" % thisdayweather["temperatureMax"],
|
"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")
|
txt = draw_format_infos(infos, (1-self.middle_align) * width, 20, fnt_R, fnt_B, 5, margin_bf_first=False, fill="black", anchor="lt")
|
||||||
@ -105,13 +144,12 @@ class WeatherJumboCurrentModule:
|
|||||||
# day
|
# day
|
||||||
fnt_Rig = ImageFont.truetype(config.fnt_R_path, 20)
|
fnt_Rig = ImageFont.truetype(config.fnt_R_path, 20)
|
||||||
|
|
||||||
dailyweather = WeatherAPI().get_daily()
|
dayweather = list(itertools.islice(WeatherAPI().get_hourly(), 2))
|
||||||
dayweather = WeatherAPI().get_hourly()
|
|
||||||
|
|
||||||
if dayweather[0]["values"]["weatherCode"] != dayweather[1]["values"]["weatherCode"]:
|
if dayweather[0]["condition"]["text"] != dayweather[1]["condition"]["text"]:
|
||||||
display_longtext(draw, (width*self.middle_align, height/1.28), WeatherAPI.get_description(dayweather[1]["values"]["weatherCode"]) + " la prochaine heure.\n" + WeatherAPI.get_description(dailyweather[0]["values"]["weatherCodeMax"]) + " aujourd'hui.", fill="black", anchor="lm", font=fnt_Rig, maxwidth=(1-self.middle_align)*width)
|
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:
|
else:
|
||||||
display_longtext(draw, (width*self.middle_align, height/1.28), WeatherAPI.get_description(dayweather[0]["values"]["weatherCodeMax"]) + " aujourd'hui", 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
|
return image
|
||||||
|
|
||||||
@ -133,12 +171,12 @@ class WeatherSunModule:
|
|||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
fnt_R = ImageFont.truetype(config.fnt_R_path, int(height*0.7))
|
fnt_R = ImageFont.truetype(config.fnt_R_path, int(height*0.7))
|
||||||
|
|
||||||
thisdayweather = WeatherAPI().get_daily()[0]["values"]
|
thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]
|
||||||
|
|
||||||
import time
|
import time
|
||||||
infos = {
|
infos = {
|
||||||
"sunrise": WeatherAPI().read_timestamp(thisdayweather["sunriseTime"]).strftime("%X"),
|
"sunrise": thisdayweather["sunrise"],
|
||||||
"sunset": WeatherAPI().read_timestamp(thisdayweather["sunsetTime"]).strftime("%X"),
|
"sunset": thisdayweather["sunset"],
|
||||||
}
|
}
|
||||||
|
|
||||||
align = start_align
|
align = start_align
|
||||||
@ -163,32 +201,35 @@ class WeatherRainModule:
|
|||||||
|
|
||||||
def draw_module(self, config, width, height):
|
def draw_module(self, config, width, height):
|
||||||
if datetime.now().hour >= 21:
|
if datetime.now().hour >= 21:
|
||||||
thisdayweather = WeatherAPI().get_daily()[1]["values"]
|
thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 2))[1]
|
||||||
else:
|
else:
|
||||||
thisdayweather = WeatherAPI().get_daily()[0]["values"]
|
thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 1))[0]
|
||||||
|
|
||||||
gauge = pygal.SolidGauge(config.pygal_config, half_pie=True, inner_radius=0.70, width=width, height=height*1.55, style=config.pygal_custom_style, show_legend=False, margin_top=-height*0.58, margin_left=1, margin_right=1)
|
gauge = pygal.SolidGauge(config.pygal_config, half_pie=True, inner_radius=0.70, width=width, height=height*1.55, style=config.pygal_custom_style, show_legend=False, margin_top=-height*0.58, margin_left=1, margin_right=1)
|
||||||
percent_formatter = lambda x: '{:.10g}%'.format(x)
|
percent_formatter = lambda x: '{:.10g}%'.format(x)
|
||||||
gauge.value_formatter = percent_formatter
|
gauge.value_formatter = percent_formatter
|
||||||
|
|
||||||
if thisdayweather["precipitationProbabilityMax"] == 0 and thisdayweather["uvIndexMax"] > 4:
|
if thisdayweather["daily_chance_of_snow"] > 0:
|
||||||
gauge.add('Index UV', [{'value': thisdayweather["uvIndexMax"] * 10}])
|
gauge.add('Neige', [{'value': thisdayweather["daily_chance_of_snow"] + 1}])
|
||||||
icon_path = "wi-hot.png"
|
icon_path = "wi-snowflake-cold.png"
|
||||||
elif thisdayweather["precipitationProbabilityMax"] > 0:
|
elif thisdayweather["daily_chance_of_rain"] > 0:
|
||||||
gauge.add('Pluie', [{'value': thisdayweather["precipitationProbabilityMax"] + 1}])
|
gauge.add('Pluie', [{'value': thisdayweather["daily_chance_of_rain"] + 1}])
|
||||||
icon_path = "wi-umbrella.png"
|
icon_path = "wi-umbrella.png"
|
||||||
elif thisdayweather["windSpeedMax"] > 50:
|
elif thisdayweather["uv"] > 4:
|
||||||
gauge.add('Vent', [{'value': thisdayweather["windSpeedMax"], 'color': '#999'}])
|
gauge.add('Index UV', [{'value': thisdayweather["uv"] * 10}])
|
||||||
|
icon_path = "wi-hot.png"
|
||||||
|
elif thisdayweather["maxwind_kph"] > 50:
|
||||||
|
gauge.add('Vent', [{'value': thisdayweather["maxwind_kph"], 'color': '#999'}])
|
||||||
icon_path = "wi-strong-wind.png"
|
icon_path = "wi-strong-wind.png"
|
||||||
elif thisdayweather["visibilityMin"] < 10:
|
elif thisdayweather["avgvis_km"] < 10:
|
||||||
gauge.add('Visibilité', [{'value': thisdayweather["visibilityMin"] * 10, 'color': '#999'}])
|
gauge.add('Visibilité', [{'value': thisdayweather["avgvis_km"] * 10, 'color': '#999'}])
|
||||||
icon_path = "wi-fog.png"
|
icon_path = "wi-fog.png"
|
||||||
elif thisdayweather["cloudCoverAvg"] > 40:
|
|
||||||
gauge.add('Couverture nuageuse', [{'value': thisdayweather["cloudCoverAvg"], 'color': '#999'}])
|
|
||||||
icon_path = "wi-cloudy.png"
|
|
||||||
else:
|
else:
|
||||||
gauge.add('Pluie', [{'value': thisdayweather["precipitationProbabilityMax"]}])
|
gauge.add("Qualité de l'air", [{'value': thisdayweather["air_quality"]["gb-defra-index"] * 10}])
|
||||||
icon_path = "wi-na.png"
|
if thisdayweather["air_quality"]["gb-defra-index"] >= 5:
|
||||||
|
icon_path = "wi-smog.png"
|
||||||
|
else:
|
||||||
|
icon_path = "wi-smoke.png"
|
||||||
|
|
||||||
image = Image.open(io.BytesIO(gauge.render_to_png()))
|
image = Image.open(io.BytesIO(gauge.render_to_png()))
|
||||||
|
|
||||||
@ -204,33 +245,33 @@ class WeatherTemperatureModule:
|
|||||||
self.limit_futur = 30
|
self.limit_futur = 30
|
||||||
|
|
||||||
def draw_module(self, config, width, height):
|
def draw_module(self, config, width, height):
|
||||||
thisdayweather = WeatherAPI().get_daily()
|
thisdayweather = list(itertools.islice(WeatherAPI().get_daily(), 2))
|
||||||
if datetime.now().hour >= 19 and thisdayweather[1]["precipitationProbabilityMax"] > thisdayweather[0]["precipitationProbabilityMax"]:
|
if datetime.now().hour >= 19 and thisdayweather[1]["totalprecip_mm"] > thisdayweather[0]["totalprecip_mm"]:
|
||||||
thisdayweather = thisdayweather[1]
|
thisdayweather = thisdayweather[1]
|
||||||
else:
|
else:
|
||||||
thisdayweather = thisdayweather[0]
|
thisdayweather = thisdayweather[0]
|
||||||
|
|
||||||
hours_weather = WeatherAPI().get_hourly()
|
hours_weather = [h for h in WeatherAPI().get_hourly()]
|
||||||
|
|
||||||
hourly_min = 0
|
hourly_min = 0
|
||||||
hourly_max = 0
|
hourly_max = 0
|
||||||
for h in hours_weather:
|
for h in hours_weather:
|
||||||
if hourly_min > h["values"]["temperature"]:
|
if hourly_min > h["temp_c"]:
|
||||||
hourly_min = h["values"]["temperature"]
|
hourly_min = h["temp_c"]
|
||||||
if hourly_max < h["values"]["temperature"]:
|
if hourly_max < h["temp_c"]:
|
||||||
hourly_max = h["values"]["temperature"]
|
hourly_max = h["temp_c"]
|
||||||
|
|
||||||
line_chart = pygal.Line(config.pygal_config, interpolate='cubic', width=width+10, height=height, inverse_y_axis=False, x_label_rotation=45, range=(hourly_min, hourly_max), secondary_range=(0,100) if thisdayweather["values"]["precipitationProbabilityMax"] > 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.value_formatter = lambda x: "%d" % x
|
||||||
|
|
||||||
line_chart.x_labels = [WeatherAPI().read_timestamp(d["time"]).strftime("%Hh") if WeatherAPI().read_timestamp(d["time"]).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["values"]["temperature"] 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["values"]["precipitationProbabilityMax"] > 0:
|
if thisdayweather["totalprecip_mm"] + thisdayweather["totalsnow_cm"] > 0:
|
||||||
line_chart.add('Précipitations', [d["values"]["precipitationProbability"] for d in hours_weather[:self.limit_futur]], secondary=True, show_dots=False, fill=True if hourly_min == 0 else False)
|
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:
|
else:
|
||||||
line_chart.add('Index UV', [d["values"]["uvIndex"] 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()))
|
img = Image.open(io.BytesIO(line_chart.render_to_png()))
|
||||||
draw = ImageDraw.Draw(img)
|
draw = ImageDraw.Draw(img)
|
||||||
@ -255,60 +296,60 @@ class WeeklyWeatherModule:
|
|||||||
fnt_R = ImageFont.truetype(config.fnt_R_path, 14)
|
fnt_R = ImageFont.truetype(config.fnt_R_path, 14)
|
||||||
fnt_B = ImageFont.truetype(config.fnt_RB_path, 14)
|
fnt_B = ImageFont.truetype(config.fnt_RB_path, 14)
|
||||||
|
|
||||||
weekweather = WeatherAPI().get_daily()
|
weekweather = [d for d in WeatherAPI().get_daily()]
|
||||||
|
|
||||||
nbdays = len(weekweather[self.first_day:self.first_day+self.limit_futur])
|
nbdays = len(weekweather[self.first_day:self.first_day+self.limit_futur])
|
||||||
day_size = min(40, int(height / (nbdays + 1)))
|
day_size = min(40, int(height / (nbdays + 1)))
|
||||||
|
|
||||||
display_longtext(draw,
|
display_longtext(draw,
|
||||||
(day_size + (width - day_size)/2, day_size / 2 + 5),
|
(day_size + (width - day_size)/2, day_size / 2 + 5),
|
||||||
weekweather["summary"] if "summary" in weekweather else "",
|
weekweather["summary"] if "summary" in weekweather else "",
|
||||||
fill="black", anchor="mm", font=fnt_B,
|
fill="black", anchor="mm", font=fnt_B,
|
||||||
maxwidth=width - day_size
|
maxwidth=width - day_size
|
||||||
)
|
)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for day in weekweather[self.first_day:self.first_day+self.limit_futur]:
|
for day in weekweather[self.first_day:self.first_day+self.limit_futur]:
|
||||||
temp.append(day["values"]["temperatureMin"])
|
temp.append(day["mintemp_c"])
|
||||||
temp.append(day["values"]["temperatureMax"])
|
temp.append(day["maxtemp_c"])
|
||||||
t_min = min(temp)
|
t_min = min(temp)
|
||||||
t_max = max(temp)
|
t_max = max(temp)
|
||||||
t_scale = (width - day_size - 30) / (t_max - t_min)
|
t_scale = (width - day_size - 30) / (t_max - t_min)
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
for day in weekweather[self.first_day:self.first_day+self.limit_futur]:
|
for day in weekweather[self.first_day:self.first_day+self.limit_futur]:
|
||||||
icon = Image.open("icons/" + WeatherAPI.get_icon(day["values"]["weatherCodeMax"])).resize((day_size, day_size))
|
icon = Image.open("icons/" + WeatherAPI.get_icon(day["condition"]["code"])).resize((day_size, day_size))
|
||||||
image.paste(icon, (0, i * day_size), icon)
|
image.paste(icon, (0, i * day_size), icon)
|
||||||
|
|
||||||
draw.text(
|
draw.text(
|
||||||
(15 + 2 + day_size + int((day["values"]["temperatureMin"]-t_min)*t_scale), i*day_size + 4),
|
(15 + 2 + day_size + int((day["mintemp_c"]-t_min)*t_scale), i*day_size + 4),
|
||||||
"%d˚" % math.trunc(day["values"]["temperatureMax"]),
|
"%d˚" % math.trunc(day["mintemp_c"]),
|
||||||
fill="black", anchor="rt", font=fnt_R
|
fill="black", anchor="rt", font=fnt_R
|
||||||
)
|
)
|
||||||
summary_size = fnt_R.getsize(WeatherAPI().read_timestamp(day["time"]).strftime("%a") + " : " + (day["values"]["summary"] if "summary" in day["values"] else WeatherAPI.get_description(day["values"]["weatherCodeMax"])))[0]
|
summary_size = fnt_R.getsize(day["date"].strftime("%a") + " : " + day["condition"]["text"])[0]
|
||||||
draw.text(
|
draw.text(
|
||||||
(day_size + (width - day_size - summary_size) / 2, (i + 1) * day_size - 6),
|
(day_size + (width - day_size - summary_size) / 2, (i + 1) * day_size - 6),
|
||||||
WeatherAPI().read_timestamp(day["time"]).strftime("%a") + " : ",
|
day["date"].strftime("%a") + " : ",
|
||||||
fill="#666", anchor="ls", font=fnt_R
|
fill="#666", anchor="ls", font=fnt_R
|
||||||
)
|
)
|
||||||
draw.text(
|
draw.text(
|
||||||
(day_size + (width - day_size + summary_size) / 2, (i + 1) * day_size - 6),
|
(day_size + (width - day_size + summary_size) / 2, (i + 1) * day_size - 6),
|
||||||
day["values"]["summary"] if "summary" in day["values"] else WeatherAPI.get_description(day["values"]["weatherCodeMax"]),
|
day["condition"]["text"],
|
||||||
fill="black", anchor="rs", font=fnt_R
|
fill="black", anchor="rs", font=fnt_R
|
||||||
)
|
)
|
||||||
draw.text(
|
draw.text(
|
||||||
(day_size + int((day["values"]["temperatureMax"]-t_min)*t_scale) + 2, i * day_size + 4),
|
(day_size + int((day["maxtemp_c"]-t_min)*t_scale) + 2, i * day_size + 4),
|
||||||
"%d˚" % math.trunc(day["values"]["temperatureMax"]),
|
"%d˚" % math.trunc(day["maxtemp_c"]),
|
||||||
fill="black", anchor="lt", font=fnt_R
|
fill="black", anchor="lt", font=fnt_R
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
draw.rounded_rectangle(
|
draw.rounded_rectangle(
|
||||||
(15 + day_size + int((day["values"]["temperatureMin"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["values"]["temperatureMax"]-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")
|
radius=5, fill="black")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
draw.rectangle(
|
draw.rectangle(
|
||||||
(15 + day_size + int((day["values"]["temperatureMin"]-t_min)*t_scale),i*day_size + 4,day_size + int((day["values"]["temperatureMax"]-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")
|
fill="black")
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
@ -320,30 +361,63 @@ class WeatherAlerts:
|
|||||||
def gen_alerts(self):
|
def gen_alerts(self):
|
||||||
alerts = []
|
alerts = []
|
||||||
|
|
||||||
if WeatherAPI().has_alerts():
|
for alert in WeatherAPI().get_alerts():
|
||||||
for alert in WeatherAPI().get_alerts():
|
if alert["severity"] == "Moderate":
|
||||||
if alert["severity"] == "watch" or alert["title"].startswith("Moderate"):
|
icon = "wi-small-craft-advisory.png"
|
||||||
icon = "wi-small-craft-advisory.png"
|
elif alert["severity"] != "Moderate":
|
||||||
elif alert["severity"] == "warning":
|
icon = "wi-gale-warning.png"
|
||||||
icon = "wi-gale-warning.png"
|
else:
|
||||||
else:
|
icon = None
|
||||||
icon = None
|
|
||||||
|
|
||||||
startTime = WeatherAPI().read_timestamp(alert["time"])
|
startTime = WeatherAPI().read_timestamp(alert["effective"])
|
||||||
endTime = WeatherAPI().read_timestamp(alert["expires"])
|
endTime = WeatherAPI().read_timestamp(alert["expires"])
|
||||||
# Show alert timing if under a day
|
# Show alert timing if under a day
|
||||||
if startTime.hour != endTime.hour:
|
if startTime.hour != endTime.hour:
|
||||||
subtitle = startTime.strftime(("%x " if startTime.day != datetime.now().day else "") + "%X") + " - " + endTime.strftime(("%x " if startTime.day != endTime.day else "") + "%X")
|
subtitle = startTime.strftime(("%x " if startTime.day != datetime.now().day else "") + "%X") + " - " + endTime.strftime(("%x " if startTime.day != endTime.day else "") + "%X")
|
||||||
elif startTime.day != datetime.now().day:
|
elif startTime.day != datetime.now().day:
|
||||||
subtitle = startTime.strftime("%x")
|
subtitle = startTime.strftime("%x")
|
||||||
else:
|
else:
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
|
|
||||||
alerts.append({
|
alerts.append({
|
||||||
"icon": icon,
|
"icon": icon,
|
||||||
"title": alert["title"],
|
"title": alert["headline"],
|
||||||
"subtitle": subtitle,
|
"subtitle": subtitle,
|
||||||
"description": alert["description"],
|
"description": alert["desc"],
|
||||||
})
|
})
|
||||||
|
|
||||||
return alerts
|
return alerts
|
||||||
|
|
||||||
|
|
||||||
|
class WeatherAirQualityModule:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.label_margin = 3
|
||||||
|
|
||||||
|
def draw_module(self, config, width, height):
|
||||||
|
image = Image.new('RGB', (width, height), 'white')
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
fnt_R = ImageFont.truetype(config.fnt_R_path, 16)
|
||||||
|
fnt_B = ImageFont.truetype(config.fnt_RB_path, 20)
|
||||||
|
|
||||||
|
weather = WeatherAPI().get_currently()
|
||||||
|
|
||||||
|
infos1 = {
|
||||||
|
"CO": "%d μg/m³" % weather["air_quality"]["co"],
|
||||||
|
"O3": "%d μg/m³" % (weather["air_quality"]["o3"]),
|
||||||
|
"NO2": "%d μg/m³" % (weather["air_quality"]["no2"]),
|
||||||
|
}
|
||||||
|
infos2 = {
|
||||||
|
"SO2": "%d μg/m³" % (weather["air_quality"]["so2"]),
|
||||||
|
"PM2.5": "%d μg/m³" % (weather["air_quality"]["pm2_5"]),
|
||||||
|
"PM10": "%d μg/m³" % (weather["air_quality"]["pm10"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
txt = draw_format_array(infos1, width, height/2, fnt_R, fnt_B, self.label_margin, align_height=height/4, anchor="mm", fill="black")
|
||||||
|
image.paste(txt, (0,0), txt)
|
||||||
|
|
||||||
|
txt = draw_format_array(infos2, width, height/2, fnt_R, fnt_B, self.label_margin, align_height=height/4, anchor="mm", fill="black")
|
||||||
|
image.paste(txt, (0,int(height/2)), txt)
|
||||||
|
|
||||||
|
return image
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
import itertools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import urllib.error
|
import urllib.error
|
||||||
@ -6,33 +7,41 @@ import urllib.parse
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
class TomorrowAPI:
|
class WeatherAPI:
|
||||||
|
|
||||||
def __init__(self, apikey=None, gps=None, opts={"units": "metric"}):
|
def __init__(self, apikey=None, gps=None, opts={"lang": "fr", "units": "metric"}):
|
||||||
self.apikey = apikey or os.environ["TOMORROWAPIKEY"]
|
self.apikey = apikey or os.environ["WEATHERAPIKEY"]
|
||||||
self.baseurl = "https://api.tomorrow.io/v4"
|
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.default_gps = gps or ("GPS" in os.environ and os.environ["GPS"]) or "48.8127,2.3437"
|
||||||
self.opts = opts
|
self.opts = opts
|
||||||
|
|
||||||
self._cached_file = ".weather-%s-%s.cache"
|
self._cached_file = ".weather-%s-%s.cache"
|
||||||
|
|
||||||
|
|
||||||
def get_weather(self, apitype="forecast", gps=None):
|
def get_weather(self, apitype="forecast", gps=None, **params):
|
||||||
if gps is None:
|
if gps is None:
|
||||||
gps = self.default_gps
|
gps = self.default_gps
|
||||||
|
|
||||||
|
cached_filename = self._cached_file % (apitype + ("" if "dt" not in params else "-{}".format(params["dt"])), gps)
|
||||||
# Read the mod time
|
# Read the mod time
|
||||||
statinfo = None
|
statinfo = None
|
||||||
try:
|
try:
|
||||||
statinfo = os.stat(self._cached_file % (apitype, gps))
|
statinfo = os.stat(cached_filename)
|
||||||
except:
|
except:
|
||||||
pass
|
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):
|
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
|
# Do the request and save it
|
||||||
try:
|
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 urllib.request.urlopen(self.baseurl + "/" + apitype + ".json?" + urllib.parse.urlencode(params)) as f:
|
||||||
with open(self._cached_file % (apitype, gps), 'wb') as fd:
|
with open(cached_filename, 'wb') as fd:
|
||||||
fd.write(f.read())
|
fd.write(f.read())
|
||||||
except ConnectionResetError:
|
except ConnectionResetError:
|
||||||
pass
|
pass
|
||||||
@ -42,7 +51,7 @@ class TomorrowAPI:
|
|||||||
|
|
||||||
# Retrieve cached data
|
# Retrieve cached data
|
||||||
res = {}
|
res = {}
|
||||||
with open(self._cached_file % (apitype, gps)) as f:
|
with open(cached_filename) as f:
|
||||||
res = json.load(f)
|
res = json.load(f)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -54,366 +63,153 @@ class TomorrowAPI:
|
|||||||
return "wi-day-sunny.png"
|
return "wi-day-sunny.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-clear.png"
|
return "wi-night-clear.png"
|
||||||
elif icon == 1100:
|
elif icon == 1003:
|
||||||
|
if not night:
|
||||||
|
return "wi-day-cloudy.png"
|
||||||
|
else:
|
||||||
|
return "wi-night-alt-cloudy.png"
|
||||||
|
elif icon == 1006:
|
||||||
|
return "wi-cloudy.png"
|
||||||
|
elif icon == 1009:
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-sunny-overcast.png"
|
return "wi-day-sunny-overcast.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-alt-partly-cloudy.png"
|
return "wi-night-alt-partly-cloudy.png"
|
||||||
elif icon == 1101:
|
elif icon == 1030 or icon == 1072:
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-cloudy-high.png"
|
return "wi-day-haze.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:
|
else:
|
||||||
return "wi-night-fog.png"
|
return "wi-night-fog.png"
|
||||||
elif icon == 4000:
|
elif icon == 1063:
|
||||||
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:
|
if not night:
|
||||||
return "wi-day-rain.png"
|
return "wi-day-rain.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-alt-rain.png"
|
return "wi-night-alt-rain.png"
|
||||||
elif icon == 5001 or icon == 5100 or icon == 5000 or icon == 5101:
|
elif icon == 1066:
|
||||||
return "wi-snow.png"
|
if not night:
|
||||||
elif icon == 5115 or icon == 5116:
|
return "wi-day-snow.png"
|
||||||
|
else:
|
||||||
|
return "wi-night-alt-snow.png"
|
||||||
|
elif icon == 1069 or icon == 1204:
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-sleet.png"
|
return "wi-day-sleet.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-sleet.png"
|
return "wi-night-alt-sleet.png"
|
||||||
elif icon == 5122 or icon == 5110 or icon == 5108 or icon == 5114 or icon == 5112:
|
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"
|
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:
|
elif icon == 1210 or icon == 1213 or icon == 1216 or icon == 1255:
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-snow.png"
|
return "wi-day-snow.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-snow.png"
|
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:
|
elif icon == 1237:
|
||||||
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"
|
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:
|
elif icon == 1261 or icon == 1264:
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-hail.png"
|
return "wi-day-hail.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-hail.png"
|
return "wi-night-hail.png"
|
||||||
elif icon == 8000:
|
elif icon == 1273 or icon == 1276:
|
||||||
return "wi-thunderstorm.png"
|
|
||||||
elif icon == 8001 or icon == 8003 or icon == 8002:
|
|
||||||
if not night:
|
if not night:
|
||||||
return "wi-day-thunderstorm.png"
|
return "wi-day-thunderstorm.png"
|
||||||
else:
|
else:
|
||||||
return "wi-night-thunderstorm.png"
|
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:
|
else:
|
||||||
return "wi-alien.png"
|
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):
|
def get_moon_icon(self, day=0):
|
||||||
moon_phase = self.get_daily()["data"][day]["moonPhase"]
|
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"
|
return "wi-moon-alt-new.png"
|
||||||
elif moon_phase < 0.071:
|
elif moon_phase == "Waxing Crescent":
|
||||||
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"
|
return "wi-moon-alt-waxing-crescent-4.png"
|
||||||
elif moon_phase < 0.214:
|
elif moon_phase == "First Quarter":
|
||||||
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"
|
return "wi-moon-alt-first-quarter.png"
|
||||||
elif moon_phase < 0.321:
|
elif moon_phase == "Waxing Gibbous":
|
||||||
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"
|
return "wi-moon-alt-waxing-gibbous-4.png"
|
||||||
elif moon_phase < 0.464:
|
elif moon_phase == "Full Moon":
|
||||||
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"
|
return "wi-moon-alt-full.png"
|
||||||
elif moon_phase < 0.571:
|
elif moon_phase == "Waning Gibbous":
|
||||||
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"
|
return "wi-moon-alt-waning-gibbous-4.png"
|
||||||
elif moon_phase < 0.714:
|
elif moon_phase == "Last Quarter":
|
||||||
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"
|
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:
|
else:
|
||||||
return "wi-moon-alt-waning-crescent-6.png"
|
return "wi-moon-alt-waning-crescent-3.png"
|
||||||
|
|
||||||
|
|
||||||
def get_currently(self, *args, **kwargs):
|
def get_currently(self, *args, **kwargs):
|
||||||
return self.get_weather("realtime", *args, **kwargs)["data"]["values"]
|
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"))["forecast"]["forecastday"][0]
|
||||||
|
v["day"]["date"] = enddt
|
||||||
|
yield v
|
||||||
|
|
||||||
def get_hourly(self, *args, **kwargs):
|
def get_hourly(self, *args, **kwargs):
|
||||||
return self.get_weather(*args, **kwargs)["timelines"]["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):
|
def get_daily(self, *args, **kwargs):
|
||||||
return self.get_weather(*args, **kwargs)["timelines"]["daily"]
|
for d in self.get_forecast(*args, **kwargs):
|
||||||
|
yield {
|
||||||
def has_alerts(self, *args, **kwargs):
|
**d["day"],
|
||||||
return "alerts" in self.get_weather(*args, **kwargs) and len(self.get_weather(*args, **kwargs)["alerts"]) > 0
|
**d["astro"]
|
||||||
|
}
|
||||||
|
|
||||||
def get_alerts(self, *args, **kwargs):
|
def get_alerts(self, *args, **kwargs):
|
||||||
return self.get_weather(*args, **kwargs)["alerts"]
|
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):
|
def read_timestamp(self, timestamp, *args, **kwargs):
|
||||||
PARIS = ZoneInfo("Europe/Paris")
|
PARIS = ZoneInfo("Europe/Paris")
|
||||||
return datetime.fromisoformat(timestamp.replace("Z", "+00:00")).astimezone(PARIS)
|
return datetime.fromtimestamp(timestamp, tz=timezone.utc).astimezone(PARIS)
|
||||||
|
|
||||||
|
|
||||||
WeatherAPI = TomorrowAPI
|
#WeatherAPI = TomorrowAPI
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
dsa = TomorrowAPI()
|
dsa = WeatherAPI()
|
||||||
print(dsa.get_currently())
|
print(json.dumps(dsa.get_currently()))
|
||||||
print(dsa.get_daily())
|
#print(json.dumps([d for d in dsa.get_daily()][0]))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user