class Inkywhatrbw(Observer): def __init__(self, observable, mode): super().__init__(observable=observable) self.inky_display = InkyWHAT("red") self.inky_display.set_border(self.inky_display.WHITE) self.image = Image.new('P', (SCREEN_WIDTH, SCREEN_HEIGHT)) self.mode = mode def form_image(self, prices): WHITE = self.inky_display.WHITE RED = self.inky_display.RED BLACK = self.inky_display.BLACK screen_draw = ImageDraw.Draw(self.image) screen_draw.rectangle([0,0,SCREEN_WIDTH,SCREEN_HEIGHT],fill=WHITE) if self.mode == "candle": Plot.candle(prices, size=(SCREEN_WIDTH - LEFT_MARGIN, SCREEN_HEIGHT - BOTTOM_MARGIN), position=(LEFT_MARGIN, 0), draw=screen_draw, fill_neg=RED, fill_pos=BLACK) else: last_prices = [x[3] for x in prices] Plot.line(last_prices, size=(SCREEN_WIDTH - LEFT_MARGIN, SCREEN_HEIGHT - BOTTOM_MARGIN), position=(LEFT_MARGIN, 0), draw=screen_draw, fill=BLACK) flatten_prices = [item for sublist in prices for item in sublist] Plot.y_axis_labels(flatten_prices, FONT_SMALL, (0, 0), (LEFT_MARGIN, SCREEN_HEIGHT - BOTTOM_MARGIN - SMALL_FONT_SIZE - 3), draw=screen_draw, fill=BLACK) screen_draw.line([(0, SCREEN_HEIGHT - BOTTOM_MARGIN), (SCREEN_WIDTH, SCREEN_HEIGHT - BOTTOM_MARGIN)], fill=BLACK) screen_draw.line([(LEFT_MARGIN, 0), (LEFT_MARGIN, SCREEN_HEIGHT - BOTTOM_MARGIN)], fill=BLACK) Plot.caption(flatten_prices[len(flatten_prices) - 1], SCREEN_HEIGHT - BOTTOM_MARGIN, SCREEN_WIDTH, FONT_LARGE, screen_draw, fill=BLACK, currency_offset=LEFT_MARGIN, price_offset=LEFT_MARGIN) def update(self, data): self.form_image(data) self.inky_display.set_image(self.image) self.inky_display.show() def close(self): pass
def inky_show(im): im = Image.open(im) im = im.transpose(Image.ROTATE_90) inky_display = InkyWHAT("black") inky_display.set_border(inky_display.WHITE) assert inky_display.WIDTH == 400 assert inky_display.HEIGHT == 300 inky_display.set_image(im) inky_display.show()
def send_to_display(): # TODO: do only in python... was having some issues with svg2png output_svg_path = os.path.abspath("output.svg") output_png_path = os.path.abspath("output.png") os.system( f"inkscape -z -w 400 -h 300 {output_svg_path} -e {output_png_path}") img = Image.open(output_png_path) pal_img = Image.new("P", (1, 1)) pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252) img = img.convert("RGB").quantize(palette=pal_img) # send inky_display = InkyWHAT("red") inky_display.set_border(inky_display.RED) inky_display.set_image(img) inky_display.show()
def glyphe(): # Load the icons and rotate them each time. This gives us # a horoscope which rotates during the day, potentially # making us think differently about their meaning print("Starting...") icons = pickle.load( open( "glyphes.pickle", "rb" ) ) print(icons) icons.insert(0, icons.pop()) # put the last element at the front pickle.dump( icons, open( "glyphes.pickle", "wb" ) ) print(icons) total_width = 1947 # to make this 400/300 on resize total_height = 1460 # 2 * 700 + 60 new_image = Image.new('RGBA', (total_width, total_height), 255) offset = ( int(total_width / 2) - 700, 30) new_image.paste( Image.open(dir + 'icons/' + str(icons[0])), offset ) offset = ( int(total_width / 2), 30 ) new_image.paste( Image.open(dir + 'icons/' + icons[1]), offset ) offset = ( int(total_width / 2) - 700, int(new_image.height / 2) + 10) new_image.paste( Image.open(dir + 'icons/' + icons[2]), offset ) offset = ( int(new_image.width / 2), int(new_image.height / 2) + 10 ) new_image.paste( Image.open(dir + 'icons/' + icons[3]), offset ) new_image.convert('P') new_image.save(dir + 'saved/glyphe.png') # Now resize the image to 400x300 and save it for eink display img = Image.open(dir + 'saved/glyphe.png') img = img.resize((400, 300), resample=Image.LANCZOS) img = img.quantize() img.save(dir + 'saved/glyphe-resized.png') from inky import InkyWHAT inky_display = InkyWHAT("black") inky_display.set_border(inky_display.WHITE) inky_display.set_image(img) inky_display.show() print("Done!")
def main(): # Initialize Inky wHAT display inky_display = InkyWHAT('black') # Download image and save locally img_data = requests.get(image_url).content with open(img_file, 'wb') as handler: handler.write(img_data) # Open downloaded image img = Image.open(img_file) # Dither downloaded image pal_img = Image.new('P', (1, 1)) pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252) img = img.convert('RGB').quantize(palette=pal_img) # Display dithered image inky_display.set_image(img) inky_display.show()
def create(quote): inky_display = InkyWHAT("black") inky_display.set_border(inky_display.WHITE) # From https://gist.github.com/jasondilworth56/27764ae8ed2327a34ceebb06e75c30ea from PIL import ImageFont, ImageDraw, Image text = quote[0] author = quote[1] title = quote[2] #font = "/usr/share/fonts/truetype/freefont/FreeMono.ttf" font = "/usr/share/fonts/truetype/freefont/FreeSans.ttf" #font = "/home/pi/.fonts/Roboto-Bold.ttf" #font = "/home/pi/.fonts/Bitter-Regular.otf" #font = "/home/pi/.fonts/ufonts.com_rockwell.ttf" #font = "/home/pi/.fonts/Bookerly/Bookerly-Regular.ttf" #font = "/home/pi/.fonts/static/PlayfairDisplay-Regular.ttf" #font = "/home/pi/.fonts/PlayfairDisplay-VariableFont_wght.ttf" font = "/home/pi/.fonts/FredokaOne-Regular.ttf" #font = "/home/pi/.fonts/AmaticSC-Regular.ttf" img = ImageText((400, 300), background=(255, 255, 255)) img.fill_text_box((20, 20), text, box_width=340, box_height=250, font_filename=font) meta_line = author + ', ' + title #img.write_text( (10, inky_display.HEIGHT - 20), meta_line, font_filename=font) #img.fill_text_box((30, inky_display.HEIGHT - 40), meta_line, box_width=320, box_height=30, font_filename=font) img.write_text_box((30, inky_display.HEIGHT - 30), meta_line, box_width=320, font_filename=font, font_size=12, place='right') filename = '/home/pi/src/coten/images/coten.png' img.save(filename) img = Image.open(filename) img = img.quantize() inky_display.set_image(img) inky_display.set_border(inky_display.WHITE) inky_display.show()
pixels[x, y] = (256, 256, 256) return canvas def assemble_canvas(org, tweet, pomodoro, inky_display): canvas = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) # insert org canvas.paste(org, (0, 0)) # no offset # insert tweet canvas.paste(tweet, (org.width, 0)) # insert pomodoro canvas.paste(tomato, (org.width, inky_display.HEIGHT - tomato.size[1])) return canvas # Generate org file image org_accomplishments = Image.open('./org.png') ow, oh = org_accomplishments.size tomato = Image.open('./assets/tomato_3.png') tw, th = tomato.size rem_w = inky_display.WIDTH - ow rem_h = inky_display.HEIGHT - th tweet = get_recent_care_tweet() tweet_img = get_text_image(tweet, rem_w, rem_h) org_img = org_accomplishments canvas = assemble_canvas(org_img, tweet_img, tomato, inky_display) # canvas.show() inky_display.set_image(canvas) inky_display.show()
draw.text( ((displayWidth - updateTextWidth), (headerHeight - updateTextHeight)), tweet_update, white, updateFont) ######################## ## FINALISE THE IMAGE ## ######################## # Set a PIL image, numpy array or list to Inky's internal buffer. The image dimensions should match the dimensions of the pHAT or wHAT you're using. # You should use PIL to create an image. PIL provides an ImageDraw module which allow you to draw text, lines and shapes over your image. # See: https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html inky.set_image(img) ############################### ## SET DISPLAY BORDER COLOUR ## ############################### # .set_border(colour) sets the colour at the edge of the display # colour should be one of 'inky.RED', 'inky.YELLOW', 'inky.WHITE' or 'inky.BLACK' with available colours depending on your display type. inky.set_border(white) ######################## ## UPDATE THE DISPLAY ## ######################## # Once you've prepared and set your image, and chosen a border colour, you can update your e-ink display with .show() if test == True: img.save("debug.png") else: inky.show()
from inky import InkyWHAT from PIL import Image import sys inkywhat = InkyWHAT('red') #img = Image.open("InkywHAT-400x300.png") img = Image.open("keystrokes_bw.png") inkywhat.set_image(img) inkywhat.show()
def create_pimoroni(quote): import argparse import random import sys from inky import InkyWHAT from PIL import Image, ImageFont, ImageDraw from font_source_serif_pro import SourceSerifProSemibold from font_source_sans_pro import SourceSansProSemibold # Set up the correct display and scaling factors inky_display = InkyWHAT('black') inky_display.set_border(inky_display.WHITE) # inky_display.set_rotation(180) w = inky_display.WIDTH h = inky_display.HEIGHT # Create a new canvas to draw on img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) draw = ImageDraw.Draw(img) # Load the fonts font_size = 24 author_font_size = 18 quote_font = ImageFont.truetype(SourceSansProSemibold, font_size) author_font = ImageFont.truetype(SourceSerifProSemibold, author_font_size) # The amount of padding around the quote. Note that # a value of 30 means 15 pixels padding left and 15 # pixels padding right. # # Also define the max width and height for the quote. padding = 50 max_width = w - padding max_height = h - padding - author_font.getsize("ABCD ")[1] below_max_length = False # Only pick a quote that will fit in our defined area # once rendered in the font and size defined. while not below_max_length: reflowed = reflow_quote(quote, max_width, quote_font) p_w, p_h = quote_font.getsize(reflowed) # Width and height of quote p_h = p_h * (reflowed.count("\n") + 1) # Multiply through by number of lines if p_h < max_height: below_max_length = True # The quote fits! Break out of the loop. else: font_size = font_size - 1 author_font_size = author_font_size - 1 quote_font = ImageFont.truetype(SourceSansProSemibold, font_size) author_font = ImageFont.truetype(SourceSerifProSemibold, author_font_size) continue # x- and y-coordinates for the top left of the quote quote_x = (w - max_width) / 2 quote_y = ((h - max_height) + (max_height - p_h - author_font.getsize("ABCD ")[1])) / 2 # x- and y-coordinates for the top left of the author author_x = quote_x author_y = quote_y + p_h author = [ '— ' + quote[1] + ', ' + quote[2] ] reflowed_author = reflow_quote(author, max_width, author_font) # Write our quote and author to the canvas draw.multiline_text((quote_x, quote_y), reflowed, fill=inky_display.BLACK, font=quote_font, align="left") draw.multiline_text((author_x, author_y), reflowed_author, fill=inky_display.BLACK, font=author_font, align="right") print(reflowed + "\n" + reflowed_author + "\n") # Display the completed canvas on Inky wHAT inky_display.set_image(img) inky_display.show()
def main(): first_day = pd.to_datetime('2020-1-20') # first case in US print('Pulling infection histories') iowa = get_infection_history("https://api.covidtracking.com/v1/states/ia/daily.csv") us = get_infection_history("https://api.covidtracking.com/v1/us/daily.csv") print('Received infection histories') print('Pulling vaccination histories') # "Immune" is a little iffy here. i assume if someone had covid they are immune. i also assume that if a person # received one shot they are 80% immune, and 95% with two us_vacc, iowa_vacc, us_immune = get_number_vaccinations("https://www.nytimes.com/interactive/2020/us/covid-19-vaccine-doses.html") us_vacc_per_100 = 100 * us_vacc / us_pop iowa_vacc_per_100 = 100 * iowa_vacc / iowa_pop print('Received vaccination histories') latest_date = iowa['date'][0] iowa_death_average = int(iowa['deathRollingSeven'].iloc[0]) us_death_average = int(us['deathRollingSeven'].iloc[0]) iowa_positive_average = int(iowa['positiveRollingSeven'].iloc[0]) us_positive_average = int(us['positiveRollingSeven'].iloc[0]) # CDC estimates that about 1 in 4 cases of COVID have been caught by tests # https://www.cdc.gov/coronavirus/2019-ncov/cases-updates/burden.html us_immune += us['positive'].iloc[0] * 4 # Set Inky Display stuff print('Initializing inky display') display = InkyWHAT(colour='red') img = Image.new("P", (display.WIDTH, display.HEIGHT)) draw = ImageDraw.Draw(img) lrg_font, med_font, sml_font = ImageFont.truetype(FredokaOne, 26), ImageFont.truetype(FredokaOne, 22), ImageFont.truetype(FredokaOne, 16) left_pad = 3 middle = display.WIDTH / 2 header_y = 45 y_cursor = header_y + 3 max_line_length = 190 extra_y_spacer = 3 # header and section lines draw.text((left_pad, 5), f" {latest_date.strftime('%b %d')} Day {(latest_date - first_day).days:,d}" f" of COVID", display.BLACK, lrg_font) draw.line((left_pad, header_y, display.WIDTH - left_pad, header_y), fill=display.BLACK, width=3) draw.line((left_pad, header_y + (InkyWHAT.HEIGHT - header_y) / 3, display.WIDTH - left_pad, header_y + (InkyWHAT.HEIGHT - header_y) / 3), fill=display.BLACK, width=3) draw.line((left_pad, header_y + 2 * (InkyWHAT.HEIGHT - header_y) / 3, display.WIDTH - left_pad, header_y + 2 * (InkyWHAT.HEIGHT - header_y) / 3), fill=display.BLACK, width=3) draw.line((middle, header_y, middle, display.HEIGHT), fill=display.BLACK, width=3) # new deaths draw.text((get_centered_x('New Deaths', sml_font), y_cursor), 'New Deaths', display.RED, sml_font) y_cursor += sml_font.getsize('New Deaths')[1] + 5 new_deaths_txt = f"IA: {iowa['deathIncrease'][0]:,d} | US: {us['deathIncrease'][0]:,d}" draw.text((get_centered_x(new_deaths_txt, med_font), y_cursor), new_deaths_txt, display.BLACK, med_font) y_cursor += med_font.getsize(new_deaths_txt)[1] + extra_y_spacer rolling_deaths_txt = f"Avg IA: {iowa_death_average:,d} | US: {us_death_average:,d}" rolling_deaths_font = ImageFont.truetype(FredokaOne, max_font_size(rolling_deaths_txt, max_line_length)) draw.text((get_centered_x(rolling_deaths_txt, rolling_deaths_font), y_cursor), rolling_deaths_txt, display.BLACK, rolling_deaths_font) y_cursor = header_y + (InkyWHAT.HEIGHT - header_y) / 3 + 3 # reset cursor to beginning of col 1 row 2 # new infections draw.text((get_centered_x('New Infections', sml_font), y_cursor), 'New Infections', display.RED, sml_font) y_cursor += sml_font.getsize('New Infections')[1] + 5 new_positive_txt = f"IA: {iowa['positiveIncrease'][0]:,d} | US: {us['positiveIncrease'][0]:,d}" new_positive_font = ImageFont.truetype(FredokaOne, max_font_size(new_positive_txt, max_line_length)) draw.text((get_centered_x(new_positive_txt, new_positive_font), y_cursor), new_positive_txt, display.BLACK, new_positive_font) y_cursor += med_font.getsize(new_positive_txt)[1] + extra_y_spacer rolling_positive_txt = f"Avg IA: {iowa_positive_average:,d} | US: {us_positive_average:,d}" rolling_positive_font = ImageFont.truetype(FredokaOne, max_font_size(rolling_positive_txt, max_line_length)) draw.text((get_centered_x(rolling_positive_txt, rolling_positive_font), y_cursor), rolling_positive_txt, display.BLACK, rolling_positive_font) y_cursor = header_y + 2 * (InkyWHAT.HEIGHT - header_y) / 3 + 3 # reset cursor to beginning of col 1 row 2 # positive test rate draw.text((get_centered_x('Positive Test Rate', sml_font), y_cursor), 'Positive Test Rate', display.RED, sml_font) y_cursor += sml_font.getsize('Positive Test Rate')[1] + 5 # positive test rate numbers ptr_txt = f"IA: {100 * iowa['positiveTestRate'].iloc[0]:.1f}% | US: {100 * us['positiveTestRate'].iloc[0]:.1f}%" ptr_font = ImageFont.truetype(FredokaOne, max_font_size(ptr_txt, max_line_length)) draw.text((get_centered_x(ptr_txt, ptr_font), y_cursor), ptr_txt, display.BLACK, ptr_font) y_cursor += med_font.getsize(ptr_txt)[1] + extra_y_spacer rolling_positive_txt = f"LW IA: {100 * iowa['positiveTestRate'].iloc[7]:.1f}% | " \ f"US: {100 * us['positiveTestRate'].iloc[7]:.1f}%" rolling_positive_font = ImageFont.truetype(FredokaOne, max_font_size(rolling_positive_txt, max_line_length)) draw.text((get_centered_x(rolling_positive_txt, rolling_positive_font), y_cursor), rolling_positive_txt, display.BLACK, rolling_positive_font) # % dead y_cursor = header_y + 3 draw.text((get_centered_x('US Percent Dead', sml_font, 'third'), y_cursor), 'US Percent Dead', display.RED, sml_font) y_cursor += sml_font.getsize('US Percent Dead')[1] + 10 death_pct_text = f'{100 * us["death"].iloc[0] / us_pop:.4f}%' death_pct_font = ImageFont.truetype(FredokaOne, max_font_size(death_pct_text, max_line_length)) draw.text((get_centered_x(death_pct_text, death_pct_font, 'third'), y_cursor), death_pct_text, display.BLACK, death_pct_font) y_cursor = header_y + (InkyWHAT.HEIGHT - header_y) / 3 + 3 # reset cursor to beginning of col 1 row 3 # immunity draw.text((get_centered_x('US Percent Vaccinated', sml_font, 'third'), y_cursor), 'US Percent Vaccinated', display.RED, sml_font) y_cursor += sml_font.getsize('US Percent Vaccinated')[1] + 10 immune_text = f'{100 * us_vacc / us_pop:.2f}%' # have to subtract dead from immune :( immune_font = ImageFont.truetype(FredokaOne, max_font_size(immune_text, max_line_length)) draw.text((get_centered_x(immune_text, immune_font, 'third'), y_cursor), immune_text, display.BLACK, immune_font) y_cursor = header_y + 2 * (InkyWHAT.HEIGHT - header_y) / 3 + 3 # reset cursor to beginning of col 1 row 3 # num vaccinated draw.text((get_centered_x('People Vaccinated', sml_font, 'third'), y_cursor), 'People Vaccinated', display.RED, sml_font) y_cursor += sml_font.getsize('People Vaccinated')[1] + 3 total_vacc_txt = f'US: {int(us_vacc):,d}' total_vacc_font = ImageFont.truetype(FredokaOne, max_font_size(total_vacc_txt, max_line_length, upper_lim=20)) draw.text((get_centered_x(total_vacc_txt, total_vacc_font, 'third'), y_cursor), total_vacc_txt, display.BLACK, total_vacc_font) y_cursor += sml_font.getsize('Vaccination')[1] + 10 # vacc per hundred vacc_per_hundred_txt = f'IA: {iowa_vacc_per_100:.1f}% | {iowa_vacc / 1_000_000:.3f}M' vacc_per_hundred_font = ImageFont.truetype(FredokaOne, max_font_size(vacc_per_hundred_txt, max_line_length, upper_lim=20)) draw.text((get_centered_x(vacc_per_hundred_txt, vacc_per_hundred_font, 'third'), y_cursor), vacc_per_hundred_txt, display.BLACK, vacc_per_hundred_font) # update display display.set_image(img) print('Updating display') display.show() print('Display updated')
def on_message(mosq, obj, msg): # Set MQTT Message message = str(msg.payload.decode('UTF-8')) print(message) # Set Font etc and Refresh inky_display = InkyWHAT("red") inky_display.set_border(inky_display.WHITE) img = Image.open("/home/pi/scripts/resources/whatbackground.png" ) #adds a red line at the bottom draw = ImageDraw.Draw(img) # Display the completed canvas on Inky wHAT if 'Weather' in message: fonttop = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 30) # any ttf font can be used font = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 30) # any ttf font can be used wrapped = textwrap.wrap(message, width=24) if (len(wrapped) >= 1): # Status Line 1 status_one = wrapped[0] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 10 - (h / 2) draw.text((15, 30), status_one, inky_display.RED, fonttop, align="left") if (len(wrapped) >= 2): # Status Line 2 status_two = wrapped[1] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 35 - (h / 2) draw.text((15, 70), status_two, inky_display.BLACK, font, align="left") if (len(wrapped) >= 3): # Status Line 3 status_three = wrapped[2] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 60 - (h / 2) draw.text((15, 110), status_three, inky_display.BLACK, font, align="left") if (len(wrapped) >= 4): # Status Line 4 status_four = wrapped[3] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 150), status_four, inky_display.BLACK, font, align="left") if (len(wrapped) >= 5): # Status Line 5 status_five = wrapped[4] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 190), status_five, inky_display.BLACK, font, align="left") if (len(wrapped) >= 6): # Status Line 5 status_six = wrapped[5] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 230), status_six, inky_display.BLACK, font, align="left") elif 'Time' in message: fonttop = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 50) # larger text for time font = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 50) # larger text for time wrapped = textwrap.wrap(message, width=14) if (len(wrapped) >= 1): # Status Line 1 status_one = wrapped[0] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 10 - (h / 2) draw.text((15, 40), status_one, inky_display.RED, fonttop, align="left") if (len(wrapped) >= 2): # Status Line 2 status_two = wrapped[1] w, h = font.getsize(status_two) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 35 - (h / 2) draw.text((15, 100), status_two, inky_display.BLACK, font, align="left") if (len(wrapped) >= 3): # Status Line 3 status_three = wrapped[2] w, h = font.getsize(status_three) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 60 - (h / 2) draw.text((15, 160), status_three, inky_display.BLACK, font, align="left") if (len(wrapped) >= 4): # Status Line 4 status_four = wrapped[3] w, h = font.getsize(status_four) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 220), status_four, inky_display.BLACK, font, align="left") else: fonttop = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 24) font = ImageFont.truetype("/home/pi/scripts/resources/hm.ttf", 26) wrapped = textwrap.wrap(message, width=27.5) if (len(wrapped) >= 1): # Status Line 1 status_one = wrapped[0] w, h = font.getsize(status_one) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 10 - (h / 2) draw.text((15, 10), status_one, inky_display.RED, fonttop, align="left") if (len(wrapped) >= 2): # Status Line 2 status_two = wrapped[1] w, h = font.getsize(status_two) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 35 - (h / 2) draw.text((15, 50), status_two, inky_display.BLACK, font, align="left") if (len(wrapped) >= 3): # Status Line 3 status_three = wrapped[2] w, h = font.getsize(status_three) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 60 - (h / 2) draw.text((15, 90), status_three, inky_display.BLACK, font, align="left") if (len(wrapped) >= 4): # Status Line 4 status_four = wrapped[3] w, h = font.getsize(status_four) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 130), status_four, inky_display.BLACK, font, align="left") if (len(wrapped) >= 5): # Status Line 5 status_five = wrapped[4] w, h = font.getsize(status_five) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 170), status_five, inky_display.BLACK, font, align="left") if (len(wrapped) >= 6): # Status Line 5 status_six = wrapped[5] w, h = font.getsize(status_six) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 210), status_six, inky_display.BLACK, font, align="left") if (len(wrapped) >= 7): # Status Line 5 status_seven = wrapped[6] w, h = font.getsize(status_seven) # Center the text and align it with the name strip x = (inky_display.WIDTH / 2) - (w / 2) y = 85 - (h / 2) draw.text((15, 250), status_seven, inky_display.BLACK, font, align="left") # Display the completed canvas on Inky wHAT inky_display.set_image(img) inky_display.show() print(message)
break while len(splutForecast) > 0: theWord = splutForecast.pop() before = forecastL2 forecastL2 = forecastL2 + ' ' + theWord w, h = titleFont.getsize(forecastL2) if w > panelWidth: splutForecast.append(theWord) forecastL2 = before break while len(splutForecast) > 0: theWord = splutForecast.pop() before = forecastL3 forecastL3 = forecastL3 + ' ' + theWord w, h = titleFont.getsize(forecastL3) if w > panelWidth: splutForecast.append(theWord) forecastL3 = before break draw.text((12, 38), forecastL1, inkR.BLACK, font) draw.text((12, 50), forecastL2, inkR.BLACK, font) draw.text((12, 62), forecastL3, inkR.BLACK, font) draw.text((12, 74), ' Temperature: ' + str(forecast.currently.temperature) + '°f', inkR.BLACK, font) draw.text((12, 86), ' High: ' + str(forecast.daily.data[0].temperature_high) + '°f Low: ' + str(forecast.daily.data[0].temperature_low) + '°f', inkR.BLACK, font) inkR.set_image(im) inkR.show()
def update_inky(): raw = get_today() data = build_today(raw) print(data) # INIT # ==== inky = InkyWHAT("yellow") inky.set_border(inky.BLACK) # initiate image img = Image.new("P", (inky.WIDTH, inky.HEIGHT)) draw = ImageDraw.Draw(img) # HEADER # ====== # add name x = "WFH" font = build_font(20) w, h = get_font_size(font, x) draw.text((X_EDGE, Y_EDGE), x, inky.YELLOW, font) # add date now = data["now"] date_str = now.strftime("%d.%m.%Y") wd, _ = get_font_size(font, date_str) time_str = now.strftime("%H:%M") wt, _ = get_font_size(font, time_str) draw.text((inky.WIDTH - wt - wd - X_EDGE, Y_EDGE), date_str, inky.BLACK, font) draw.text((inky.WIDTH - wt, Y_EDGE), time_str, inky.YELLOW, font) # add divider HEADER_HEIGHT = h + Y_EDGE draw.line( (X_EDGE, HEADER_HEIGHT + 2, inky.WIDTH - X_EDGE, HEADER_HEIGHT + 2), fill=inky.BLACK, width=DIVIDER_HEIGHT) # FOOTER # ====== TIME_END = inky.WIDTH - 2 * X_EDGE dx = (TIME_END - X_LABEL) // len(TIMES) x = X_LABEL + X_EDGE y = inky.HEIGHT - ICON_SIZE * 1.5 # determine absolute start/end time (for referencing) TIME_START = x + (dx + ICON_SIZE) // 2 TIME_END -= ICON_SIZE // 2 - X_EDGE # add ENTIRE time bar draw.line((TIME_START, y, TIME_END, y), fill=inky.BLACK, width=DIVIDER_HEIGHT) # add icons for k, v in TIMES.items(): icon = load_image(v, ICON_SIZE) # add icons img.paste(icon, box=(x + dx // 2, inky.HEIGHT - ICON_SIZE - 2 * Y_EDGE)) # add MAJOR time ticks draw.line((x + (dx + ICON_SIZE) // 2, y + TICK_HEIGHT, x + (dx + ICON_SIZE) // 2, y - TICK_HEIGHT), fill=inky.BLACK, width=int(1.25 * DIVIDER_HEIGHT)) # add MINOR time ticks draw.line((x + dx + ICON_SIZE // 2, y + TICK_HEIGHT, x + dx + ICON_SIZE // 2, y - TICK_HEIGHT), fill=inky.BLACK, width=int(0.75 * DIVIDER_HEIGHT)) x += dx now_px = _convert_time(data["now"], TIME_START, TIME_END) draw.line((now_px, y + TICK_HEIGHT, now_px, y - TICK_HEIGHT), fill=inky.YELLOW, width=int(1.25 * DIVIDER_HEIGHT)) # WORK # ==== Y = 60 # add label label = "Work" font = build_font(30) w, h = get_font_size(font, label[0]) draw.text((X_EDGE + w, Y - h // 2), label[1:], inky.BLACK, font) draw.text((X_EDGE, Y - h // 2), label[0], inky.YELLOW, font) # fix logoff if not existing if len(data["login"]) != len(data["logoff"]): data["logoff"].append(data["now"]) working_times = list(zip(data["login"], data["logoff"])) total_sec = 0 font = build_font(18) for working_time in working_times: # start start_px = _convert_time(working_time[0], TIME_START, TIME_END) start_str = working_time[0].strftime("%H:%M") draw.text((start_px, Y + 4), start_str, inky.BLACK, font) # end stop_px = _convert_time(working_time[1], TIME_START, TIME_END) if (stop_px - start_px) > 100: stop_str = working_time[1].strftime("%H:%M") w, h = get_font_size(font, stop_str) draw.text((stop_px - int(0.75 * w), Y + 4), stop_str, inky.BLACK, font) # total total_sec += (working_time[1] - working_time[0]).total_seconds() # add time bar for working time draw.line((start_px, Y, stop_px, Y), fill=inky.YELLOW, width=3 * DIVIDER_HEIGHT) # write total work time total_str = "Σ {:.2f} hr".format(total_sec / 3600) font = build_font(25) w, h = get_font_size(font, total_str) draw.text((inky.WIDTH - w - X_EDGE, HEADER_HEIGHT + 5), total_str, inky.YELLOW, font) # FOOD # ==== Y = 128 # add label label = "Food" font = build_font(30) w, h = get_font_size(font, label[0]) draw.text((2 * X_EDGE + w // 3, Y - h // 2), label[1:], inky.BLACK, font) draw.text((X_EDGE, Y - h // 2), label[0], inky.YELLOW, font) # add lunch icon if data["lunch"]: icon = load_image(ICONS["lunch"], int(ICON_SIZE * .75)) lunch_px = _convert_time(data["lunch"][0], TIME_START, TIME_END) # ASSUME SINGLE LUNCH img.paste(icon, box=(lunch_px, Y - ICON_SIZE)) # add coffee icons for coffee in data["coffee"]: icon = load_image(ICONS["coffee"], ICON_SIZE) coffee_px = _convert_time(coffee, TIME_START, TIME_END) img.paste(icon, box=(coffee_px - ICON_SIZE // 2, Y + ICON_SIZE // 3)) # HEALTH # ====== Y = 205 # add label label = "Health" font = build_font(30) w, h = get_font_size(font, label[0]) draw.text((X_EDGE + w, Y - h // 2), label[1:], inky.BLACK, font) draw.text((X_EDGE, Y - h // 2), label[0], inky.YELLOW, font) # add pushup icons for pushups in data["pushups"]: icon = load_image(ICONS["pushups"], ICON_SIZE) pushups_px = _convert_time(pushups, TIME_START, TIME_END) img.paste(icon, box=(pushups_px - ICON_SIZE // 2, Y - ICON_SIZE)) # add move icons for move in data["move"]: icon = load_image(ICONS["move"], ICON_SIZE) move_px = _convert_time(move, TIME_START, TIME_END) img.paste(icon, box=(move_px - ICON_SIZE // 2, Y + ICON_SIZE // 3)) # set image to inky inky.set_image(img) inky.show()
def changeScreens(cardID): """Changes the screen shown on the Raspberry Pi Hat. Checks to see if the card is in the database and displays the corresponding screen. We are using the inkywHAT Red/Black/White: https://shop.pimoroni.com/products/inky-what?variant=13590497624147 Documentation: https://github.com/pimoroni/inky TODO: Change the screen back to "please scan card" after a set amount of time has passed. Needs some multithreading or something to accomplish that I think. """ ######## Pi Hat Setup ######## print("Changing screens...") inkywhat = InkyWHAT('black') # Establish a new connection for checking if the card is in the DB. # Not sure if this is required, but this is the easiest way I could find to do it. # Copy-pasted from the earlier mqtt_connection variable. pi_hat_connection = mqtt_connection_builder.mtls_from_path( endpoint=args.endpoint, cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, ca_filepath=args.root_ca, client_id=args.client_id, clean_session=False, keep_alive_secs=6) # Query the DB to see if that card is in it. # Use the config defined at the top of the file to hide connection information. cardInDBResult = False try: mysqlConn = mysql.connector.connect(host=config['database']['dbHostname'], database=config['database']['dbName'], user=config['database']['dbUsername'], password=config['database']['dbPassword']) if mysqlConn.is_connected(): print("Connected to database...") cursor = mysqlConn.cursor() cursor.execute("select database();") record = cursor.fetchone() lookForCardQuery = "SELECT * FROM Student_ID WHERE SID = " + str(cardID) cursor.execute(lookForCardQuery) records = cursor.fetchall() if(cursor.rowcount > 0): cardInDBResult = True except Error as e: print("Error while connecting to MySQL Database", e) finally: if (mysqlConn.is_connected()): cursor.close() mysqlConn.close() print("Disconnected from database...") # If the card is in the database, set the screen to be shown to the "Welcome" screen if (cardInDBResult): print("ID Found - Displaying Welcome...") imgURL = "../../../qrcode/displayScreens/welcome_bw.png" im = Image.open(imgURL) # Resizing the image so it fits to the screen # This is specific to the inkywhat, otherwise it claims that there is a size-mismatch. size = (400,300) out = im.resize(size) out.save('../../../qrcode/resize-output.png') img = Image.open('../../../qrcode/resize-output.png') pal_img = Image.new("P", (1, 1)) pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252) img = img.convert("RGB").quantize(palette=pal_img) # If the card is not in the database, make a QR code and show the "QR Code" screen elif (not cardInDBResult): print("ID Not Found - Displaying Registration...") imgURL = "../../../qrcode/displayScreens/qrscan_bw.png" displayURL = "../../../qrcode/qrCodeScreenMerged.png" # Generate a QR Code Object qr = qrcode.QRCode( version=None, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=3, border=2, ) # Generate the URL the code points to and add it to the QR Code wURL = config['aws_endpoints']['registrationSite'] + str(cardID) qr.add_data(wURL) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") # Save the generated QR Code to the proper folder img.save('../../../qrcode/qr_image.png') im = Image.open(imgURL) # Resizing the image so it fits to the screen size = (400,300) out = im.resize(size) out.save('../../../qrcode/resize-output.png') # We are using ImageMagick installed on the Raspberry Pi to # composite our generated QR Code onto the premade image # with this subprocess call. This could be changed to a # bash script called in the same way. s = pyshorteners.Shortener(Shorteners.TINYURL) shortLink = s.short(wURL) composeQRCodeOntoImageCommand = 'composite -blend 100 -gravity center ../../../qrcode/qr_image.png ../../../qrcode/resize-output.png ../../../qrcode/qrCodeScreenMerged.png' composeURLOntoImageCommand = 'convert ../../../qrcode/qrCodeScreenMerged.png -gravity North -pointsize 24 -annotate +0+215 \'' + shortLink + '\' ../../../qrcode/qrCodeScreenMerged.png' subprocess.call(composeQRCodeOntoImageCommand, shell=True) subprocess.call(composeURLOntoImageCommand, shell=True) img = Image.open(displayURL) # Continue resizing, inkywhat complains otherwise. pal_img = Image.new("P", (1, 1)) pal_img.putpalette((255, 255, 255, 0, 0, 0, 255, 0, 0) + (0, 0, 0) * 252) img = img.convert("RGB").quantize(palette=pal_img) # Update the image on the screen and show it. inkywhat.set_image(img) inkywhat.set_border('white') inkywhat.show() print("Finished changing screens...")
def main(forecast, latlong, bg_file, bg_map, zoom, show_on_inky, inky_colour, show_image, save_image, banner, location_banner, verbose): import os import datetime #choose and format data for image display #don't use temperature_high_time= forecast.daily.data[0].temperature_high_time #because high for the next day would appear after midnight #and I wanted to see the low at 7am (or whenever it was still coming) high = max(forecast.hourly.data[:24], key=lambda x: x.temperature) low = min(forecast.hourly.data[:24], key=lambda x: x.temperature) #daily[0] = TODAY sunrise_time = forecast.daily.data[0].sunrise_time sunset_time = forecast.daily.data[0].sunset_time temperature_msg = str(round(forecast.currently.temperature)) + "°" #hi/lo if ( low.time < high.time ) and low.time - forecast.currently.time > datetime.timedelta( hours=1 ): # and (low.time- forecast.currently.time).seconds > datetime.timedelta(hours= 1).seconds: high_next = False hi_lo_msg = "low {}°\n{}".format(str(round(low.temperature)), low.time.strftime("%H:%M")) else: #high time is next high_next = True hi_lo_msg = "high {}°\n{}".format(str(round(high.temperature)), high.time.strftime("%H:%M")) #sunrise/sunset time try: if (sunrise_time < forecast.currently.time < sunset_time): #it's day time sun_msg = "sunset\n{}".format(sunset_time.strftime("%H:%M")) summary = forecast.hourly.summary.rstrip(".") else: # night time sun_msg = "sunrise\n{}".format(sunrise_time.strftime("%H:%M")) summary = forecast.daily.summary.rstrip(".") except: #We're at the North pole and there's no sunset sun_msg = "" summary = forecast.hourly.summary.rstrip(".") #replace summary with soonest alert alerts = forecast.alerts if alerts: min_alert = min(alerts, key=lambda x: x.time) alert = "{}: {}".format(min_alert.time.strftime("%A %H:%M"), min_alert.title) else: alert = None if alert: summary = alert #replace summary with alert #here is where you could do a check to see if the screen needs updating- #save old version and if new != old, update # create display image # Set up the correct display and scaling factors try: w, h, ink_black, ink_color = setup_inky(inky_colour) except: #go_to_screen= True# ...get screen size? w, h, ink_black, ink_color = setup_screen() #setup canvas try: if bg_file: #user has specified a background if os.path.isfile(bg_file): #load image from absolute file path or file path relative to their location img = Image.open(bg_file) elif os.path.isfile( os.path.join(os.path.dirname(__file__), bg_file)): #load file relative to this script img = Image.open( os.path.join(os.path.dirname(__file__), bg_file)) elif os.path.isdir(bg_file): #choose icon from named structure within folder #the dirs are icon names basedir = os.path.join(bg_file, forecast.currently.icon) if os.path.isdir(basedir): img = choose_bg_from_folder(basedir) else: #choose random bg from folder img = choose_bg_from_folder(bg_file) elif os.path.isdir(os.path.join(os.path.dirname(__file__), bg_file)): #choose icon from named structure within folder #the dirs are icon names basedir = os.path.join( os.path.join(os.path.dirname(__file__), bg_file), forecast.currently.icon) if os.path.isdir(basedir): img = choose_bg_from_folder(basedir) else: #choose random bg from folder img = choose_bg_from_folder( os.path.join(os.path.dirname(__file__), bg_file)) else: print( "Can't load \n{}\n as background. Please specify a directory or filename. Try using an absolute path?" .format(os.path.abspath(bg_file))) img = remove_transparency(img) img = resize_fill(img, w, h) elif bg_map: #load map image img = load_map(latlong) img = resize_distort(img, w, h) elif zoom: #load zoomed map image img = load_map_zoom(latlong) else: #choose from default icon list basedir = os.path.join(os.path.dirname(__file__), 'icons', 'default', forecast.currently.icon) img = choose_bg_from_folder(basedir) img = resize_fill(img, w, h) except Exception as e: print(e, ": using blank background.") #blank bg img = Image.new("RGB", (w, h), color=(255, 255, 255)) #add soft white top and bottom softshadow = Image.new("RGBA", (w, h), color=(255, 255, 255, 255)) draw = ImageDraw.Draw(softshadow) draw.rectangle((0, 10, w, h - 50), fill=(0, 0, 0, 0)) #strongshadow= bg.filter(ImageFilter.GaussianBlur(25)) softshadow = softshadow.filter(ImageFilter.GaussianBlur(50)) img.paste("white", mask=softshadow) img.convert("RGB") draw = ImageDraw.Draw(img) # messages at top of screen: banner, location_banner, forecast time top_line = 0 # banner if banner: img = write_in_box(img, 0, 0, 400, 40, banner, 20, summary_font_loader(20), fill=(0, 0, 0, 255), spacing=0, align_x="center", align_y="top") top_line += 25 # location_banner if location_banner: img = write_in_box(img, 0, top_line, 400, 40 + top_line, location_banner, 20, summary_font_loader(20), fill=(0, 0, 0, 255), spacing=0, align_x="center", align_y="top") top_line += 25 # forecast time img = write_in_box(img, 0, top_line, 400, 40 + top_line, forecast.currently.time.strftime("%A %d %b %Y"), 20, summary_font_loader(20), fill=(0, 0, 0, 255), spacing=0, align_x="center", align_y="top") #current temperature x0, y0, x1, y1 = text_box2(img, 0, 0, w, h - 90, temperature_msg, int(50 * 2.2), temperature_font_loader(int(50 * 2.2)), fill=(255, 255, 0, 255), spacing=0, align_x="center", align_y="center") temperature_y = (y1 - y0) / 2 #HI/Lo on LHS MIDDLE padding = 50 max_width = w - padding max_height = 250 font_size = 24 below_max_length = False scale_adjust = 1 msg = hi_lo_msg while not below_max_length: summary_font = summary_font_loader(font_size * scale_adjust) reflowed = reflow_summary(msg, max_width, summary_font) p_w, p_h = summary_font.getsize( reflowed) # Width and height of summary p_h = p_h * (reflowed.count("\n") + 1 ) # Multiply through by number of lines if p_h < max_height: below_max_length = True # The summary fits! Break out of the loop. else: # scale down text to fit scale_adjust *= .95 # x- and y-coordinates for the top left of the summary summary_x = 5 #do i need to check for the longest linw and get size of that? summary_y = temperature_y + 48 #draw it now bg = Image.new("RGBA", img.size, color=(0, 0, 0, 0)) draw = ImageDraw.Draw(bg) if mean_of_area(img, summary_x, summary_y, summary_x + p_w, summary_y + p_h) > .5 * 255: #area is white, use black text and white shadow fill = (0, 0, 0, 255) shadowfill = (255, 255, 255) else: fill = (255, 255, 255, 255) shadowfill = (0, 0, 0) draw.multiline_text((summary_x, summary_y), reflowed, fill=fill, font=summary_font, align="left") strongshadow = bg.filter(ImageFilter.GaussianBlur(25)) softshadow = bg.filter(ImageFilter.GaussianBlur(50)) img.paste(shadowfill, mask=softshadow) img.convert("RGB") img.paste(shadowfill, mask=strongshadow) img.convert("RGB") img.paste(bg, mask=bg) img.convert("RGB") draw = ImageDraw.Draw(img) #sunrise/sunset on RHS MIDDLE padding = 0 max_width = w - padding max_height = 250 font_size = 24 below_max_length = False scale_adjust = 1 msg = sun_msg if msg: while not below_max_length: summary_font = summary_font_loader(font_size * scale_adjust) reflowed = reflow_summary(msg, max_width, summary_font) p_w, p_h = max( (summary_font.getsize(line) for line in reflowed.splitlines() )) # Width and height of summary p_h = p_h * (reflowed.count("\n") + 1 ) # Multiply through by number of lines if p_h < max_height: below_max_length = True # The summary fits! Break out of the loop. else: # scale down text to fit scale_adjust *= .95 # x and y coordinates for the top left of the summary summary_x = w - p_w - 5 summary_y = temperature_y + 48 bg = Image.new("RGBA", img.size, color=(0, 0, 0, 0)) draw = ImageDraw.Draw(bg) #print (mean_of_area(img, summary_x, summary_y, summary_x+ p_w, summary_y+ p_h)) if mean_of_area(img, summary_x, summary_y, summary_x + p_w, summary_y + p_h) > .5 * 255: #area is white, use black text and white shadow fill = (0, 0, 0, 255) shadowfill = (255, 255, 255) else: fill = (255, 255, 255, 255) shadowfill = (0, 0, 0) draw.multiline_text((summary_x, summary_y), reflowed, fill=fill, font=summary_font, align="right") strongshadow = bg.filter(ImageFilter.GaussianBlur(25)) softshadow = bg.filter(ImageFilter.GaussianBlur(50)) img.paste(shadowfill, mask=softshadow) img.convert("RGB") img.paste(shadowfill, mask=strongshadow) img.convert("RGB") img.paste(bg, mask=bg) img.convert("RGB") #rain graphic / sun (UV) strength y0 = 0 y1 = 130 rain_img = Image.new("RGBA", (w, y1), color=(255, 255, 255, 0)) draw = ImageDraw.Draw(rain_img) font = summary_font_loader(14) for i, hour in enumerate(forecast.hourly.data[:24]): t = hour.time.strftime("%H") p = int(hour.precip_probability * hour.precip_intensity * 25500) #should be x 255 if verbose: print(hour.time.strftime("%A %d %b %Y %H:%M"), hour.precip_probability, hour.precip_intensity) if p: print(hour.precip_type, p) x0 = int(w / 24 * i) x1 = int(w / 24 * (i + 1)) #pcolor=255- p pcolor = int(hour.precip_probability * 255 * .5) #.5 is a fade factor - don't want bars too strong #tcolor=(pcolor+ 128)% 255 tcolor = 0 if p: #>17:#>17 gives a value when inkied #amount and probability of rain #the /2 means scale london weather down to look good - your country may vary #rain_indicator draw.rectangle((x0, y1 - 16 - 1, x1 - 1, y1 - 16 - 3), fill=(0, 0, 0, p)) #color), outline= (0, 0, 0, p)) #rain bars draw.rectangle( (x0, clamp(y0, y1 - (hour.precip_intensity / 2 * (y1 - y0)), y1 - 16), x1 - 1, y1 - 16), fill=(0, 0, 0, pcolor), outline=(0, 0, 0, 255)) #UV rectangles if hour.uv_index: if hour.uv_index == 1: uv = int(255 * .025) elif hour.uv_index == 2: uv = int(255 * .05) elif hour.uv_index == 3: uv = int(255 * .075) else: uv = (hour.uv_index > 3) * 255 draw.rectangle((x0, y1, x1 - 1, y1 - 16), fill=(255, 255, 255, 255), outline=(0, 0, 0, 255)) draw.rectangle((x0, y1, x1 - 1, y1 - 16), fill=(255, 255, 0, uv), outline=(0, 0, 0, 255)) draw.text((x0 + 2, y0 - 16 + y1), str(t), fill=(0, 0, 0, 255), font=font, align='center') #added a plus one to look better lined up img.paste(rain_img, box=(0, 300 - y1), mask=rain_img) img.convert("RGB") #forecast hourly summary at bottom img = write_in_box(img, 0, 280 - 120, 400, 270, summary, 20, summary_font_loader(20), fill=(0, 0, 0, 255), spacing=0, align_x="center", align_y="bottom") #print(img.mode) if show_on_inky: #dither before saving or displaying img.convert("RGB") img = inky_dither(img) if save_image: img.convert("RGB") #save image img.save(save_image) print(save_image) if show_image: img.convert("RGB", 0) #show image img.show() # Display the completed canvas on Inky wHAT if show_on_inky: from inky import InkyWHAT inky_display = InkyWHAT(inky_colour) inky_display.set_image(img) #To Show upside down inky_display.set_image(img.rotate(180)) inky_display.show()