2022-08-11 12:46:36 +00:00
from datetime import datetime , timezone , timedelta
import io
2023-04-18 12:31:25 +00:00
import itertools
2022-08-11 12:46:36 +00:00
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
2023-04-18 13:17:17 +00:00
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
2022-08-11 12:46:36 +00:00
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 = {
2023-04-18 12:31:25 +00:00
" Vent " : " %d km/h " % weather [ " wind_kph " ] ,
2023-04-09 13:33:43 +00:00
" Humidité " : " %d %% " % ( weather [ " humidity " ] ) ,
2023-04-18 12:31:25 +00:00
" Indice UV " : str ( int ( weather [ " uv " ] ) ) ,
" Pression " : " %d hPa " % weather [ " pressure_mb " ] ,
2022-08-11 12:46:36 +00:00
}
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 ( )
2023-04-18 12:31:25 +00:00
icon = Image . open ( " icons/ " + WeatherAPI . get_icon ( curweather [ " condition " ] [ " code " ] , night = not curweather [ " is_day " ] , 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 ) ,
2023-04-18 12:31:25 +00:00
" %d ˚ %s . " % ( math . trunc ( curweather [ " temp_c " ] ) , curweather [ " condition " ] [ " text " ] ) ,
2022-08-14 16:25:06 +00:00
fill = " black " , anchor = " ld " , font = fnt_Big
2022-08-11 12:46:36 +00:00
)
2023-04-18 12:31:25 +00:00
thisdayweather = list ( itertools . islice ( WeatherAPI ( ) . get_daily ( ) , 1 ) ) [ 0 ]
2022-08-11 12:46:36 +00:00
infos = {
2023-04-18 12:31:25 +00:00
" Ressentie " : " %d ˚ " % curweather [ " feelslike_c " ] ,
" Minimale " : " %d ˚ " % thisdayweather [ " mintemp_c " ] ,
" Maximale " : " %d ˚ " % thisdayweather [ " maxtemp_c " ] ,
2022-08-11 12:46:36 +00:00
}
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 )
2023-04-18 12:31:25 +00:00
dayweather = list ( itertools . islice ( WeatherAPI ( ) . get_hourly ( ) , 2 ) )
2022-08-11 12:46:36 +00:00
2023-04-18 12:31:25 +00:00
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 )
2022-08-14 11:33:07 +00:00
else :
2023-04-18 12:31:25 +00:00
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 )
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 ) )
2023-04-18 12:31:25 +00:00
thisdayweather = list ( itertools . islice ( WeatherAPI ( ) . get_daily ( ) , 1 ) ) [ 0 ]
2022-08-11 12:46:36 +00:00
import time
infos = {
2023-04-18 12:31:25 +00:00
" sunrise " : thisdayweather [ " sunrise " ] ,
" sunset " : thisdayweather [ " sunset " ] ,
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 :
2023-04-18 12:31:25 +00:00
thisdayweather = list ( itertools . islice ( WeatherAPI ( ) . get_daily ( ) , 2 ) ) [ 1 ]
2022-08-13 21:41:00 +00:00
else :
2023-04-18 12:31:25 +00:00
thisdayweather = list ( itertools . islice ( WeatherAPI ( ) . get_daily ( ) , 1 ) ) [ 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
2023-04-18 12:31:25 +00:00
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 } ] )
2022-08-21 10:42:01 +00:00
icon_path = " wi-umbrella.png "
2023-04-18 12:31:25 +00:00
elif thisdayweather [ " uv " ] > 4 :
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 ' } ] )
2022-08-21 10:42:01 +00:00
icon_path = " wi-strong-wind.png "
2023-04-18 12:31:25 +00:00
elif thisdayweather [ " avgvis_km " ] < 10 :
gauge . add ( ' Visibilité ' , [ { ' value ' : thisdayweather [ " avgvis_km " ] * 10 , ' color ' : ' #999 ' } ] )
2022-08-21 10:42:01 +00:00
icon_path = " wi-fog.png "
2023-05-03 10:34:29 +00:00
elif " air_quality " in thisdayweather :
2023-04-18 12:31:25 +00:00
gauge . add ( " Qualité de l ' air " , [ { ' value ' : thisdayweather [ " air_quality " ] [ " gb-defra-index " ] * 10 } ] )
if thisdayweather [ " air_quality " ] [ " gb-defra-index " ] > = 5 :
icon_path = " wi-smog.png "
else :
icon_path = " wi-smoke.png "
2023-05-03 10:34:29 +00:00
else :
gauge . add ( ' Vent ' , [ { ' value ' : thisdayweather [ " maxwind_kph " ] , ' color ' : ' #999 ' } ] )
icon_path = " wi-wind-beaufort-1.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 ) :
2023-04-18 12:31:25 +00:00
thisdayweather = list ( itertools . islice ( WeatherAPI ( ) . get_daily ( ) , 2 ) )
if datetime . now ( ) . hour > = 19 and thisdayweather [ 1 ] [ " totalprecip_mm " ] > thisdayweather [ 0 ] [ " totalprecip_mm " ] :
2022-08-13 21:41:00 +00:00
thisdayweather = thisdayweather [ 1 ]
else :
thisdayweather = thisdayweather [ 0 ]
2022-08-11 12:46:36 +00:00
2023-04-18 12:31:25 +00:00
hours_weather = [ h for h in WeatherAPI ( ) . get_hourly ( ) ]
2022-08-11 12:46:36 +00:00
2022-08-14 11:31:54 +00:00
hourly_min = 0
hourly_max = 0
2023-04-09 13:33:43 +00:00
for h in hours_weather :
2023-04-18 12:31:25 +00:00
if hourly_min > h [ " temp_c " ] :
hourly_min = h [ " temp_c " ]
if hourly_max < h [ " temp_c " ] :
hourly_max = h [ " temp_c " ]
2022-08-14 11:31:54 +00:00
2023-04-18 12:31:25 +00:00
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 )
2022-08-14 11:31:54 +00:00
line_chart . value_formatter = lambda x : " %d " % x
2023-04-18 12:31:25 +00:00
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 ] ]
2022-08-11 12:46:36 +00:00
2023-04-18 12:31:25 +00:00
line_chart . add ( ' Températures ' , [ d [ " temp_c " ] for d in hours_weather [ : self . limit_futur ] ] , show_dots = False )
2022-08-11 12:46:36 +00:00
2023-04-18 12:31:25 +00:00
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 )
2022-08-11 12:46:36 +00:00
else :
2023-04-18 12:31:25 +00:00
line_chart . add ( ' Index UV ' , [ d [ " uv " ] for d in hours_weather [ : self . limit_futur ] ] , secondary = True , show_dots = False )
2023-04-09 13:33:43 +00:00
img = Image . open ( io . BytesIO ( line_chart . render_to_png ( ) ) )
draw = ImageDraw . Draw ( img )
# Add EcoWatt signal
#for ecowatt
#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")
2022-08-11 12:46:36 +00:00
2023-04-09 13:33:43 +00:00
return img
2022-08-11 12:46:36 +00:00
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
2023-04-18 12:31:25 +00:00
weekweather = [ d for d in WeatherAPI ( ) . get_daily ( ) ]
2022-08-11 12:46:36 +00:00
2023-04-09 13:33:43 +00:00
nbdays = len ( weekweather [ self . first_day : self . first_day + self . limit_futur ] )
day_size = min ( 40 , int ( height / ( nbdays + 1 ) ) )
2022-08-11 12:46:36 +00:00
2022-08-12 11:04:21 +00:00
display_longtext ( draw ,
( day_size + ( width - day_size ) / 2 , day_size / 2 + 5 ) ,
2023-04-18 12:31:25 +00:00
weekweather [ " summary " ] if " summary " in weekweather else " " ,
2022-08-12 11:04:21 +00:00
fill = " black " , anchor = " mm " , font = fnt_B ,
maxwidth = width - day_size
2022-08-11 12:46:36 +00:00
)
temp = [ ]
2023-04-09 13:33:43 +00:00
for day in weekweather [ self . first_day : self . first_day + self . limit_futur ] :
2023-04-18 12:31:25 +00:00
temp . append ( day [ " mintemp_c " ] )
temp . append ( day [ " maxtemp_c " ] )
2022-08-11 12:46:36 +00:00
t_min = min ( temp )
t_max = max ( temp )
t_scale = ( width - day_size - 30 ) / ( t_max - t_min )
i = 1
2023-04-09 13:33:43 +00:00
for day in weekweather [ self . first_day : self . first_day + self . limit_futur ] :
2023-04-18 12:31:25 +00:00
icon = Image . open ( " icons/ " + WeatherAPI . get_icon ( day [ " condition " ] [ " code " ] ) ) . resize ( ( day_size , day_size ) )
2022-08-11 12:46:36 +00:00
image . paste ( icon , ( 0 , i * day_size ) , icon )
draw . text (
2023-04-18 12:31:25 +00:00
( 15 + 2 + day_size + int ( ( day [ " mintemp_c " ] - t_min ) * t_scale ) , i * day_size + 4 ) ,
" %d ˚ " % math . trunc ( day [ " mintemp_c " ] ) ,
2022-08-11 12:46:36 +00:00
fill = " black " , anchor = " rt " , font = fnt_R
)
2023-04-18 12:31:25 +00:00
summary_size = fnt_R . getsize ( day [ " date " ] . strftime ( " %a " ) + " : " + day [ " condition " ] [ " text " ] ) [ 0 ]
2022-08-12 11:04:21 +00:00
draw . text (
( day_size + ( width - day_size - summary_size ) / 2 , ( i + 1 ) * day_size - 6 ) ,
2023-04-18 12:31:25 +00:00
day [ " date " ] . strftime ( " %a " ) + " : " ,
2022-08-12 11:04:21 +00:00
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 ) ,
2023-04-18 12:31:25 +00:00
day [ " condition " ] [ " text " ] ,
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 (
2023-04-18 12:31:25 +00:00
( day_size + int ( ( day [ " maxtemp_c " ] - t_min ) * t_scale ) + 2 , i * day_size + 4 ) ,
" %d ˚ " % math . trunc ( day [ " maxtemp_c " ] ) ,
2022-08-11 12:46:36 +00:00
fill = " black " , anchor = " lt " , font = fnt_R
)
try :
draw . rounded_rectangle (
2023-04-18 12:31:25 +00:00
( 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 ) ,
2022-08-11 12:46:36 +00:00
radius = 5 , fill = " black " )
except AttributeError :
draw . rectangle (
2023-04-18 12:31:25 +00:00
( 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 ) ,
2022-08-11 12:46:36 +00:00
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
2023-04-18 12:31:25 +00:00
for alert in WeatherAPI ( ) . get_alerts ( ) :
if alert [ " severity " ] == " Moderate " :
icon = " wi-small-craft-advisory.png "
elif alert [ " severity " ] != " Moderate " :
icon = " wi-gale-warning.png "
else :
icon = None
startTime = WeatherAPI ( ) . read_timestamp ( alert [ " effective " ] )
endTime = WeatherAPI ( ) . read_timestamp ( alert [ " expires " ] )
# Show alert timing if under a day
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 " )
elif startTime . day != datetime . now ( ) . day :
subtitle = startTime . strftime ( " %x " )
else :
subtitle = " "
alerts . append ( {
" icon " : icon ,
" title " : alert [ " headline " ] ,
" subtitle " : subtitle ,
" description " : alert [ " desc " ] ,
} )
2022-08-11 14:09:01 +00:00
2022-08-14 13:32:52 +00:00
return alerts
2023-04-18 13:17:17 +00:00
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