2022-08-11 12:46:36 +00:00
from datetime import datetime , timezone , timedelta
import io
import math
from PIL import Image , ImageDraw , ImageFont
import pygal
2022-08-14 13:32:52 +00:00
from . import display_longtext
2022-08-11 12:46:36 +00:00
from . weather_api import WeatherAPI
def draw_format_infos ( 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
for k , v in infos . items ( ) :
size + = fnt_R . getsize ( k ) [ 0 ]
size + = label_margin
size + = fnt_B . getsize ( v ) [ 0 ]
margin = ( width - size ) / nb_infos
align = 0
if margin_bf_first :
align + = margin / 2
for k , v in infos . items ( ) :
draw . text (
( align , align_height ) ,
k ,
font = fnt_R , * * kwargs
)
align + = fnt_R . getsize ( k ) [ 0 ] + label_margin
draw . text (
( align , align_height ) ,
v ,
font = fnt_B , * * kwargs
)
align + = fnt_B . getsize ( v ) [ 0 ] + margin
return image
class WeatherToolbarModule :
def __init__ ( self ) :
self . label_margin = 3
def draw_module ( self , config , width , height ) :
image = Image . new ( ' RGB ' , ( width , height ) , ' black ' )
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 ( )
infos = {
" Vent " : " %d km/h " % weather [ " windSpeed " ] ,
" Humidité " : " %d %% " % ( weather [ " humidity " ] * 100 ) ,
" Indice UV " : str ( weather [ " uvIndex " ] ) ,
" Pression " : " %d hPa " % weather [ " pressure " ] ,
}
txt = draw_format_infos ( infos , width , height , fnt_R , fnt_B , self . label_margin , align_height = height / 2 , anchor = " lm " )
image . paste ( txt , ( 0 , 0 ) , txt )
return image
class WeatherJumboCurrentModule :
def __init__ ( self ) :
self . middle_align = 1 / 3
def draw_module ( self , config , width , height ) :
2022-08-14 16:33:32 +00:00
image = Image . new ( ' RGB ' , ( width , height ) , ' #fff ' )
2022-08-11 12:46:36 +00:00
draw = ImageDraw . Draw ( image )
2022-08-11 14:51:20 +00:00
fnt_Big = ImageFont . truetype ( config . fnt_RB_path , 33 )
2022-08-11 12:46:36 +00:00
fnt_R = ImageFont . truetype ( config . fnt_R_path , 16 )
fnt_B = ImageFont . truetype ( config . fnt_RB_path , 16 )
# current
curweather = WeatherAPI ( ) . get_currently ( )
2022-08-12 10:48:54 +00:00
icon = Image . open ( " icons/ " + WeatherAPI . get_icon ( curweather [ " icon " ] , current = True ) ) . resize ( ( height , height ) )
2022-08-11 12:46:36 +00:00
image . paste ( icon , ( int ( width * self . middle_align - height ) , 0 ) , icon )
draw . text (
( width * self . middle_align , height / 3 ) ,
" %d ˚ %s . " % ( math . trunc ( curweather [ " temperature " ] ) , curweather [ " summary " ] ) ,
2022-08-14 16:25:06 +00:00
fill = " black " , anchor = " ld " , font = fnt_Big
2022-08-11 12:46:36 +00:00
)
thisdayweather = WeatherAPI ( ) . get_daily ( ) [ " data " ] [ 0 ]
infos = {
" Ressentie " : " %d ˚ " % curweather [ " apparentTemperature " ] ,
" Minimale " : " %d ˚ " % thisdayweather [ " temperatureLow " ] ,
" Maximale " : " %d ˚ " % thisdayweather [ " temperatureHigh " ] ,
}
txt = draw_format_infos ( infos , ( 1 - self . middle_align ) * width , 20 , fnt_R , fnt_B , 5 , margin_bf_first = False , fill = " black " , anchor = " lt " )
image . paste ( txt , ( int ( self . middle_align * width ) , int ( height / 2.6 ) ) , txt )
# day
fnt_Rig = ImageFont . truetype ( config . fnt_R_path , 20 )
dayweather = WeatherAPI ( ) . get_hourly ( )
2022-08-14 11:33:07 +00:00
if dayweather [ " data " ] [ 0 ] [ " summary " ] != dayweather [ " data " ] [ 1 ] [ " summary " ] :
display_longtext ( draw , ( width * self . middle_align , height / 1.28 ) , dayweather [ " data " ] [ 1 ] [ " summary " ] + " la prochaine heure. \n " + dayweather [ " summary " ] , fill = " black " , anchor = " lm " , font = fnt_Rig , maxwidth = ( 1 - self . middle_align ) * width )
else :
display_longtext ( draw , ( width * self . middle_align , height / 1.28 ) , dayweather [ " summary " ] , fill = " black " , anchor = " lm " , font = fnt_Rig , maxwidth = ( 1 - self . middle_align ) * width )
2022-08-11 12:46:36 +00:00
return image
2022-08-14 11:32:52 +00:00
class WeatherMoonPhaseModule :
2022-08-11 12:46:36 +00:00
def draw_module ( self , config , width , height ) :
2022-08-14 11:32:52 +00:00
image = Image . new ( ' RGBA ' , ( width , height ) , ' #fff0 ' )
2022-08-11 12:46:36 +00:00
icon = Image . open ( " icons/ " + WeatherAPI ( ) . get_moon_icon ( ) ) . resize ( ( height , height ) )
image . paste ( icon , ( 0 , 0 ) , icon )
2022-08-14 11:32:52 +00:00
return image
class WeatherSunModule :
def draw_module ( self , config , width , height , start_align = 0 ) :
image = Image . new ( ' RGB ' , ( width , height ) , ' #fff ' )
draw = ImageDraw . Draw ( image )
fnt_R = ImageFont . truetype ( config . fnt_R_path , int ( height * 0.7 ) )
2022-08-11 12:46:36 +00:00
thisdayweather = WeatherAPI ( ) . get_daily ( ) [ " data " ] [ 0 ]
import time
infos = {
2022-08-12 10:48:39 +00:00
" sunrise " : WeatherAPI ( ) . read_timestamp ( thisdayweather [ " sunriseTime " ] ) . strftime ( " %X " ) ,
" sunset " : WeatherAPI ( ) . read_timestamp ( thisdayweather [ " sunsetTime " ] ) . strftime ( " %X " ) ,
2022-08-11 12:46:36 +00:00
}
2022-08-14 11:32:52 +00:00
align = start_align
2022-08-11 12:46:36 +00:00
for icon , info in infos . items ( ) :
if info == " " :
continue
2022-08-14 11:32:52 +00:00
icon = Image . open ( " icons/wi- " + icon + " .png " ) . resize ( ( height , height ) )
image . paste ( icon , ( align , 0 ) , icon )
align + = height + 2
2022-08-11 12:46:36 +00:00
draw . text (
2022-08-14 11:32:52 +00:00
( align , height / 2 ) ,
2022-08-11 12:46:36 +00:00
info ,
fill = " black " , anchor = " lm " , font = fnt_R
)
2022-08-14 11:32:52 +00:00
align + = fnt_R . getsize ( info ) [ 0 ]
align + = 10
2022-08-11 12:46:36 +00:00
return image
2022-08-14 11:32:52 +00:00
2022-08-11 12:46:36 +00:00
class WeatherRainModule :
def draw_module ( self , config , width , height ) :
2022-08-13 21:41:00 +00:00
if datetime . now ( ) . hour > = 21 :
thisdayweather = WeatherAPI ( ) . get_daily ( ) [ " data " ] [ 1 ]
else :
thisdayweather = WeatherAPI ( ) . get_daily ( ) [ " data " ] [ 0 ]
2022-08-11 12:46:36 +00:00
2022-08-14 11:31:54 +00:00
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 )
2022-08-11 12:46:36 +00:00
percent_formatter = lambda x : ' {:.10g} % ' . format ( x )
gauge . value_formatter = percent_formatter
2022-08-11 13:19:51 +00:00
if thisdayweather [ " precipProbability " ] == 0 and thisdayweather [ " uvIndex " ] > 4 :
gauge . add ( ' Index UV ' , [ { ' value ' : thisdayweather [ " uvIndex " ] * 10 } ] )
icon_path = " wi-hot.png "
else :
gauge . add ( ' Pluie ' , [ { ' value ' : thisdayweather [ " precipProbability " ] * 100 + 1 } ] )
if thisdayweather [ " precipProbability " ] > 0 :
icon_path = " wi-umbrella.png "
else :
icon_path = " wi-na.png "
2022-08-11 12:46:36 +00:00
image = Image . open ( io . BytesIO ( gauge . render_to_png ( ) ) )
2022-08-11 13:19:51 +00:00
if icon_path :
icon = Image . open ( " icons/ " + icon_path ) . resize ( ( int ( height / 1.25 ) , int ( height / 1.25 ) ) )
image . paste ( icon , ( int ( width / 2 - height / 2.5 ) , int ( height - height / 1.25 ) ) , icon )
2022-08-11 12:46:36 +00:00
return image
class WeatherTemperatureModule :
def __init__ ( self ) :
self . limit_futur = 30
def draw_module ( self , config , width , height ) :
2022-08-13 21:41:00 +00:00
thisdayweather = WeatherAPI ( ) . get_daily ( ) [ " data " ]
if datetime . now ( ) . hour > = 19 and thisdayweather [ 1 ] [ " precipProbability " ] > thisdayweather [ 0 ] [ " precipProbability " ] :
thisdayweather = thisdayweather [ 1 ]
else :
thisdayweather = thisdayweather [ 0 ]
2022-08-11 12:46:36 +00:00
hours_weather = WeatherAPI ( ) . get_hourly ( )
2022-08-14 11:31:54 +00:00
hourly_min = 0
hourly_max = 0
for h in hours_weather [ " data " ] :
if hourly_min > h [ " temperature " ] :
hourly_min = h [ " temperature " ]
if hourly_max < h [ " temperature " ] :
hourly_max = h [ " temperature " ]
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 [ " precipProbability " ] > 0 else ( 0 , 10 ) , * * config . charts_opts )
line_chart . value_formatter = lambda x : " %d " % x
2022-08-12 10:48:39 +00:00
line_chart . x_labels = [ WeatherAPI ( ) . read_timestamp ( d [ " time " ] ) . strftime ( " % Hh " ) if datetime . fromtimestamp ( d [ " time " ] ) . hour % 2 == 0 else " " for d in hours_weather [ " data " ] [ : self . limit_futur ] ]
2022-08-11 12:46:36 +00:00
line_chart . add ( ' Températures ' , [ d [ " temperature " ] for d in hours_weather [ " data " ] [ : self . limit_futur ] ] , show_dots = False )
if thisdayweather [ " precipProbability " ] > 0 :
2022-08-14 11:31:54 +00:00
line_chart . add ( ' Précipitations ' , [ d [ " precipProbability " ] * 100 for d in hours_weather [ " data " ] [ : self . limit_futur ] ] , secondary = True , show_dots = False , fill = True if hourly_min == 0 else False )
2022-08-11 12:46:36 +00:00
else :
2022-08-11 13:20:48 +00:00
line_chart . add ( ' Index UV ' , [ d [ " uvIndex " ] for d in hours_weather [ " data " ] [ : self . limit_futur ] ] , secondary = True , show_dots = False )
2022-08-11 12:46:36 +00:00
return Image . open ( io . BytesIO ( line_chart . render_to_png ( ) ) )
class WeeklyWeatherModule :
def __init__ ( self ) :
self . first_day = 1
self . limit_futur = 7
def draw_module ( self , config , width , height ) :
image = Image . new ( ' RGB ' , ( width , height ) , ' white ' )
draw = ImageDraw . Draw ( image )
2022-08-12 11:04:21 +00:00
fnt_R = ImageFont . truetype ( config . fnt_R_path , 14 )
fnt_B = ImageFont . truetype ( config . fnt_RB_path , 14 )
2022-08-11 12:46:36 +00:00
weekweather = WeatherAPI ( ) . get_daily ( )
daysweather = weekweather [ " data " ]
nbdays = len ( daysweather [ self . first_day : self . first_day + self . limit_futur ] )
day_size = int ( height / ( nbdays + 1 ) )
2022-08-12 11:04:21 +00:00
display_longtext ( draw ,
( day_size + ( width - day_size ) / 2 , day_size / 2 + 5 ) ,
weekweather [ " summary " ] ,
fill = " black " , anchor = " mm " , font = fnt_B ,
maxwidth = width - day_size
2022-08-11 12:46:36 +00:00
)
temp = [ ]
for day in daysweather [ self . first_day : self . first_day + self . limit_futur ] :
temp . append ( day [ " temperatureLow " ] )
temp . append ( day [ " temperatureHigh " ] )
t_min = min ( temp )
t_max = max ( temp )
t_scale = ( width - day_size - 30 ) / ( t_max - t_min )
i = 1
for day in daysweather [ self . first_day : self . first_day + self . limit_futur ] :
icon = Image . open ( " icons/ " + WeatherAPI . get_icon ( day [ " icon " ] ) ) . resize ( ( day_size , day_size ) )
image . paste ( icon , ( 0 , i * day_size ) , icon )
draw . text (
( 15 + 2 + day_size + int ( ( day [ " temperatureLow " ] - t_min ) * t_scale ) , i * day_size + 4 ) ,
" %d ˚ " % math . trunc ( day [ " temperatureLow " ] ) ,
fill = " black " , anchor = " rt " , font = fnt_R
)
2022-08-12 11:04:21 +00:00
summary_size = fnt_R . getsize ( WeatherAPI ( ) . read_timestamp ( day [ " time " ] ) . strftime ( " %a " ) + " : " + day [ " summary " ] ) [ 0 ]
draw . text (
( day_size + ( width - day_size - summary_size ) / 2 , ( i + 1 ) * day_size - 6 ) ,
WeatherAPI ( ) . read_timestamp ( day [ " time " ] ) . strftime ( " %a " ) + " : " ,
fill = " #666 " , anchor = " ls " , font = fnt_R
)
2022-08-11 12:46:36 +00:00
draw . text (
2022-08-12 11:04:21 +00:00
( day_size + ( width - day_size + summary_size ) / 2 , ( i + 1 ) * day_size - 6 ) ,
2022-08-11 12:46:36 +00:00
day [ " summary " ] ,
2022-08-12 11:04:21 +00:00
fill = " black " , anchor = " rs " , font = fnt_R
2022-08-11 12:46:36 +00:00
)
draw . text (
( day_size + int ( ( day [ " temperatureHigh " ] - t_min ) * t_scale ) + 2 , i * day_size + 4 ) ,
" %d ˚ " % math . trunc ( day [ " temperatureHigh " ] ) ,
fill = " black " , anchor = " lt " , font = fnt_R
)
try :
draw . rounded_rectangle (
( 15 + day_size + int ( ( day [ " temperatureLow " ] - t_min ) * t_scale ) , i * day_size + 4 , day_size + int ( ( day [ " temperatureHigh " ] - t_min ) * t_scale ) , i * day_size + 14 ) ,
radius = 5 , fill = " black " )
except AttributeError :
draw . rectangle (
( 15 + day_size + int ( ( day [ " temperatureLow " ] - t_min ) * t_scale ) , i * day_size + 4 , day_size + int ( ( day [ " temperatureHigh " ] - t_min ) * t_scale ) , i * day_size + 14 ) ,
fill = " black " )
i + = 1
return image
2022-08-11 14:09:01 +00:00
2022-08-14 13:32:52 +00:00
class WeatherAlerts :
2022-08-11 14:09:01 +00:00
2022-08-14 13:32:52 +00:00
def gen_alerts ( self ) :
alerts = [ ]
2022-08-11 14:09:01 +00:00
if WeatherAPI ( ) . has_alerts ( ) :
2022-08-14 13:32:52 +00:00
for alert in WeatherAPI ( ) . get_alerts ( ) :
2022-08-11 14:35:37 +00:00
if alert [ " severity " ] == " watch " or alert [ " title " ] . startswith ( " Moderate " ) :
2022-08-11 14:09:01 +00:00
icon = " wi-small-craft-advisory.png "
elif alert [ " severity " ] == " warning " :
icon = " wi-gale-warning.png "
else :
icon = None
2022-08-12 10:48:39 +00:00
startTime = WeatherAPI ( ) . read_timestamp ( alert [ " time " ] )
endTime = WeatherAPI ( ) . read_timestamp ( alert [ " expires " ] )
2022-08-11 14:09:01 +00:00
# Show alert timing if under a day
if startTime . hour != endTime . hour :
2022-08-14 13:32:52 +00:00
subtitle = startTime . strftime ( ( " %x " if startTime . day != datetime . now ( ) . day else " " ) + " %X " ) + " - " + endTime . strftime ( ( " %x " if startTime . day != endTime . day else " " ) + " %X " )
2022-08-11 14:09:01 +00:00
elif startTime . day != datetime . now ( ) . day :
2022-08-14 13:32:52 +00:00
subtitle = startTime . strftime ( " %x " )
else :
subtitle = " "
2022-08-11 14:09:01 +00:00
2022-08-14 13:32:52 +00:00
alerts . append ( {
" icon " : icon ,
" title " : alert [ " title " ] ,
" subtitle " : subtitle ,
" description " : alert [ " description " ] ,
} )
2022-08-11 14:09:01 +00:00
2022-08-14 13:32:52 +00:00
return alerts