From 4459d47eceabcee6a677e62d8573c9d0262a2620 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 15:35:53 +0100 Subject: [PATCH 01/25] Minor improvements Due to the new driver files, two separate images are required; one for black-white pixels and the other for red-white pixels. Instead of splitting the final image into two, the software now uses 2 images. The calibration cycle won't waste too much space by printing too many lines. An improvement in the write_text function leads to more consistent line heights. Leaving sections empty now will no longer cause problems. --- settings/configuration.py | 135 ++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 56 deletions(-) diff --git a/settings/configuration.py b/settings/configuration.py index 357ef13..67fd07d 100644 --- a/settings/configuration.py +++ b/settings/configuration.py @@ -34,10 +34,26 @@ else: """Create 3 sections of the display, based on percentage""" top_section_width = middle_section_width = bottom_section_width = display_width -top_section_height = int(display_height*0.11) -middle_section_height = int(display_height*0.65) -bottom_section_height = int(display_height - middle_section_height - - top_section_height) +if top_section and bottom_section: + print('top and bottom section occupied') + top_section_height = int(display_height*0.11) + bottom_section_height = int(display_height*0.24) + +elif top_section and not bottom_section: + print('top section occupied') + top_section_height = int(display_height*0.11) + bottom_section_height = 0 + +elif bottom_section and not top_section: + print('bottom_section occupied') + top_section_height = 0 + bottom_section_height = int(display_height*0.24) + +elif not top_section and not bottom_section: + top_section_height = bottom_section_height = 0 + +middle_section_height = int(display_height - top_section_height - + bottom_section_height) """Find out the y-axis position of each section""" top_section_offset = 0 @@ -60,48 +76,52 @@ NotoSansCJK = fontpath+'NotoSansCJK/NotoSansCJKsc-' NotoSans = fontpath+'NotoSans/NotoSans-SemiCondensed' weatherfont = fontpath+'WeatherFont/weathericons-regular-webfont.ttf' -"""Fonts sizes""" -default_font_size = 18 -agenda_font_size = 14 -calendar_font_size = 16 -rss_font_size = 14 -weather_font_size = 12 +"""Fontsizes""" +default_fontsize = 18 +agenda_fontsize = 14 +calendar_fontsize = 14 +rss_fontsize = 14 +weather_fontsize = 12 """Automatically select correct fonts to support set language""" if language in ['ja','zh','zh_tw','ko']: - default = ImageFont.truetype(NotoSansCJK+'Regular.otf', default_font_size) - semi = ImageFont.truetype(NotoSansCJK+'Medium.otf', default_font_size) - bold = ImageFont.truetype(NotoSansCJK+'Bold.otf', default_font_size) + default = ImageFont.truetype(NotoSansCJK+'Regular.otf', default_fontsize) + semi = ImageFont.truetype(NotoSansCJK+'Medium.otf', default_fontsize) + bold = ImageFont.truetype(NotoSansCJK+'Bold.otf', default_fontsize) else: - default = ImageFont.truetype(NotoSans+'.ttf', default_font_size) - semi = ImageFont.truetype(NotoSans+'Medium.ttf', default_font_size) - bold = ImageFont.truetype(NotoSans+'SemiBold.ttf', default_font_size) + default = ImageFont.truetype(NotoSans+'.ttf', default_fontsize) + semi = ImageFont.truetype(NotoSans+'Medium.ttf', default_fontsize) + bold = ImageFont.truetype(NotoSans+'SemiBold.ttf', default_fontsize) -w_font = ImageFont.truetype(weatherfont, weather_font_size) +w_font = ImageFont.truetype(weatherfont, weather_fontsize) -"""Create image with given parameters""" +"""Create a blank image for black pixels and a colour image for coloured pixels""" image = Image.new('RGB', (display_width, display_height), background_colour) +image_col = Image.new('RGB', (display_width, display_height), 'white') + draw = ImageDraw.Draw(image) +draw_col = ImageDraw.Draw(image_col) + """Custom function to add text on an image""" def write_text(space_width, space_height, text, tuple, font=default, alignment='middle', autofit = False, fill_width = 1.0, fill_height = 0.8, colour = text_colour, rotation = None): - + """tuple refers to (x,y) position on display""" if autofit == True or fill_width != 1.0 or fill_height != 0.8: size = 8 font = ImageFont.truetype(font.path, size) - text_width, text_height = font.getsize(text) + text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] while text_width < int(space_width * fill_width) and text_height < int(space_height * fill_height): size += 1 font = ImageFont.truetype(font.path, size) - text_width, text_height = font.getsize(text) + text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] - text_width, text_height = font.getsize(text) + text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] while (text_width, text_height) > (space_width, space_height): text=text[0:-1] - text_width, text_height = font.getsize(text) + text_width, text_height = font.getsize(text)[0], font.getsize('hg')[1] if alignment is "" or "middle" or None: x = int((space_width / 2) - (text_width / 2)) if alignment is 'left': @@ -115,7 +135,11 @@ def write_text(space_width, space_height, text, tuple, ImageDraw.Draw(space).text((x, y), text, fill=colour, font=font) if rotation != None: space.rotate(rotation, expand = True) - image.paste(space, tuple, space) + + if colour == 'black' or 'white': + image.paste(space, tuple, space) + else: + image_col.paste(space, tuple, space) def clear_image(section, colour = background_colour): """Clear the image""" @@ -124,6 +148,10 @@ def clear_image(section, colour = background_colour): box = Image.new('RGB', (width, height), colour) image.paste(box, position) + if three_colour_support == True: + image_col.paste(box, position) + + def crop_image(input_image, section): """Crop an input image to the desired section""" x1, y1 = 0, eval(section+'_offset') @@ -161,15 +189,26 @@ def draw_square(tuple, radius, width, height, colour=text_colour, line_width=1): c3, c4 = ((x+width)-diameter, y), (x+width, y+diameter) c5, c6 = ((x+width)-diameter, (y+height)-diameter), (x+width, y+height) c7, c8 = (x, (y+height)-diameter), (x+diameter, y+height) - - draw.line( (p1, p2) , fill=colour, width = line_width) - draw.line( (p3, p4) , fill=colour, width = line_width) - draw.line( (p5, p6) , fill=colour, width = line_width) - draw.line( (p7, p8) , fill=colour, width = line_width) - draw.arc( (c1, c2) , 180, 270, fill=colour, width=line_width) - draw.arc( (c3, c4) , 270, 360, fill=colour, width=line_width) - draw.arc( (c5, c6) , 0, 90, fill=colour, width=line_width) - draw.arc( (c7, c8) , 90, 180, fill=colour, width=line_width) + + if three_colour_support == True: + draw_col.line( (p1, p2) , fill=colour, width = line_width) + draw_col.line( (p3, p4) , fill=colour, width = line_width) + draw_col.line( (p5, p6) , fill=colour, width = line_width) + draw_col.line( (p7, p8) , fill=colour, width = line_width) + draw_col.arc( (c1, c2) , 180, 270, fill=colour, width=line_width) + draw_col.arc( (c3, c4) , 270, 360, fill=colour, width=line_width) + draw_col.arc( (c5, c6) , 0, 90, fill=colour, width=line_width) + draw_col.arc( (c7, c8) , 90, 180, fill=colour, width=line_width) + else: + draw.line( (p1, p2) , fill=colour, width = line_width) + draw.line( (p3, p4) , fill=colour, width = line_width) + draw.line( (p5, p6) , fill=colour, width = line_width) + draw.line( (p7, p8) , fill=colour, width = line_width) + draw.arc( (c1, c2) , 180, 270, fill=colour, width=line_width) + draw.arc( (c3, c4) , 270, 360, fill=colour, width=line_width) + draw.arc( (c5, c6) , 0, 90, fill=colour, width=line_width) + draw.arc( (c7, c8) , 90, 180, fill=colour, width=line_width) + def internet_available(): """check if the internet is available""" @@ -206,24 +245,6 @@ def image_cleanup(): os.remove(temp_files) print('Done') -def split_colours(image): - if three_colour_support == True: - """Split image into two, one for red pixels, the other for black pixels""" - buffer = numpy.array(image.convert('RGB')) - red, green = buffer[:, :, 0], buffer[:, :, 1] - buffer_red, buffer_black = numpy.array(image), numpy.array(image) - - buffer_red[numpy.logical_and(red >= 200, green <= 90)] = [0,0,0] #red->black - red1 = buffer_red[:,:,0] - buffer_red[red1 != 0] = [255,255,255] #white - red_im = Image.fromarray(buffer_red).convert('1',dither=True).rotate(270,expand=True) - - buffer_black[numpy.logical_and(red <= 180, red == green)] = [0,0,0] #black - red2 = buffer_black[:,:,0] - buffer_black[red2 != 0] = [255,255,255] # white - black_im = Image.fromarray(buffer_black).convert('1', dither=True).rotate(270,expand=True) - return black_im, red_im - def calibrate_display(no_of_cycles): """How many times should each colour be calibrated? Default is 3""" epaper = driver.EPD() @@ -235,18 +256,20 @@ def calibrate_display(no_of_cycles): print('----------Started calibration of E-Paper display----------') if 'colour' in model: for _ in range(no_of_cycles): - print('Calibrating black...') + print('Calibrating...', end= ' ') + print('black...', end= ' ') epaper.display(epaper.getbuffer(black), epaper.getbuffer(white)) - print('Calibrating red/yellow...') + print('colour...', end = ' ') epaper.display(epaper.getbuffer(white), epaper.getbuffer(black)) - print('Calibrating white...') + print('white...') epaper.display(epaper.getbuffer(white), epaper.getbuffer(white)) print('Cycle {0} of {1} complete'.format(_+1, no_of_cycles)) else: for _ in range(no_of_cycles): - print('Calibrating black...') + print('Calibrating...', end= ' ') + print('black...', end = ' ') epaper.display(epaper.getbuffer(black)) - print('Calibrating white...') + print('white...') epaper.display(epaper.getbuffer(white)), print('Cycle {0} of {1} complete'.format(_+1, no_of_cycles)) From 08547c8ecbff81ced0be0de580f97781b669ab52 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 15:38:32 +0100 Subject: [PATCH 02/25] Minor improvements No more splitting image by colours. Added option to turn image upside_down. Better import handling. No more problems with empty sections. Middle section will expand to all empty areas. --- modules/inkycal.py | 64 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/modules/inkycal.py b/modules/inkycal.py index 896f0bc..3546c07 100644 --- a/modules/inkycal.py +++ b/modules/inkycal.py @@ -17,17 +17,35 @@ import gc """Perepare for execution of main programm""" calibration_countdown = 'initial' skip_calibration = False +upside_down = False + image_cleanup() -top_section_module = importlib.import_module(top_section) -middle_section_module = importlib.import_module(middle_section) -bottom_section_module = importlib.import_module(bottom_section) +try: + top_section_module = importlib.import_module(top_section) +except ValueError: + print('Something went wrong while importing the top-section module:', top_section) + pass + +try: + middle_section_module = importlib.import_module(middle_section) +except ValueError: + print('Something went wrong while importing the middle_section module', middle_section) + pass + +try: + bottom_section_module = importlib.import_module(bottom_section) +except ValueError: + print('Something went wrong while importing the bottom_section module', bottom_section) + pass """Check time and calibrate display if time """ while True: now = arrow.now(tz=get_tz()) for _ in range(1): image = Image.new('RGB', (display_width, display_height), background_colour) + if three_colour_support == True: + image_col = Image.new('RGB', (display_width, display_height), 'white') """------------------Add short info------------------""" print('Current Date: {0} \nCurrent Time: {1}'.format(now.format( @@ -53,35 +71,58 @@ while True: 'displays causes ghosting') - """----------------Generating and assembling images------""" + """----------------------top-section-image-----------------------------""" try: top_section_module.main() - top_section_image = Image.open(image_path + top_section+'.png') + top_section_image = Image.open(image_path + top_section+'.png').convert('1', dither=True) image.paste(top_section_image, (0, 0)) - print('Done') + + if three_colour_support == True: + top_section_image_col = Image.open(image_path + top_section+'_col.png').convert('1', dither=True) + image_col.paste(top_section_image_col, (0, 0)) + except Exception as error: print(error) pass + """----------------------middle-section-image---------------------------""" try: middle_section_module.main() - middle_section_image = Image.open(image_path + middle_section+'.png') + middle_section_image = Image.open(image_path + middle_section+'.png').convert('1', dither=True) image.paste(middle_section_image, (0, middle_section_offset)) - print('Done') + + if three_colour_support == True: + middle_section_image_col = Image.open(image_path + middle_section+'_col.png').convert('1', dither=True) + image_col.paste(middle_section_image_col, (0, middle_section_offset)) + except Exception as error: print(error) pass + + """----------------------bottom-section-image---------------------------""" try: bottom_section_module.main() - bottom_section_image = Image.open(image_path + bottom_section+'.png') + bottom_section_image = Image.open(image_path + bottom_section+'.png').convert('1', dither=True) image.paste(bottom_section_image, (0, bottom_section_offset)) - print('Done') + + if three_colour_support == True: + bottom_section_image_col = Image.open(image_path + bottom_section+'_col.png').convert('1', dither=True) + image_col.paste(bottom_section_image_col, (0, bottom_section_offset)) + except Exception as error: print(error) pass + """---------------------------------------------------------------------""" + if upside_down == True: + image = image.rotate(180, expand=True) + if three_colour_support == True: + image_col = image_col.rotate(180, expand=True) + image.save(image_path + 'canvas.png') + if three_colour_support == True: + image_col.save(image_path+'canvas_col.png') """---------Refreshing E-Paper with newly created image-----------""" epaper = driver.EPD() @@ -91,8 +132,7 @@ while True: if three_colour_support == True: print('Sending image data and refreshing display...', end='') - black_im, red_im = split_colours(image) - epaper.display(epaper.getbuffer(black_im), epaper.getbuffer(red_im)) + epaper.display(epaper.getbuffer(image), epaper.getbuffer(image_col)) print('Done') else: print('Sending image data and refreshing display...', end='') From cbb6e5fa84f1ffed7f1d4da880fb700b634665cf Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 16:38:36 +0100 Subject: [PATCH 03/25] Minor improvements Support for 2-image mode due to new drivers. Support for auto-expansion (agenda module will expand to top and bottom if space is available). Errors will be shown directly on the E-Paper if something goes wrong. --- modules/inkycal_agenda.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/modules/inkycal_agenda.py b/modules/inkycal_agenda.py index e362b3d..e569549 100644 --- a/modules/inkycal_agenda.py +++ b/modules/inkycal_agenda.py @@ -18,7 +18,7 @@ border_top = int(middle_section_height * 0.02) border_left = int(middle_section_width * 0.02) """Choose font optimised for the agenda section""" -font = ImageFont.truetype(NotoSans+'Medium.ttf', agenda_font_size) +font = ImageFont.truetype(NotoSans+'Medium.ttf', agenda_fontsize) line_height = int(font.getsize('hg')[1] * 1.2) + 1 line_width = int(middle_section_width - (border_left*2)) @@ -33,10 +33,7 @@ event_col_start = time_col_start + time_col_width """Find max number of lines that can fit in the middle section and allocate a position for each line""" -if bottom_section: - max_lines = int((middle_section_height - border_top*2) // line_height) -else: - max_lines = int(middle_section_height+bottom_section_height - +max_lines = int(middle_section_height+bottom_section_height - (border_top * 2))// line_height line_pos = [(border_left, int(top_section_height + border_top + line * line_height)) @@ -104,8 +101,14 @@ def generate_image(): agenda_events[events]['date_str'], line_pos[events], font = font) previous_date = agenda_events[events]['date'] - draw.line((date_col_start, line_pos[events][1], - line_width,line_pos[events][1]), fill = 'red' if three_colour_support == True else 'black') + + if three_colour_support == True: + draw_col.line((date_col_start, line_pos[events][1], + line_width,line_pos[events][1]), fill = 'black') + else: + draw.line((date_col_start, line_pos[events][1], + line_width,line_pos[events][1]), fill = 'black') + elif agenda_events[events]['type'] == 'timed_event': write_text(time_col_width, line_height, agenda_events[events]['time'], @@ -123,12 +126,13 @@ def generate_image(): (event_col_start, line_pos[events][1]), alignment = 'left', font = font) """Crop the image to show only the middle section""" - if bottom_section: - agenda_image = crop_image(image, 'middle_section') - else: - agenda_image = image.crop((0,middle_section_offset,display_width, display_height)) - + agenda_image = image.crop((0,middle_section_offset,display_width, display_height)) agenda_image.save(image_path+'inkycal_agenda.png') + + if three_colour_support == True: + agenda_image_col = image_col.crop((0,middle_section_offset,display_width, display_height)) + agenda_image_col.save(image_path+'inkycal_agenda_col.png') + print('Done') except Exception as e: @@ -136,8 +140,16 @@ def generate_image(): print('Failed!') print('Error in Agenda module!') print('Reason: ',e) + + clear_image('middle_section') + write_text(middle_section_width, middle_section_height, str(e), + (0, middle_section_offset), font = font) + calendar_image = crop_image(image, 'middle_section') + calendar_image.save(image_path+'inkycal_agenda.png') pass + + def main(): generate_image() From ae2d4feb4989b114abed48733e2a855e45caaffb Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 16:41:47 +0100 Subject: [PATCH 04/25] Minor improvements Support for 2-image mode (due to new driver files). Improved formatting of events below the calendar. In case something goes wrong, the error will be shown in the correspondig section on the E-Paper. --- modules/inkycal_calendar.py | 68 ++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/modules/inkycal_calendar.py b/modules/inkycal_calendar.py index ce5284c..8ff1698 100644 --- a/modules/inkycal_calendar.py +++ b/modules/inkycal_calendar.py @@ -16,7 +16,7 @@ at_in_your_language = 'at' event_icon = 'square' # dot #square style = "DD MMM" -font = ImageFont.truetype(NotoSans+'.ttf', calendar_font_size) +font = ImageFont.truetype(NotoSans+'.ttf', calendar_fontsize) space_between_lines = 0 if show_events == True: @@ -98,7 +98,7 @@ def generate_image(): """Add the numbers on the correct positions""" for i in range(len(calendar_flat)): - if calendar_flat[i] != 0: + if calendar_flat[i] not in (0, int(now.day)): write_text(icon_width, icon_height, str(calendar_flat[i]), grid[i]) """Draw a red/black circle with the current day of month in white""" @@ -110,11 +110,13 @@ def generate_image(): x_text = int((icon_width / 2) - (text_width / 2)) y_text = int((icon_height / 2) - (text_height / 1.7)) ImageDraw.Draw(icon).ellipse((x_circle-radius, y_circle-radius, - x_circle+radius, y_circle+radius), fill= 'red' if - three_colour_support == True else 'black', outline=None) + x_circle+radius, y_circle+radius), fill= 'black', outline=None) ImageDraw.Draw(icon).text((x_text, y_text), str(now.day), fill='white', font=bold) - image.paste(icon, current_day_pos, icon) + if three_colour_support == True: + image_col.paste(icon, current_day_pos, icon) + else: + image.paste(icon, current_day_pos, icon) """Create some reference points for the current month""" days_current_month = calendar.monthrange(now.year, now.month)[1] @@ -152,38 +154,47 @@ def generate_image(): for days in days_with_events: draw_square((int(grid[calendar_flat.index(days)][0]+center_x), int(grid[calendar_flat.index(days)][1] + center_y )), - 8, square_size , square_size) + 8, square_size , square_size, colour='black') """Add a small section showing events of today and tomorrow""" - event_list = ['{0} {1} {2} : {3}'.format(today_in_your_language, - at_in_your_language, event.begin.format('HH:mm' if hours == 24 else - 'hh:mm'), event.name) for event in calendar_events if event.begin.day - == now.day and now < event.end] - - event_list += ['{0} {1} {2} : {3}'.format(tomorrow_in_your_language, - at_in_your_language, event.begin.format('HH:mm' if hours == 24 else - 'hh:mm'), event.name) for event in calendar_events if event.begin.day - == now.replace(days=1).day] - + event_list = [] after_two_days = now.replace(days=2).floor('day') - event_list += ['{0} {1} {2} : {3}'.format(event.begin.format('D MMM'), - at_in_your_language, event.begin.format('HH:mm' if hours == 24 else - 'hh:mm'), event.name) for event in upcoming_events if event.end > - after_two_days] + for event in calendar_events: + if event.begin.day == now.day and now < event.end: + if event.all_day: + event_list.append('{}: {}'.format(today_in_your_language, event.name)) + else: + event_list.append('{0} {1} {2} : {3}'.format(today_in_your_language, + at_in_your_language, event.begin.format('HH:mm' if hours == '24' else + 'hh:mm a'), event.name)) + + if event.begin.day == now.replace(days=1).day: + if event.all_day: + event_list.append('{}: {}'.format(tomorrow_in_your_language, event.name)) + else: + event_list.append('{0} {1} {2} : {3}'.format(tomorrow_in_your_language, + at_in_your_language, event.begin.format('HH:mm' if hours == '24' else + 'hh:mm a'), event.name)) + + if event.end > after_two_days: + if event.all_day: + event_list.append('{}: {}'.format(event.begin.format('D MMM'), event.name)) + else: + event_list.append('{0} {1} {2} : {3}'.format(event.begin.format('D MMM'), + at_in_your_language, event.begin.format('HH:mm' if hours == '24' else + 'hh:mm a'), event.name)) del event_list[max_event_lines:] if event_list: for lines in event_list: write_text(main_area_width, int(events_height/max_event_lines), lines, - event_lines[event_list.index(lines)], alignment='left', - fill_height = 0.7) + event_lines[event_list.index(lines)], font=font, alignment='left') else: write_text(main_area_width, int(events_height/max_event_lines), - 'No upcoming events.', event_lines[0], alignment='left', - fill_height = 0.7) + 'No upcoming events.', event_lines[0], font=font, alignment='left') """Set print_events_to True to print all events in this month""" style = 'DD MMM YY HH:mm' @@ -197,6 +208,10 @@ def generate_image(): calendar_image = crop_image(image, 'middle_section') calendar_image.save(image_path+'inkycal_calendar.png') + if three_colour_support == True: + calendar_image_col = crop_image(image_col, 'middle_section') + calendar_image_col.save(image_path+'inkycal_calendar_col.png') + print('Done') except Exception as e: @@ -204,6 +219,11 @@ def generate_image(): print('Failed!') print('Error in Calendar module!') print('Reason: ',e) + clear_image('middle_section') + write_text(middle_section_width, middle_section_height, str(e), + (0, middle_section_offset), font = font) + calendar_image = crop_image(image, 'middle_section') + calendar_image.save(image_path+'inkycal_calendar.png') pass def main(): From 6b74808a9f66e4146d537c6d25a20656da007577 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 16:43:41 +0100 Subject: [PATCH 05/25] All-day events fix All-day events will be marked as such (no starting time will be shown). --- modules/inkycal_icalendar.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/inkycal_icalendar.py b/modules/inkycal_icalendar.py index c61b248..421c174 100644 --- a/modules/inkycal_icalendar.py +++ b/modules/inkycal_icalendar.py @@ -74,6 +74,7 @@ def fetch_events(): for events in upcoming_events: if events.all_day and events.duration.days > 1: events.end = events.end.replace(days=-2) + events.make_all_day() if not events.all_day: events.end = events.end.to(timezone) From 407dbcffdeb5fe5870271eb06d39e8caf0c9b1bd Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 16:45:22 +0100 Subject: [PATCH 06/25] Minor improvements Added support for 2-image mode (due to new driver files). In case something goes wrong, the error will be displayed on the E-Paper, at the corresponding section. --- modules/inkycal_rss.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/inkycal_rss.py b/modules/inkycal_rss.py index 8c4ce4b..0293258 100644 --- a/modules/inkycal_rss.py +++ b/modules/inkycal_rss.py @@ -14,7 +14,7 @@ border_top = int(bottom_section_height * 0.05) border_left = int(bottom_section_width * 0.02) """Choose font optimised for the weather section""" -font = ImageFont.truetype(NotoSans+'.ttf', rss_font_size) +font = ImageFont.truetype(NotoSans+'.ttf', rss_fontsize) space_between_lines = 1 line_height = font.getsize('hg')[1] + space_between_lines line_width = bottom_section_width - (border_left*2) @@ -69,6 +69,11 @@ def generate_image(): rss_image = crop_image(image, 'bottom_section') rss_image.save(image_path+'inkycal_rss.png') + + if three_colour_support == True: + rss_image_col = crop_image(image_col, 'bottom_section') + rss_image_col.save(image_path+'inkycal_rss_col.png') + print('Done') except Exception as e: @@ -76,8 +81,14 @@ def generate_image(): print('Failed!') print('Error in RSS module!') print('Reason: ',e) + clear_image('bottom_section') + write_text(bottom_section_width, bottom_section_height, str(e), + (0, bottom_section_offset), font = font) + rss = crop_image(image, 'bottom_section') + rss.save(image_path+'inkycal_rss.png') pass + def main(): generate_image() From 30a202fe504dfe156c454bcdb6cacb57e420486b Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 4 Feb 2020 16:49:20 +0100 Subject: [PATCH 07/25] Minor improvements + bugfix OWM uses UTC-time to provide weather forecasts. Previously, a timezone-aware stamp was used which caused problems with the forecasts for some users. By switching to UTC time, the problem is fixed. In case somethign goes wrong, the error message will be printed directly on the coresponding section. Added support for 2-colour mode (due to new driver files). --- modules/inkycal_weather.py | 42 +++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/modules/inkycal_weather.py b/modules/inkycal_weather.py index 294e8e4..6e361cf 100644 --- a/modules/inkycal_weather.py +++ b/modules/inkycal_weather.py @@ -120,7 +120,7 @@ def to_units(kelvin): ndigits = decimal_places_temperature) if units == 'metric': conversion = str(degrees_celsius) + '°C' - + if units == 'imperial': conversion = str(fahrenheit) + 'F' @@ -171,7 +171,7 @@ def generate_image(): forecast = owm.three_hours_forecast(location) """Round the hour to the nearest multiple of 3""" - now = arrow.now(tz=get_tz()) + now = arrow.utcnow() if (now.hour % 3) != 0: hour_gap = 3 - (now.hour % 3) else: @@ -258,9 +258,10 @@ def generate_image(): write_text(icon_small, icon_small, '\uf0b1', windspeed_icon_now_pos, font = w_font, fill_height = 0.9, rotation = -wind_degrees) - write_text(coloumn_width-icon_small, row_height, - temperature_now, temperature_now_pos, font = font, colour = - red_temp(temperature_now)) + write_text(coloumn_width-icon_small, row_height, temperature_now, + temperature_now_pos, font = font, colour= red_temp(temperature_now)) + + write_text(coloumn_width-icon_small, row_height, humidity_now+'%', humidity_now_pos, font = font) write_text(coloumn_width-icon_small, row_height, wind, @@ -326,24 +327,31 @@ def generate_image(): draw.line((coloumn5, line_start_y, coloumn5, line_end_y), fill='black') draw.line((coloumn6, line_start_y, coloumn6, line_end_y), fill='black') draw.line((coloumn7, line_start_y, coloumn7, line_end_y), fill='black') - draw.line((0, top_section_height-border_top, top_section_width- - border_left, top_section_height-border_top), - fill='red' if three_colour_support == 'True' else 'black' , width=3) - weather_image = crop_image(image, 'top_section') + if three_colour_support == True: + draw_col.line((0, top_section_height-border_top, top_section_width- + border_left, top_section_height-border_top), fill='black', width=3) + else: + draw.line((0, top_section_height-border_top, top_section_width- + border_left, top_section_height-border_top), fill='black', width=3) + + weather_image = crop_image(image, 'top_section') weather_image.save(image_path+'inkycal_weather.png') + + if three_colour_support == True: + weather_image_col = crop_image(image_col, 'top_section') + weather_image_col.save(image_path+'inkycal_weather_col.png') + print('Done') except Exception as e: - """If no response was received from the openweathermap - api server, add the cloud with question mark""" - print('__________OWM-ERROR!__________') + """If something went wrong, print a Error message on the Terminal""" + print('Failed!') + print('Error in weather module!') print('Reason: ',e) - write_text(icon_medium, icon_medium, '\uf07b', weather_icon_now_pos, - font = w_font, fill_height = 1.0) - message = 'No internet connectivity or API timeout' - write_text(coloumn_width*6, row_height, message, humidity_icon_now_pos, - font = font) + clear_image('top_section') + write_text(top_section_width, top_section_height, str(e), + (0, 0), font = font) weather_image = crop_image(image, 'top_section') weather_image.save(image_path+'inkycal_weather.png') pass From 363f2729f35c57652a31120089ea6abb94fa19ed Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 11 Feb 2020 23:33:47 +0100 Subject: [PATCH 08/25] Added experimental image rendering module Use at own risk! --- modules/inky_image.py | 167 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 modules/inky_image.py diff --git a/modules/inky_image.py b/modules/inky_image.py new file mode 100644 index 0000000..aad5bd3 --- /dev/null +++ b/modules/inky_image.py @@ -0,0 +1,167 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +""" +Experimental image module for Inky-Calendar software +Displays an image on the E-Paper. Work in progress! +Copyright by aceisace +""" +from __future__ import print_function +from configuration import * +from os import path +from PIL import ImageOps +import requests +import numpy + +"""----------------------------------------------------------------""" +path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' +mode = 'auto' # 'horizontal' # 'vertical' # 'auto' +upside_down = False # Flip image by 180 deg (upside-down) +alignment = 'center' # top_center, top_left, center_left, bottom_right etc. +colours = 'bwr' # bwr # bwy # bw +render = True # show image on E-Paper? +"""----------------------------------------------------------------""" + +"""Try to open the image if it exists and is an image file""" +try: + if 'http' in path: + im = Image.open(requests.get(path, stream=True).raw) + else: + im = Image.open(path) +except FileNotFoundError: + print('Your file could not be found. Please check the path to your file.') + raise +except OSError: + print('Please check if the path points to an image file.') + raise + +"""Turn image upside-down if specified""" +if upside_down == True: + im.rotate(180, expand = True) + +if mode == 'horizontal': + display_width, display_height == display_height, display_width + +if mode == 'vertical': + pass + +if mode == 'auto': + if (im.width > im.height) and (display_width < display_height): + print('display vertical, image horizontal -> flipping image') + im = im.rotate(90, expand=True) + if (im.width < im.height) and (display_width > display_height): + print('display horizontal, image vertical -> flipping image') + im = im.rotate(90, expand=True) + +def fit_width(image, width): + """Resize an image to desired width""" + print('resizing width from', image.width, 'to', end = ' ') + wpercent = (display_width/float(image.width)) + hsize = int((float(image.height)*float(wpercent))) + img = image.resize((width, hsize), Image.ANTIALIAS) + print(img.width) + return img + +def fit_height(image, height): + """Resize an image to desired height""" + print('resizing height from', image.height, 'to', end = ' ') + hpercent = (height / float(image.height)) + wsize = int(float(image.width) * float(hpercent)) + img = image.resize((wsize, height), Image.ANTIALIAS) + print(img.height) + return img + +if im.width > display_width: + im = fit_width(im, display_width) +if im.height > display_height: + im = fit_height(im, display_height) + +if alignment == 'center': + x,y = int((display_width-im.width)/2), int((display_height-im.height)/2) +elif alignment == 'center_right': + x, y = display_width-im.width, int((display_height-im.height)/2) +elif alignment == 'center_left': + x, y = 0, int((display_height-im.height)/2) + +elif alignment == 'top_center': + x, y = int((display_width-im.width)/2), 0 +elif alignment == 'top_right': + x, y = display_width-im.width, 0 +elif alignment == 'top_left': + x, y = 0, 0 + +elif alignment == 'bottom_center': + x, y = int((display_width-im.width)/2), display_height-im.height +elif alignment == 'bottom_right': + x, y = display_width-im.width, display_height-im.height +elif alignment == 'bottom_left': + x, y = display_width-im.width, display_height-im.height + +if len(im.getbands()) == 4: + print('removing transparency') + bg = Image.new('RGBA', (im.width, im.height), 'white') + im = Image.alpha_composite(bg, im) + +image.paste(im, (x,y)) +im = image + +if colours == 'bw': + """For black-white images, use monochrome dithering""" + black = im.convert('1', dither=True) +elif colours == 'bwr': + """For black-white-red images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] +elif colours == 'bwy': + """For black-white-yellow images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,255,0, 255,255,255] + + +"""Map each pixel of the opened image to the Palette""" +if colours != 'bw': + palette_im = Image.new('P', (3,1)) + palette_im.putpalette(pal * 64) + quantized_im = im.quantize(palette=palette_im) + quantized_im.convert('RGB').save(output) + quantized_im.save('/home/pi/Desktop/quan.png') + + """Create buffer for coloured pixels""" + buffer1 = numpy.array(quantized_im.convert('RGB')) + r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] + + """Create buffer for black pixels""" + buffer2 = numpy.array(quantized_im.convert('RGB')) + r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] + + if colours == 'bwr': + """Create image for only red pixels""" + buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white + buffer2[numpy.logical_and(r2 == 255, b2 == 0)] = [0,0,0] #red->black + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(r1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + + if colours == 'bwy': + """Create image for only yellow pixels""" + buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white + buffer2[numpy.logical_and(g2 == 255, b2 == 0)] = [0,0,0] #yellow -> black + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(g1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + +if render == True: + epaper = driver.EPD() + print('Initialising E-Paper...', end = '') + epaper.init() + print('Done') + + print('Sending image data and refreshing display...', end='') + if three_colour_support == True: + epaper.display(epaper.getbuffer(black), epaper.getbuffer(colour)) + else: + epaper.display(epaper.getbuffer(black)) + print('Done') + + print('Sending E-Paper to deep sleep...', end = '') + epaper.sleep() +print('Done') From 5e4d33c4ab10b6a82290d7b684981bcfc2e52e90 Mon Sep 17 00:00:00 2001 From: Ace Date: Tue, 11 Feb 2020 23:50:13 +0100 Subject: [PATCH 09/25] don't save temp images --- modules/inky_image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/inky_image.py b/modules/inky_image.py index aad5bd3..541492d 100644 --- a/modules/inky_image.py +++ b/modules/inky_image.py @@ -120,8 +120,7 @@ if colours != 'bw': palette_im = Image.new('P', (3,1)) palette_im.putpalette(pal * 64) quantized_im = im.quantize(palette=palette_im) - quantized_im.convert('RGB').save(output) - quantized_im.save('/home/pi/Desktop/quan.png') + quantized_im.convert('RGB') """Create buffer for coloured pixels""" buffer1 = numpy.array(quantized_im.convert('RGB')) From 2e9e2b80bb6be639bb080ccddd07fe66ebc9ce62 Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 09:32:20 +0100 Subject: [PATCH 10/25] Rendering improvements Updated release tag, switched from dithering to self-defined colour-optimisation function 'optimise_colours' for better rendering --- modules/inkycal.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/inkycal.py b/modules/inkycal.py index 3546c07..b265503 100644 --- a/modules/inkycal.py +++ b/modules/inkycal.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- """ -v1.7.1 +v1.7.2 Main file of Inky-Calendar software. Creates dynamic images for each section, assembles them and sends it to the E-Paper @@ -74,11 +74,11 @@ while True: """----------------------top-section-image-----------------------------""" try: top_section_module.main() - top_section_image = Image.open(image_path + top_section+'.png').convert('1', dither=True) + top_section_image = Image.open(image_path + top_section+'.png') image.paste(top_section_image, (0, 0)) if three_colour_support == True: - top_section_image_col = Image.open(image_path + top_section+'_col.png').convert('1', dither=True) + top_section_image_col = Image.open(image_path + top_section+'_col.png') image_col.paste(top_section_image_col, (0, 0)) except Exception as error: @@ -88,11 +88,11 @@ while True: """----------------------middle-section-image---------------------------""" try: middle_section_module.main() - middle_section_image = Image.open(image_path + middle_section+'.png').convert('1', dither=True) + middle_section_image = Image.open(image_path + middle_section+'.png') image.paste(middle_section_image, (0, middle_section_offset)) if three_colour_support == True: - middle_section_image_col = Image.open(image_path + middle_section+'_col.png').convert('1', dither=True) + middle_section_image_col = Image.open(image_path + middle_section+'_col.png') image_col.paste(middle_section_image_col, (0, middle_section_offset)) except Exception as error: @@ -103,11 +103,11 @@ while True: """----------------------bottom-section-image---------------------------""" try: bottom_section_module.main() - bottom_section_image = Image.open(image_path + bottom_section+'.png').convert('1', dither=True) + bottom_section_image = Image.open(image_path + bottom_section+'.png') image.paste(bottom_section_image, (0, bottom_section_offset)) if three_colour_support == True: - bottom_section_image_col = Image.open(image_path + bottom_section+'_col.png').convert('1', dither=True) + bottom_section_image_col = Image.open(image_path + bottom_section+'_col.png') image_col.paste(bottom_section_image_col, (0, bottom_section_offset)) except Exception as error: @@ -120,8 +120,11 @@ while True: if three_colour_support == True: image_col = image_col.rotate(180, expand=True) + image = optimise_colours(image) image.save(image_path + 'canvas.png') + if three_colour_support == True: + image_col = optimise_colours(image_col) image_col.save(image_path+'canvas_col.png') """---------Refreshing E-Paper with newly created image-----------""" @@ -136,7 +139,7 @@ while True: print('Done') else: print('Sending image data and refreshing display...', end='') - epaper.display(epaper.getbuffer(image.convert('1', dither=True))) + epaper.display(epaper.getbuffer(image)) print('Done') print('Sending E-Paper to deep sleep...', end = '') From 002ed089f3396c42db10533e7cb8b6d3e39f73db Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 09:34:20 +0100 Subject: [PATCH 11/25] Updated for new driver files --- modules/inkycal_image.py | 206 +++++++++++++++++++++++++++------------ 1 file changed, 145 insertions(+), 61 deletions(-) diff --git a/modules/inkycal_image.py b/modules/inkycal_image.py index 209afb0..0840347 100644 --- a/modules/inkycal_image.py +++ b/modules/inkycal_image.py @@ -2,82 +2,166 @@ # -*- coding: utf-8 -*- """ Experimental image module for Inky-Calendar software -Displays an image on the E-Paper. Currently only supports black and white +Displays an image on the E-Paper. Work in progress! Copyright by aceisace """ from __future__ import print_function -from PIL import Image from configuration import * -import os +from os import path +from PIL import ImageOps +import requests +import numpy -import inkycal_drivers as drivers +"""----------------------------------------------------------------""" +#path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' +path ='/home/pi/Inky-Calendar/images/canvas.png' +mode = 'auto' # 'horizontal' # 'vertical' # 'auto' +upside_down = True # Flip image by 180 deg (upside-down) +alignment = 'center' # top_center, top_left, center_left, bottom_right etc. +colours = 'bwr' # bwr # bwy # bw +render = True # show image on E-Paper? +"""----------------------------------------------------------------""" -display = drivers.EPD() +"""Try to open the image if it exists and is an image file""" +try: + if 'http' in path: + im = Image.open(requests.get(path, stream=True).raw) + else: + im = Image.open(path) +except FileNotFoundError: + print('Your file could not be found. Please check the path to your file.') + raise +except OSError: + print('Please check if the path points to an image file.') + raise -# Where is the image? -path = '/home/pi//Desktop/test.JPG' +"""Turn image upside-down if specified""" +if upside_down == True: + im.rotate(180, expand = True) -class inkycal_image: +if mode == 'horizontal': + display_width, display_height == display_height, display_width - def __init__(self, path): - self.image = Image.open(path) - self.im_width = self.image.width - self.im_height = self.image.height +if mode == 'vertical': + pass - def check_mode(self): - if self.image.mode != 'RGB' or 'L' or '1': - print('Image mode not supported, converting') - self.image = self.image.convert('RGB') +if mode == 'auto': + if (im.width > im.height) and (display_width < display_height): + print('display vertical, image horizontal -> flipping image') + im = im.rotate(90, expand=True) + if (im.width < im.height) and (display_width > display_height): + print('display horizontal, image vertical -> flipping image') + im = im.rotate(90, expand=True) - def preview(self): - self.image.save(path+'temp.png') - os.system("gpicview "+path+'temp.png') - os.system('rm '+path+'temp.png') - +def fit_width(image, width): + """Resize an image to desired width""" + print('resizing width from', image.width, 'to', end = ' ') + wpercent = (display_width/float(image.width)) + hsize = int((float(image.height)*float(wpercent))) + img = image.resize((width, hsize), Image.ANTIALIAS) + print(img.width) + return img - def check_size(self, alignment = 'middle', padding_colour='white'): - if display_height < self.im_height or display_width < self.im_width: - print('Image too large for the display, cropping image') - if alignment == 'middle' or None: - x1 = int((self.im_width - display_width) / 2) - y1 = int((self.im_height - display_height) / 2) - x2,y2 = x1+display_width, y1+display_height - self.image = self.image.crop((x1,y1,x2,y2)) - - if alignment != 'middle' or None: - print('Sorry, this feature has not been implemented yet') - raise NotImplementedError +def fit_height(image, height): + """Resize an image to desired height""" + print('resizing height from', image.height, 'to', end = ' ') + hpercent = (height / float(image.height)) + wsize = int(float(image.width) * float(hpercent)) + img = image.resize((wsize, height), Image.ANTIALIAS) + print(img.height) + return img - elif display_height > self.im_height and display_width > self.im_width: - print('Image smaller than display, shifting image to center') - x = int( (display_width - self.im_width) /2) - y = int( (display_height - self.im_height) /2) - canvas = Image.new('RGB', (display_width, display_height), color=padding_colour) - canvas.paste(self.image, (x,y)) - self.image = canvas +if im.width > display_width: + im = fit_width(im, display_width) +if im.height > display_height: + im = fit_height(im, display_height) - else: - print('Image file exact. no further action required') +if alignment == 'center': + x,y = int((display_width-im.width)/2), int((display_height-im.height)/2) +elif alignment == 'center_right': + x, y = display_width-im.width, int((display_height-im.height)/2) +elif alignment == 'center_left': + x, y = 0, int((display_height-im.height)/2) - def auto_flip(self): - if self.im_height < self.im_width: - print('rotating image') - self.image = self.image.rotate(270, expand=True) - self.im_width = self.image.width - self.im_height = self.image.height - - - def to_mono(self): - self.image = self.image.convert('1', dither=True) +elif alignment == 'top_center': + x, y = int((display_width-im.width)/2), 0 +elif alignment == 'top_right': + x, y = display_width-im.width, 0 +elif alignment == 'top_left': + x, y = 0, 0 - def prepare_image(self, alignment='middle'): - self.check_mode() - self.auto_flip() - self.check_size(alignment = alignment) - self.to_mono() +elif alignment == 'bottom_center': + x, y = int((display_width-im.width)/2), display_height-im.height +elif alignment == 'bottom_right': + x, y = display_width-im.width, display_height-im.height +elif alignment == 'bottom_left': + x, y = display_width-im.width, display_height-im.height - return self.image +if len(im.getbands()) == 4: + print('removing transparency') + bg = Image.new('RGBA', (im.width, im.height), 'white') + im = Image.alpha_composite(bg, im) -#single line command: -display.show_image(inkycal_image(path).prepare_image(), reduce_colours=False) - +image.paste(im, (x,y)) +im = image + +if colours == 'bw': + """For black-white images, use monochrome dithering""" + black = im.convert('1', dither=True) +elif colours == 'bwr': + """For black-white-red images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] +elif colours == 'bwy': + """For black-white-yellow images, create corresponding palette""" + pal = [255,255,255, 0,0,0, 255,255,0, 255,255,255] + + +"""Map each pixel of the opened image to the Palette""" +if colours != 'bw': + palette_im = Image.new('P', (3,1)) + palette_im.putpalette(pal * 64) + quantized_im = im.quantize(palette=palette_im) + quantized_im.convert('RGB') + + """Create buffer for coloured pixels""" + buffer1 = numpy.array(quantized_im.convert('RGB')) + r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] + + """Create buffer for black pixels""" + buffer2 = numpy.array(quantized_im.convert('RGB')) + r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] + + if colours == 'bwr': + """Create image for only red pixels""" + buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white + buffer2[numpy.logical_and(r2 == 255, b2 == 0)] = [0,0,0] #red->black + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(r1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + + if colours == 'bwy': + """Create image for only yellow pixels""" + buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white + buffer2[numpy.logical_and(g2 == 255, b2 == 0)] = [0,0,0] #yellow -> black + colour = Image.fromarray(buffer2) + """Create image for only black pixels""" + buffer1[numpy.logical_and(g1 == 255, b1 == 0)] = [255,255,255] + black = Image.fromarray(buffer1) + +if render == True: + epaper = driver.EPD() + print('Initialising E-Paper...', end = '') + epaper.init() + print('Done') + + print('Sending image data and refreshing display...', end='') + if three_colour_support == True: + epaper.display(epaper.getbuffer(black), epaper.getbuffer(colour)) + else: + epaper.display(epaper.getbuffer(black)) + print('Done') + + print('Sending E-Paper to deep sleep...', end = '') + epaper.sleep() +print('Done') From 62a67c068a5eb2dee95d31304bfb4b02fee4494c Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 09:34:48 +0100 Subject: [PATCH 12/25] renamed inky_image to inkycal_image --- modules/inky_image.py | 166 ------------------------------------------ 1 file changed, 166 deletions(-) delete mode 100644 modules/inky_image.py diff --git a/modules/inky_image.py b/modules/inky_image.py deleted file mode 100644 index 541492d..0000000 --- a/modules/inky_image.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -""" -Experimental image module for Inky-Calendar software -Displays an image on the E-Paper. Work in progress! -Copyright by aceisace -""" -from __future__ import print_function -from configuration import * -from os import path -from PIL import ImageOps -import requests -import numpy - -"""----------------------------------------------------------------""" -path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' -mode = 'auto' # 'horizontal' # 'vertical' # 'auto' -upside_down = False # Flip image by 180 deg (upside-down) -alignment = 'center' # top_center, top_left, center_left, bottom_right etc. -colours = 'bwr' # bwr # bwy # bw -render = True # show image on E-Paper? -"""----------------------------------------------------------------""" - -"""Try to open the image if it exists and is an image file""" -try: - if 'http' in path: - im = Image.open(requests.get(path, stream=True).raw) - else: - im = Image.open(path) -except FileNotFoundError: - print('Your file could not be found. Please check the path to your file.') - raise -except OSError: - print('Please check if the path points to an image file.') - raise - -"""Turn image upside-down if specified""" -if upside_down == True: - im.rotate(180, expand = True) - -if mode == 'horizontal': - display_width, display_height == display_height, display_width - -if mode == 'vertical': - pass - -if mode == 'auto': - if (im.width > im.height) and (display_width < display_height): - print('display vertical, image horizontal -> flipping image') - im = im.rotate(90, expand=True) - if (im.width < im.height) and (display_width > display_height): - print('display horizontal, image vertical -> flipping image') - im = im.rotate(90, expand=True) - -def fit_width(image, width): - """Resize an image to desired width""" - print('resizing width from', image.width, 'to', end = ' ') - wpercent = (display_width/float(image.width)) - hsize = int((float(image.height)*float(wpercent))) - img = image.resize((width, hsize), Image.ANTIALIAS) - print(img.width) - return img - -def fit_height(image, height): - """Resize an image to desired height""" - print('resizing height from', image.height, 'to', end = ' ') - hpercent = (height / float(image.height)) - wsize = int(float(image.width) * float(hpercent)) - img = image.resize((wsize, height), Image.ANTIALIAS) - print(img.height) - return img - -if im.width > display_width: - im = fit_width(im, display_width) -if im.height > display_height: - im = fit_height(im, display_height) - -if alignment == 'center': - x,y = int((display_width-im.width)/2), int((display_height-im.height)/2) -elif alignment == 'center_right': - x, y = display_width-im.width, int((display_height-im.height)/2) -elif alignment == 'center_left': - x, y = 0, int((display_height-im.height)/2) - -elif alignment == 'top_center': - x, y = int((display_width-im.width)/2), 0 -elif alignment == 'top_right': - x, y = display_width-im.width, 0 -elif alignment == 'top_left': - x, y = 0, 0 - -elif alignment == 'bottom_center': - x, y = int((display_width-im.width)/2), display_height-im.height -elif alignment == 'bottom_right': - x, y = display_width-im.width, display_height-im.height -elif alignment == 'bottom_left': - x, y = display_width-im.width, display_height-im.height - -if len(im.getbands()) == 4: - print('removing transparency') - bg = Image.new('RGBA', (im.width, im.height), 'white') - im = Image.alpha_composite(bg, im) - -image.paste(im, (x,y)) -im = image - -if colours == 'bw': - """For black-white images, use monochrome dithering""" - black = im.convert('1', dither=True) -elif colours == 'bwr': - """For black-white-red images, create corresponding palette""" - pal = [255,255,255, 0,0,0, 255,0,0, 255,255,255] -elif colours == 'bwy': - """For black-white-yellow images, create corresponding palette""" - pal = [255,255,255, 0,0,0, 255,255,0, 255,255,255] - - -"""Map each pixel of the opened image to the Palette""" -if colours != 'bw': - palette_im = Image.new('P', (3,1)) - palette_im.putpalette(pal * 64) - quantized_im = im.quantize(palette=palette_im) - quantized_im.convert('RGB') - - """Create buffer for coloured pixels""" - buffer1 = numpy.array(quantized_im.convert('RGB')) - r1,g1,b1 = buffer1[:, :, 0], buffer1[:, :, 1], buffer1[:, :, 2] - - """Create buffer for black pixels""" - buffer2 = numpy.array(quantized_im.convert('RGB')) - r2,g2,b2 = buffer2[:, :, 0], buffer2[:, :, 1], buffer2[:, :, 2] - - if colours == 'bwr': - """Create image for only red pixels""" - buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white - buffer2[numpy.logical_and(r2 == 255, b2 == 0)] = [0,0,0] #red->black - colour = Image.fromarray(buffer2) - """Create image for only black pixels""" - buffer1[numpy.logical_and(r1 == 255, b1 == 0)] = [255,255,255] - black = Image.fromarray(buffer1) - - if colours == 'bwy': - """Create image for only yellow pixels""" - buffer2[numpy.logical_and(r2 == 0, b2 == 0)] = [255,255,255] # black->white - buffer2[numpy.logical_and(g2 == 255, b2 == 0)] = [0,0,0] #yellow -> black - colour = Image.fromarray(buffer2) - """Create image for only black pixels""" - buffer1[numpy.logical_and(g1 == 255, b1 == 0)] = [255,255,255] - black = Image.fromarray(buffer1) - -if render == True: - epaper = driver.EPD() - print('Initialising E-Paper...', end = '') - epaper.init() - print('Done') - - print('Sending image data and refreshing display...', end='') - if three_colour_support == True: - epaper.display(epaper.getbuffer(black), epaper.getbuffer(colour)) - else: - epaper.display(epaper.getbuffer(black)) - print('Done') - - print('Sending E-Paper to deep sleep...', end = '') - epaper.sleep() -print('Done') From 39f528ad5c8f72df3a1c9c1ec877af38fa7fc51c Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 09:37:46 +0100 Subject: [PATCH 13/25] Added colour optimisation function A new function handles correct rendering of images (especially text) by converting grey areas to black. --- settings/configuration.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/settings/configuration.py b/settings/configuration.py index 67fd07d..1f5327d 100644 --- a/settings/configuration.py +++ b/settings/configuration.py @@ -35,17 +35,14 @@ else: top_section_width = middle_section_width = bottom_section_width = display_width if top_section and bottom_section: - print('top and bottom section occupied') top_section_height = int(display_height*0.11) bottom_section_height = int(display_height*0.24) elif top_section and not bottom_section: - print('top section occupied') top_section_height = int(display_height*0.11) bottom_section_height = 0 elif bottom_section and not top_section: - print('bottom_section occupied') top_section_height = 0 bottom_section_height = int(display_height*0.24) @@ -150,7 +147,7 @@ def clear_image(section, colour = background_colour): if three_colour_support == True: image_col.paste(box, position) - + def crop_image(input_image, section): """Crop an input image to the desired section""" @@ -180,8 +177,8 @@ def draw_square(tuple, radius, width, height, colour=text_colour, line_width=1): """Draws a square with round corners at position (x,y) from tuple""" x, y, diameter = tuple[0], tuple[1], radius*2 line_length = width - diameter - - p1, p2 = (x+radius, y), (x+radius+line_length, y) + + p1, p2 = (x+radius, y), (x+radius+line_length, y) p3, p4 = (x+width, y+radius), (x+width, y+radius+line_length) p5, p6 = (p2[0], y+height), (p1[0], y+height) p7, p8 = (x, p4[1]), (x,p3[1]) @@ -208,7 +205,7 @@ def draw_square(tuple, radius, width, height, colour=text_colour, line_width=1): draw.arc( (c3, c4) , 270, 360, fill=colour, width=line_width) draw.arc( (c5, c6) , 0, 90, fill=colour, width=line_width) draw.arc( (c7, c8) , 90, 180, fill=colour, width=line_width) - + def internet_available(): """check if the internet is available""" @@ -245,6 +242,13 @@ def image_cleanup(): os.remove(temp_files) print('Done') +def optimise_colours(image, threshold=220): + buffer = numpy.array(image.convert('RGB')) + red, green = buffer[:, :, 0], buffer[:, :, 1] + buffer[numpy.logical_and(red <= threshold, green <= threshold)] = [0,0,0] #grey->black + image = Image.fromarray(buffer) + return image + def calibrate_display(no_of_cycles): """How many times should each colour be calibrated? Default is 3""" epaper = driver.EPD() @@ -272,6 +276,6 @@ def calibrate_display(no_of_cycles): print('white...') epaper.display(epaper.getbuffer(white)), print('Cycle {0} of {1} complete'.format(_+1, no_of_cycles)) - + print('-----------Calibration complete----------') epaper.sleep() From cd0b6541a3bfe0f332c09dda734ab71ec54a9eb4 Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 16:23:44 +0100 Subject: [PATCH 14/25] Added function to check for software updates --- settings/configuration.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/settings/configuration.py b/settings/configuration.py index 1f5327d..63e80b2 100644 --- a/settings/configuration.py +++ b/settings/configuration.py @@ -16,6 +16,7 @@ from pytz import timezone import os from glob import glob import importlib +import subprocess as subp """Set the image background colour and text colour""" background_colour = 'white' @@ -279,3 +280,17 @@ def calibrate_display(no_of_cycles): print('-----------Calibration complete----------') epaper.sleep() + +def check_for_updates(): + with open(path+'Info.txt','r') as file: + lines = file.readlines() + installed_release = 'v'+lines[2].rstrip().split(' ')[1] + temp = subp.check_output(['curl','-s','https://github.com/aceisace/Inky-Calendar/releases/latest']) + latest_release_url = str(temp).split('"')[1] + latest_release = latest_release_url.split('/tag/')[1] + if installed_release == latest_release: + print('You are using the latest version of the Inky-Calendar software:', end = ' ') + print(installed_release) + else: + print('New update available!. Please update to the latest version') + print('current release:', installed_release, 'new version:', latest_release) From aa97cdd65a8db40858adaab9987e571d7cfd5c38 Mon Sep 17 00:00:00 2001 From: Ace Date: Wed, 12 Feb 2020 16:26:40 +0100 Subject: [PATCH 15/25] Updated for new release --- Installer.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Installer.sh b/Installer.sh index 6cfc1a5..159b542 100644 --- a/Installer.sh +++ b/Installer.sh @@ -1,6 +1,6 @@ #!/bin/bash # E-Paper-Calendar software installer for Raspberry Pi running Debian 10 (a.k.a. Buster) with Desktop -# Version: 1.7 (Early Dec 2019) +# Version: 1.7.2 (Mid Feb 2020) echo -e "\e[1mPlease select an option from below:" echo -e "\e[97mEnter \e[91m[1]\e[97m to update Inky-Calendar software" #Option 1 : UPDATE @@ -95,8 +95,8 @@ if [ "$option" = 1 ] || [ "$option" = 2 ]; then # This happens when installing o cat > /home/pi/Inky-Calendar/Info.txt << EOF This document contains a short info of the Inky-Calendar software version -Version: 1.7 -Installer version: 1.7 (Mid December 2019) +Version: 1.7.2 +Installer version: 1.7.2 (Mid Feb 2020) settings file: /home/$USER/Inky-Calendar/settings/settings.py If the time was set correctly, you installed this software on: $(date) From 426765a70db571f7626f947cc4cc769ef6a9461c Mon Sep 17 00:00:00 2001 From: Ace Date: Thu, 13 Feb 2020 15:55:16 +0100 Subject: [PATCH 16/25] Updated file --- Changelog.md | 67 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1d5fa76..607d61b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,11 +4,30 @@ The order is from latest to oldest and structured in the following way: * Version name with date of publishing * Sections with either 'added', 'fixed', 'updated' and 'changed' -## [1.7] Mid December 2019 (date not confirmed yet) +## [1.7.1] Mid January 2020 + +### Added +* Added support for 4.2", 5.83", 7.5" (v2) E-Paper display +* Added driver files for above mentioned E-Paper displays + +### Changed +* Slight changes in naming of generated images +* Slight changes in importing module names (now using dynamic imports) +* Changed driver files for all E-Papers with the latest ones from waveshare (v4) +* Slightly changed the way modules are executed + +### Removed +* Removed option for selecting colour from settings file + +### Fixed +* Fixed a problem where the calibration function would only update half the display on the 7.5" black-white E-Paper +* Implemented a possible bugfix for 'begin must be before end' error. + +## [1.7] Mid December 2019 ### Added * Added support for sections (top-,middle-,and bottom section) -* Added support for weather forecasts. +* Added support for weather forecasts. * Added support for moon phase * Added support for events in Calendar module * Added support for coloured negative temperature @@ -16,31 +35,37 @@ The order is from latest to oldest and structured in the following way: * Added support for wind direction in weather module * Added support for decimal places in weather module * Added extra customisation options (see configuration file) +* Added support for recurring events +* Added forecasts in weather module +* Added info about moon phase in weather module +* Added info about sunrise and sunset time in weather module +* Added support for colour-changing temperature (for coloured E-Paper displays, the temperature will red if it drops below 0°Celcius) +* Added support for decimal places in weather section (wind speed, temperature) +* Added beaufort scale to show windspeed +* Added option to show wind direction with an arrow +* Added new event and today icon in Calendar module +* Added sections showing upcoming events within Calendar module +* Added configuration file for additional configuration options +* Added new fonts with better readability +* Added support to manually change fontsize in each module +* Added more design customisation (text colour, background colours etc.) ### Changed -* Refactoring of software. Split software into several smaller modules -* Re-arranged weather section layout -* Icons (today, events) are generated on demand -* Merged calibration files into inkycal_drivers -* Changed layout of Agenda module -* Changed icons for marking today on Calendar module -* Added more options in function 'write_text' -* Text does not have any background colour anymore (transparent) -* Optimised calibration function for faster calibration, especially for coloured E-Papers -* Changed settings file +* Changed folder structure (Full software refactoring) +* Split main file into smaller modules, each with a specific task +* Changed layout of E-Paper (top_section, middle_section, bottom_section) +* Changed settings file, installer and web-UI +* Black and white E-Papers now use dithering option to map pixels to either black and white ### Removed -* Removed last-updated feature -* Removed all icons stored as images -* Removed calibration file (calibration.py) - +* Removed non-readable fonts +* Removed all icons in form of image files. The new icons are generated with PIL on the spot +* Removed option to reduce colours for black and white E-Papers ### Fixed -* Fixed a few bugs related to the ics library -* Fine-tuned image pre-processing (mapping pixels to specific colours) -* Fixed a problem where RSS feeds would not display more than one post -* Fixed a problem where certain weather icons would not be shown - +* Fixed problem with RSS feeds not displaying more than one feed +* Fixed image rendering +* Fixed problems when setting the weekstart to Sunday ## [1.6] Mid May 2019 From acc598785dd062c0e5aa3b8a243133d1d63bdd4b Mon Sep 17 00:00:00 2001 From: Ace Date: Thu, 13 Feb 2020 16:34:10 +0100 Subject: [PATCH 17/25] Created release file for updates management This file contains the release version (like v1.7.1, or v.1.8 etc.) and is used for update management. If the latest release from github is greater than the current release, a noctice will appear on the E-Paper about a new update). --- release.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 release.txt diff --git a/release.txt b/release.txt new file mode 100644 index 0000000..3b34d22 --- /dev/null +++ b/release.txt @@ -0,0 +1 @@ +v1.7.2 From 7e34b70f8815ed0983520e0c606aca6e58ed8ab9 Mon Sep 17 00:00:00 2001 From: Ace Date: Thu, 13 Feb 2020 16:38:11 +0100 Subject: [PATCH 18/25] Info.txt grabs version form release file now Info.txt will grab the release version from release.txt instead of a hardcoded release version. --- Installer.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Installer.sh b/Installer.sh index 159b542..dcccabd 100644 --- a/Installer.sh +++ b/Installer.sh @@ -92,11 +92,11 @@ if [ "$option" = 1 ] || [ "$option" = 2 ]; then # This happens when installing o ln -s /home/"$USER"/Inky-Calendar/settings/configuration.py /home/"$USER"/Inky-Calendar/modules/ # add a short info + currentRelease=$(cat release.txt) cat > /home/pi/Inky-Calendar/Info.txt << EOF This document contains a short info of the Inky-Calendar software version -Version: 1.7.2 -Installer version: 1.7.2 (Mid Feb 2020) +Version: $currentRelease settings file: /home/$USER/Inky-Calendar/settings/settings.py If the time was set correctly, you installed this software on: $(date) From 59ac74c19417f35c920d0f0d5016d900af723e3e Mon Sep 17 00:00:00 2001 From: Ace Date: Thu, 13 Feb 2020 16:42:55 +0100 Subject: [PATCH 19/25] Better update handling Info.txt will not be created when installing manually. As a result, any dependency of info.txt will backfire. This approach switches from Info.txt to release.txt. Info.txt is depreceated now as it is no longer required. --- settings/configuration.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/settings/configuration.py b/settings/configuration.py index 63e80b2..361afe2 100644 --- a/settings/configuration.py +++ b/settings/configuration.py @@ -282,15 +282,25 @@ def calibrate_display(no_of_cycles): epaper.sleep() def check_for_updates(): - with open(path+'Info.txt','r') as file: + with open(path+'release.txt','r') as file: lines = file.readlines() - installed_release = 'v'+lines[2].rstrip().split(' ')[1] + installed_release = lines[0].rstrip() + temp = subp.check_output(['curl','-s','https://github.com/aceisace/Inky-Calendar/releases/latest']) latest_release_url = str(temp).split('"')[1] latest_release = latest_release_url.split('/tag/')[1] - if installed_release == latest_release: - print('You are using the latest version of the Inky-Calendar software:', end = ' ') - print(installed_release) - else: + + def get_id(version): + if not version.startswith('v'): + print('incorrect release format!') + v = ''.join(version.split('v')[1].split('.')) + if len(v) == 2: + v += '0' + return int(v) + + if get_id(installed_release) < get_id(latest_release): print('New update available!. Please update to the latest version') print('current release:', installed_release, 'new version:', latest_release) + else: + print('You are using the latest version of the Inky-Calendar software:', end = ' ') + print(installed_release) From 2fa92f8fd0c0c41a137e5e80804a241cb732dc92 Mon Sep 17 00:00:00 2001 From: Ace Date: Thu, 13 Feb 2020 16:45:27 +0100 Subject: [PATCH 20/25] Removed non-required file The purpose of Info.txt was to serve as an info about the release. By switching from Info.txt to release.txt, this is no longer required as python can check for updates by itself now. --- Installer.sh | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Installer.sh b/Installer.sh index dcccabd..e09477d 100644 --- a/Installer.sh +++ b/Installer.sh @@ -90,17 +90,6 @@ if [ "$option" = 1 ] || [ "$option" = 2 ]; then # This happens when installing o # Create symlinks of settings and configuration file ln -s /home/"$USER"/Inky-Calendar/settings/settings.py /home/"$USER"/Inky-Calendar/modules/ ln -s /home/"$USER"/Inky-Calendar/settings/configuration.py /home/"$USER"/Inky-Calendar/modules/ - - # add a short info - currentRelease=$(cat release.txt) - cat > /home/pi/Inky-Calendar/Info.txt << EOF -This document contains a short info of the Inky-Calendar software version - -Version: $currentRelease -settings file: /home/$USER/Inky-Calendar/settings/settings.py -If the time was set correctly, you installed this software on: -$(date) -EOF echo "" echo -e "\e[97mDo you want the software to start automatically at boot?" From f8546da0b40c867746e6ea5e1efb9e62eb03b849 Mon Sep 17 00:00:00 2001 From: Robert Sirre Date: Fri, 14 Feb 2020 22:16:35 +0100 Subject: [PATCH 21/25] Added configuration for inkycal_image Allowed to obtain image using http POST besides GET --- modules/inkycal_image.py | 28 ++++++++++++++++++++-------- settings/settings.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/modules/inkycal_image.py b/modules/inkycal_image.py index 0840347..6ee4ab8 100644 --- a/modules/inkycal_image.py +++ b/modules/inkycal_image.py @@ -14,7 +14,9 @@ import numpy """----------------------------------------------------------------""" #path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' -path ='/home/pi/Inky-Calendar/images/canvas.png' +#path ='/home/pi/Inky-Calendar/images/canvas.png' +path = inkycal_image_path +path_body = inkycal_image_path_body mode = 'auto' # 'horizontal' # 'vertical' # 'auto' upside_down = True # Flip image by 180 deg (upside-down) alignment = 'center' # top_center, top_left, center_left, bottom_right etc. @@ -22,10 +24,26 @@ colours = 'bwr' # bwr # bwy # bw render = True # show image on E-Paper? """----------------------------------------------------------------""" +# First determine dimensions +if mode == 'horizontal': + display_width, display_height == display_height, display_width + +if mode == 'vertical': + pass + +# .. Then substitute possibly parameterized path +# TODO Get (assigned) panel dimensions instead of display dimensions +path = path.replace('{model}', model).replace('{width}',str(display_width)).replace('{height}',str(display_height)) + """Try to open the image if it exists and is an image file""" try: if 'http' in path: - im = Image.open(requests.get(path, stream=True).raw) + if path_body is None: + # Plain GET + im = Image.open(requests.get(path, stream=True).raw) + else: + # POST request, passing path_body in the body + im = Image.open(requests.post(path, json=path_body, stream=True).raw) else: im = Image.open(path) except FileNotFoundError: @@ -39,12 +57,6 @@ except OSError: if upside_down == True: im.rotate(180, expand = True) -if mode == 'horizontal': - display_width, display_height == display_height, display_width - -if mode == 'vertical': - pass - if mode == 'auto': if (im.width > im.height) and (display_width < display_height): print('display vertical, image horizontal -> flipping image') diff --git a/settings/settings.py b/settings/settings.py index 9ce7610..5b46cf3 100644 --- a/settings/settings.py +++ b/settings/settings.py @@ -23,6 +23,38 @@ bottom_section = "inkycal_rss" # "inkycal_rss" # URLs should have this sign (") on both side -> "url1" # If more than one URL is used, separate each one with a comma -> "url1", "url2" +######################## +# inkycal_image config: +# +# inkycal_image_path +# The url or file path to obtain the image from. +# The following parameters within accolades ({}) will be substituted: +# - model +# - width +# - height +# +# Samples : +# The inkycal logo: +# inkycal_image_path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' +# +# A dynamic image with a demo-calendar +# inkycal_image_path = 'https://inkycal.robertsirre.nl/panel/test/{model}/image?width={width}&height={height}' +# +# Dynamic image with configurable calendars (see https://inkycal.robertsirre.nl/ and parameter inkycal_image_path_body) +# inkycal_image_path = 'https://inkycal.robertsirre.nl/panel/calendar/{model}?width={width}&height={height}' + +inkycal_image_path ='/home/pi/Inky-Calendar/images/canvas.png' + +# Optional: inkycal_image_path_body +# Allows obtaining complexer configure images. +# When inkycal_image_path starts with `http` and inkycal_image_path_body is specified, the image is obtained using POST instead of GET. +# NOTE: structure of the body depends on the web-based image service +# inkycal_image_path_body = [ +# 'https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics', +# 'https://www.calendarlabs.com/ical-calendar/ics/101/Netherlands_Holidays.ics' +# ] +######################## + """Supported E-Paper models""" # epd_7_in_5_v2_colour # 7.5" high-res black-white-red/yellow # epd_7_in_5_v2 # 7.5" high-res black-white From 0ce1722c7db820b24ee34679a3b5e44fdcd2be89 Mon Sep 17 00:00:00 2001 From: Robert Sirre Date: Sat, 15 Feb 2020 22:54:03 +0100 Subject: [PATCH 22/25] Added configuration for image panel in settings UI --- settings/settings-UI.html | 85 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/settings/settings-UI.html b/settings/settings-UI.html index 411ddcd..f409d64 100644 --- a/settings/settings-UI.html +++ b/settings/settings-UI.html @@ -78,7 +78,6 @@ body{ -
@@ -273,7 +272,7 @@ body{
-
+
@@ -282,11 +281,52 @@ body{
+
+ + +
+ +
+
@@ -333,8 +373,31 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
diff --git a/settings/settings.jsonc b/settings/settings.jsonc new file mode 100644 index 0000000..7277439 --- /dev/null +++ b/settings/settings.jsonc @@ -0,0 +1,91 @@ +{ + "language" : "en", // "en", "de", "fr", "jp" etc. + "units" : "metric", // "metric", "imperial" + "hours" : 24, // 24, 12 + "model" : "epd_7_in_5_v2_colour", // For supported E-paper models, see below + "update_interval" : 60, // 10, 15, 20, 30, 60 + "calibration_hours" : [0,12,18], // Do not change unlesss you know what you are doing + + //For now three panels can be defined for three unique locations: 'top', 'middle' and 'bottom' + "panels" : [ + { + "location" : "top", + "type" : "inkycal_weather", + "config" : { + "api_key" : "", //Your openweathermap API-KEY -> "api-key" + "location" : "Stuttgart, DE" //"City name, Country code" + } + }, + { + "location" : "middle", + "type" : "inkycal_calendar", // "inkycal_calendar" and "inkycal_agenda" have the same parameters, but are displayed differently + "config" : { + "week_starts_on" : "Monday", //"Sunday", "Monday"... + "ical_urls" : [ + "https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics", + "https://www.calendarlabs.com/ical-calendar/ics/101/Netherlands_Holidays.ics" + ] + } + }, + { + "location" : "bottom", + "type" : "inkycal_rss", + "config" : { + "rss_feeds" : [ + "http://feeds.bbci.co.uk/news/world/rss.xml#", + "https://github.com/aceisace/Inky-Calendar/releases.atom" + ] + } + }, + { + "location" : "middle", + "type" : "inkycal_image", + "config" : { + /* + The url or file path to obtain the image from. + The following parameters within accolades ({}) will be substituted: + - model + - width + - height + + Samples + The inkycal logo: + inkycal_image_path = 'https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png' + + A dynamic image with a demo-calendar + inkycal_image_path = 'https://inkycal.robertsirre.nl/panel/test/{model}/image?width={width}&height={height}' + + Dynamic image with configurable calendars (see https://inkycal.robertsirre.nl/ and parameter inkycal_image_path_body) + inkycal_image_path = 'https://inkycal.robertsirre.nl/panel/calendar/{model}?width={width}&height={height}' + + inkycal_image_path ='/home/pi/Inky-Calendar/images/canvas.png' + */ + "image_path" : "https://github.com/aceisace/Inky-Calendar/raw/master/Gallery/Inky-Calendar-logo.png", + + /* + Optional: inkycal_image_path_body + Allows obtaining complexer configure images. + When `inkycal_image_path` starts with `http` and `inkycal_image_path_body` is specified, the image is obtained using POST instead of GET. + NOTE: structure of the body depends on the web-based image service + */ + + // inkycal_image_path_body = [ + // 'https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics', + // 'https://www.calendarlabs.com/ical-calendar/ics/101/Netherlands_Holidays.ics' + // ] + } + } + ] +} + +/* +Supported E-Paper models""" +epd_7_in_5_v2_colour # 7.5" high-res black-white-red/yellow +epd_7_in_5_v2 # 7.5" high-res black-white +epd_7_in_5_colour # 7.5" black-white-red/yellow +epd_7_in_5 # 7.5" black-white +epd_5_in_83_colour # 5.83" black-white-red/yellow +epd_5_in_83 # 5.83" black-white +epd_4_in_2_colour # 4.2" black-white-red/yellow +epd_4_in_2 # 4.2" black-white +*/ \ No newline at end of file From ceae63fc67168afcf1a0d7798fac745d6a982ae5 Mon Sep 17 00:00:00 2001 From: Robert Sirre Date: Sat, 15 Feb 2020 22:54:59 +0100 Subject: [PATCH 24/25] Minor spelling correction --- settings/settings-UI.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/settings-UI.html b/settings/settings-UI.html index be05509..4d6ce39 100644 --- a/settings/settings-UI.html +++ b/settings/settings-UI.html @@ -35,7 +35,7 @@ body{
-
+
@@ -345,7 +345,7 @@ body{

Instructions
-Insert your peesonal details and preferences and click on 'Generate'. Copy the downloaded file to the Raspberry Pi and place it in: '/home/pi/Inky-Calendar/settings/' (inside the settings folder within the Inky-Calendar folder. Lastly, reboot the Raspberry Pi to apply the changes. You can also manually run the software with: +Insert your personal details and preferences and click on 'Generate'. Copy the downloaded file to the Raspberry Pi and place it in: '/home/pi/Inky-Calendar/settings/' (inside the settings folder within the Inky-Calendar folder. Lastly, reboot the Raspberry Pi to apply the changes. You can also manually run the software with: python3 /home/pi/Inky-Calendar/modules/inkycal.py.

From 860f62d501179e5fd8bf6bd24fdb3883a8faf3bb Mon Sep 17 00:00:00 2001 From: Ace Date: Sun, 16 Feb 2020 22:43:13 +0100 Subject: [PATCH 25/25] Fix for incorrect (duplicate) event entries. --- modules/inkycal_calendar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/inkycal_calendar.py b/modules/inkycal_calendar.py index 8ff1698..0218d55 100644 --- a/modules/inkycal_calendar.py +++ b/modules/inkycal_calendar.py @@ -170,7 +170,7 @@ def generate_image(): at_in_your_language, event.begin.format('HH:mm' if hours == '24' else 'hh:mm a'), event.name)) - if event.begin.day == now.replace(days=1).day: + elif event.begin.day == now.replace(days=1).day: if event.all_day: event_list.append('{}: {}'.format(tomorrow_in_your_language, event.name)) else: @@ -178,7 +178,7 @@ def generate_image(): at_in_your_language, event.begin.format('HH:mm' if hours == '24' else 'hh:mm a'), event.name)) - if event.end > after_two_days: + elif event.begin > after_two_days: if event.all_day: event_list.append('{}: {}'.format(event.begin.format('D MMM'), event.name)) else: