Merge pull request #31 from aceisace/master

Master
This commit is contained in:
Ace 2019-05-12 16:21:20 +02:00 committed by GitHub
commit 30b6c036f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 904 additions and 729 deletions

Binary file not shown.

View File

@ -13,24 +13,15 @@ from datetime import datetime, date, timedelta
from time import sleep
from dateutil.rrule import *
from dateutil.parser import parse
import arrow
import re
import random
import gc
try:
import feedparser
except ImportError:
print("Please install feedparser with: sudo pip3 install feedparser")
print("and")
print("pip3 install feedparser")
try:
import numpy as np
except ImportError:
print("Please install numpy with: sudo apt-get install python3-numpy")
import feedparser
import numpy as np
from settings import *
from icon_positions_locations import *
from image_data import *
from PIL import Image, ImageDraw, ImageFont, ImageOps
import pyowm
@ -40,29 +31,23 @@ try:
except Exception as e:
print("Something didn't work right, maybe you're offline?"+e.reason)
if display_colours is "bwr":
import epd7in5b
epd = epd7in5b.EPD()
if display_colours is "bw":
import epd7in5
epd = epd7in5.EPD()
import e_paper_drivers
epd = e_paper_drivers.EPD()
from calibration import calibration
EPD_WIDTH = 640
EPD_HEIGHT = 384
font = ImageFont.truetype(path+'Assistant-Regular.ttf', 18)
default = ImageFont.truetype(fpath+'NotoSans/NotoSans-SemiCondensedLight.ttf', 18)
semi = ImageFont.truetype(fpath+'NotoSans/NotoSans-SemiCondensed.ttf', 18)
bold = ImageFont.truetype(fpath+'NotoSans/NotoSans-SemiCondensedMedium.ttf', 18)
month_font = ImageFont.truetype(fpath+'NotoSans/NotoSans-SemiCondensedLight.ttf', 40)
im_open = Image.open
owm = pyowm.OWM(api_key)
possible_update_values = [10, 15, 20, 30, 60]
if int(update_interval) not in possible_update_values:
print('Selected update-interval: ',update_interval, 'minutes')
print('Please select an update interval from these values:', possible_update_values)
raise ValueError
"""Main loop starts from here"""
def main():
calibration_countdown = 'initial'
@ -73,6 +58,7 @@ def main():
year = int(time.now().strftime('%Y'))
mins = int(time.strftime("%M"))
seconds = int(time.strftime("%S"))
now = arrow.now()
for i in range(1):
print('_________Starting new loop___________'+'\n')
@ -94,213 +80,174 @@ def main():
"""Create a blank white page first"""
image = Image.new('RGB', (EPD_HEIGHT, EPD_WIDTH), 'white')
"""Add the icon with the current month's name"""
image.paste(im_open(mpath+str(time.strftime("%B")+'.jpeg')), monthplace)
"""Add the line seperating the weather and Calendar section"""
image.paste(seperator, seperatorplace)
"""Add weekday-icons (Mon, Tue...) and draw a circle on the
current weekday"""
if (week_starts_on is "Monday"):
calendar.setfirstweekday(calendar.MONDAY)
image.paste(weekmon, weekplace)
image.paste(weekday, weekdaysmon[(time.strftime("%a"))], weekday)
"""For those whose week starts on Sunday, change accordingly"""
if (week_starts_on is "Sunday"):
calendar.setfirstweekday(calendar.SUNDAY)
image.paste(weeksun, weekplace)
image.paste(weekday, weekdayssun[(time.strftime("%a"))], weekday)
"""Using the built-in calendar function, draw icons for each
number of the month (1,2,3,...28,29,30)"""
cal = calendar.monthcalendar(time.year, time.month)
for numbers in cal[0]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['a'+str(cal[0].index(numbers)+1)])
for numbers in cal[1]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['b'+str(cal[1].index(numbers)+1)])
for numbers in cal[2]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['c'+str(cal[2].index(numbers)+1)])
for numbers in cal[3]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['d'+str(cal[3].index(numbers)+1)])
for numbers in cal[4]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['e'+str(cal[4].index(numbers)+1)])
if len(cal) is 6:
for numbers in cal[5]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['f'+str(cal[5].index(numbers)+1)])
"""Custom function to display text on the E-Paper.
Tuple refers to the x and y coordinates of the E-Paper display,
with (0, 0) being the top left corner of the display."""
def write_text(box_width, box_height, text, tuple):
text_width, text_height = font.getsize(text)
if (text_width, text_height) > (box_width, box_height):
raise ValueError('Sorry, your text is too big for the box')
else:
x = int((box_width / 2) - (text_width / 2))
y = int((box_height / 2) - (text_height / 2))
space = Image.new('RGB', (box_width, box_height), color='white')
ImageDraw.Draw(space).text((x, y), text, fill='black', font=font)
image.paste(space, tuple)
"""Connect to Openweathermap API to fetch weather data"""
print("Connecting to Openweathermap API servers...")
if owm.is_API_online() is True:
observation = owm.weather_at_place(location)
print("weather data:")
weather = observation.get_weather()
weathericon = weather.get_weather_icon_name()
Humidity = str(weather.get_humidity())
cloudstatus = str(weather.get_clouds())
weather_description = (str(weather.get_status()))
if units is "metric":
Temperature = str(int(weather.get_temperature(unit='celsius')['temp']))
windspeed = str(int(weather.get_wind()['speed']))
write_text(50, 35, Temperature + " °C", (334, 0))
write_text(100, 35, windspeed+" km/h", (114, 0))
if units is "imperial":
Temperature = str(int(weather.get_temperature('fahrenheit')['temp']))
windspeed = str(int(weather.get_wind()['speed']*0.621))
write_text(50, 35, Temperature + " °F", (334, 0))
write_text(100, 35, windspeed+" mph", (114, 0))
if hours is "24":
sunrisetime = str(datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix'))).strftime('%-H:%M'))
sunsettime = str(datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix'))).strftime('%-H:%M'))
if hours is "12":
sunrisetime = str(datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix'))).strftime('%-I:%M'))
sunsettime = str(datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix'))).strftime('%-I:%M'))
print('Temperature: '+Temperature+' °C')
print('Humidity: '+Humidity+'%')
#print('Icon code: '+weathericon)
print('weather-icon name: '+weathericons[weathericon])
print('Wind speed: '+windspeed+'km/h')
print('Sunrise-time: '+sunrisetime)
print('Sunset time: '+sunsettime)
print('Cloudiness: ' + cloudstatus+'%')
print('Weather description: '+weather_description+'\n')
"""Add the weather icon at the top left corner"""
image.paste(im_open(wpath+weathericons[weathericon]+'.jpeg'), wiconplace)
"""Add the temperature icon at it's position"""
image.paste(tempicon, tempplace)
"""Add the humidity icon and display the humidity"""
image.paste(humicon, humplace)
write_text(50, 35, Humidity + " %", (334, 35))
"""Add the sunrise icon and display the sunrise time"""
image.paste(sunriseicon, sunriseplace)
write_text(50, 35, sunrisetime, (249, 0))
"""Add the sunset icon and display the sunrise time"""
image.paste(sunseticon, sunsetplace)
write_text(50, 35, sunsettime, (249, 35))
"""Add the wind icon at it's position"""
image.paste(windicon, windiconspace)
"""Add a short weather description"""
write_text(144, 35, weather_description, (70, 35))
else:
"""If no response was received from the openweathermap
api server, add the cloud with question mark"""
image.paste(no_response, wiconplace)
"""Algorithm for filtering and sorting events from your
iCalendar/s"""
print('Fetching events from your calendar'+'\n')
events_this_month = []
upcoming = []
today = date.today()
"""Create a time span using the events_max_range value (in days)
to filter events in that range"""
time_span = today + timedelta(days=int(events_max_range))
for icalendars in ical_urls:
decode = str(urlopen(icalendars).read().decode())
beginAlarmIndex = 0
while beginAlarmIndex >= 0:
beginAlarmIndex = decode.find('BEGIN:VALARM')
if beginAlarmIndex >= 0:
endAlarmIndex = decode.find('END:VALARM')
decode = decode[:beginAlarmIndex] + decode[endAlarmIndex+12:]
ical = Calendar(decode)
for events in ical.events:
if re.search('RRULE',str(events)) is not None:
r = re.search('RRULE:(.+?)\n',str(events))
r_start = re.search('DTSTART:(.+?)\n',str(events))
if r_start is not None: # if r_start is None the format of DTSTART is not recognized
if time.now().month == 12:
r_string=(r.group(1).rstrip()+';UNTIL='+'%04d%02d%02d'+'T000000Z') % (time.now().year+1, 1, 1)
else:
r_string=(r.group(1).rstrip()+';UNTIL='+'%04d%02d%02d'+'T000000Z') % (time.now().year, time.now().month+1, 1)
rule=rrulestr(r_string,dtstart=parse(r_start.group(1)))
for i in rule:
if i.year == time.now().year and i.month == time.now().month and i.day >= time.now().day:
upcoming.append({'date':str(time.now().year) + " " + time.now().strftime('%m')+ " " + str(i.day).zfill(2), 'event':events.name})
if i.day not in events_this_month:
events_this_month.append(i.day)
# uncomment this line to see fetched recurring events
#print ("Appended recurring event: " + events.name + " on " + str(time.now().year) + " " + time.now().strftime('%m')+ " " + str(i.day).zfill(2))
else:
if events.begin.date().month == today.month:
if int((events.begin).format('D')) not in events_this_month:
events_this_month.append(int((events.begin).format('D')))
if today <= events.begin.date() <= time_span:
upcoming.append({'date':events.begin.format('YYYY MM DD'), 'event':events.name})
def takeDate(elem):
return elem['date']
upcoming.sort(key=takeDate)
#print('Upcoming events:',upcoming) #Display fetched events
def write_text_left(box_width, box_height, text, tuple):
"""Custom function to display text on the E-Paper"""
def write_text(box_width, box_height, text, tuple, font=default, alignment='middle'):
text_width, text_height = font.getsize(text)
while (text_width, text_height) > (box_width, box_height):
text=text[0:-1]
text_width, text_height = font.getsize(text)
y = int((box_height / 2) - (text_height / 2))
if alignment is "" or "middle" or None:
x = int((box_width / 2) - (text_width / 2))
if alignment is 'left':
x = 0
y = int((box_height / 2) - (text_height / 1.7))
space = Image.new('RGB', (box_width, box_height), color='white')
ImageDraw.Draw(space).text((0, y), text, fill='black', font=font)
ImageDraw.Draw(space).text((x, y), text, fill='black', font=font)
image.paste(space, tuple)
"""Write event dates and names on the E-Paper"""
if additional_feature is "events":
if len(cal) is 5:
del upcoming[6:]
"""Check if internet is available by trying to reach google"""
def internet_available():
try:
urlopen('https://google.com',timeout=5)
return True
except URLError as err:
return False
for dates in range(len(upcoming)):
readable_date = datetime.strptime(upcoming[dates]['date'], '%Y %m %d').strftime('%-d %b')
write_text(70, 25, readable_date, date_positions['d'+str(dates+1)])
for events in range(len(upcoming)):
write_text_left(314, 25, (upcoming[events]['event']), event_positions['e'+str(events+1)])
"""Connect to Openweathermap API and fetch weather data"""
if top_section is "Weather" and api_key != "" and owm.is_API_online() is True:
try:
print("Connecting to Openweathermap API servers...")
observation = owm.weather_at_place(location)
print("weather data:")
weather = observation.get_weather()
weathericon = weather.get_weather_icon_name()
Humidity = str(weather.get_humidity())
cloudstatus = str(weather.get_clouds())
weather_description = (str(weather.get_status()))
if units is "metric":
Temperature = str(int(weather.get_temperature(unit='celsius')['temp']))
windspeed = str(int(weather.get_wind()['speed']))
write_text(50, 35, Temperature + " °C", (334, 0))
write_text(100, 35, windspeed+" km/h", (114, 0))
if units is "imperial":
Temperature = str(int(weather.get_temperature('fahrenheit')['temp']))
windspeed = str(int(weather.get_wind()['speed']*0.621))
write_text(50, 35, Temperature + " °F", (334, 0))
write_text(100, 35, windspeed+" mph", (114, 0))
if hours is "24":
sunrisetime = str(datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix'))).strftime('%-H:%M'))
sunsettime = str(datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix'))).strftime('%-H:%M'))
if hours is "12":
sunrisetime = str(datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix'))).strftime('%-I:%M'))
sunsettime = str(datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix'))).strftime('%-I:%M'))
"""Show the fetched weather data"""
print('Temperature: '+ Temperature+' °C')
print('Humidity: '+ Humidity+'%')
print('weather-icon name: '+weathericons[weathericon])
print('Wind speed: '+ windspeed+'km/h')
print('Sunrise-time: '+ sunrisetime)
print('Sunset time: '+ sunsettime)
print('Cloudiness: ' + cloudstatus+'%')
print('Weather description: '+ weather_description+'\n')
"""Add the weather icon at the top left corner"""
image.paste(im_open(wpath + weathericons[weathericon] +'.jpeg'), wiconplace)
"""Add the temperature icon at it's position"""
image.paste(tempicon, tempplace)
"""Add the humidity icon and display the humidity"""
image.paste(humicon, humplace)
write_text(50, 35, Humidity + " %", (334, 35))
"""Add the sunrise icon and display the sunrise time"""
image.paste(sunriseicon, sunriseplace)
write_text(50, 35, sunrisetime, (249, 0))
"""Add the sunset icon and display the sunrise time"""
image.paste(sunseticon, sunsetplace)
write_text(50, 35, sunsettime, (249, 35))
"""Add the wind icon at it's position"""
image.paste(windicon, windiconspace)
"""Add a short weather description"""
write_text(144, 35, weather_description, (70, 35))
except Exception as e:
"""If no response was received from the openweathermap
api server, add the cloud with question mark"""
print('__________OWM-ERROR!__________'+'\n')
print('Reason: ',e+'\n')
image.paste(no_response, wiconplace)
pass
"""Set the Calendar to start on the day specified by the settings file """
if week_starts_on is "Monday":
calendar.setfirstweekday(calendar.MONDAY)
"""For those whose week starts on Sunday, change accordingly"""
if week_starts_on is "Sunday":
calendar.setfirstweekday(calendar.SUNDAY)
"""Using the built-in calendar to generate the monthly Calendar
template"""
cal = calendar.monthcalendar(time.year, time.month)
if middle_section is "Calendar":
"""Add the icon with the current month's name"""
write_text(384,60, now.format('MMMM',locale=language), monthplace, font=month_font)
"""Add the line seperating the weather and Calendar section"""
image.paste(seperator, seperatorplace)
"""Create a list containing the weekday abbrevations for the
chosen language"""
if week_starts_on is "Monday":
prev_weekstart = now.replace(days = - now.weekday())
image.paste(weekday, weekday_pos['pos'+str(now.weekday())], weekday)
if week_starts_on is "Sunday":
prev_weekstart = now.replace(days = - now.isoweekday())
image.paste(weekday, weekday_pos['pos'+str(now.isoweekday())], weekday)
weekday_names_list = []
for i in range(7):
weekday_name = prev_weekstart.replace(days=+i)
weekday_names_list.append(weekday_name.format('ddd',locale=language))
for i in range(len(weekday_names_list)):
write_text(54, 28, weekday_names_list[i], weekday_pos['pos'+str(i)])
"""Create the calendar template of the current month"""
for numbers in cal[0]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['a'+str(cal[0].index(numbers)+1)])
for numbers in cal[1]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['b'+str(cal[1].index(numbers)+1)])
for numbers in cal[2]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['c'+str(cal[2].index(numbers)+1)])
for numbers in cal[3]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['d'+str(cal[3].index(numbers)+1)])
for numbers in cal[4]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['e'+str(cal[4].index(numbers)+1)])
if len(cal) is 6:
del upcoming[4:]
for numbers in cal[5]:
image.paste(im_open(dpath+str(numbers)+'.jpeg'), positions['f'+str(cal[5].index(numbers)+1)])
for dates in range(len(upcoming)):
readable_date = datetime.strptime(upcoming[dates]['date'], '%Y %m %d').strftime('%-d %b')
write_text(70, 25, readable_date, date_positions['d'+str(dates+3)])
for events in range(len(upcoming)):
write_text_left(314, 25, (upcoming[events]['event']), event_positions['e'+str(events+3)])
"""Draw a larger square on today's date"""
today = time.day
if today in cal[0]:
image.paste(dateicon, positions['a'+str(cal[0].index(today)+1)], dateicon)
if today in cal[1]:
image.paste(dateicon, positions['b'+str(cal[1].index(today)+1)], dateicon)
if today in cal[2]:
image.paste(dateicon, positions['c'+str(cal[2].index(today)+1)], dateicon)
if today in cal[3]:
image.paste(dateicon, positions['d'+str(cal[3].index(today)+1)], dateicon)
if today in cal[4]:
image.paste(dateicon, positions['e'+str(cal[4].index(today)+1)], dateicon)
if len(cal) is 6 and today in cal[5]:
image.paste(dateicon, positions['f'+str(cal[5].index(today)+1)], dateicon)
"""Add rss-feeds at the bottom section of the Calendar"""
if additional_feature is "rss":
if bottom_section is "RSS" and rss_feeds != []:
def multiline_text(text, max_width):
"""Custom function to display longer text into multiple lines (wrapping)"""
def multiline_text(text, max_width, font=default):
lines = []
if font.getsize(text)[0] <= max_width:
lines.append(text)
@ -318,69 +265,154 @@ def main():
lines.append(line)
return lines
"""Parse the RSS-feed titles and save them to a list"""
rss_feed = []
for feeds in rss_feeds:
text = feedparser.parse(feeds)
for posts in text.entries:
rss_feed.append(posts.title)
rss_feed.append(posts.summary)#title
"""Shuffle the list to prevent displaying the same titles over and over"""
random.shuffle(rss_feed)
news = []
if len(cal) is 5:
"""Remove all titles except the first 4 or 6,
depenfing on how much space is available on the """
if middle_section is 'Calendar' and len(cal) is 5 or middle_section is 'Agenda':
del rss_feed[6:]
if len(cal) is 6:
del rss_feed[4:]
"""Split titles of the rss feeds into lines that can fit
on the Calendar and add them to a list"""
for title in range(len(rss_feeds)):
news.append(multiline_text(rss_feed[title], 384))
news = [j for i in news for j in i]
if len(cal) is 5:
"""Display the split lines of the titles"""
if middle_section is 'Calendar' and len(cal) is 5 or middle_section is 'Agenda':
if len(news) > 6:
del news[6:]
for lines in range(len(news)):
write_text_left(384, 25, news[lines], rss_places['line_'+str(lines+1)])
write_text(384, 25, news[lines], rss_places['line_'+str(lines+1)], alignment = 'left')
if len(cal) is 6:
if len(news) > 4:
del news[4:]
for lines in range(len(news)):
write_text_left(384, 25, news[lines], rss_places['line_'+str(lines+3)])
write_text(384, 25, news[lines], rss_places['line_'+str(lines+3)], alignment = 'left')
"""Draw smaller squares on days with events"""
for numbers in events_this_month:
if numbers in cal[0]:
image.paste(eventicon, positions['a'+str(cal[0].index(numbers)+1)], eventicon)
if numbers in cal[1]:
image.paste(eventicon, positions['b'+str(cal[1].index(numbers)+1)], eventicon)
if numbers in cal[2]:
image.paste(eventicon, positions['c'+str(cal[2].index(numbers)+1)], eventicon)
if numbers in cal[3]:
image.paste(eventicon, positions['d'+str(cal[3].index(numbers)+1)], eventicon)
if numbers in cal[4]:
image.paste(eventicon, positions['e'+str(cal[4].index(numbers)+1)], eventicon)
if len(cal) is 6:
if numbers in cal[5]:
if middle_section is "Calendar" or "Agenda":
"""Algorithm for filtering and sorting events from your
iCalendar/s"""
events_this_month = []
upcoming = []
today = time.today()
"""Create a time span using the events_max_range value (in days)
to filter events in that range"""
agenda_max_days = arrow.now().replace(days=+22)
calendar_max_days = arrow.now().replace(days=+int(events_max_range))
if internet_available() is True:
print('Internet connection test passed'+'\n')
print('Fetching events from your calendar'+'\n')
for icalendars in ical_urls:
decode = str(urlopen(icalendars).read().decode())
beginAlarmIndex = 0
while beginAlarmIndex >= 0:
beginAlarmIndex = decode.find('BEGIN:VALARM')
if beginAlarmIndex >= 0:
endAlarmIndex = decode.find('END:VALARM')
decode = decode[:beginAlarmIndex] + decode[endAlarmIndex+12:]
ical = Calendar(decode)
for events in ical.events:
if events.begin.date().year == today.year and events.begin.date().month is today.month:
if int((events.begin).format('D')) not in events_this_month:
events_this_month.append(int((events.begin).format('D')))
if middle_section is 'Agenda' and events in ical.timeline.included(now, agenda_max_days):
upcoming.append(events)
if middle_section is 'Calendar' and events in ical.timeline.included(now, calendar_max_days):
upcoming.append(events)
def event_begins(elem):
return elem.begin
upcoming.sort(key=event_begins)
else:
print("Could not fetch events from your iCalendar.")
print("Either the internet connection is too weak or we're offline.")
if middle_section is 'Agenda':
"""For the agenda view, create a list containing dates and events of the next 22 days"""
if len(upcoming) is not 0:
while (upcoming[-1].begin.date().day - now.day) + len(upcoming) >= 22:
del upcoming[-1]
agenda_list = []
for i in range(22):
date = now.replace(days=+i)
agenda_list.append({'value':date.format('ddd D MMM YY', locale=language),'type':'date'})
for events in upcoming:
if events.begin.date().day == date.day:
if not events.all_day:
agenda_list.append({'value':events.begin.format('HH:mm')+ ' '+ str(events.name), 'type':'timed_event'})
else:
agenda_list.append({'value':events.name, 'type':'full_day_event'})
if bottom_section is not "":
del agenda_list[16:]
image.paste(seperator2, agenda_view_lines['line17'])
if bottom_section is "":
del agenda_list[22:]
image.paste(seperator2, agenda_view_lines['line22'])
for lines in range(len(agenda_list)):
if agenda_list[lines]['type'] is 'date':
write_text(384, 25, agenda_list[lines]['value'], agenda_view_lines['line'+str(lines+1)], font=semi, alignment='left')
image.paste(seperator2, agenda_view_lines['line'+str(lines+1)])
elif agenda_list[lines]['type'] is 'timed_event':
write_text(384, 25, agenda_list[lines]['value'], agenda_view_lines['line'+str(lines+1)], alignment='left')
else:
write_text(384, 25, agenda_list[lines]['value'], agenda_view_lines['line'+str(lines+1)])
if middle_section is 'Calendar':
"""Draw smaller squares on days with events"""
for numbers in events_this_month:
if numbers in cal[0]:
image.paste(eventicon, positions['a'+str(cal[0].index(numbers)+1)], eventicon)
if numbers in cal[1]:
image.paste(eventicon, positions['b'+str(cal[1].index(numbers)+1)], eventicon)
if numbers in cal[2]:
image.paste(eventicon, positions['c'+str(cal[2].index(numbers)+1)], eventicon)
if numbers in cal[3]:
image.paste(eventicon, positions['d'+str(cal[3].index(numbers)+1)], eventicon)
if numbers in cal[4]:
image.paste(eventicon, positions['e'+str(cal[4].index(numbers)+1)], eventicon)
if len(cal) is 6 and numbers in cal[5]:
image.paste(eventicon, positions['f'+str(cal[5].index(numbers)+1)], eventicon)
"""Draw a larger square on today's date"""
today = time.day
if today in cal[0]:
image.paste(dateicon, positions['a'+str(cal[0].index(today)+1)], dateicon)
if today in cal[1]:
image.paste(dateicon, positions['b'+str(cal[1].index(today)+1)], dateicon)
if today in cal[2]:
image.paste(dateicon, positions['c'+str(cal[2].index(today)+1)], dateicon)
if today in cal[3]:
image.paste(dateicon, positions['d'+str(cal[3].index(today)+1)], dateicon)
if today in cal[4]:
image.paste(dateicon, positions['e'+str(cal[4].index(today)+1)], dateicon)
if len(cal) is 6:
if today in cal[5]:
image.paste(dateicon, positions['f'+str(cal[5].index(today)+1)], dateicon)
"""Write event dates and names on the E-Paper"""
if bottom_section is "Events":
if len(cal) is 5:
del upcoming[6:]
for dates in range(len(upcoming)):
readable_date = datetime.strptime(upcoming[dates]['date'], '%Y %m %d').strftime('%-d %b')
write_text(70, 25, readable_date, date_positions['d'+str(dates+1)])
for events in range(len(upcoming)):
write_text(314, 25, (upcoming[events]['event']), event_positions['e'+str(events+1)], alignment = 'left')
if len(cal) is 6:
del upcoming[4:]
for dates in range(len(upcoming)):
readable_date = datetime.strptime(upcoming[dates]['date'], '%Y %m %d').strftime('%-d %b')
write_text(70, 25, readable_date, date_positions['d'+str(dates+3)])
for events in range(len(upcoming)):
write_text(314, 25, (upcoming[events]['event']), event_positions['e'+str(events+3)], alignment = 'left')
"""
Map all pixels of the generated image to red, white and black
@ -396,7 +428,7 @@ def main():
if display_colours is "bw":
buffer[np.logical_and(r > 240, g > 240)] = [255,255,255] #white
buffer[g < 255] = [0,0,0] #black
improved_image = Image.fromarray(buffer).rotate(270, expand=True)
print('Initialising E-Paper Display')
epd.init()
@ -407,20 +439,25 @@ def main():
print('______Powering off the E-Paper until the next loop______'+'\n')
epd.sleep()
del events_this_month
del upcoming
if middle_section is 'Calendar':
del events_this_month
del upcoming
del weekday_names_list
if additional_feature is "rss":
if bottom_section is 'RSS':
del rss_feed
del news
if middle_section is 'Agenda':
del agenda_list
del buffer
del image
del improved_image
gc.collect()
if calibration_countdown is 'initial':
calibration_countdown = 0
calibration_countdown = 0
calibration_countdown += 1
for i in range(1):
@ -433,7 +470,7 @@ def main():
for update_times in timings:
if update_times >= mins:
sleep_for_minutes = update_times - mins
next_update_countdown = sleep_for_minutes*60 + (60-seconds)
print(sleep_for_minutes,'Minutes and ', (60-seconds),'Seconds left until next loop')

Binary file not shown.

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Settings-File Generator</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
@ -20,7 +21,7 @@ body{
<div class="ts header">
Setting Generator
<div class="sub header"><a href="https://github.com/aceisace/Inky-Calendar">For Inky-Calendar Project of Ace-Innovation Laboratory (by aceisace)</a><br>
<img src="https://github.com/aceisace/Inky-Calendar/blob/Stable/Gallery/Inky-Calendar-logo.png?raw=true" width="800">
<img src="https://github.com/aceisace/Inky-Calendar/blob/Stable/Gallery/Inky-Calendar-logo.png?raw=true" width="800" alt="Inky-Calendar-logo">
<div>
</div>
<ins>If no value is filled in for any of the row, the default value will be used.</ins>
@ -30,12 +31,12 @@ body{
</div>
<form class="ts form">
<div class="field">
<label>iCalendar URL. If you want to add multiple URLs, seperate each one with a comma.</label>
<label>Google iCalendar URL. If you want to add multiple URLs, seperate each one with a comma.</label>
<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">
<label>RSS-Feed URL. To add more than one feed, seperate each URL with a comma.</label>
<label>RSS-Feeds. To add more than one feed, seperate each URL with a comma.</label>
<input id="rss_urls" type="text" placeholder="http://feeds.bbci.co.uk/news/world/rss.xml#">
</div>
@ -61,31 +62,33 @@ body{
</div>
</div>
<div class="field">
<label>What do you want to be displayed below the Calendar?</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="event_feature" type="radio" name="af">
<label for="event_feature">Events from my iCalendar</label>
</div>
<div class="ts radio checkbox">
<input id="rss_feature" type="radio" name="af" checked>
<label for="rss_feature">RSS-Feeds</label>
</div>
</div>
</div>
<div class="field">
<label>Openweathermap API Key</label>
<details class="ts accordion">
<summary>
<i class="dropdown icon"></i> Info
</summary>
<div class="content">
<p> Please insert your own Openweathermap API-key to fetch the latest weather info. To find out how to create your own key, please click here: <a href="https://github.com/aceisace/Inky-Calendar/wiki/Openweathermap-API">Creating an openweathermap api-key</a>. If you don't add an api-key, the top section will not show any weather info</p></div>
</details>
<input id="api_key" type="text" placeholder="">
</div>
<div class="field">
<label>Location</label>
<label>Location (for weather data)</label>
<details class="ts accordion">
<summary>
<i class="dropdown icon"></i> Info
</summary>
<div class="content">
<p>Location refers to the closest weather station from your place. It isn't necessarily the place you live in. To find this location, type your city name in the search box on <a href="https://openweathermap.org/">openweathermap</a>. The output should be in the following format: City Name, Country ISO-Code. Not sure what your ISO code is? Check here: <a href="https://countrycode.org/">(find iso-code)</a></p></div>
</details>
<input id="location" type="text" placeholder="Stuttgart, DE">
</div>
<div class="field">
<label>Week starts on</label>
<label>On which day does the week start on in your country?</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="week_monday" type="radio" name="hr" checked>
@ -104,7 +107,14 @@ body{
</div>
<div class="field">
<label>At which hours should the display be calibrated?</label>
<label>At which hours (in 24 hour-format) should the display be calibrated? Leave blank if you're not sure.</label>
<details class="ts accordion">
<summary>
<i class="dropdown icon"></i> Info
</summary>
<div class="content">
<p>Calibration refers to the process of flushing the display with a single colour to prevent 'ghosting' (an effect specific to E-Paper displays where the remnants of the previous image can be seen on the current one). It takes several minutes to finish the calibration(around 10 mins for the 2-colour displays and around 20 mins for the 3-colour displays) so please choose hours where you are less likely to need the display. It is recommended to calibrate at least thrice a day.</p></div>
</details>
<input id="calibration_hours" type="text" placeholder="0,12,18">
</div>
@ -129,14 +139,54 @@ body{
<input id="language_en" type="radio" name="la" checked>
<label for="language_en">English</label>
</div>
<div class="ts radio checkbox">
<input id="language_zh_tw" type="radio" name="la">
<label for="language_zh_tw">Chinese/Taiwanese</label>
</div>
<div class="ts radio checkbox">
<input id="language_de" type="radio" name="la">
<label for="language_de">German</label>
</div>
<div class="ts radio checkbox">
<input id="language_ru" type="radio" name="la">
<label for="language_ru">Russian</label>
</div>
<div class="ts radio checkbox">
<input id="language_it" type="radio" name="la">
<label for="language_it">Italian</label>
</div>
<div class="ts radio checkbox">
<input id="language_es" type="radio" name="la">
<label for="language_es">Spanish</label>
</div>
<div class="ts radio checkbox">
<input id="language_fr" type="radio" name="la">
<label for="language_fr">French</label>
</div>
<div class="ts radio checkbox">
<input id="language_el" type="radio" name="la">
<label for="language_el">Greek</label>
</div>
<div class="ts radio checkbox">
<input id="language_sv" type="radio" name="la">
<label for="language_sv">Swedish</label>
</div>
<div class="ts radio checkbox">
<input id="language_nl" type="radio" name="la">
<label for="language_nl">Dutch</label>
</div>
<div class="ts radio checkbox">
<input id="language_pl" type="radio" name="la">
<label for="language_pl">Polish</label>
</div>
<div class="ts radio checkbox">
<input id="language_ua" type="radio" name="la">
<label for="language_ua">Ukrainian</label>
</div>
<div class="ts radio checkbox">
<input id="language_nb" type="radio" name="la">
<label for="language_nb">Norwegian</label>
</div>
<div class="ts radio checkbox">
<input id="language_vi" type="radio" name="la">
<label for="language_vi">Vietnamese</label>
</div>
</div>
</div>
@ -157,13 +207,63 @@ body{
<div class="field">
<label>Which hour-format do you prefer?</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="24_hours" type="radio" name="tf" checked>
<label for="24_hours">24-hour format</label>
</div>
<div class="ts radio checkbox">
<input id="12_hours" type="radio" name="tf">
<label for="12_hours">12-hour format</label>
</div>
</div>
</div>
<div class="field">
<label>What should be displayed in the top section?</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="24_hours" type="radio" name="tf" checked>
<label for="24_hours">24-hour format</label>
<input id="Weather" type="radio" name="ts" checked>
<label for="Weather">Events from my iCalendar</label>
</div>
<div class="ts radio checkbox">
<input id="top_blank" type="radio" name="ts">
<label for="top_blank">Nothing</label>
</div>
</div>
</div>
<div class="field">
<label>What should be displayed in the middle (main) section?</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="Calendar" type="radio" name="ms" checked>
<label for="Calendar">A monthly Calendar</label>
</div>
<div class="ts radio checkbox">
<input id="Agenda" type="radio" name="ms">
<label for="Agenda">Agenda-like overview</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">
<label>What should be displayed in the bottom section? If you've chosen the Agenda View for the middle section, should choose either RSS-feeds or nothing.</label>
<div class="ts checkboxes">
<div class="ts radio checkbox">
<input id="RSS" type="radio" name="bs" checked>
<label for="RSS">RSS-feeds</label>
</div>
<div class="ts radio checkbox">
<input id="Events" type="radio" name="bs">
<label for="Events">Events from my iCalendar</label>
</div>
<div class="ts radio checkbox">
<input id="bottom_blank" type="radio" name="bs">
<label for="bottom_blank">Nothing</label>
</div>
</div>
</div>
@ -172,7 +272,7 @@ body{
<br>
<button class="ts primary button" onClick="generate();">Generate</button>
<br><br>
<kbd>Developed by Toby Chui for Inky-Calendar Project, modified by aceisace Licensed under MIT</kbd>
<kbd>Developed by Toby Chui for Inky-Calendar Project, modified by aceisace. Licensed under MIT</kbd>
<details class="ts accordion">
<summary>
<i class="dropdown icon"></i> MIT License
@ -192,7 +292,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
<br>
<script>
var template = 'ical_urls = [\n"{ical_urls}"\n]\nrss_feeds = [\n"{rss_urls}"\n]\nupdate_interval = "{update_interval}"\nadditional_feature = "{additional_feature}"\napi_key = "{api_key}"\nlocation = "{location}"\nweek_starts_on = "{week_starts_on}"\nevents_max_range = "{events_max_range}"\ncalibration_hours = [{calibration_hours}]\ndisplay_colours = "{display_colours}"\nlanguage = "{language}"\nunits = "{units}"\nhours = "{hours}"';
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}"\nevents_max_range = "{events_max_range}"\ncalibration_hours = [{calibration_hours}]\ndisplay_colours = "{display_colours}"\nlanguage = "{language}"\nunits = "{units}"\nhours = "{hours}"\ntop_section = "{top_section}"\nmiddle_section = "{middle_section}"\nbottom_section = "{bottom_section}"';
function generate(){
var ical_urls = $("#ical_urls").val().trim();
@ -217,11 +317,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
update_interval = "60";
}
var additional_feature = "events";
if ($('#rss').is(':checked')){
additional_feature = "rss";
}
var api_key = $("#api_key").val().trim();
if (api_key == ""){
api_key = "";
@ -256,8 +351,38 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if ($('#language_de').is(':checked')){
language = "de";
}
if ($('#language_zh_tw').is(':checked')){
language = "zh_tw";
if ($('#language_ru').is(':checked')){
language = "ru";
}
if ($('#language_it').is(':checked')){
language = "it";
}
if ($('#language_es').is(':checked')){
language = "es";
}
if ($('#language_fr').is(':checked')){
language = "fr";
}
if ($('#language_el').is(':checked')){
language = "el";
}
if ($('#language_sv').is(':checked')){
language = "sv";
}
if ($('#language_nl').is(':checked')){
language = "nl";
}
if ($('#language_pl').is(':checked')){
language = "pl";
}
if ($('#language_ua').is(':checked')){
language = "ua";
}
if ($('#language_nb').is(':checked')){
language = "nb";
}
if ($('#language_vi').is(':checked')){
language = "vi";
}
var units = "metric";
@ -269,8 +394,31 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
if ($('#12_hours').is(':checked')){
hours = "12";
}
//console.log(ical_urls, rss_urls,update_interval, additional_feature, api_key, location, week_starts_on, events_max_range, calibration_hours, display_colours, language, units, hours);
createPythonSetting(ical_urls, rss_urls,update_interval, additional_feature, api_key, location, week_starts_on, events_max_range, calibration_hours, display_colours, language, units, hours);
var top_section = "Weather";
if ($('#top_blank').is(':checked')){
top_section = "";
}
var middle_section = "Calendar";
if ($('#Agenda').is(':checked')){
middle_section = "Agenda";
}
if ($('#middle_blank').is(':checked')){
middle_section = "";
}
var bottom_section = "RSS";
if ($('#Events').is(':checked')){
bottom_section = "Events";
}
if ($('#bottom_blank').is(':checked')){
bottom_section = "";
}
//console.log(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, events_max_range, calibration_hours, display_colours, language, units, hours, top_section, middle_section, bottom_section);
createPythonSetting(ical_urls, rss_urls, update_interval, api_key, location, week_starts_on, events_max_range, calibration_hours, display_colours, language, units, hours, top_section, middle_section, bottom_section);
}
function rk(content,key,value){
@ -278,21 +426,25 @@ 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){
function createPythonSetting(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){
var box = template;
box = rk(box,"ical_urls",a);
box = rk(box,"rss_urls",b);
box = rk(box,"update_interval",c);
box = rk(box,"additional_feature",d);
box = rk(box,"api_key",e);
box = rk(box,"location",f);
box = rk(box,"week_starts_on",g);
box = rk(box,"events_max_range",h);
box = rk(box,"calibration_hours",i);
box = rk(box,"display_colours",j);
box = rk(box,"language",k);
box = rk(box,"units",l);
box = rk(box,"hours",m);
box = rk(box,"api_key",d);
box = rk(box,"location",e);
box = rk(box,"week_starts_on",f);
box = rk(box,"events_max_range",g);
box = rk(box,"calibration_hours",h);
box = rk(box,"display_colours",i);
box = rk(box,"language",j);
box = rk(box,"units",k);
box = rk(box,"hours",l);
box = rk(box,"top_section",m);
box = rk(box,"middle_section",n);
box = rk(box,"bottom_section",o);
var config = new Blob([box], {type : "text/plain"});
var link = document.createElement('link');
link.href = window.URL.createObjectURL(config);

View File

@ -9,18 +9,14 @@ ghosting.
from __future__ import print_function
import time
from settings import display_colours
from icon_positions_locations import black, white, red
from image_data import black, white, red
def calibration():
"""Function for Calibration"""
if display_colours == "bwr":
import epd7in5b
epd = epd7in5b.EPD()
print('_________Calibration for 3-Colour E-Paper started_________'+'\n')
if display_colours == "bw":
import epd7in5
epd = epd7in5.EPD()
print('_________Calibration for 2-Colour E-Paper started_________'+'\n')
import e_paper_drivers
epd = e_paper_drivers.EPD()
print('_________Calibration for E-Paper started_________'+'\n')
for i in range(2):
epd.init()
print('Calibrating black...')
@ -32,7 +28,7 @@ def calibration():
epd.display_frame(epd.get_frame_buffer(white))
epd.sleep()
print('Cycle', str(i+1)+'/2', 'complete'+'\n')
print('Calibration complete')
print('Calibration complete')
def main():
"""Added timer"""

View File

@ -1,183 +1,213 @@
import epdif
from PIL import Image
import RPi.GPIO as GPIO
# Display resolution
EPD_WIDTH = 640
EPD_HEIGHT = 384
# EPD7IN5 commands
PANEL_SETTING = 0x00
POWER_SETTING = 0x01
POWER_OFF = 0x02
POWER_OFF_SEQUENCE_SETTING = 0x03
POWER_ON = 0x04
POWER_ON_MEASURE = 0x05
BOOSTER_SOFT_START = 0x06
DEEP_SLEEP = 0x07
DATA_START_TRANSMISSION_1 = 0x10
DATA_STOP = 0x11
DISPLAY_REFRESH = 0x12
IMAGE_PROCESS = 0x13
LUT_FOR_VCOM = 0x20
LUT_BLUE = 0x21
LUT_WHITE = 0x22
LUT_GRAY_1 = 0x23
LUT_GRAY_2 = 0x24
LUT_RED_0 = 0x25
LUT_RED_1 = 0x26
LUT_RED_2 = 0x27
LUT_RED_3 = 0x28
LUT_XON = 0x29
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_COMMAND = 0x40
TEMPERATURE_CALIBRATION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
TCON_RESOLUTION = 0x61
SPI_FLASH_CONTROL = 0x65
REVISION = 0x70
GET_STATUS = 0x71
AUTO_MEASUREMENT_VCOM = 0x80
READ_VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
class EPD:
def __init__(self):
self.reset_pin = epdif.RST_PIN
self.dc_pin = epdif.DC_PIN
self.busy_pin = epdif.BUSY_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
def digital_write(self, pin, value):
epdif.epd_digital_write(pin, value)
def digital_read(self, pin):
return epdif.epd_digital_read(pin)
def delay_ms(self, delaytime):
epdif.epd_delay_ms(delaytime)
def send_command(self, command):
self.digital_write(self.dc_pin, GPIO.LOW)
# the parameter type is list but not int
# so use [command] instead of command
epdif.spi_transfer([command])
def send_data(self, data):
self.digital_write(self.dc_pin, GPIO.HIGH)
# the parameter type is list but not int
# so use [data] instead of data
epdif.spi_transfer([data])
def init(self):
if (epdif.epd_init() != 0):
return -1
self.reset()
self.send_command(POWER_SETTING)
self.send_data(0x37)
self.send_data(0x00)
self.send_command(PANEL_SETTING)
self.send_data(0xCF)
self.send_data(0x08)
self.send_command(BOOSTER_SOFT_START)
self.send_data(0xc7)
self.send_data(0xcc)
self.send_data(0x28)
self.send_command(POWER_ON)
self.wait_until_idle()
self.send_command(PLL_CONTROL)
self.send_data(0x3c)
self.send_command(TEMPERATURE_CALIBRATION)
self.send_data(0x00)
self.send_command(VCOM_AND_DATA_INTERVAL_SETTING)
self.send_data(0x77)
self.send_command(TCON_SETTING)
self.send_data(0x22)
self.send_command(TCON_RESOLUTION)
self.send_data(0x02) #source 640
self.send_data(0x80)
self.send_data(0x01) #gate 384
self.send_data(0x80)
self.send_command(VCM_DC_SETTING)
self.send_data(0x1E) #decide by LUT file
self.send_command(0xe5) #FLASH MODE
self.send_data(0x03)
def wait_until_idle(self):
while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle
self.delay_ms(100)
def reset(self):
self.digital_write(self.reset_pin, GPIO.LOW) # module reset
self.delay_ms(200)
self.digital_write(self.reset_pin, GPIO.HIGH)
self.delay_ms(200)
def get_frame_buffer(self, image):
buf = [0x00] * int(self.width * self.height / 4)
# Set buffer to value of Python Imaging Library image.
# Image must be in mode L.
image_grayscale = image.convert('L', dither=None)
imwidth, imheight = image_grayscale.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))
pixels = image_grayscale.load()
for y in range(self.height):
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0: # black
buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2))
elif pixels[x, y] == 76: #was 110 # convert gray to red
buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2))
buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2)
else: # white
buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2)
return buf #due to python2 -> python3, int had to be added in 'get_frame
def display_frame(self, frame_buffer):
self.send_command(DATA_START_TRANSMISSION_1)
for i in range(0, int(self.width / 4 * self.height)):
#the above line had to be modified due to python2 -> python3
#the issue lies in division, which returns integers in python2
#but floats in python3
temp1 = frame_buffer[i]
j = 0
while (j < 4):
if ((temp1 & 0xC0) == 0xC0):
temp2 = 0x03
elif ((temp1 & 0xC0) == 0x00):
temp2 = 0x00
else:
temp2 = 0x04
temp2 = (temp2 << 4) & 0xFF
temp1 = (temp1 << 2) & 0xFF
j += 1
if((temp1 & 0xC0) == 0xC0):
temp2 |= 0x03
elif ((temp1 & 0xC0) == 0x00):
temp2 |= 0x00
else:
temp2 |= 0x04
temp1 = (temp1 << 2) & 0xFF
self.send_data(temp2)
j += 1
self.send_command(DISPLAY_REFRESH)
self.delay_ms(100)
self.wait_until_idle()
def sleep(self):
self.send_command(POWER_OFF)
self.wait_until_idle()
self.send_command(DEEP_SLEEP)
self.send_data(0xa5)
### END OF FILE ###
import epdif
from PIL import Image
import RPi.GPIO as GPIO
from settings import display_colours
# Display resolution
EPD_WIDTH = 640
EPD_HEIGHT = 384
# EPD7IN5 commands
PANEL_SETTING = 0x00
POWER_SETTING = 0x01
POWER_OFF = 0x02
POWER_OFF_SEQUENCE_SETTING = 0x03
POWER_ON = 0x04
POWER_ON_MEASURE = 0x05
BOOSTER_SOFT_START = 0x06
DEEP_SLEEP = 0x07
DATA_START_TRANSMISSION_1 = 0x10
DATA_STOP = 0x11
DISPLAY_REFRESH = 0x12
IMAGE_PROCESS = 0x13
LUT_FOR_VCOM = 0x20
LUT_BLUE = 0x21
LUT_WHITE = 0x22
LUT_GRAY_1 = 0x23
LUT_GRAY_2 = 0x24
LUT_RED_0 = 0x25
LUT_RED_1 = 0x26
LUT_RED_2 = 0x27
LUT_RED_3 = 0x28
LUT_XON = 0x29
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_COMMAND = 0x40
TEMPERATURE_CALIBRATION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
TCON_RESOLUTION = 0x61
SPI_FLASH_CONTROL = 0x65
REVISION = 0x70
GET_STATUS = 0x71
AUTO_MEASUREMENT_VCOM = 0x80
READ_VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
class EPD:
def __init__(self):
self.reset_pin = epdif.RST_PIN
self.dc_pin = epdif.DC_PIN
self.busy_pin = epdif.BUSY_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
def digital_write(self, pin, value):
epdif.epd_digital_write(pin, value)
def digital_read(self, pin):
return epdif.epd_digital_read(pin)
def delay_ms(self, delaytime):
epdif.epd_delay_ms(delaytime)
def send_command(self, command):
self.digital_write(self.dc_pin, GPIO.LOW)
# the parameter type is list but not int
# so use [command] instead of command
epdif.spi_transfer([command])
def send_data(self, data):
self.digital_write(self.dc_pin, GPIO.HIGH)
# the parameter type is list but not int
# so use [data] instead of data
epdif.spi_transfer([data])
def init(self):
if (epdif.epd_init() != 0):
return -1
self.reset()
self.send_command(POWER_SETTING)
self.send_data(0x37)
self.send_data(0x00)
self.send_command(PANEL_SETTING)
self.send_data(0xCF)
self.send_data(0x08)
self.send_command(BOOSTER_SOFT_START)
self.send_data(0xc7)
self.send_data(0xcc)
self.send_data(0x28)
self.send_command(POWER_ON)
self.wait_until_idle()
self.send_command(PLL_CONTROL)
self.send_data(0x3c)
self.send_command(TEMPERATURE_CALIBRATION)
self.send_data(0x00)
self.send_command(VCOM_AND_DATA_INTERVAL_SETTING)
self.send_data(0x77)
self.send_command(TCON_SETTING)
self.send_data(0x22)
self.send_command(TCON_RESOLUTION)
self.send_data(0x02) #source 640
self.send_data(0x80)
self.send_data(0x01) #gate 384
self.send_data(0x80)
self.send_command(VCM_DC_SETTING)
self.send_data(0x1E) #decide by LUT file
self.send_command(0xe5) #FLASH MODE
self.send_data(0x03)
def wait_until_idle(self):
while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle
self.delay_ms(100)
def reset(self):
self.digital_write(self.reset_pin, GPIO.LOW) # module reset
self.delay_ms(200)
self.digital_write(self.reset_pin, GPIO.HIGH)
self.delay_ms(200)
def get_frame_buffer(self, image):
if display_colours is 'bwr':
buf = [0x00] * int(self.width * self.height / 4)
image_grayscale = image.convert('L', dither=None)
imwidth, imheight = image_grayscale.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))
pixels = image_grayscale.load()
for y in range(self.height):
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0: # black
buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2))
elif pixels[x, y] == 76: # convert gray to red
buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2))
buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2)
else: # white
buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2)
return buf
if display_colours is 'bw':
buf = [0x00] * int(self.width * self.height / 8)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))
pixels = image_monocolor.load()
for y in range(self.height):
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] != 0:
buf[int((x + y * self.width) / 8)] |= 0x80 >> (x % 8)
return buf
def display_frame(self, frame_buffer):
self.send_command(DATA_START_TRANSMISSION_1)
if display_colours is 'bwr':
for i in range(0, int(self.width / 4 * self.height)):
temp1 = frame_buffer[i]
j = 0
while (j < 4):
if ((temp1 & 0xC0) == 0xC0):
temp2 = 0x03
elif ((temp1 & 0xC0) == 0x00):
temp2 = 0x00
else:
temp2 = 0x04
temp2 = (temp2 << 4) & 0xFF
temp1 = (temp1 << 2) & 0xFF
j += 1
if((temp1 & 0xC0) == 0xC0):
temp2 |= 0x03
elif ((temp1 & 0xC0) == 0x00):
temp2 |= 0x00
else:
temp2 |= 0x04
temp1 = (temp1 << 2) & 0xFF
self.send_data(temp2)
j += 1
if display_colours is 'bw':
for i in range(0, 30720):
temp1 = frame_buffer[i]
j = 0
while (j < 8):
if(temp1 & 0x80):
temp2 = 0x03
else:
temp2 = 0x00
temp2 = (temp2 << 4) & 0xFF
temp1 = (temp1 << 1) & 0xFF
j += 1
if(temp1 & 0x80):
temp2 |= 0x03
else:
temp2 |= 0x00
temp1 = (temp1 << 1) & 0xFF
self.send_data(temp2)
j += 1
self.send_command(DISPLAY_REFRESH)
self.delay_ms(100)
self.wait_until_idle()
def sleep(self):
self.send_command(POWER_OFF)
self.wait_until_idle()
self.send_command(DEEP_SLEEP)
self.send_data(0xa5)

View File

@ -1,180 +0,0 @@
import epdif
from PIL import Image
import RPi.GPIO as GPIO
# Display resolution
EPD_WIDTH = 640
EPD_HEIGHT = 384
# EPD7IN5 commands
PANEL_SETTING = 0x00
POWER_SETTING = 0x01
POWER_OFF = 0x02
POWER_OFF_SEQUENCE_SETTING = 0x03
POWER_ON = 0x04
POWER_ON_MEASURE = 0x05
BOOSTER_SOFT_START = 0x06
DEEP_SLEEP = 0x07
DATA_START_TRANSMISSION_1 = 0x10
DATA_STOP = 0x11
DISPLAY_REFRESH = 0x12
IMAGE_PROCESS = 0x13
LUT_FOR_VCOM = 0x20
LUT_BLUE = 0x21
LUT_WHITE = 0x22
LUT_GRAY_1 = 0x23
LUT_GRAY_2 = 0x24
LUT_RED_0 = 0x25
LUT_RED_1 = 0x26
LUT_RED_2 = 0x27
LUT_RED_3 = 0x28
LUT_XON = 0x29
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_COMMAND = 0x40
TEMPERATURE_CALIBRATION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
TCON_RESOLUTION = 0x61
SPI_FLASH_CONTROL = 0x65
REVISION = 0x70
GET_STATUS = 0x71
AUTO_MEASUREMENT_VCOM = 0x80
READ_VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
class EPD:
def __init__(self):
self.reset_pin = epdif.RST_PIN
self.dc_pin = epdif.DC_PIN
self.busy_pin = epdif.BUSY_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
def digital_write(self, pin, value):
epdif.epd_digital_write(pin, value)
def digital_read(self, pin):
return epdif.epd_digital_read(pin)
def delay_ms(self, delaytime):
epdif.epd_delay_ms(delaytime)
def send_command(self, command):
self.digital_write(self.dc_pin, GPIO.LOW)
# the parameter type is list but not int
# so use [command] instead of command
epdif.spi_transfer([command])
def send_data(self, data):
self.digital_write(self.dc_pin, GPIO.HIGH)
# the parameter type is list but not int
# so use [data] instead of data
epdif.spi_transfer([data])
def init(self):
if (epdif.epd_init() != 0):
return -1
self.reset()
self.send_command(POWER_SETTING)
self.send_data(0x37)
self.send_data(0x00)
self.send_command(PANEL_SETTING)
self.send_data(0xCF)
self.send_data(0x08)
self.send_command(BOOSTER_SOFT_START)
self.send_data(0xc7)
self.send_data(0xcc)
self.send_data(0x28)
self.send_command(POWER_ON)
self.wait_until_idle()
self.send_command(PLL_CONTROL)
self.send_data(0x3c)
self.send_command(TEMPERATURE_CALIBRATION)
self.send_data(0x00)
self.send_command(VCOM_AND_DATA_INTERVAL_SETTING)
self.send_data(0x77)
self.send_command(TCON_SETTING)
self.send_data(0x22)
self.send_command(TCON_RESOLUTION)
self.send_data(0x02) #source 640
self.send_data(0x80)
self.send_data(0x01) #gate 384
self.send_data(0x80)
self.send_command(VCM_DC_SETTING)
self.send_data(0x1E) #decide by LUT file
self.send_command(0xe5) #FLASH MODE
self.send_data(0x03)
def wait_until_idle(self):
while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle
self.delay_ms(100)
def reset(self):
self.digital_write(self.reset_pin, GPIO.LOW) # module reset
self.delay_ms(200)
self.digital_write(self.reset_pin, GPIO.HIGH)
self.delay_ms(200)
def get_frame_buffer(self, image):
buf = [0x00] * int(self.width * self.height / 8)
# Set buffer to value of Python Imaging Library image.
# Image must be in mode 1.
image_monocolor = image.convert('1') #with ot withour dithering?
imwidth, imheight = image_monocolor.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))
pixels = image_monocolor.load()
for y in range(self.height):
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] != 0:
buf[int((x + y * self.width) / 8)] |= 0x80 >> (x % 8)
return buf
def display_frame(self, frame_buffer):
self.send_command(DATA_START_TRANSMISSION_1)
for i in range(0, 30720):
temp1 = frame_buffer[i]
j = 0
while (j < 8):
if(temp1 & 0x80):
temp2 = 0x03
else:
temp2 = 0x00
temp2 = (temp2 << 4) & 0xFF
temp1 = (temp1 << 1) & 0xFF
j += 1
if(temp1 & 0x80):
temp2 |= 0x03
else:
temp2 |= 0x00
temp1 = (temp1 << 1) & 0xFF
self.send_data(temp2)
j += 1
self.send_command(DISPLAY_REFRESH)
self.delay_ms(100)
self.wait_until_idle()
def sleep(self):
self.send_command(POWER_OFF)
self.wait_until_idle()
self.send_command(DEEP_SLEEP)
self.send_data(0xa5)
### END OF FILE ###

View File

@ -0,0 +1,92 @@
This Font Software is licensed under the SIL Open Font License,
Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font
creation efforts of academic and linguistic communities, and to
provide a free and open framework in which fonts may be shared and
improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply to
any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software
components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to,
deleting, or substituting -- in part or in whole -- any of the
components of the Original Version, by changing formats or by porting
the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed,
modify, redistribute, and sell modified and unmodified copies of the
Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in
Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the
corresponding Copyright Holder. This restriction only applies to the
primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created using
the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,11 @@
This package is part of the noto project. Visit
google.com/get/noto for more information.
Built on 2017-10-24 from the following noto repository:
-----
Repo: noto-fonts
Tag: v2017-10-24-phase3-second-cleanup
Date: 2017-10-24 12:10:34 GMT
Commit: 8ef14e6c606a7a0ef3943b9ca01fd49445620d79
Remove some files that aren't for release.

View File

@ -6,24 +6,25 @@ It also contains the positions of these icons on the E-Paper display
"""
from PIL import Image
from settings import language
im_open = Image.open
import os
path = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/")
if path != "" and path[-1] != "/":
path += "/"
path = '/home/pi/Inky-Calendar/Calendar/'
wpath = path+'weather-icons/'
mpath = path+'translations/'+language+'/months/'
weekpath = path+'translations/'+language+'/week/'
dpath = path+'days/'
opath = path+'other/'
fpath = path+'fonts/'
weekday = im_open(opath+'weekday.png')
eventicon = im_open(opath+'event.png')
dateicon = im_open(opath+'today.png')
seperator = im_open(opath+'seperator.jpeg')
seperator2 = im_open(opath+'seperator2.jpeg')
tempicon = im_open(opath+'temperature.jpeg')
humicon = im_open(opath+'humidity.jpeg')
weekmon = im_open(weekpath+'week-mon.jpeg')
weeksun = im_open(weekpath+'week-sun.jpeg')
no_response = im_open(opath+'cloud-no-response.jpeg')
sunriseicon = im_open(opath+'wi-sunrise.jpeg')
sunseticon = im_open(opath+'wi-sunset.jpeg')
@ -42,6 +43,22 @@ windiconspace = (79, 0)
sunriseplace = (214, 0)
sunsetplace = (214, 35)
col = 0
agenda_view_lines = {
'line1': (col, 75), 'line2': (col, 100),
'line3': (col, 125), 'line4': (col, 150),
'line5': (col, 175), 'line6': (col, 200),
'line7': (col, 225), 'line8': (col, 250),
'line9': (col, 275), 'line10': (col, 300),
'line11': (col, 325), 'line12': (col, 350),
'line13': (col, 375), 'line14': (col, 400),
'line15': (col, 425), 'line16': (col, 450),
'line17': (col, 475), 'line18': (col, 500),
'line19': (col, 525), 'line20': (col, 550),
'line21': (col, 575), 'line22': (col, 600),
}
rss_places = {
'line_1' : (0, 490), 'line_2' : (0, 515), 'line_3' : (0, 540),
'line_4' : (0, 565), 'line_5' : (0, 590), 'line_6' : (0, 615)
@ -105,16 +122,10 @@ positions = {
week_row = 134
weekdaysmon = {
'Mon': (col1, week_row), 'Tue': (col2, week_row), 'Wed': (col3, week_row),
'Thu': (col4, week_row), 'Fri': (col5, week_row), 'Sat': (col6, week_row),
'Sun': (col7, week_row)
}
weekdayssun = {
'Sun': (col1, week_row), 'Mon': (col2, week_row), 'Tue': (col3, week_row),
'Wed': (col4, week_row), 'Thu': (col5, week_row), 'Fri': (col6, week_row),
'Sat': (col7, week_row)
weekday_pos = {
'pos0': (col1, week_row), 'pos1': (col2, week_row), 'pos2': (col3, week_row),
'pos3': (col4, week_row), 'pos4': (col5, week_row), 'pos5': (col6, week_row),
'pos6': (col7, week_row)
}
weathericons = {

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

View File

@ -1,11 +1,6 @@
ical_urls = [
"https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics"
]
rss_feeds = [
"http://feeds.bbci.co.uk/news/world/rss.xml#"
]
ical_urls = ["https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics"]
rss_feeds = ["http://feeds.bbci.co.uk/news/world/rss.xml#"]
update_interval = "60"
additional_feature = "events"
api_key = ""
location = "Stuttgart, DE"
week_starts_on = "Monday"
@ -15,3 +10,6 @@ display_colours = "bwr"
language = "en"
units = "metric"
hours = "24"
top_section = "Weather"
middle_section = "Agenda"
bottom_section = "RSS"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -46,6 +46,8 @@ if [ "$option" = 1 ]; then
sleep 2
cp -r /home/pi/Inky-Calendar /home/pi/Inky-Calendar-old
sudo rm -r /home/pi/Inky-Calendar
mv /home/pi/Inky-Calendar-old/Calendar /home/pi/Inky-Calendar-old/Calendar-old #added due to relative path which searches for
# the 'Calendar' folder
echo "Updating now..."
cd
else
@ -238,6 +240,7 @@ stdout_logfile = /home/pi/Inky-Calendar/E-Paper.log
stdout_logfile_maxbytes = 1MB
stderr_logfile = /home/pi/Inky-Calendar/E-Paper-err.log
stderr_logfile_maxbytes = 1MB
autorestart = true
EOF
sudo service supervisor start E-Paper

View File

@ -15,7 +15,7 @@ A software written in python3 that allows you to transform an E-Paper display (l
This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E-Paper display from waveshare/gooddisplay and works with Raspberry Pi 2, 3 and 0 (Zero, Zero W, Zero WH).
## News:
* **Updated and optimised the installer for faster installation and updating (Mid April 2019)
* **Updated and optimised the installer for faster installation and updating (Mid April 2019)**
* **Added a user-friendly Web-UI for adding details to the programm** (Credit to TobyChui for the template)
* **Version 1.5 released (Early February 2019) with a new layout, displayed events and many back-end improvements**
@ -25,7 +25,7 @@ This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E
</p>
## Main features
* Monthly Calendar which automatically updates itself to the current day
* Monthly Calendar which automatically update itself to the current day
* Fetch appointments/events from your Google Calendar and display them on the Display
* Fetch live weather data (temperature, humidity, sunrise- & sunset time, wind speed, weather-icon) from Openweathermap servers and display them on the E-Paper
* Fetch RSS-feeds from given RSS-feed URLs and display the content (news, quotes etc.) on the E-Paper
@ -40,7 +40,7 @@ This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E
* Or: Raspberry Pi Zero W. In this case, you'll need to solder 2x20 pin GPIO headers yourself
* MicroSD card (min. 4GB)
* MicroUSB cable (for power)
* Something to be used as a case (e.g. a (RIBBA) picture frame or a 3D-printed case)
* Something to be used as a case (e.g. a RIBBA photo-frame or a 3D-printed case)
# Setup
## Getting the Raspberry Pi Zero W ready
@ -56,7 +56,9 @@ This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E
## Installing required packages for python 3.x
Execute the following command in the Terminal to install all required packages. This will work on both, Raspbian Stretch with Desktop and Raspbian Stretch lite.
**`bash -c "$(curl -sL https://raw.githubusercontent.com/aceisace/Inky-Calendar/Stable/Installer-with-debug.sh)"`**
## Attention: this is the Installer of the master branch (development branch). It is not guaranteed to work and is only here for testing
**`bash -c "$(curl -sL https://raw.githubusercontent.com/aceisace/Inky-Calendar/master/Installer-with-debug.sh)"`**
If the Installer should fail for any reason, kindly open an issue and paste the error. Thanks.
@ -65,33 +67,28 @@ If the Installer should fail for any reason, kindly open an issue and paste the
<img src="https://github.com/aceisace/Inky-Calendar/blob/Stable/Gallery/installer-v1.6-part1.png" width="650"><img src="https://github.com/aceisace/Inky-Calendar/blob/Stable/Gallery/installer-v1.6-part2.png" width="650">
## Adding details to the programm
There are currently 2 ways to add details to the programm, the user-friendly method and the legacy method. It's recommended to use the
user-friendly option first. If you encounter any bugs, please use the legacy method and write a comment describing the bug.
1) **User-friendly method**
To use the new WEB-UI (Web-User-Interface), simply double-click the file Settings-Web-UI.html located in /home/pi/Inky-Calendar/Calendar/ to open up the document with the browser (Chrome etc.). Next, fill in the details (you can use the table below for a reference) and click on generate to create your settings.py file. Please add your details without these signs `""`. For example, your api_key should be entered like this: `wadiln3ilioejo` and _not_ `"wadiln3ilioejo"`. Lastly, copy the generated 'settings.py' file to /home/pi/Inky-Calendar/Calendar (the same path where the settings.py file is) and try starting the main script with:
To add details to the programm, please use the web-ui (user-interface). Simply double-click the file Settings-Web-UI.html located in /home/pi/Inky-Calendar/Calendar/ to open up the document with the browser (Chrome etc.). Next, fill in the details (you can use the table below for a reference) and click on generate to create your settings.py file. Lastly, copy the generated 'settings.py' file to /home/pi/Inky-Calendar/Calendar (the same path where the settings.py file is) and try starting the main script with:
python3.5 /home/pi/Inky-Calendar/Calendar/E-Paper.py.
If you encounter any issues, please leave a comment here or via email. Thanks in advance.
2) **Legacy method**
Navigate to the home directory, open 'Inky-Calendar' and open the file 'settings.py' inside the Calendar folder. Adjust the values using the list below as a reference. You can edit the settings.py file by typing:
`nano /home/pi/Inky-Calendar/Calendar/settings.py` in the Terminal.
If you encounter any issues, please leave a comment in the issues or via email. Thanks in advance.
## This part will soon shift to the settings web-ui
| Parameter | Description |
| :---: | :---: |
| ical_urls | Your iCalendar URL/s. To add more than one URL, seperate each with a comma, for example: "ical-url1", "ical-url2"|
| rss_feeds | Here, you can add RSS-feed URLs which are used to fetch news etc. for example: "rss-url1", "rss-url2"|
| update_interval | How often should be Display be updated in one hour? The default option is `"60"`, which means once every 30 hour. You can select one of the values from: `"10"`, `"15"`, `"20"`, `"30"`, `"60"`. Please note that ghosting will occur when updating too frequently. To prevent ghosting, it's recommended to run 1 calibration for every 6 updates. For example, with an update interval of 1 hour, the calibration should be executed every 6 hours. |
| additional_feature | What would you like the Display to show in the section below the Calendar? The default option is `"rss"` (for RSS-feeds. You may choose `"events`" if you want to display events instead of RSS-feeds|
| ical_urls | Your iCalendar URL/s. To add more than one URL, seperate each with a comma, for example: ical-url1, ical-url2|
| rss_feeds | Here, you can add RSS-feed URLs which are used to fetch news etc. for example: rss-url1, rss-url2|
| update_interval | How often should be Display be updated in one hour? The default option is `60`, which means once every hour. You can select one of the values from: `10`, `15`, `20`, `30`, `60`. Please note that ghosting will occur when updating too frequently. To prevent ghosting, it's recommended to run 1 calibration for every 6 updates. For example, with an update interval of 1 hour, the calibration should be executed every 6 hours. |
| api_key | Your __personal__ openweathermap API-key which you can generate and find in your Account info |
| location | Location refers to the closest weather station from your place. It isn't necessarily the place you live in. To find this location, type your city name in the search box on [openweathermap](https://openweathermap.org/). The output should be in the following format: City Name, Country ISO-Code. Not sure what your ISO code is? Check here: [(find iso-code)](https://countrycode.org/) |
| week_starts_on | When does the week start on your Region? Possible options are `"Monday"` or `"Sunday"`|
|events_max_range| How far in the future should events from your iCalendar be fetched. The value is given in days. By default, events in the next 60 days will be fetched from the Calendar. Can be any integer from `"1"` to `"365"`|
| calibration_hours | At which hours would you like the Display to 'calibrate' itself? Calibration is strongly recommended at least 3 times a day. In the list, you have to include hours in 24-hour format, seperated by a comma. The default option is `[1,12,18]` and refers to 1 am, 12 am, and 6 pm |
display_colours | This should normally be set by the installer when you choose the type of your display. Options include `"bw"` if you're using the black and white E-Paper or `"bwr"` when you're using the black-white-red or black-white-yellow E-Paper|
| language | Choosing the language allows changing the language of the month and week-icons. Possible options are `"en"` for english, `"de"` for german and `"zh-tw"` for Taiwan Chinese (Hong Kong Chinese)|
|units| Selecting units allows switching units from km/h (kilometer per hour) and °C (degree Celcius) to mph (miles per hour) and °F (degree Fahrenheit). Possible options are `"metric"` or `"imperial"`|
|hours | Which time format do you prefer? This will change the sunrise and sunset times from 24-hours format to 12-hours format. Possible options are `"24"` for 24-hours and `"12"` for 12-hours.|
| week_starts_on | When does the week start on your Region? Possible options are `Monday` or `Sunday`|
| events_max_range | How far in the future should events from your iCalendar be fetched. The value is given in days. By default, events in the next 60 days will be fetched from the Calendar. Can be any integer from `1` to `365`|
| calibration_hours | At which hours would you like the Display to 'calibrate' itself? Calibration is strongly recommended at least 3 times a day. In the list, you have to include hours in 24-hour format, seperated by a comma. The default option is `1,12,18` and refers to 1 am, 12 am, and 6 pm |
| display_colours | This should normally be set by the installer when you choose the type of your display. Options include `black-white` if you're using the black and white E-Paper or `black-white-red/yellow` when you're using the black-white-red or black-white-yellow E-Paper|
| language | Choosing the language allows changing the language of the month and week-icons. Possible options are `en` for english, `de` for german and `zh-tw` for Taiwan Chinese (Hong Kong Chinese)|
| units| Selecting units allows switching units from km/h (kilometer per hour) and °C (degree Celcius) to mph (miles per hour) and °F (degree Fahrenheit). Possible options are `metric` or `imperial`|
| hours | Which time format do you prefer? This will change the sunrise and sunset times from 24-hours format to 12-hours format. Possible options are `24` for 24-hours and `12` for 12-hours.|
| top_section | What would you like the Display to show in the top section? Currently, only weather is available|
| middle_section | What would you like the Display to show in the main section? The default option is `Calendar` (for RSS-feeds. You may choose `Agenda` if you want to display events instead of the Calendar |
| bottom_section | What would you like the Display to show in the bottom section? |
## iCalendar
Currently, only Google Calendar is fully supported and has proven to run more stable than others. While it is possible that a non-Google iCalendar may work, it is often not the case. If you're not using Google-Calendar and the script is throwing errors related to your iCalendar, please export your iCalendar (as an .ics file), create a new Calendar at Google Calendar and import your previous Calendar's .ics file. After importing, navigate to the section 'Integrate Calendar', copy the 'Secret address in iCal format' and paste it in the ical_urls section in the settings.py file (see instructions above).

28
auto-update.sh Normal file
View File

@ -0,0 +1,28 @@
#!/bin/bash
# Script for updating the Inky-Calendar software. This will automatically
# transfer the user's own details with the placeholders in the settings.py file
# To-do: Delete the old settings.py file after all operations are done
in="/home/pi/settings.py.old"
out="/home/pi/Inky-Calendar/Calendar/settings.py"
# replace template iCalendar URLs with user-defined URLs
sed -n -e "/^ical_urls/r $in" -i -e "/^ical_urls/d" $out
sed -n -e "/^rss_feeds/r $in" -i -e "/^rss_feeds/d" $out
sed -n -e "/^update_interval/r $in" -i -e "/^update_interval/d" $out
sed -n -e "/^api_key/r $in" -i -e "/^api_key/d" $out
sed -n -e "/^location/r $in" -i -e "/^location/d" $out
sed -n -e "/^week_starts_on/r $in" -i -e "/^week_starts_on/d" $out
sed -n -e "/^events_max_range/r $in" -i -e "/^events_max_range/d" $out
sed -n -e "/^calibration_hours/r $in" -i -e "/^calibration_hours/d" $out
sed -n -e "/^display_colours/r $in" -i -e "/^display_colours/d" $out
sed -n -e "/^language/r $in" -i -e "/^language/d" $out
sed -n -e "/^units/r $in" -i -e "/^units/d" $out
sed -n -e "/^hours/r $in" -i -e "/^hours/d" $out
sed -n -e "/^top_section/r $in" -i -e "/^top_section/d" $out
sed -n -e "/^middle_section/r $in" -i -e "/^middle_section/d" $out
sed -n -e "/^bottom_section/r $in" -i -e "/^bottom_section/d" $out
echo -e 'All operations done'