Merge pull request #81 from aceisace/dev

v1.7.1 -> v1.7.2
This commit is contained in:
Ace 2020-02-16 22:50:35 +01:00 committed by GitHub
commit 2d3ca3727d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 795 additions and 231 deletions

View File

@ -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

View File

@ -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?"

View File

@ -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 = '')

View File

@ -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()

View File

@ -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():

View File

@ -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)

View File

@ -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')

View File

@ -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()

View File

@ -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
View File

@ -0,0 +1 @@
v1.7.2

View File

@ -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)

View File

@ -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
View 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
*/

View File

@ -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