67
									
								
								Changelog.md
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								Installer.sh
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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 | ||||
| @@ -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 | ||||
|     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) | ||||
| 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?" | ||||
|   | ||||
| @@ -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 | ||||
| @@ -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,36 +71,62 @@ 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') | ||||
|       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') | ||||
|         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') | ||||
|       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') | ||||
|         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') | ||||
|       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') | ||||
|         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 = 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-----------""" | ||||
|     epaper = driver.EPD() | ||||
|     print('Initialising E-Paper...', end = '') | ||||
| @@ -91,12 +135,11 @@ 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='') | ||||
|       epaper.display(epaper.getbuffer(image.convert('1', dither=True))) | ||||
|       epaper.display(epaper.getbuffer(image)) | ||||
|       print('Done') | ||||
|  | ||||
|     print('Sending E-Paper to deep sleep...', end = '') | ||||
|   | ||||
| @@ -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() | ||||
|  | ||||
|   | ||||
| @@ -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)) | ||||
|  | ||||
|           elif 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)) | ||||
|  | ||||
|           elif event.begin > 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(): | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -2,82 +2,178 @@ | ||||
| # -*- 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' | ||||
| 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. | ||||
| colours = 'bwr'       # bwr # bwy # bw | ||||
| render = True         # show image on E-Paper? | ||||
| """----------------------------------------------------------------""" | ||||
|  | ||||
| display = drivers.EPD() | ||||
| # First determine dimensions | ||||
| if mode == 'horizontal': | ||||
|   display_width, display_height == display_height, display_width | ||||
|  | ||||
| # Where is the image? | ||||
| path = '/home/pi//Desktop/test.JPG' | ||||
| if mode == 'vertical': | ||||
|   pass | ||||
|  | ||||
| class inkycal_image: | ||||
|  | ||||
|   def __init__(self, path): | ||||
|     self.image = Image.open(path) | ||||
|     self.im_width = self.image.width | ||||
|     self.im_height = self.image.height | ||||
|  | ||||
|   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') | ||||
|  | ||||
|   def preview(self): | ||||
|     self.image.save(path+'temp.png') | ||||
|     os.system("gpicview "+path+'temp.png') | ||||
|     os.system('rm '+path+'temp.png') | ||||
|      | ||||
|  | ||||
|   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 | ||||
|  | ||||
|     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 | ||||
| # .. 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: | ||||
|     if path_body is None: | ||||
|       # Plain GET | ||||
|       im = Image.open(requests.get(path, stream=True).raw) | ||||
|     else: | ||||
|       print('Image file exact. no further action required') | ||||
|       # 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: | ||||
|   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 | ||||
|  | ||||
|   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) | ||||
| """Turn image upside-down if specified""" | ||||
| if upside_down == True: | ||||
|   im.rotate(180, expand = True) | ||||
|  | ||||
|   def prepare_image(self, alignment='middle'): | ||||
|     self.check_mode() | ||||
|     self.auto_flip() | ||||
|     self.check_size(alignment = alignment) | ||||
|     self.to_mono() | ||||
| 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) | ||||
|  | ||||
|     return self.image | ||||
| 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 | ||||
|  | ||||
| #single line command: | ||||
| display.show_image(inkycal_image(path).prepare_image(), reduce_colours=False) | ||||
|          | ||||
| 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') | ||||
|   | ||||
| @@ -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() | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										1
									
								
								release.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								release.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| v1.7.2 | ||||
| @@ -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' | ||||
| @@ -34,10 +35,23 @@ 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: | ||||
|   top_section_height = int(display_height*0.11) | ||||
|   bottom_section_height = int(display_height*0.24) | ||||
|  | ||||
| elif top_section and not bottom_section: | ||||
|   top_section_height = int(display_height*0.11) | ||||
|   bottom_section_height = 0 | ||||
|  | ||||
| elif bottom_section and not top_section: | ||||
|   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 +74,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 +133,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 +146,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') | ||||
| @@ -152,8 +178,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]) | ||||
| @@ -161,15 +187,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,23 +243,12 @@ 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 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""" | ||||
| @@ -235,20 +261,46 @@ 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)) | ||||
|          | ||||
|  | ||||
|     print('-----------Calibration complete----------') | ||||
|     epaper.sleep() | ||||
|  | ||||
| def check_for_updates(): | ||||
|   with open(path+'release.txt','r') as file: | ||||
|     lines = file.readlines() | ||||
|     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] | ||||
|  | ||||
|   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) | ||||
|   | ||||
| @@ -35,7 +35,7 @@ body{ | ||||
|         <input id="ical_urls" type="text" placeholder="'https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics'"> | ||||
|       </div> | ||||
|  | ||||
|             <div class="field"> | ||||
|       <div class="field"> | ||||
|         <label>RSS-Feed URL/s in the following format: 'URL 1', 'URL 2', 'URL 3'</label> | ||||
|         <input id="rss_urls" type="text" placeholder="'http://feeds.bbci.co.uk/news/world/rss.xml#'"> | ||||
|       </div> | ||||
| @@ -78,7 +78,6 @@ body{ | ||||
|         <input id="api_key" type="text" placeholder=""> | ||||
|       </div> | ||||
|  | ||||
|  | ||||
|       <div class="field"> | ||||
|         <label>Location (for weather data)</label> | ||||
|         <details class="ts accordion"> | ||||
| @@ -273,7 +272,7 @@ body{ | ||||
|  | ||||
|       <div class="field"> | ||||
|         <label>What should be displayed in the middle (main) section?</label> | ||||
|         <div class="ts checkboxes"> | ||||
|         <div class="ts checkboxes" id="cb_middle_section"> | ||||
|           <div class="ts radio checkbox"> | ||||
|             <input id="Calendar" type="radio" name="ms" checked> | ||||
|             <label for="Calendar">A monthly Calendar</label> | ||||
| @@ -282,11 +281,52 @@ body{ | ||||
|             <input id="Agenda" type="radio" name="ms"> | ||||
|             <label for="Agenda">Agenda of upcoming events</label> | ||||
|           </div> | ||||
|           <div class="ts radio checkbox"> | ||||
|             <input id="Image" type="radio" name="ms"> | ||||
|             <label for="Image">An image</label> | ||||
|           </div> | ||||
|           <div class="ts radio checkbox"> | ||||
|             <input id="middle_blank" type="radio" name="ms"> | ||||
|             <label for="middle_blank">Nothing</label> | ||||
|           </div> | ||||
|         </div> | ||||
|          | ||||
|       </div> | ||||
|       <div class="field" id="Image_Config" style="display:none;"> | ||||
|           <div class="field"> | ||||
|             <label>What is the URl or path of the image?</label> | ||||
|             <details class="ts accordion"> | ||||
|               <summary> | ||||
|                 <i class="dropdown icon"></i> Info | ||||
|               </summary> | ||||
|               <div class="content"> | ||||
|                 The following parameters will be substituted: | ||||
|                 <ul> | ||||
|                   <li><code>{model}</code> - substituted by the E-Paper model name.</li> | ||||
|                   <li><code>{width}</code> - substituted by the panel width.</li> | ||||
|                   <li><code>{height}</code> - substituted by the panel width.</li> | ||||
|                 </ul> | ||||
|               </div> | ||||
|             </details> | ||||
|             <input id="image_path" type="text" placeholder="https://github.com/aceisace/Inky-Calendar/blob/master/Gallery/Inky-Calendar-logo.png?raw=true"/> | ||||
|           </div> | ||||
|  | ||||
|           <div class="field"> | ||||
|             <label>Do you want to send extra data while obtaining the image?</label> | ||||
|             <details class="ts accordion"> | ||||
|               <summary> | ||||
|                 <i class="dropdown icon"></i> Info | ||||
|               </summary> | ||||
|               <div class="content"> | ||||
|                 <p>Optional data. When specified, this data is sent as Json to the image url using POST. | ||||
|                   <br/>This is useful for some dynamically generated images. | ||||
|                 </p> | ||||
|               </div> | ||||
|             </details> | ||||
|             <textarea  id="image_path_body" type="text" rows="4" placeholder='[ | ||||
|   "https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics" | ||||
| ]'></textarea> | ||||
|           </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="field"> | ||||
| @@ -305,13 +345,14 @@ body{ | ||||
|  | ||||
|       <div class="content"> | ||||
|       <p>Instructions<br> | ||||
| 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.</p> | ||||
|     </div> | ||||
|  | ||||
|     </form> | ||||
|     <br> | ||||
|     <button class="ts primary button" onClick="generate();">Generate</button> | ||||
|     <button class="ts primary button" onClick="generate(false);">Generate</button> | ||||
|     <button class="ts secondary button" onClick="generate(true);">Generate (as JSON, experimental)</button> | ||||
|   <br><br> | ||||
|   <kbd>Developed by Toby Chui for Inky-Calendar Project, modified by aceisace. Licensed under MIT</kbd> | ||||
|   <details class="ts accordion"> | ||||
| @@ -333,9 +374,32 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|   <br> | ||||
|    | ||||
|   <script> | ||||
|   var template = 'ical_urls = [{ical_urls}]\nrss_feeds = [{rss_urls}]\nupdate_interval = "{update_interval}"\napi_key = "{api_key}"\nlocation = "{location}"\nweek_starts_on = "{week_starts_on}"\ncalibration_hours = [{calibration_hours}]\nmodel = "{model}"\nlanguage = "{language}"\nunits = "{units}"\nhours = "{hours}"\ntop_section = "{top_section}"\nmiddle_section = "{middle_section}"\nbottom_section = "{bottom_section}"'; | ||||
|   var template = `ical_urls = [{ical_urls}] | ||||
| rss_feeds = [{rss_urls}] | ||||
| update_interval = "{update_interval}" | ||||
| api_key = "{api_key}" | ||||
| location = "{location}" | ||||
| week_starts_on = "{week_starts_on}" | ||||
| calibration_hours = [{calibration_hours}] | ||||
| model = "{model}" | ||||
| language = "{language}" | ||||
| units = "{units}" | ||||
| hours = "{hours}" | ||||
| top_section = "{top_section}" | ||||
| middle_section = "{middle_section}" | ||||
| bottom_section = "{bottom_section}" | ||||
| inkycal_image_path = "{image_path}" | ||||
| inkycal_image_path_body = "{image_path_body}"`; | ||||
|    | ||||
|   function generate(){ | ||||
|   $('#cb_middle_section').change(function(){ | ||||
|     if($('#Image').prop("checked")) { | ||||
|       $('#Image_Config').show(); | ||||
|     } else { | ||||
|       $('#Image_Config').hide(); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   function generate(json){ | ||||
|     var ical_urls = $("#ical_urls").val().trim(); | ||||
|     if (ical_urls == ""){ | ||||
|       ical_urls = $("#ical_urls").attr("placeholder"); | ||||
| @@ -475,6 +539,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|     if ($('#Agenda').is(':checked')){ | ||||
|       middle_section = "inkycal_agenda"; | ||||
|     } | ||||
|     if ($('#Image').is(':checked')){ | ||||
|       middle_section = "inkycal_image"; | ||||
|     } | ||||
|     if ($('#middle_blank').is(':checked')){ | ||||
|       middle_section = ""; | ||||
|     } | ||||
| @@ -484,9 +551,18 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|       bottom_section = ""; | ||||
|     } | ||||
|  | ||||
|     var image_path = $("#image_path").val().trim(); | ||||
|     if (image_path == ""){ | ||||
|       image_path = $("#image_path").attr("placeholder"); | ||||
|     } | ||||
|  | ||||
|     var image_path_body = $("#image_path").val().trim(); | ||||
|  | ||||
|     //console.log(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, calibration_hours, model, language, units, hours, top_section, middle_section, bottom_section); | ||||
|     createPythonSetting(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, calibration_hours, model, language, units, hours, top_section, middle_section, bottom_section); | ||||
|     if(json) | ||||
|       downloadSettingsAsJson(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, calibration_hours, model, language, units, hours, top_section, middle_section, bottom_section, image_path, image_path_body) | ||||
|     else | ||||
|       createPythonSetting(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, calibration_hours, model, language, units, hours, top_section, middle_section, bottom_section, image_path, image_path_body); | ||||
|   } | ||||
|    | ||||
|   function rk(content,key,value){ | ||||
| @@ -494,7 +570,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|     return content.split("{" + key + "}").join(value); | ||||
|   } | ||||
|    | ||||
|   function createPythonSetting(a,b,c,d,e,f,g,h,i,j,k,l,m,n){ | ||||
|   function createPythonSetting(a,b,c,d,e,f,g,h,i,j,k,l,m,n, image_path, image_path_body){ | ||||
|     var box = template; | ||||
|     box = rk(box,"ical_urls",a); | ||||
|     box = rk(box,"rss_urls",b); | ||||
| @@ -510,7 +586,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|     box = rk(box,"top_section",l); | ||||
|     box = rk(box,"middle_section",m); | ||||
|     box = rk(box,"bottom_section",n); | ||||
|  | ||||
|     box = rk(box,"image_path",image_path); | ||||
|     box = rk(box,"image_path_body",image_path_body); | ||||
|  | ||||
|     var config = new Blob([box], {type : "text/plain"}); | ||||
|     var link = document.createElement('link'); | ||||
| @@ -523,6 +600,112 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | ||||
|     a.click(); | ||||
|     document.body.removeChild(a); | ||||
|   } | ||||
|  | ||||
|   function TrimSingleQuotes(text){ | ||||
|       return text.replace(/^'+/g,"").replace(/'+$/g,"") | ||||
|   } | ||||
|  | ||||
|   function downloadSettingsAsJson( | ||||
|     ical_urls, | ||||
|     rss_urls, | ||||
|     update_interval, | ||||
|     api_key, | ||||
|     location, | ||||
|     week_starts_on, | ||||
|     calibration_hours, | ||||
|     model, | ||||
|     language, | ||||
|     units, | ||||
|     hours, | ||||
|     top_section, | ||||
|     middle_section, | ||||
|     bottom_section, | ||||
|     image_path, | ||||
|     image_path_body | ||||
|   ) { | ||||
|     var result = { | ||||
|       "language"          : language,               // "en", "de", "fr", "jp" etc. | ||||
|       "units"             : units,               // "metric", "imperial" | ||||
|       "hours"             : Number(hours),       // 24, 12 | ||||
|       "model"             : model, | ||||
|       "update_interval"   : Number(update_interval),       // 10, 15, 20, 30, 60 | ||||
|       "calibration_hours" : calibration_hours.split(",").map(function(x){ return Number(x);}),              // Do not change unless you know what you are doing | ||||
|       "panels" : [] | ||||
|     }; | ||||
|  | ||||
|     switch(top_section){ | ||||
|       case "inkycal_weather": | ||||
|         result.panels.push( | ||||
|           { | ||||
|             "location"  : "top", | ||||
|             "type"      : "inkycal_weather", | ||||
|             "config"    : { | ||||
|                 "api_key"  : api_key,     //Your openweathermap API-KEY -> "api-key" | ||||
|                 "location" : location      //"City name, Country code" | ||||
|             } | ||||
|           } | ||||
|         ) | ||||
|         break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     switch(middle_section){ | ||||
|       case "inkycal_agenda": | ||||
|       case "inkycal_calendar": | ||||
|         result.panels.push( | ||||
|           { | ||||
|             "location"  : "middle", | ||||
|             "type"      : middle_section, | ||||
|             "config"    : { | ||||
|                 "week_starts_on" : week_starts_on,    //"Sunday", "Monday"... | ||||
|                 "ical_urls"  : ical_urls.split().map(function(x){ return TrimSingleQuotes(x);}) | ||||
|             } | ||||
|           } | ||||
|         ) | ||||
|         break; | ||||
|       case "inkycal_image": | ||||
|         result.panels.push( | ||||
|           { | ||||
|             "location"  : "middle", | ||||
|             "type"      : middle_section, | ||||
|             "config"    : { | ||||
|                 "image_path"  : TrimSingleQuotes(image_path), | ||||
|                 "image_path_body" : image_path_body | ||||
|             } | ||||
|           } | ||||
|         ) | ||||
|         break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     switch(bottom_section){ | ||||
|       case "inkycal_rss": | ||||
|         result.panels.push( | ||||
|           { | ||||
|             "location"  : "bottom", | ||||
|             "type"      : bottom_section, | ||||
|             "config"    : { | ||||
|                 "rss_urls"  : rss_urls.split().map(function(x){ return TrimSingleQuotes(x);}) | ||||
|             } | ||||
|           } | ||||
|         ) | ||||
|         break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|     var config = new Blob([JSON.stringify(result, null, "\t")], {type : "text/json"}); | ||||
|     var link = document.createElement('link'); | ||||
|     link.href = window.URL.createObjectURL(config); | ||||
|     var a = document.createElement('A'); | ||||
|     a.href = link.href; | ||||
|     a.download = link.href.substr(link.href.lastIndexOf('/') + 1); | ||||
|     document.body.appendChild(a); | ||||
|     $(a).attr('download','settings.jsonc'); | ||||
|     a.click(); | ||||
|     document.body.removeChild(a); | ||||
|   } | ||||
|    | ||||
|   </script> | ||||
| </body> | ||||
|   | ||||
							
								
								
									
										91
									
								
								settings/settings.jsonc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								settings/settings.jsonc
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| */ | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user