Compare commits
No commits in common. "master" and "Crypto-feature" have entirely different histories.
master
...
Crypto-fea
61 changed files with 756 additions and 1408 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,6 +16,4 @@
|
||||||
/Calendar/CalendarEvent.pyc
|
/Calendar/CalendarEvent.pyc
|
||||||
/Calendar/design_exported_old.png
|
/Calendar/design_exported_old.png
|
||||||
/Calendar/settings_dev.py
|
/Calendar/settings_dev.py
|
||||||
/Calendar/settings_pers.py
|
|
||||||
/.vscode
|
/.vscode
|
||||||
/Calendar/images/
|
|
|
@ -8,17 +8,14 @@ from settings import line_thickness
|
||||||
|
|
||||||
separator_width = line_thickness
|
separator_width = line_thickness
|
||||||
|
|
||||||
|
|
||||||
class AgendaListDesign (DesignEntity):
|
class AgendaListDesign (DesignEntity):
|
||||||
'''Lists upcoming events in chronological order and groups them by days'''
|
'''Lists upcoming events in chronological order and groups them by days'''
|
||||||
|
def __init__ (self, size, calendar, line_spacing = 0, col_spacing = 8, text_size = defaultfontsize, start_date = date.today(), always_add_start_row = True):
|
||||||
def __init__(self, size, calendar, line_spacing=0, col_spacing=8, text_size=defaultfontsize, start_date=date.today(), always_add_start_row=True, day_limit_foresight=91):
|
|
||||||
super(AgendaListDesign, self).__init__(size)
|
super(AgendaListDesign, self).__init__(size)
|
||||||
self.calendar = calendar
|
self.calendar = calendar
|
||||||
self.line_spacing = line_spacing
|
self.line_spacing = line_spacing
|
||||||
self.col_spacing = col_spacing
|
self.col_spacing = col_spacing
|
||||||
self.text_size = text_size
|
self.text_size = text_size
|
||||||
self.day_limit_foresight = day_limit_foresight
|
|
||||||
self.start_dt = date(start_date.year, start_date.month, start_date.day)
|
self.start_dt = date(start_date.year, start_date.month, start_date.day)
|
||||||
self.always_add_start_row = always_add_start_row
|
self.always_add_start_row = always_add_start_row
|
||||||
|
|
||||||
|
@ -38,8 +35,7 @@ class AgendaListDesign (DesignEntity):
|
||||||
self.infos = []
|
self.infos = []
|
||||||
self.cell_props = []
|
self.cell_props = []
|
||||||
fetch_day = self.start_dt
|
fetch_day = self.start_dt
|
||||||
days_foresight = 0
|
while len(self.infos) < self.__event_number__:
|
||||||
while len(self.infos) < self.__event_number__ and days_foresight < self.day_limit_foresight:
|
|
||||||
day_events = self.calendar.get_day_events(fetch_day)
|
day_events = self.calendar.get_day_events(fetch_day)
|
||||||
fetch_day_added_once = False
|
fetch_day_added_once = False
|
||||||
for event in day_events:
|
for event in day_events:
|
||||||
|
@ -56,7 +52,6 @@ class AgendaListDesign (DesignEntity):
|
||||||
|
|
||||||
self.infos.append(row)
|
self.infos.append(row)
|
||||||
fetch_day = fetch_day + timedelta(1)
|
fetch_day = fetch_day + timedelta(1)
|
||||||
days_foresight = days_foresight + 1
|
|
||||||
|
|
||||||
if self.infos[0][1] != date_summary_str(self.start_dt) and self.always_add_start_row:
|
if self.infos[0][1] != date_summary_str(self.start_dt) and self.always_add_start_row:
|
||||||
row = ["", date_summary_str(self.start_dt), "", ""]
|
row = ["", date_summary_str(self.start_dt), "", ""]
|
||||||
|
@ -65,8 +60,7 @@ class AgendaListDesign (DesignEntity):
|
||||||
self.cell_props.insert(0, props)
|
self.cell_props.insert(0, props)
|
||||||
|
|
||||||
def __draw_infos__ (self):
|
def __draw_infos__ (self):
|
||||||
table = TableDesign(self.size, self.infos, fontsize=self.__date_fontsize__,
|
table = TableDesign(self.size, self.infos, fontsize = self.__date_fontsize__, line_spacing=self.__date_linespace__, col_spacing = self.col_spacing, cell_properties=self.cell_props)
|
||||||
line_spacing=self.__date_linespace__, col_spacing=self.col_spacing, cell_properties=self.cell_props)
|
|
||||||
self.draw_design(table)
|
self.draw_design(table)
|
||||||
|
|
||||||
def __draw_lines__ (self):
|
def __draw_lines__ (self):
|
||||||
|
@ -79,8 +73,7 @@ class AgendaListDesign (DesignEntity):
|
||||||
pos = (0, ypos)
|
pos = (0, ypos)
|
||||||
positions = [ pos, (self.size[0], ypos) ]
|
positions = [ pos, (self.size[0], ypos) ]
|
||||||
|
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line(positions, fill=colors["fg"], width=separator_width)
|
||||||
positions, fill=colors["fg"], width=separator_width)
|
|
||||||
|
|
||||||
def __get_row_props__ (self, event = None):
|
def __get_row_props__ (self, event = None):
|
||||||
color = colors["fg"]
|
color = colors["fg"]
|
||||||
|
|
|
@ -13,17 +13,14 @@ seperator_width = line_thickness
|
||||||
infolist_size = (1, 0.24)
|
infolist_size = (1, 0.24)
|
||||||
infolist_padding = 0
|
infolist_padding = 0
|
||||||
|
|
||||||
|
|
||||||
class AgendaListPanel (PanelDesign):
|
class AgendaListPanel (PanelDesign):
|
||||||
'''Lists upcoming events in chronological order and groups them by days'''
|
'''Lists upcoming events in chronological order and groups them by days'''
|
||||||
|
|
||||||
def __init__(self, size):
|
def __init__(self, size):
|
||||||
super(AgendaListPanel, self).__init__(size)
|
super(AgendaListPanel, self).__init__(size)
|
||||||
self.weather_size = (0, 0)
|
self.weather_size = (0, 0)
|
||||||
self.info_size = (0, 0)
|
self.info_size = (0, 0)
|
||||||
if general_settings["weather-info"]:
|
if general_settings["weather-info"]:
|
||||||
self.weather_size = (
|
self.weather_size = (self.size[0], self.size[1] * weatherheader_height)
|
||||||
self.size[0], self.size[1] * weatherheader_height)
|
|
||||||
|
|
||||||
def add_weather (self, weather):
|
def add_weather (self, weather):
|
||||||
self.weather = weather
|
self.weather = weather
|
||||||
|
@ -68,8 +65,7 @@ class AgendaListPanel (PanelDesign):
|
||||||
self.__draw_weather__()
|
self.__draw_weather__()
|
||||||
|
|
||||||
def __draw_seperator__ (self, height, color):
|
def __draw_seperator__ (self, height, color):
|
||||||
ImageDraw.Draw(self.__image__).line([self.__abs_pos__(
|
ImageDraw.Draw(self.__image__).line([ self.__abs_pos__((0, height)), self.__abs_pos__((1, height)) ], fill=color, width=seperator_width)
|
||||||
(0, height)), self.__abs_pos__((1, height))], fill=color, width=seperator_width)
|
|
||||||
|
|
||||||
def __abs_pos__ (self, pos, size = None):
|
def __abs_pos__ (self, pos, size = None):
|
||||||
if size is None:
|
if size is None:
|
||||||
|
@ -77,8 +73,7 @@ class AgendaListPanel (PanelDesign):
|
||||||
return (int(pos[0] * size[0]), int(pos[1] * size[1]))
|
return (int(pos[0] * size[0]), int(pos[1] * size[1]))
|
||||||
|
|
||||||
def __draw_calendar__(self):
|
def __draw_calendar__(self):
|
||||||
size = (self.size[0], self.size[1] - self.weather_size[1] -
|
size = (self.size[0], self.size[1] - self.weather_size[1] - self.info_size[1] - agenda_ypadding)
|
||||||
self.info_size[1] - agenda_ypadding)
|
|
||||||
|
|
||||||
agenda = AgendaListDesign(size, self.calendar)
|
agenda = AgendaListDesign(size, self.calendar)
|
||||||
agenda.pos = (0, agenda_ypadding + self.weather_size[1])
|
agenda.pos = (0, agenda_ypadding + self.weather_size[1])
|
||||||
|
|
|
@ -47,49 +47,3 @@ colors = {
|
||||||
"fg" : "black",
|
"fg" : "black",
|
||||||
"bg" : "white"
|
"bg" : "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
supported_img_formats = [
|
|
||||||
"BMP",
|
|
||||||
"DIB",
|
|
||||||
"EPS",
|
|
||||||
"GIF",
|
|
||||||
"ICNS",
|
|
||||||
"ICO",
|
|
||||||
"IM",
|
|
||||||
"JPG",
|
|
||||||
"JPEG",
|
|
||||||
"J2K",
|
|
||||||
"J2P",
|
|
||||||
"JPX",
|
|
||||||
"MSP",
|
|
||||||
"PCX",
|
|
||||||
"PNG",
|
|
||||||
"PPM",
|
|
||||||
"SGI",
|
|
||||||
"SPI",
|
|
||||||
"TGA",
|
|
||||||
"TIFF",
|
|
||||||
"WEBP",
|
|
||||||
"XBM",
|
|
||||||
"BLP",
|
|
||||||
"CUR",
|
|
||||||
"DCX",
|
|
||||||
"DDS",
|
|
||||||
"FLI",
|
|
||||||
"FLC",
|
|
||||||
"FPX",
|
|
||||||
"FTEX",
|
|
||||||
"GBR",
|
|
||||||
"GD",
|
|
||||||
"IMT",
|
|
||||||
"IPTC",
|
|
||||||
"NAA",
|
|
||||||
"MCIDAS",
|
|
||||||
"MIC",
|
|
||||||
"MPO",
|
|
||||||
"PCD",
|
|
||||||
"PIXAR",
|
|
||||||
"PSD",
|
|
||||||
"WAL",
|
|
||||||
"XPM",
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
from DesignEntity import DesignEntity
|
from DesignEntity import DesignEntity
|
||||||
from PIL import ImageDraw, ImageOps
|
from PIL import ImageDraw, ImageOps
|
||||||
|
|
||||||
|
|
||||||
class BoxDesign (DesignEntity):
|
class BoxDesign (DesignEntity):
|
||||||
"""Redefinition of ImageDraw.Draw.Rectangle"""
|
"""Redefinition of ImageDraw.Draw.Rectangle"""
|
||||||
|
|
||||||
def __init__(self, size, fill=None, outline=None, width=0):
|
def __init__(self, size, fill=None, outline=None, width=0):
|
||||||
super(BoxDesign, self).__init__((size[0]+1, size[1]+1), mask=True)
|
super(BoxDesign, self).__init__((size[0]+1, size[1]+1), mask=True)
|
||||||
self.size = size
|
self.size = size
|
||||||
|
@ -19,5 +17,4 @@ class BoxDesign (DesignEntity):
|
||||||
self.corners = [topleft, bottomright]
|
self.corners = [topleft, bottomright]
|
||||||
|
|
||||||
def __finish_image__ (self):
|
def __finish_image__ (self):
|
||||||
ImageDraw.Draw(self.__image__).rectangle(
|
ImageDraw.Draw(self.__image__).rectangle(self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
||||||
self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
|
|
@ -1,6 +1,5 @@
|
||||||
class CalendarEvent (object):
|
class CalendarEvent (object):
|
||||||
"""Defines a calendar event, independent of any implementation"""
|
"""Defines a calendar event, independent of any implementation"""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
self.begin_datetime = None
|
self.begin_datetime = None
|
||||||
self.end_datetime = None
|
self.end_datetime = None
|
||||||
|
@ -15,7 +14,6 @@ class CalendarEvent (object):
|
||||||
self.highlight = None
|
self.highlight = None
|
||||||
|
|
||||||
self.calendar_name = None
|
self.calendar_name = None
|
||||||
self.calendar_url = None
|
|
||||||
|
|
||||||
self.location = None
|
self.location = None
|
||||||
self.fetch_datetime = None
|
self.fetch_datetime = None
|
||||||
|
|
|
@ -3,15 +3,11 @@ from datetime import datetime, timezone, timedelta, date
|
||||||
from dateutil.rrule import rrulestr
|
from dateutil.rrule import rrulestr
|
||||||
from dateutil.parser import parse
|
from dateutil.parser import parse
|
||||||
import calendar
|
import calendar
|
||||||
from CalendarEvent import CalendarEvent
|
|
||||||
|
|
||||||
|
|
||||||
class CalendarInterface (DataSourceInterface):
|
class CalendarInterface (DataSourceInterface):
|
||||||
"""Interface for fetching and processing calendar event information."""
|
"""Interface for fetching and processing calendar event information."""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
self.events = []
|
self.events = []
|
||||||
self.excluded_urls = []
|
|
||||||
|
|
||||||
def reload (self):
|
def reload (self):
|
||||||
if self.is_available() == False:
|
if self.is_available() == False:
|
||||||
|
@ -19,9 +15,6 @@ class CalendarInterface (DataSourceInterface):
|
||||||
self.events = self.__get_events__()
|
self.events = self.__get_events__()
|
||||||
self.events = self.__sort_events__(self.events)
|
self.events = self.__sort_events__(self.events)
|
||||||
|
|
||||||
def exclude_calendars(self, urls=[]):
|
|
||||||
self.excluded_urls = urls
|
|
||||||
|
|
||||||
def __sort_events__ (self, events):
|
def __sort_events__ (self, events):
|
||||||
events.sort(key=lambda x : x.begin_datetime)
|
events.sort(key=lambda x : x.begin_datetime)
|
||||||
return events
|
return events
|
||||||
|
@ -29,8 +22,7 @@ class CalendarInterface (DataSourceInterface):
|
||||||
def __sort_event_types__ (self, events):
|
def __sort_event_types__ (self, events):
|
||||||
multiday = [ev for ev in events if ev.multiday]
|
multiday = [ev for ev in events if ev.multiday]
|
||||||
allday = [ev for ev in events if ev.allday and ev.multiday == False]
|
allday = [ev for ev in events if ev.allday and ev.multiday == False]
|
||||||
timed = [ev for ev in events if ev.allday ==
|
timed = [ev for ev in events if ev.allday == False and ev.multiday == False]
|
||||||
False and ev.multiday == False]
|
|
||||||
return multiday + allday + timed
|
return multiday + allday + timed
|
||||||
|
|
||||||
def __get_events__ (self):
|
def __get_events__ (self):
|
||||||
|
@ -49,11 +41,9 @@ class CalendarInterface (DataSourceInterface):
|
||||||
|
|
||||||
def get_day_events (self, day):
|
def get_day_events (self, day):
|
||||||
if type(day) is not type(date.today()):
|
if type(day) is not type(date.today()):
|
||||||
raise TypeError(
|
raise TypeError("get_day_events only takes date-objects as parameters, not \"%s\"" % str(type(day)))
|
||||||
"get_day_events only takes date-objects as parameters, not \"%s\"" % str(type(day)))
|
|
||||||
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
||||||
day_start = datetime(day.year, day.month, day.day,
|
day_start = datetime(day.year, day.month, day.day, 0, 0, 0, 0, local_tzinfo)
|
||||||
0, 0, 0, 0, local_tzinfo)
|
|
||||||
return self.__get_events_in_range__(day_start, timedelta(1))
|
return self.__get_events_in_range__(day_start, timedelta(1))
|
||||||
|
|
||||||
def get_month_events (self, month = -1, year = -1):
|
def get_month_events (self, month = -1, year = -1):
|
||||||
|
@ -64,8 +54,7 @@ class CalendarInterface (DataSourceInterface):
|
||||||
|
|
||||||
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
||||||
month_start = datetime(year, month, 1, 0, 0, 0, 0, local_tzinfo)
|
month_start = datetime(year, month, 1, 0, 0, 0, 0, local_tzinfo)
|
||||||
month_days = calendar.monthrange(
|
month_days = calendar.monthrange(month_start.year, month_start.month)[1]
|
||||||
month_start.year, month_start.month)[1]
|
|
||||||
return self.__get_events_in_range__(month_start, timedelta(month_days))
|
return self.__get_events_in_range__(month_start, timedelta(month_days))
|
||||||
|
|
||||||
def __get_events_in_range__ (self, start, duration):
|
def __get_events_in_range__ (self, start, duration):
|
||||||
|
@ -77,12 +66,7 @@ class CalendarInterface (DataSourceInterface):
|
||||||
|
|
||||||
events_in_range = []
|
events_in_range = []
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
# Is excluded?
|
event_occurrence = self.__get_if_event_in_range__(event, start, duration)
|
||||||
if event.calendar_url in self.excluded_urls:
|
|
||||||
continue
|
|
||||||
|
|
||||||
event_occurrence = self.__get_if_event_in_range__(
|
|
||||||
event, start, duration)
|
|
||||||
if event_occurrence:
|
if event_occurrence:
|
||||||
events_in_range.extend(event_occurrence)
|
events_in_range.extend(event_occurrence)
|
||||||
|
|
||||||
|
@ -118,65 +102,37 @@ class CalendarInterface (DataSourceInterface):
|
||||||
end = start + duration
|
end = start + duration
|
||||||
occurrences = []
|
occurrences = []
|
||||||
|
|
||||||
try:
|
|
||||||
r_string = ""
|
|
||||||
r_string=self.__add_timezoneawarness__(event.rrule)
|
r_string=self.__add_timezoneawarness__(event.rrule)
|
||||||
rule=rrulestr(r_string,dtstart=event.begin_datetime)
|
rule=rrulestr(r_string,dtstart=event.begin_datetime)
|
||||||
for occurrence in rule:
|
for occurrence in rule:
|
||||||
if occurrence - end > timedelta(0):
|
if occurrence - end > timedelta(0):
|
||||||
return occurrences
|
return occurrences
|
||||||
merged_event = self.__merge_event_data__(
|
merged_event = self.__merge_event_data__(event, start=occurrence)
|
||||||
event, start=occurrence)
|
|
||||||
if self.__is_onetime_in_range__(merged_event, start, duration):
|
if self.__is_onetime_in_range__(merged_event, start, duration):
|
||||||
occurrences.append(merged_event)
|
occurrences.append(merged_event)
|
||||||
return occurrences
|
return occurrences
|
||||||
except Exception as ex:
|
|
||||||
print("\"is_repeating_in_range\" failed while processing: dtstart="+str(event.begin_datetime) +
|
|
||||||
" dtstart.tzinfo="+str(event.begin_datetime.tzinfo)+" rrule="+r_string)
|
|
||||||
raise ex
|
|
||||||
|
|
||||||
def __merge_event_data__ (self, event, start = None):
|
def __merge_event_data__ (self, event, start = None):
|
||||||
merged_event = CalendarEvent()
|
|
||||||
|
|
||||||
merged_event.begin_datetime = event.begin_datetime
|
|
||||||
merged_event.end_datetime = event.end_datetime
|
|
||||||
merged_event.duration = event.duration
|
|
||||||
merged_event.allday = event.allday
|
|
||||||
merged_event.multiday = event.multiday
|
|
||||||
merged_event.rrule = event.rrule
|
|
||||||
|
|
||||||
merged_event.title = event.title
|
|
||||||
merged_event.description = event.description
|
|
||||||
merged_event.attendees = event.attendees
|
|
||||||
merged_event.highlight = event.highlight
|
|
||||||
|
|
||||||
merged_event.calendar_name = event.calendar_name
|
|
||||||
merged_event.calendar_url = event.calendar_url
|
|
||||||
|
|
||||||
merged_event.location = event.location
|
|
||||||
merged_event.fetch_datetime = event.fetch_datetime
|
|
||||||
|
|
||||||
if start is not None:
|
if start is not None:
|
||||||
merged_event.begin_datetime = start
|
event.begin_datetime = start
|
||||||
merged_event.end_datetime = start + event.duration
|
event.end_datetime = start + event.duration
|
||||||
|
|
||||||
return merged_event
|
return event
|
||||||
|
|
||||||
def __add_timezoneawarness__ (self, rrule):
|
def __add_timezoneawarness__ (self, rrule):
|
||||||
"""UNTIL must be specified in UTC when DTSTART is timezone-aware (which it is)"""
|
|
||||||
if "UNTIL" not in rrule:
|
if "UNTIL" not in rrule:
|
||||||
return rrule
|
return rrule
|
||||||
|
|
||||||
timezone_str = "T000000Z"
|
timezone_str = "T000000Z"
|
||||||
until_template = "UNTIL=YYYYMMDD"
|
until_example = "UNTIL=YYYYMMDD"
|
||||||
|
|
||||||
until_index = rrule.index("UNTIL")
|
until_index = rrule.index("UNTIL")
|
||||||
|
|
||||||
tz_index = until_index + len(until_template)
|
tz_index = until_index + len(until_example)
|
||||||
if until_index < 0 or (tz_index < len(rrule) and rrule[tz_index] is "T"):
|
if tz_index < 0 or tz_index >= len(rrule):
|
||||||
|
return rrule
|
||||||
|
|
||||||
|
if rrule[tz_index] is "T":
|
||||||
return rrule
|
return rrule
|
||||||
|
|
||||||
if tz_index == len(rrule):
|
|
||||||
return rrule + timezone_str
|
|
||||||
else:
|
|
||||||
return rrule[:tz_index] + timezone_str + rrule[tz_index:]
|
return rrule[:tz_index] + timezone_str + rrule[tz_index:]
|
|
@ -1,6 +1,5 @@
|
||||||
from DataSourceInterface import DataSourceInterface
|
from DataSourceInterface import DataSourceInterface
|
||||||
|
|
||||||
|
|
||||||
class CryptoInterface(DataSourceInterface):
|
class CryptoInterface(DataSourceInterface):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.crypto_coins = []
|
self.crypto_coins = []
|
||||||
|
|
|
@ -6,7 +6,6 @@ from settings import crypto_coins
|
||||||
|
|
||||||
xpadding = 5
|
xpadding = 5
|
||||||
|
|
||||||
|
|
||||||
class CryptoListDesign (DesignEntity):
|
class CryptoListDesign (DesignEntity):
|
||||||
def __init__ (self, size, crypto, text_size = defaultfontsize):
|
def __init__ (self, size, crypto, text_size = defaultfontsize):
|
||||||
super(CryptoListDesign, self).__init__(size)
|
super(CryptoListDesign, self).__init__(size)
|
||||||
|
@ -19,8 +18,7 @@ class CryptoListDesign (DesignEntity):
|
||||||
if len(self.matrix) > 0:
|
if len(self.matrix) > 0:
|
||||||
col_spacing = (self.size[0] / len(self.matrix[0])) * 0.5
|
col_spacing = (self.size[0] / len(self.matrix[0])) * 0.5
|
||||||
|
|
||||||
table_design = TableDesign(self.size, matrix=self.matrix, col_spacing=col_spacing,
|
table_design = TableDesign(self.size, matrix=self.matrix, col_spacing=col_spacing, fontsize = self.text_size, mask=False, truncate_rows=True)
|
||||||
fontsize=self.text_size, mask=False, truncate_rows=True)
|
|
||||||
table_design.pos = (xpadding, 0)
|
table_design.pos = (xpadding, 0)
|
||||||
self.draw_design(table_design)
|
self.draw_design(table_design)
|
||||||
|
|
||||||
|
@ -28,8 +26,7 @@ class CryptoListDesign (DesignEntity):
|
||||||
matrix = []
|
matrix = []
|
||||||
coins = self.crypto.get_coins()
|
coins = self.crypto.get_coins()
|
||||||
for coin in coins:
|
for coin in coins:
|
||||||
row = [coin.symbol.upper(), coin.name, coin.currency + " " +
|
row = [ coin.symbol.upper(), coin.name, coin.currency + " " + str(coin.price), "% " + str(coin.day_change) ]
|
||||||
str(coin.price), "% " + str(coin.day_change)]
|
|
||||||
matrix.append(row)
|
matrix.append(row)
|
||||||
return matrix
|
return matrix
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class DataSourceInterface (object):
|
class DataSourceInterface (object):
|
||||||
"""Interface for child interfaces that fetch data."""
|
"""Interface for child interfaces that fetch data."""
|
||||||
|
|
||||||
def is_available (self):
|
def is_available (self):
|
||||||
raise NotImplementedError("Functions needs to be implemented")
|
raise NotImplementedError("Functions needs to be implemented")
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,8 @@ from TextDesign import TextDesign
|
||||||
|
|
||||||
header_height = 0.2
|
header_height = 0.2
|
||||||
|
|
||||||
|
|
||||||
class DayBoxDesign (DesignEntity):
|
class DayBoxDesign (DesignEntity):
|
||||||
"""Represents a day with its events in a box."""
|
"""Represents a day with its events in a box."""
|
||||||
|
|
||||||
def __init__(self, size, date):
|
def __init__(self, size, date):
|
||||||
super(DayBoxDesign, self).__init__(size)
|
super(DayBoxDesign, self).__init__(size)
|
||||||
self.date = date
|
self.date = date
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
from datetime import date, datetime, timedelta, timezone
|
|
||||||
from settings import line_thickness, general_settings
|
|
||||||
from DayHeaderDesign import DayHeaderDesign
|
|
||||||
from HourListDesign import HourListDesign
|
|
||||||
from DayRowDesign import DayRowDesign
|
|
||||||
from PanelDesign import PanelDesign
|
|
||||||
from Assets import colors
|
|
||||||
from PIL import ImageDraw
|
|
||||||
|
|
||||||
HEADER_SIZE = (1, 0.2)
|
|
||||||
HOURLIST_HEIGHT = 0.3
|
|
||||||
HOURLIST_SIZE = (1, HOURLIST_HEIGHT)
|
|
||||||
DAYLIST_YPOS = HEADER_SIZE[1] + HOURLIST_SIZE[1]
|
|
||||||
DAYLIST_HEIGHT = 1 - HEADER_SIZE[1] - HOURLIST_SIZE[1]
|
|
||||||
DAYLIST_SIZE = (1, DAYLIST_HEIGHT)
|
|
||||||
HOURS_COUNT = 6
|
|
||||||
DAYROW_MIN_FORMAT = 40 / 384
|
|
||||||
DAYROW_MAX_FORMAT = 60 / 384
|
|
||||||
PANEL_LINE_THICKNESS = line_thickness
|
|
||||||
|
|
||||||
|
|
||||||
class DayFocusListPanel (PanelDesign):
|
|
||||||
"""Shows Day-View for today and a short Day-List for
|
|
||||||
the upcoming days."""
|
|
||||||
|
|
||||||
def __init__(self, size):
|
|
||||||
super(DayFocusListPanel, self).__init__(size)
|
|
||||||
self.hours_count = HOURS_COUNT
|
|
||||||
self.__init_modules__()
|
|
||||||
|
|
||||||
def __abs_co__(self, coordinates):
|
|
||||||
return (int(coordinates[0] * self.size[0]), int(coordinates[1] * self.size[1]))
|
|
||||||
|
|
||||||
def __init_modules__(self):
|
|
||||||
self.__init_header__()
|
|
||||||
self.__init_hourlist__()
|
|
||||||
self.__init_daylist__()
|
|
||||||
|
|
||||||
def __init_header__(self):
|
|
||||||
self.__header__ = DayHeaderDesign(
|
|
||||||
self.__abs_co__(HEADER_SIZE), date.today())
|
|
||||||
self.__header__.pos = (0, 0)
|
|
||||||
|
|
||||||
def __init_hourlist__(self):
|
|
||||||
start, end = self.__get_current_hour_range__()
|
|
||||||
size = self.__abs_co__(HOURLIST_SIZE)
|
|
||||||
|
|
||||||
self.__hourlist__ = HourListDesign(size, start, end)
|
|
||||||
self.__hourlist__.pos = (0, self.__header__.size[1])
|
|
||||||
|
|
||||||
def __init_daylist__(self):
|
|
||||||
self.__daylist_rows__ = []
|
|
||||||
self.__calc_dayrow_size__()
|
|
||||||
self.__create_day_rows__()
|
|
||||||
|
|
||||||
def __calc_dayrow_size__(self):
|
|
||||||
max_area_height = DAYLIST_HEIGHT * self.size[1]
|
|
||||||
max_row_number = max_area_height / (DAYROW_MIN_FORMAT * self.size[0])
|
|
||||||
min_row_number = max_area_height / (DAYROW_MAX_FORMAT * self.size[0])
|
|
||||||
average_row_number = (max_row_number + min_row_number) / 2
|
|
||||||
self.dayrow_count = round(average_row_number)
|
|
||||||
row_height = max_area_height / self.dayrow_count
|
|
||||||
self.dayrow_size = (1, row_height / self.size[1])
|
|
||||||
|
|
||||||
def __create_day_rows__(self):
|
|
||||||
following_days = self.__get_following_days__()
|
|
||||||
for i, date in enumerate(following_days):
|
|
||||||
row = DayRowDesign(self.__abs_co__(self.dayrow_size), date)
|
|
||||||
row.pos = self.__get_day_row_pos__(i)
|
|
||||||
self.__daylist_rows__.append(row)
|
|
||||||
|
|
||||||
def __get_following_days__(self):
|
|
||||||
following_days = []
|
|
||||||
for i in range(self.dayrow_count):
|
|
||||||
following_days.append(date.today() + timedelta(days=i + 1))
|
|
||||||
return following_days
|
|
||||||
|
|
||||||
def __get_day_row_pos__(self, i):
|
|
||||||
ypos = self.size[1] * DAYLIST_YPOS
|
|
||||||
down_shift = i * self.dayrow_size[1] * self.size[1]
|
|
||||||
return (0, int(ypos + down_shift))
|
|
||||||
|
|
||||||
def __finish_panel__(self):
|
|
||||||
self.draw_design(self.__header__)
|
|
||||||
self.draw_design(self.__hourlist__)
|
|
||||||
|
|
||||||
for row in self.__daylist_rows__:
|
|
||||||
self.draw_design(row)
|
|
||||||
self.__draw_daylist_lines__()
|
|
||||||
|
|
||||||
def __draw_daylist_lines__(self):
|
|
||||||
positions = []
|
|
||||||
for i in range(len(self.__daylist_rows__)):
|
|
||||||
positions.append(self.__get_day_row_pos__(i)[1])
|
|
||||||
for ypos in positions:
|
|
||||||
line_start = (0, ypos)
|
|
||||||
line_end = (self.size[0], ypos)
|
|
||||||
ImageDraw.Draw(self.__image__).line(
|
|
||||||
[line_start, line_end], fill=colors["fg"], width=PANEL_LINE_THICKNESS)
|
|
||||||
|
|
||||||
def __get_current_hour_range__(self):
|
|
||||||
start_hour = datetime.now().hour
|
|
||||||
additional_hours = self.hours_count - 1
|
|
||||||
|
|
||||||
if start_hour + additional_hours > 23:
|
|
||||||
start_hour = 23 - additional_hours
|
|
||||||
|
|
||||||
return start_hour, start_hour + additional_hours
|
|
||||||
|
|
||||||
def add_weather(self, weather):
|
|
||||||
self.__header__.add_weather(weather)
|
|
||||||
|
|
||||||
def add_calendar(self, calendar):
|
|
||||||
allday_ev, timed_ev = self.__split_events__(
|
|
||||||
calendar.get_today_events())
|
|
||||||
self.__header__.add_events(allday_ev)
|
|
||||||
self.__hourlist__.add_events(timed_ev)
|
|
||||||
|
|
||||||
self.__add_calendar_daylist__(calendar)
|
|
||||||
|
|
||||||
def __split_events__(self, events):
|
|
||||||
allday_ev = []
|
|
||||||
timed_ev = []
|
|
||||||
|
|
||||||
for event in events:
|
|
||||||
if event.allday:
|
|
||||||
allday_ev.append(event)
|
|
||||||
elif event.multiday:
|
|
||||||
if self.__is_today__(event.begin_datetime):
|
|
||||||
timed_ev.append(event)
|
|
||||||
elif self.__is_today__(event.end_datetime):
|
|
||||||
timed_ev.append(event)
|
|
||||||
else:
|
|
||||||
allday_ev.append(event)
|
|
||||||
else:
|
|
||||||
timed_ev.append(event)
|
|
||||||
return allday_ev, timed_ev
|
|
||||||
|
|
||||||
def __is_today__(self, dt):
|
|
||||||
today = date.today()
|
|
||||||
return dt.day == today.day and \
|
|
||||||
dt.month == today.month and \
|
|
||||||
dt.year == today.year
|
|
||||||
|
|
||||||
def __add_calendar_daylist__(self, calendar):
|
|
||||||
calendar.exclude_calendars(general_settings["extra-excluded-urls"])
|
|
||||||
|
|
||||||
for row in self.__daylist_rows__:
|
|
||||||
row.add_calendar(calendar)
|
|
||||||
|
|
||||||
calendar.exclude_calendars()
|
|
|
@ -27,10 +27,8 @@ numberbox_font_color = colors["bg"]
|
||||||
numberbox_background_color = colors["hl"]
|
numberbox_background_color = colors["hl"]
|
||||||
weekday_font = fonts["bold"]
|
weekday_font = fonts["bold"]
|
||||||
|
|
||||||
|
|
||||||
class DayHeaderDesign (DesignEntity):
|
class DayHeaderDesign (DesignEntity):
|
||||||
"""Detailed and big view of a given date."""
|
"""Detailed and big view of a given date."""
|
||||||
|
|
||||||
def __init__ (self, size, date):
|
def __init__ (self, size, date):
|
||||||
super(DayHeaderDesign, self).__init__(size)
|
super(DayHeaderDesign, self).__init__(size)
|
||||||
self.weather_column_width = 0
|
self.weather_column_width = 0
|
||||||
|
@ -40,11 +38,9 @@ class DayHeaderDesign (DesignEntity):
|
||||||
if general_settings["weather-info"] == False:
|
if general_settings["weather-info"] == False:
|
||||||
return
|
return
|
||||||
|
|
||||||
forecast = weather.get_forecast_in_days(
|
forecast = weather.get_forecast_in_days(self.date.day - date.today().day)
|
||||||
self.date.day - date.today().day)
|
|
||||||
self.weather_column_width = weathercolumn_y_size[0] * self.size[1]
|
self.weather_column_width = weathercolumn_y_size[0] * self.size[1]
|
||||||
size = (self.weather_column_width,
|
size = (self.weather_column_width, weathercolumn_y_size[1] * self.size[1])
|
||||||
weathercolumn_y_size[1] * self.size[1])
|
|
||||||
pos = (self.size[0] - size[0], 0)
|
pos = (self.size[0] - size[0], 0)
|
||||||
|
|
||||||
design = WeatherColumnDesign(size, forecast)
|
design = WeatherColumnDesign(size, forecast)
|
||||||
|
@ -54,10 +50,8 @@ class DayHeaderDesign (DesignEntity):
|
||||||
def add_calendar (self, calendar):
|
def add_calendar (self, calendar):
|
||||||
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
local_tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
|
||||||
now = datetime.now(local_tzinfo)
|
now = datetime.now(local_tzinfo)
|
||||||
time_until_tomorrow = (datetime(
|
time_until_tomorrow = (datetime(now.year, now.month, now.day, 0, 0, 0, 0, local_tzinfo) + timedelta(1)) - now
|
||||||
now.year, now.month, now.day, 0, 0, 0, 0, local_tzinfo) + timedelta(1)) - now
|
self.__draw_event_list__(calendar.get_upcoming_events(time_until_tomorrow, now))
|
||||||
self.__draw_event_list__(
|
|
||||||
calendar.get_upcoming_events(time_until_tomorrow, now))
|
|
||||||
|
|
||||||
def add_events (self, events):
|
def add_events (self, events):
|
||||||
self.__draw_event_list__(events)
|
self.__draw_event_list__(events)
|
||||||
|
@ -79,15 +73,12 @@ class DayHeaderDesign (DesignEntity):
|
||||||
xpadding = eventlist_xpadding * self.size[0]
|
xpadding = eventlist_xpadding * self.size[0]
|
||||||
ypadding = eventlist_ypadding * self.size[1]
|
ypadding = eventlist_ypadding * self.size[1]
|
||||||
monthbox_height = (monthbox_ypadding + month_height) * self.size[1]
|
monthbox_height = (monthbox_ypadding + month_height) * self.size[1]
|
||||||
pos = (box_xpos + box_height + xpadding,
|
pos = (box_xpos + box_height + xpadding, box_ypos + monthbox_height + ypadding)
|
||||||
box_ypos + monthbox_height + ypadding)
|
size = (self.size[0] - pos[0] - self.weather_column_width, self.size[1] - pos[1] - box_ypos)
|
||||||
size = (self.size[0] - pos[0] - self.weather_column_width,
|
|
||||||
self.size[1] - pos[1] - box_ypos)
|
|
||||||
fontsize = eventlist_static_fontsize
|
fontsize = eventlist_static_fontsize
|
||||||
|
|
||||||
rel_dates = [self.date for _ in range(len(events))]
|
rel_dates = [self.date for _ in range(len(events))]
|
||||||
event_list = SingelDayEventListDesign(
|
event_list = SingelDayEventListDesign(size, events, fontsize, event_prefix_rel_dates = rel_dates)
|
||||||
size, events, fontsize, event_prefix_rel_dates=rel_dates)
|
|
||||||
event_list.pos = pos
|
event_list.pos = pos
|
||||||
self.draw_design(event_list)
|
self.draw_design(event_list)
|
||||||
|
|
||||||
|
@ -127,8 +118,7 @@ class DayHeaderDesign (DesignEntity):
|
||||||
pos = (box_ypos, box_ypos + ypadding)
|
pos = (box_ypos, box_ypos + ypadding)
|
||||||
|
|
||||||
day_text = self.__get_day_text__()
|
day_text = self.__get_day_text__()
|
||||||
number = TextDesign(size, text=day_text, background_color=numberbox_background_color,
|
number = TextDesign(size, text=day_text, background_color=numberbox_background_color, color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment="center")
|
||||||
color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment="center")
|
|
||||||
number.pos = pos
|
number.pos = pos
|
||||||
number.mask = False
|
number.mask = False
|
||||||
self.draw_design(number)
|
self.draw_design(number)
|
||||||
|
@ -141,8 +131,7 @@ class DayHeaderDesign (DesignEntity):
|
||||||
pos = (box_ypos, box_ypos)
|
pos = (box_ypos, box_ypos)
|
||||||
|
|
||||||
week_day_name = self.date.strftime("%A")
|
week_day_name = self.date.strftime("%A")
|
||||||
week_day = TextDesign(size, text=week_day_name, background_color=numberbox_background_color, color=numberbox_font_color,
|
week_day = TextDesign(size, text=week_day_name, background_color=numberbox_background_color, color=numberbox_font_color, fontsize=font_size, horizontalalignment="center", verticalalignment = "center", font=weekday_font)
|
||||||
fontsize=font_size, horizontalalignment="center", verticalalignment="center", font=weekday_font)
|
|
||||||
week_day.pos = pos
|
week_day.pos = pos
|
||||||
week_day.mask = False
|
week_day.mask = False
|
||||||
self.draw_design(week_day)
|
self.draw_design(week_day)
|
||||||
|
|
|
@ -24,11 +24,9 @@ dayrow_max_format = 70 / 384
|
||||||
rss_y_padding = 5
|
rss_y_padding = 5
|
||||||
crypto_y_padding = 5
|
crypto_y_padding = 5
|
||||||
|
|
||||||
|
|
||||||
class DayListPanel (PanelDesign):
|
class DayListPanel (PanelDesign):
|
||||||
"""Overview that focuses on the current day and
|
"""Overview that focuses on the current day and
|
||||||
lists following days in a list below."""
|
lists following days in a list below."""
|
||||||
|
|
||||||
def __init__ (self, size):
|
def __init__ (self, size):
|
||||||
super(DayListPanel, self).__init__(size)
|
super(DayListPanel, self).__init__(size)
|
||||||
self.__day_rows__ = []
|
self.__day_rows__ = []
|
||||||
|
@ -62,8 +60,7 @@ class DayListPanel (PanelDesign):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __draw_rss_infoarea__ (self, rss):
|
def __draw_rss_infoarea__ (self, rss):
|
||||||
height = infoarea_replacedrowscount * \
|
height = infoarea_replacedrowscount * self.dayrow_size[1] * self.size[1] - rss_y_padding
|
||||||
self.dayrow_size[1] * self.size[1] - rss_y_padding
|
|
||||||
ypos = self.size[1] - height
|
ypos = self.size[1] - height
|
||||||
size = (self.size[0], height)
|
size = (self.size[0], height)
|
||||||
pos = (0, ypos)
|
pos = (0, ypos)
|
||||||
|
@ -73,8 +70,7 @@ class DayListPanel (PanelDesign):
|
||||||
self.draw_design(design)
|
self.draw_design(design)
|
||||||
|
|
||||||
def __draw_crypto_infoarea__ (self, crypto):
|
def __draw_crypto_infoarea__ (self, crypto):
|
||||||
height = infoarea_replacedrowscount * \
|
height = infoarea_replacedrowscount * self.dayrow_size[1] * self.size[1] - crypto_y_padding
|
||||||
self.dayrow_size[1] * self.size[1] - crypto_y_padding
|
|
||||||
ypos = self.size[1] - height
|
ypos = self.size[1] - height
|
||||||
size = (self.size[0], height)
|
size = (self.size[0], height)
|
||||||
pos = (0, ypos)
|
pos = (0, ypos)
|
||||||
|
@ -84,8 +80,7 @@ class DayListPanel (PanelDesign):
|
||||||
design.pos = (pos[0], pos[1] + (height - acutal_height))
|
design.pos = (pos[0], pos[1] + (height - acutal_height))
|
||||||
self.draw_design(design)
|
self.draw_design(design)
|
||||||
|
|
||||||
replaced_rows = ceil(
|
replaced_rows = ceil(acutal_height / (self.dayrow_size[1] * self.size[1]))
|
||||||
acutal_height / (self.dayrow_size[1] * self.size[1]))
|
|
||||||
self.__day_rows__ = self.__day_rows__[:-replaced_rows]
|
self.__day_rows__ = self.__day_rows__[:-replaced_rows]
|
||||||
|
|
||||||
def __draw_day_rows__ (self):
|
def __draw_day_rows__ (self):
|
||||||
|
@ -116,8 +111,7 @@ class DayListPanel (PanelDesign):
|
||||||
return following_days
|
return following_days
|
||||||
|
|
||||||
def __draw_today_header__ (self):
|
def __draw_today_header__ (self):
|
||||||
header = DayHeaderDesign(self.__abs_co__(
|
header = DayHeaderDesign(self.__abs_co__(todayheader_size), date.today())
|
||||||
todayheader_size), date.today())
|
|
||||||
header.pos = self.__abs_co__(todayheader_pos)
|
header.pos = self.__abs_co__(todayheader_pos)
|
||||||
self.__day_rows__.append(header)
|
self.__day_rows__.append(header)
|
||||||
|
|
||||||
|
@ -128,8 +122,7 @@ class DayListPanel (PanelDesign):
|
||||||
for ypos in positions:
|
for ypos in positions:
|
||||||
line_start = (0, ypos)
|
line_start = (0, ypos)
|
||||||
line_end = (self.size[0], ypos)
|
line_end = (self.size[0], ypos)
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line([line_start, line_end], fill=colors["fg"], width=lines_thickness)
|
||||||
[line_start, line_end], fill=colors["fg"], width=lines_thickness)
|
|
||||||
|
|
||||||
def __finish_panel__(self):
|
def __finish_panel__(self):
|
||||||
for design in self.__day_rows__:
|
for design in self.__day_rows__:
|
||||||
|
|
|
@ -20,10 +20,8 @@ eventlist_y_fontsize = 0.2
|
||||||
|
|
||||||
font = fonts["light"]
|
font = fonts["light"]
|
||||||
|
|
||||||
|
|
||||||
class DayRowDesign (DesignEntity):
|
class DayRowDesign (DesignEntity):
|
||||||
"""Detailed view of a given date."""
|
"""Detailed view of a given date."""
|
||||||
|
|
||||||
def __init__ (self, size, date):
|
def __init__ (self, size, date):
|
||||||
super(DayRowDesign, self).__init__(size)
|
super(DayRowDesign, self).__init__(size)
|
||||||
self.__init_image__()
|
self.__init_image__()
|
||||||
|
@ -52,14 +50,12 @@ class DayRowDesign (DesignEntity):
|
||||||
|
|
||||||
events = calendar.get_day_events(self.date)
|
events = calendar.get_day_events(self.date)
|
||||||
rel_dates = [self.date for _ in range(len(events))]
|
rel_dates = [self.date for _ in range(len(events))]
|
||||||
event_list = SingelDayEventListDesign(
|
event_list = SingelDayEventListDesign(size, events, fontsize, event_prefix_rel_dates = rel_dates)
|
||||||
size, events, fontsize, event_prefix_rel_dates=rel_dates)
|
|
||||||
event_list.pos = pos
|
event_list.pos = pos
|
||||||
self.draw_design(event_list)
|
self.draw_design(event_list)
|
||||||
|
|
||||||
def __draw_forecast__ (self, weather):
|
def __draw_forecast__ (self, weather):
|
||||||
forecast = weather.get_forecast_in_days(
|
forecast = weather.get_forecast_in_days(self.date.day - datetime.today().day)
|
||||||
self.date.day - datetime.today().day)
|
|
||||||
|
|
||||||
if forecast is None:
|
if forecast is None:
|
||||||
return
|
return
|
||||||
|
@ -78,16 +74,14 @@ class DayRowDesign (DesignEntity):
|
||||||
|
|
||||||
def __draw_weekday__ (self):
|
def __draw_weekday__ (self):
|
||||||
font_size = int(weekday_fontsize * self.size[1])
|
font_size = int(weekday_fontsize * self.size[1])
|
||||||
size = (weekday_y_size[0] * self.size[1],
|
size = (weekday_y_size[0] * self.size[1], weekday_y_size[1] * self.size[1])
|
||||||
weekday_y_size[1] * self.size[1])
|
|
||||||
ypos = weekday_ypos * self.size[1]
|
ypos = weekday_ypos * self.size[1]
|
||||||
pos = (0, ypos)
|
pos = (0, ypos)
|
||||||
|
|
||||||
color = self.__get_day_color__()
|
color = self.__get_day_color__()
|
||||||
week_day_name = self.date.strftime("%a")
|
week_day_name = self.date.strftime("%a")
|
||||||
|
|
||||||
week_day = TextDesign(size, text=week_day_name, font=font, color=color,
|
week_day = TextDesign(size, text=week_day_name, font=font, color=color, fontsize=font_size, horizontalalignment="center", verticalalignment="top")
|
||||||
fontsize=font_size, horizontalalignment="center", verticalalignment="top")
|
|
||||||
week_day.pos = pos
|
week_day.pos = pos
|
||||||
week_day.mask = False
|
week_day.mask = False
|
||||||
self.draw_design(week_day)
|
self.draw_design(week_day)
|
||||||
|
@ -95,15 +89,13 @@ class DayRowDesign (DesignEntity):
|
||||||
def __draw_day_number__ (self):
|
def __draw_day_number__ (self):
|
||||||
font_size = int(daynumber_fontsize * self.size[1])
|
font_size = int(daynumber_fontsize * self.size[1])
|
||||||
ypadding = daynumber_ypadding * self.size[1]
|
ypadding = daynumber_ypadding * self.size[1]
|
||||||
size = (daynumber_y_size[0] * self.size[1],
|
size = (daynumber_y_size[0] * self.size[1], daynumber_y_size[1] * self.size[1])
|
||||||
daynumber_y_size[1] * self.size[1])
|
|
||||||
pos = (0, ypadding)
|
pos = (0, ypadding)
|
||||||
|
|
||||||
day_text = self.__get_day_text__()
|
day_text = self.__get_day_text__()
|
||||||
color = self.__get_day_color__()
|
color = self.__get_day_color__()
|
||||||
|
|
||||||
number = TextDesign(size, text=day_text, font=font, color=color,
|
number = TextDesign(size, text=day_text, font=font, color=color, fontsize=font_size, horizontalalignment="center", verticalalignment="bottom")
|
||||||
fontsize=font_size, horizontalalignment="center", verticalalignment="bottom")
|
|
||||||
number.pos = pos
|
number.pos = pos
|
||||||
self.draw_design(number)
|
self.draw_design(number)
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,9 @@ infoarea_replaced_hours = 4
|
||||||
infoarea_borderline_width = 1
|
infoarea_borderline_width = 1
|
||||||
infoarea_padding = 5
|
infoarea_padding = 5
|
||||||
|
|
||||||
|
|
||||||
class DayViewPanel (PanelDesign):
|
class DayViewPanel (PanelDesign):
|
||||||
"""Overview that focuses on the current day and
|
"""Overview that focuses on the current day and
|
||||||
shows a timeline split into hours."""
|
shows a timeline split into hours."""
|
||||||
|
|
||||||
def __init__ (self, size):
|
def __init__ (self, size):
|
||||||
super(DayViewPanel, self).__init__(size)
|
super(DayViewPanel, self).__init__(size)
|
||||||
self.shownhours_count = default_shownhours_count
|
self.shownhours_count = default_shownhours_count
|
||||||
|
@ -38,8 +36,7 @@ class DayViewPanel (PanelDesign):
|
||||||
self.__header__.add_weather(weather)
|
self.__header__.add_weather(weather)
|
||||||
|
|
||||||
def add_calendar (self, calendar):
|
def add_calendar (self, calendar):
|
||||||
allday_ev, timed_ev = self.__split_events__(
|
allday_ev, timed_ev = self.__split_events__(calendar.get_today_events())
|
||||||
calendar.get_today_events())
|
|
||||||
self.__header__.add_events(allday_ev)
|
self.__header__.add_events(allday_ev)
|
||||||
self.__hourlist__.add_events(timed_ev)
|
self.__hourlist__.add_events(timed_ev)
|
||||||
|
|
||||||
|
@ -63,12 +60,10 @@ class DayViewPanel (PanelDesign):
|
||||||
|
|
||||||
line_start = (0, ypos)
|
line_start = (0, ypos)
|
||||||
line_end = (self.size[0], ypos)
|
line_end = (self.size[0], ypos)
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["fg"], width=infoarea_borderline_width)
|
||||||
[line_start, line_end], fill=colors["fg"], width=infoarea_borderline_width)
|
|
||||||
|
|
||||||
def __draw_rss_feed__(self, rss):
|
def __draw_rss_feed__(self, rss):
|
||||||
height = infoarea_replaced_hours * \
|
height = infoarea_replaced_hours * self.__hourlist__.row_size[1] - infoarea_padding
|
||||||
self.__hourlist__.row_size[1] - infoarea_padding
|
|
||||||
size = (self.size[0], height)
|
size = (self.size[0], height)
|
||||||
pos = (0, self.size[1] - size[1])
|
pos = (0, self.size[1] - size[1])
|
||||||
|
|
||||||
|
@ -77,8 +72,7 @@ class DayViewPanel (PanelDesign):
|
||||||
self.draw_design(rss)
|
self.draw_design(rss)
|
||||||
|
|
||||||
def __draw_crypto_feed__(self, crypto):
|
def __draw_crypto_feed__(self, crypto):
|
||||||
height = infoarea_replaced_hours * \
|
height = infoarea_replaced_hours * self.__hourlist__.row_size[1] - infoarea_padding
|
||||||
self.__hourlist__.row_size[1] - infoarea_padding
|
|
||||||
size = (self.size[0], height)
|
size = (self.size[0], height)
|
||||||
pos = (0, self.size[1] - size[1])
|
pos = (0, self.size[1] - size[1])
|
||||||
|
|
||||||
|
@ -87,9 +81,9 @@ class DayViewPanel (PanelDesign):
|
||||||
crypto.pos = (pos[0], pos[1] + (height - acutal_height))
|
crypto.pos = (pos[0], pos[1] + (height - acutal_height))
|
||||||
self.draw_design(crypto)
|
self.draw_design(crypto)
|
||||||
|
|
||||||
|
|
||||||
def __draw_event_list__(self, calendar):
|
def __draw_event_list__(self, calendar):
|
||||||
height = infoarea_replaced_hours * \
|
height = infoarea_replaced_hours * self.__hourlist__.row_size[1] - infoarea_padding
|
||||||
self.__hourlist__.row_size[1] - infoarea_padding
|
|
||||||
size = (self.size[0], height)
|
size = (self.size[0], height)
|
||||||
pos = (0, self.size[1] - size[1])
|
pos = (0, self.size[1] - size[1])
|
||||||
|
|
||||||
|
@ -105,15 +99,13 @@ class DayViewPanel (PanelDesign):
|
||||||
self.draw_design(self.__hourlist__)
|
self.draw_design(self.__hourlist__)
|
||||||
|
|
||||||
def __init_header__ (self):
|
def __init_header__ (self):
|
||||||
self.__header__ = DayHeaderDesign(
|
self.__header__ = DayHeaderDesign(self.__abs_co__(header_size), date.today())
|
||||||
self.__abs_co__(header_size), date.today())
|
|
||||||
self.__header__.pos = (0, 0)
|
self.__header__.pos = (0, 0)
|
||||||
|
|
||||||
def __init_hourlist__ (self):
|
def __init_hourlist__ (self):
|
||||||
start, end = self.__get_current_hour_range__()
|
start, end = self.__get_current_hour_range__()
|
||||||
size = self.__abs_co__(hourlist_size)
|
size = self.__abs_co__(hourlist_size)
|
||||||
size = (size[0], size[1] * self.shownhours_count /
|
size = (size[0], size[1] * self.shownhours_count / default_shownhours_count)
|
||||||
default_shownhours_count)
|
|
||||||
|
|
||||||
self.__hourlist__ = HourListDesign(size, start, end)
|
self.__hourlist__ = HourListDesign(size, start, end)
|
||||||
self.__hourlist__.pos = (0, self.__header__.size[1])
|
self.__hourlist__.pos = (0, self.__header__.size[1])
|
||||||
|
|
|
@ -3,10 +3,8 @@ from Assets import weathericons
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
class DebugConsole (DebugInterface):
|
class DebugConsole (DebugInterface):
|
||||||
"""Defines concrete console export of debug objects"""
|
"""Defines concrete console export of debug objects"""
|
||||||
|
|
||||||
def print_event (self, event):
|
def print_event (self, event):
|
||||||
print("\nCalendarEvent:")
|
print("\nCalendarEvent:")
|
||||||
print("---------------------")
|
print("---------------------")
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class DebugInterface (object):
|
class DebugInterface (object):
|
||||||
"""Defines general interface for debugging operations"""
|
"""Defines general interface for debugging operations"""
|
||||||
|
|
||||||
def print_event (self, event):
|
def print_event (self, event):
|
||||||
raise NotImplementedError("Functions needs to be implemented")
|
raise NotImplementedError("Functions needs to be implemented")
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,11 @@ from Assets import colors
|
||||||
|
|
||||||
masking_threshold = 200
|
masking_threshold = 200
|
||||||
|
|
||||||
|
|
||||||
class DesignEntity (object):
|
class DesignEntity (object):
|
||||||
"""General entity that can be drawn on to a panel design or
|
"""General entity that can be drawn on to a panel design or
|
||||||
other design entities."""
|
other design entities."""
|
||||||
|
|
||||||
def __init__ (self, size, mask=False, invert_mask=False, color_key=False):
|
def __init__ (self, size, mask=False, invert_mask=False, color_key=False):
|
||||||
self.size = size
|
self.size = size
|
||||||
# Are dimensions >= 0?
|
|
||||||
if self.size[0] < 0:
|
|
||||||
self.size = (0, self.size[1])
|
|
||||||
if self.size[1] < 0:
|
|
||||||
self.size = (self.size[0], 0)
|
|
||||||
|
|
||||||
self.pos = (0, 0)
|
self.pos = (0, 0)
|
||||||
self.mask = mask
|
self.mask = mask
|
||||||
self.invert_mask = invert_mask
|
self.invert_mask = invert_mask
|
||||||
|
@ -25,7 +17,7 @@ class DesignEntity (object):
|
||||||
|
|
||||||
def __init_image__ (self, color = colors["bg"]):
|
def __init_image__ (self, color = colors["bg"]):
|
||||||
rounded_size = (int(self.size[0]),int(self.size[1]))
|
rounded_size = (int(self.size[0]),int(self.size[1]))
|
||||||
self.__image__ = Image.new('RGBA', rounded_size, color=color)
|
self.__image__ = Image.new('RGB', rounded_size, color=color)
|
||||||
|
|
||||||
def get_image (self):
|
def get_image (self):
|
||||||
if self.__finished_image__ is False:
|
if self.__finished_image__ is False:
|
||||||
|
@ -37,13 +29,11 @@ class DesignEntity (object):
|
||||||
rounded_pos = (int(pos[0]),int(pos[1]))
|
rounded_pos = (int(pos[0]),int(pos[1]))
|
||||||
img_mask = None
|
img_mask = None
|
||||||
if mask:
|
if mask:
|
||||||
img_mask = self.__get_mask__(
|
img_mask = self.__get_mask__(subimage, invert_mask=invert_mask, color_key=color_key)
|
||||||
subimage, invert_mask=invert_mask, color_key=color_key)
|
|
||||||
self.__image__.paste(subimage, rounded_pos, mask=img_mask)
|
self.__image__.paste(subimage, rounded_pos, mask=img_mask)
|
||||||
|
|
||||||
def draw_design (self, entity):
|
def draw_design (self, entity):
|
||||||
self.draw(entity.get_image(), entity.pos, entity.mask,
|
self.draw(entity.get_image(), entity.pos, entity.mask, entity.invert_mask, entity.color_key)
|
||||||
entity.invert_mask, entity.color_key)
|
|
||||||
|
|
||||||
def draw_image (self, path, pos):
|
def draw_image (self, path, pos):
|
||||||
self.draw(Image.open(path), pos)
|
self.draw(Image.open(path), pos)
|
||||||
|
@ -54,8 +44,7 @@ class DesignEntity (object):
|
||||||
def __get_mask__ (self, image, invert_mask, color_key):
|
def __get_mask__ (self, image, invert_mask, color_key):
|
||||||
mask = image.convert('L')
|
mask = image.convert('L')
|
||||||
if color_key:
|
if color_key:
|
||||||
mask = mask.point(lambda p: 0 if p <
|
mask = mask.point(lambda p : 0 if p < masking_threshold else 255, '1').convert('L')
|
||||||
masking_threshold else 255, '1').convert('L')
|
|
||||||
if invert_mask:
|
if invert_mask:
|
||||||
mask = ImageOps.invert(mask)
|
mask = ImageOps.invert(mask)
|
||||||
return ImageOps.invert(mask)
|
return ImageOps.invert(mask)
|
|
@ -4,7 +4,6 @@ from settings import language
|
||||||
'''Takes a collection of phrases and outputs the necessary text
|
'''Takes a collection of phrases and outputs the necessary text
|
||||||
according to the language and inserts parameters.'''
|
according to the language and inserts parameters.'''
|
||||||
|
|
||||||
|
|
||||||
def get_text(dictionary, *params):
|
def get_text(dictionary, *params):
|
||||||
text = dictionary[default_language]
|
text = dictionary[default_language]
|
||||||
if language in dictionary.keys():
|
if language in dictionary.keys():
|
||||||
|
@ -12,7 +11,6 @@ def get_text(dictionary, *params):
|
||||||
|
|
||||||
return __insert_params__(text, params)
|
return __insert_params__(text, params)
|
||||||
|
|
||||||
|
|
||||||
def __insert_params__(text, params):
|
def __insert_params__(text, params):
|
||||||
index = 0
|
index = 0
|
||||||
while '*%d' % index in text and index < len(params):
|
while '*%d' % index in text and index < len(params):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class DisplayAdapter (object):
|
class DisplayAdapter (object):
|
||||||
"""Interface for CalendarDesign output channels."""
|
"""Interface for CalendarDesign output channels."""
|
||||||
|
|
||||||
def __init__(self, width, height):
|
def __init__(self, width, height):
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
|
@ -13,14 +13,12 @@ from Assets import path
|
||||||
from LoopTimer import LoopTimer
|
from LoopTimer import LoopTimer
|
||||||
import locale
|
import locale
|
||||||
from DebugConsole import DebugConsole
|
from DebugConsole import DebugConsole
|
||||||
from settings import datetime_encoding, language, render_to_display, render_to_file, display_colours, location, api_key, owm_paid_subscription, choosen_design, ical_urls, highlighted_ical_urls, rss_feeds, update_interval, calibrate_hours, crypto_coins, max_loop_count, run_on_hour
|
from settings import datetime_encoding, language, render_to_display, render_to_file, display_colours, location, api_key, owm_paid_subscription, choosen_design, ical_urls, highlighted_ical_urls, rss_feeds, update_interval, calibrate_hours, crypto_coins
|
||||||
from MonthOvPanel import MonthOvPanel
|
from MonthOvPanel import MonthOvPanel
|
||||||
from DayListPanel import DayListPanel
|
from DayListPanel import DayListPanel
|
||||||
from DayViewPanel import DayViewPanel
|
from DayViewPanel import DayViewPanel
|
||||||
from DayFocusListPanel import DayFocusListPanel
|
|
||||||
from MonthViewPanel import MonthViewPanel
|
from MonthViewPanel import MonthViewPanel
|
||||||
from AgendaListPanel import AgendaListPanel
|
from AgendaListPanel import AgendaListPanel
|
||||||
from ImageFramePanel import ImageFramePanel
|
|
||||||
import OwmForecasts
|
import OwmForecasts
|
||||||
import IcalEvents
|
import IcalEvents
|
||||||
import RssParserPosts
|
import RssParserPosts
|
||||||
|
@ -28,10 +26,8 @@ import GeckoCrypto
|
||||||
|
|
||||||
all_locales = locale.locale_alias
|
all_locales = locale.locale_alias
|
||||||
if language.lower() not in all_locales.keys():
|
if language.lower() not in all_locales.keys():
|
||||||
raise Exception(
|
raise Exception("The locale for \"%s\" is currently not supported! If you need support, please open an issue on github." % language)
|
||||||
"The locale for \"%s\" is currently not supported! If you need support, please open an issue on github." % language)
|
locale.setlocale(locale.LC_ALL, "%s.%s" % (all_locales[language.lower()].split('.')[0], datetime_encoding))
|
||||||
locale.setlocale(locale.LC_ALL, "%s.%s" % (
|
|
||||||
all_locales[language.lower()].split('.')[0], datetime_encoding))
|
|
||||||
|
|
||||||
debug = DebugConsole()
|
debug = DebugConsole()
|
||||||
output_adapters = []
|
output_adapters = []
|
||||||
|
@ -55,21 +51,15 @@ available_panels = {
|
||||||
"day-list" : DayListPanel,
|
"day-list" : DayListPanel,
|
||||||
"month-overview" : MonthOvPanel,
|
"month-overview" : MonthOvPanel,
|
||||||
"day-view" : DayViewPanel,
|
"day-view" : DayViewPanel,
|
||||||
"day-focus-list": DayFocusListPanel,
|
|
||||||
"agenda-list" : AgendaListPanel,
|
"agenda-list" : AgendaListPanel,
|
||||||
"month-view": MonthViewPanel,
|
"month-view" : MonthViewPanel
|
||||||
"image-frame": ImageFramePanel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_timer = LoopTimer(
|
loop_timer = LoopTimer(update_interval, run_on_hour=True)
|
||||||
update_interval, run_on_hour=run_on_hour, max_loop_count=max_loop_count)
|
|
||||||
|
|
||||||
"""Main loop starts from here"""
|
"""Main loop starts from here"""
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
owm = OwmForecasts.OwmForecasts(
|
owm = OwmForecasts.OwmForecasts(location, api_key, paid_api=owm_paid_subscription)
|
||||||
location, api_key, paid_api=owm_paid_subscription)
|
|
||||||
events_cal = IcalEvents.IcalEvents(ical_urls, highlighted_ical_urls)
|
events_cal = IcalEvents.IcalEvents(ical_urls, highlighted_ical_urls)
|
||||||
rss = RssParserPosts.RssParserPosts(rss_feeds)
|
rss = RssParserPosts.RssParserPosts(rss_feeds)
|
||||||
crypto = GeckoCrypto.GeckoCrypto(crypto_coins)
|
crypto = GeckoCrypto.GeckoCrypto(crypto_coins)
|
||||||
|
@ -86,8 +76,7 @@ def main():
|
||||||
if choosen_design in available_panels.keys():
|
if choosen_design in available_panels.keys():
|
||||||
design = available_panels[choosen_design]((epd.width, epd.height))
|
design = available_panels[choosen_design]((epd.width, epd.height))
|
||||||
else:
|
else:
|
||||||
raise ImportError(
|
raise ImportError("choosen_design must be valid (" + choosen_design + ")")
|
||||||
"choosen_design must be valid (" + choosen_design + ")")
|
|
||||||
|
|
||||||
debug.print_line("Fetching weather information from open weather map")
|
debug.print_line("Fetching weather information from open weather map")
|
||||||
owm.reload()
|
owm.reload()
|
||||||
|
@ -109,27 +98,18 @@ def main():
|
||||||
for i, output in enumerate(output_adapters):
|
for i, output in enumerate(output_adapters):
|
||||||
try:
|
try:
|
||||||
output.render(design)
|
output.render(design)
|
||||||
debug.print_line(str(i + 1) + " of " +
|
debug.print_line(str(i + 1) + " of " + str(len(output_adapters)) + " rendered")
|
||||||
str(len(output_adapters)) + " rendered")
|
|
||||||
except BaseException as ex:
|
except BaseException as ex:
|
||||||
debug.print_err(ex, "Failed to render output " +
|
debug.print_err(ex, "Failed to render output " + str(i + 1) + " of " + str(len(output_adapters)))
|
||||||
str(i + 1) + " of " + str(len(output_adapters)))
|
|
||||||
|
|
||||||
debug.print_line("=> Finished rendering" + "\n")
|
debug.print_line("=> Finished rendering" + "\n")
|
||||||
|
|
||||||
loop_timer.end_loop()
|
loop_timer.end_loop()
|
||||||
|
|
||||||
if loop_timer.was_last_loop():
|
|
||||||
debug.print_line("Maximum loop count " +
|
|
||||||
str(loop_timer.loop_count) + " reached, exiting.")
|
|
||||||
return
|
|
||||||
|
|
||||||
sleep_time = loop_timer.time_until_next()
|
sleep_time = loop_timer.time_until_next()
|
||||||
debug.print_line("This loop took " +
|
|
||||||
str(loop_timer.get_last_duration()) + " to execute.")
|
debug.print_line("This loop took " + str(loop_timer.get_last_duration()) + " to execute.")
|
||||||
debug.print_line("Sleeping " + str(sleep_time) + " until next loop.")
|
debug.print_line("Sleeping " + str(sleep_time) + " until next loop.")
|
||||||
sleep(sleep_time.total_seconds())
|
sleep(sleep_time.total_seconds())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
from BoxDesign import BoxDesign
|
from BoxDesign import BoxDesign
|
||||||
from PIL import ImageDraw, ImageOps
|
from PIL import ImageDraw, ImageOps
|
||||||
|
|
||||||
|
|
||||||
class EllipseDesign (BoxDesign):
|
class EllipseDesign (BoxDesign):
|
||||||
"""Redefinition of ImageDraw.Draw.Rectangle"""
|
"""Redefinition of ImageDraw.Draw.Rectangle"""
|
||||||
|
|
||||||
def __init__(self, size, fill=None, outline=None, width=0):
|
def __init__(self, size, fill=None, outline=None, width=0):
|
||||||
super(EllipseDesign, self).__init__(
|
super(EllipseDesign, self).__init__(size, fill=fill, outline=outline, width=width)
|
||||||
size, fill=fill, outline=outline, width=width)
|
|
||||||
|
|
||||||
def __finish_image__ (self):
|
def __finish_image__ (self):
|
||||||
ImageDraw.Draw(self.__image__).ellipse(
|
ImageDraw.Draw(self.__image__).ellipse(self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
||||||
self.corners, fill=self.fill, outline=self.outline, width=self.width)
|
|
|
@ -2,7 +2,6 @@ from EpdAdapter import EpdAdapter, DISPLAY_REFRESH, DATA_START_TRANSMISSION_1
|
||||||
from settings import display_colours
|
from settings import display_colours
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
|
||||||
class Epd7in5Adapter (EpdAdapter):
|
class Epd7in5Adapter (EpdAdapter):
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
super(Epd7in5Adapter, self).__init__(384, 640)
|
super(Epd7in5Adapter, self).__init__(384, 640)
|
||||||
|
|
|
@ -4,7 +4,6 @@ from PIL import Image, ImageDraw
|
||||||
from math import sqrt, pow
|
from math import sqrt, pow
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class Epd7in5bAdapter (EpdAdapter):
|
class Epd7in5bAdapter (EpdAdapter):
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
super(Epd7in5bAdapter, self).__init__(384, 640)
|
super(Epd7in5bAdapter, self).__init__(384, 640)
|
||||||
|
@ -55,11 +54,9 @@ class Epd7in5bAdapter (EpdAdapter):
|
||||||
if image_buf[x, y, 1] == 255: #White
|
if image_buf[x, y, 1] == 255: #White
|
||||||
buf[int((y + x * self.height) / 4)] |= 0xC0 >> (y % 4 * 2)
|
buf[int((y + x * self.height) / 4)] |= 0xC0 >> (y % 4 * 2)
|
||||||
elif image_buf[x, y, 0] == 0: #Black
|
elif image_buf[x, y, 0] == 0: #Black
|
||||||
buf[int((y + x * self.height) / 4)
|
buf[int((y + x * self.height) / 4)] &= ~(0xC0 >> (y % 4 * 2))
|
||||||
] &= ~(0xC0 >> (y % 4 * 2))
|
|
||||||
else: #Red
|
else: #Red
|
||||||
buf[int((y + x * self.height) / 4)
|
buf[int((y + x * self.height) / 4)] &= ~(0xC0 >> (y % 4 * 2))
|
||||||
] &= ~(0xC0 >> (y % 4 * 2))
|
|
||||||
buf[int((y + x * self.height) / 4)] |= 0x40 >> (y % 4 * 2)
|
buf[int((y + x * self.height) / 4)] |= 0x40 >> (y % 4 * 2)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,8 @@ AUTO_MEASUREMENT_VCOM = 0x80
|
||||||
READ_VCOM_VALUE = 0x81
|
READ_VCOM_VALUE = 0x81
|
||||||
VCM_DC_SETTING = 0x82
|
VCM_DC_SETTING = 0x82
|
||||||
|
|
||||||
|
|
||||||
class EpdAdapter (DisplayAdapter):
|
class EpdAdapter (DisplayAdapter):
|
||||||
"""Generalized adapter for epd7in5 and epd7in5b"""
|
"""Generalized adapter for epd7in5 and epd7in5b"""
|
||||||
|
|
||||||
def __init__ (self, width, height):
|
def __init__ (self, width, height):
|
||||||
super(EpdAdapter, self).__init__(width, height)
|
super(EpdAdapter, self).__init__(width, height)
|
||||||
|
|
||||||
|
@ -73,7 +71,7 @@ class EpdAdapter (DisplayAdapter):
|
||||||
|
|
||||||
print('Converting image to data and sending it to the display')
|
print('Converting image to data and sending it to the display')
|
||||||
print('This may take a while...' + '\n')
|
print('This may take a while...' + '\n')
|
||||||
prepared_image = design.get_image().rotate(270, expand=1).convert("RGB")
|
prepared_image = design.get_image().rotate(270, expand=1)
|
||||||
self.display_frame(self.get_frame_buffer(prepared_image))
|
self.display_frame(self.get_frame_buffer(prepared_image))
|
||||||
|
|
||||||
# Powering off the E-Paper until the next loop
|
# Powering off the E-Paper until the next loop
|
||||||
|
@ -159,14 +157,9 @@ class EpdAdapter (DisplayAdapter):
|
||||||
self.send_command(DEEP_SLEEP)
|
self.send_command(DEEP_SLEEP)
|
||||||
self.send_data(0xa5)
|
self.send_data(0xa5)
|
||||||
|
|
||||||
def wait_until_idle(self, max_wait_seconds=60):
|
def wait_until_idle (self):
|
||||||
wait_ms = 100
|
while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle
|
||||||
count = 0
|
self.delay_ms(100)
|
||||||
while(self.digital_read(self.busy_pin) == 0 and wait_ms * count < max_wait_seconds * 1000): # 0: busy, 1: idle
|
|
||||||
self.delay_ms(wait_ms)
|
|
||||||
count += 1
|
|
||||||
if wait_ms * count >= max_wait_seconds * 1000:
|
|
||||||
print("Skipped idle confirmation")
|
|
||||||
|
|
||||||
def reset (self):
|
def reset (self):
|
||||||
self.digital_write(self.reset_pin, GPIO.LOW) # module reset
|
self.digital_write(self.reset_pin, GPIO.LOW) # module reset
|
||||||
|
|
|
@ -5,11 +5,9 @@ from TextFormatter import date_str
|
||||||
from DictionaryMapper import get_text
|
from DictionaryMapper import get_text
|
||||||
from Dictionary import more_events
|
from Dictionary import more_events
|
||||||
|
|
||||||
|
|
||||||
class EventListDesign (DesignEntity):
|
class EventListDesign (DesignEntity):
|
||||||
"""Creates a TableDesign filled with event
|
"""Creates a TableDesign filled with event
|
||||||
begin date and title"""
|
begin date and title"""
|
||||||
|
|
||||||
def __init__ (self, size, events, text_size = defaultfontsize, line_spacing = 0, col_spacing = 10, event_prefix_rel_dates = [], event_prefix_func = None, font_family = None, general_color = colors["fg"], background_color = colors["bg"], highlight_color = colors["hl"], show_more_info = False):
|
def __init__ (self, size, events, text_size = defaultfontsize, line_spacing = 0, col_spacing = 10, event_prefix_rel_dates = [], event_prefix_func = None, font_family = None, general_color = colors["fg"], background_color = colors["bg"], highlight_color = colors["hl"], show_more_info = False):
|
||||||
super(EventListDesign, self).__init__(size)
|
super(EventListDesign, self).__init__(size)
|
||||||
self.events = events
|
self.events = events
|
||||||
|
@ -29,18 +27,15 @@ class EventListDesign (DesignEntity):
|
||||||
self.event_prefix_func = lambda x, y : date_str(x.begin_datetime)
|
self.event_prefix_func = lambda x, y : date_str(x.begin_datetime)
|
||||||
|
|
||||||
def __finish_image__ (self):
|
def __finish_image__ (self):
|
||||||
self.visible_event_count = int(int(
|
self.visible_event_count = int(int(self.size[1] + self.line_spacing) // (self.line_spacing + int(self.text_size)))
|
||||||
self.size[1] + self.line_spacing) // (self.line_spacing + int(self.text_size)))
|
|
||||||
self.__fill_event_matrix__()
|
self.__fill_event_matrix__()
|
||||||
|
|
||||||
col_hori_alignment = [ 'right', 'left' ]
|
col_hori_alignment = [ 'right', 'left' ]
|
||||||
table_design = TableDesign(self.size, background_color=self.background_color, font=self.font_family, line_spacing=self.line_spacing, col_spacing=self.col_spacing,
|
table_design = TableDesign(self.size, background_color = self.background_color, font=self.font_family, line_spacing=self.line_spacing, col_spacing=self.col_spacing, matrix=self.__event_matrix__, fontsize = self.text_size, column_horizontal_alignments=col_hori_alignment, mask=False, truncate_cols=False, cell_properties=self.__props_matrix__)
|
||||||
matrix=self.__event_matrix__, fontsize=self.text_size, column_horizontal_alignments=col_hori_alignment, mask=False, truncate_cols=False, cell_properties=self.__props_matrix__)
|
|
||||||
self.draw_design(table_design)
|
self.draw_design(table_design)
|
||||||
|
|
||||||
def __get_formatted_event__ (self, event, index):
|
def __get_formatted_event__ (self, event, index):
|
||||||
rel_date = None if index < 0 or index >= len(
|
rel_date = None if index < 0 or index >= len(self.event_prefix_rel_dates) else self.event_prefix_rel_dates[index]
|
||||||
self.event_prefix_rel_dates) else self.event_prefix_rel_dates[index]
|
|
||||||
prefix = self.event_prefix_func(event, rel_date)
|
prefix = self.event_prefix_func(event, rel_date)
|
||||||
return [ prefix, event.title ]
|
return [ prefix, event.title ]
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ api_price_url = api_url + "simple/price"
|
||||||
price_currency = "usd"
|
price_currency = "usd"
|
||||||
price_currency_sign = "$"
|
price_currency_sign = "$"
|
||||||
|
|
||||||
|
|
||||||
class GeckoCrypto(CryptoInterface):
|
class GeckoCrypto(CryptoInterface):
|
||||||
def __init__(self, coins):
|
def __init__(self, coins):
|
||||||
self.coin_ids = coins
|
self.coin_ids = coins
|
||||||
|
@ -27,15 +26,11 @@ class GeckoCrypto(CryptoInterface):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __get_coins__(self):
|
def __get_coins__(self):
|
||||||
if len(self.coin_ids) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
self.__prepare_metadata__()
|
self.__prepare_metadata__()
|
||||||
coins = []
|
coins = []
|
||||||
for id in self.coin_ids:
|
for id in self.coin_ids:
|
||||||
try:
|
try:
|
||||||
data = urlopen(api_price_url + "?include_24hr_change=true&ids=" +
|
data = urlopen(api_price_url + "?include_24hr_change=true&ids=" + self.metadata[id]['id'] + "&vs_currencies=" + price_currency).read()
|
||||||
self.metadata[id]['id'] + "&vs_currencies=" + price_currency).read()
|
|
||||||
dataJSON = json.loads(data.decode('utf-8'))
|
dataJSON = json.loads(data.decode('utf-8'))
|
||||||
raw = dataJSON[id][price_currency]
|
raw = dataJSON[id][price_currency]
|
||||||
price = math.ceil(raw*100) / 100
|
price = math.ceil(raw*100) / 100
|
||||||
|
@ -64,5 +59,4 @@ class GeckoCrypto(CryptoInterface):
|
||||||
self.metadata = None
|
self.metadata = None
|
||||||
data = urlopen(api_metadata_url).read()
|
data = urlopen(api_metadata_url).read()
|
||||||
dataJSON = json.loads(data.decode('utf-8'))
|
dataJSON = json.loads(data.decode('utf-8'))
|
||||||
self.metadata = {coin['id'].lower(
|
self.metadata = { coin['id'].lower() : coin for coin in dataJSON if coin['id'].lower() in self.coin_ids }
|
||||||
): coin for coin in dataJSON if coin['id'].lower() in self.coin_ids}
|
|
||||||
|
|
|
@ -19,11 +19,9 @@ currenttimeline_thickness = line_thickness
|
||||||
|
|
||||||
event_title_font = fonts['bold']
|
event_title_font = fonts['bold']
|
||||||
|
|
||||||
|
|
||||||
class HourListDesign (DesignEntity):
|
class HourListDesign (DesignEntity):
|
||||||
"""Hours of a day are listed vertically and
|
"""Hours of a day are listed vertically and
|
||||||
resemble a timeline."""
|
resemble a timeline."""
|
||||||
|
|
||||||
def __init__ (self, size, first_hour = 0, last_hour = 23):
|
def __init__ (self, size, first_hour = 0, last_hour = 23):
|
||||||
super(HourListDesign, self).__init__(size)
|
super(HourListDesign, self).__init__(size)
|
||||||
self.first_hour = first_hour
|
self.first_hour = first_hour
|
||||||
|
@ -65,8 +63,7 @@ class HourListDesign (DesignEntity):
|
||||||
for _ in range(self.number_columns):
|
for _ in range(self.number_columns):
|
||||||
column_events.append(None)
|
column_events.append(None)
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
column_events = self.__update_columns_events__(
|
column_events = self.__update_columns_events__(column_events, event)
|
||||||
column_events, event)
|
|
||||||
self.__draw_event__(event, column_events.index(event))
|
self.__draw_event__(event, column_events.index(event))
|
||||||
|
|
||||||
def __update_columns_events__ (self, column_events, new_event):
|
def __update_columns_events__ (self, column_events, new_event):
|
||||||
|
@ -94,13 +91,11 @@ class HourListDesign (DesignEntity):
|
||||||
pos = (0, self.__get_ypos_for_time__(hour) + ypadding)
|
pos = (0, self.__get_ypos_for_time__(hour) + ypadding)
|
||||||
fontsize = size[1] * hour_box_fontsize
|
fontsize = size[1] * hour_box_fontsize
|
||||||
|
|
||||||
txt = TextDesign(size, text=self.__get_hour_text__(
|
txt = TextDesign(size, text=self.__get_hour_text__(hour), fontsize=fontsize, verticalalignment="bottom", horizontalalignment="center")
|
||||||
hour), fontsize=fontsize, verticalalignment="bottom", horizontalalignment="center")
|
|
||||||
txt.pos = pos
|
txt.pos = pos
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
sub = TextDesign((width, subtext_height), text=self.__get_hour_sub_text__(
|
sub = TextDesign((width, subtext_height), text=self.__get_hour_sub_text__(hour), fontsize=sub_fontsize, verticalalignment="top", horizontalalignment="center")
|
||||||
hour), fontsize=sub_fontsize, verticalalignment="top", horizontalalignment="center")
|
|
||||||
sub.pos = (0, height + self.__get_ypos_for_time__(hour))
|
sub.pos = (0, height + self.__get_ypos_for_time__(hour))
|
||||||
self.draw_design(sub)
|
self.draw_design(sub)
|
||||||
|
|
||||||
|
@ -109,8 +104,7 @@ class HourListDesign (DesignEntity):
|
||||||
ypos = i * self.row_size[1]
|
ypos = i * self.row_size[1]
|
||||||
line_start = (0, ypos)
|
line_start = (0, ypos)
|
||||||
line_end = (self.size[0], ypos)
|
line_end = (self.size[0], ypos)
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["fg"], width=line_thickness)
|
||||||
[line_start, line_end], fill=colors["fg"], width=line_thickness)
|
|
||||||
|
|
||||||
def __get_hour_sub_text__ (self, hour):
|
def __get_hour_sub_text__ (self, hour):
|
||||||
if hours == "12":
|
if hours == "12":
|
||||||
|
@ -151,29 +145,23 @@ class HourListDesign (DesignEntity):
|
||||||
|
|
||||||
text = event.title
|
text = event.title
|
||||||
text_color = colors["bg"]
|
text_color = colors["bg"]
|
||||||
textbox_size = (size[0] - event_title_xpadding,
|
textbox_size = (size[0] - event_title_xpadding, size[1] - event_title_ypadding)
|
||||||
size[1] - event_title_ypadding)
|
txt = TextDesign(textbox_size, text = text, font=event_title_font, fontsize=event_title_fontsize, color=text_color, background_color=box_color, wrap=True)
|
||||||
txt = TextDesign(textbox_size, text=text, font=event_title_font,
|
|
||||||
fontsize=event_title_fontsize, color=text_color, background_color=box_color, wrap=True)
|
|
||||||
txt.mask = False
|
txt.mask = False
|
||||||
txt.pos = (pos[0] + event_title_xpadding,
|
txt.pos = (pos[0] + event_title_xpadding, pos[1] + event_title_ypadding)
|
||||||
pos[1] + event_title_ypadding)
|
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
half_ypadding = int(event_title_ypadding / 2)
|
half_ypadding = int(event_title_ypadding / 2)
|
||||||
line_start = (pos[0] + event_title_xpadding, pos[1] + half_ypadding)
|
line_start = (pos[0] + event_title_xpadding, pos[1] + half_ypadding)
|
||||||
line_end = (pos[0] + size[0] - event_title_xpadding,
|
line_end = (pos[0] + size[0] - event_title_xpadding, pos[1] + half_ypadding)
|
||||||
pos[1] + half_ypadding)
|
ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["bg"], width=1)
|
||||||
ImageDraw.Draw(self.__image__).line(
|
|
||||||
[line_start, line_end], fill=colors["bg"], width=1)
|
|
||||||
|
|
||||||
def __get_max_num_simultaneous_events__ (self):
|
def __get_max_num_simultaneous_events__ (self):
|
||||||
parallelity_count = 1
|
parallelity_count = 1
|
||||||
|
|
||||||
for index, event in enumerate(self.events):
|
for index, event in enumerate(self.events):
|
||||||
current_parallelity = 1
|
current_parallelity = 1
|
||||||
# Assumption: Events are ordered chronologically
|
preceding = self.events[:index] #Assumption: Events are ordered chronologically
|
||||||
preceding = self.events[:index]
|
|
||||||
for pre_event in preceding:
|
for pre_event in preceding:
|
||||||
if self.__are_simultaneous__(pre_event, event):
|
if self.__are_simultaneous__(pre_event, event):
|
||||||
current_parallelity += 1
|
current_parallelity += 1
|
||||||
|
@ -195,5 +183,4 @@ class HourListDesign (DesignEntity):
|
||||||
|
|
||||||
line_start = (0, ypos)
|
line_start = (0, ypos)
|
||||||
line_end = (self.size[0], ypos)
|
line_end = (self.size[0], ypos)
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line([ line_start, line_end ], fill=colors["hl"], width=currenttimeline_thickness)
|
||||||
[line_start, line_end], fill=colors["hl"], width=currenttimeline_thickness)
|
|
|
@ -6,10 +6,8 @@ import re
|
||||||
from settings import week_starts_on
|
from settings import week_starts_on
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
|
||||||
class IcalEvents(CalendarInterface):
|
class IcalEvents(CalendarInterface):
|
||||||
"""Fetches events from ical addresses."""
|
"""Fetches events from ical addresses."""
|
||||||
|
|
||||||
def __init__(self, urls, highlighted_urls=None):
|
def __init__(self, urls, highlighted_urls=None):
|
||||||
self.urls = urls
|
self.urls = urls
|
||||||
self.highlighted_urls = highlighted_urls
|
self.highlighted_urls = highlighted_urls
|
||||||
|
@ -51,7 +49,6 @@ class IcalEvents(CalendarInterface):
|
||||||
ical = Calendar(decode)
|
ical = Calendar(decode)
|
||||||
for event in ical.events:
|
for event in ical.events:
|
||||||
cal_event = CalendarEvent()
|
cal_event = CalendarEvent()
|
||||||
cal_event.calendar_url = calendar
|
|
||||||
|
|
||||||
cal_event.fetch_datetime = datetime.now(timezone.utc)
|
cal_event.fetch_datetime = datetime.now(timezone.utc)
|
||||||
cal_event.begin_datetime = event.begin.datetime
|
cal_event.begin_datetime = event.begin.datetime
|
||||||
|
@ -63,10 +60,8 @@ class IcalEvents(CalendarInterface):
|
||||||
cal_event.allday = event.all_day
|
cal_event.allday = event.all_day
|
||||||
cal_event.rrule = self.__extract_rrule__(event)
|
cal_event.rrule = self.__extract_rrule__(event)
|
||||||
|
|
||||||
cal_event.begin_datetime = cal_event.begin_datetime.astimezone(
|
cal_event.begin_datetime = cal_event.begin_datetime.astimezone(None)
|
||||||
None)
|
cal_event.end_datetime = cal_event.end_datetime.astimezone(None)
|
||||||
cal_event.end_datetime = cal_event.end_datetime.astimezone(
|
|
||||||
None)
|
|
||||||
|
|
||||||
if cal_event.allday:
|
if cal_event.allday:
|
||||||
cal_event = self.__fix_allday__(cal_event)
|
cal_event = self.__fix_allday__(cal_event)
|
||||||
|
@ -84,10 +79,8 @@ class IcalEvents(CalendarInterface):
|
||||||
begin_utc = event.begin_datetime.astimezone(timezone.utc)
|
begin_utc = event.begin_datetime.astimezone(timezone.utc)
|
||||||
end_utc = event.end_datetime.astimezone(timezone.utc)
|
end_utc = event.end_datetime.astimezone(timezone.utc)
|
||||||
|
|
||||||
event.begin_datetime = datetime(
|
event.begin_datetime = datetime(begin_utc.year, begin_utc.month, begin_utc.day, 0, 0, 0, 0, local_tzinfo)
|
||||||
begin_utc.year, begin_utc.month, begin_utc.day, 0, 0, 0, 0, local_tzinfo)
|
event.end_datetime = datetime(end_utc.year, end_utc.month, end_utc.day, 0, 0, 0, 0, local_tzinfo) - timedelta(1)
|
||||||
event.end_datetime = datetime(
|
|
||||||
end_utc.year, end_utc.month, end_utc.day, 0, 0, 0, 0, local_tzinfo) - timedelta(1)
|
|
||||||
event.duration = event.end_datetime - event.begin_datetime
|
event.duration = event.end_datetime - event.begin_datetime
|
||||||
|
|
||||||
return event
|
return event
|
||||||
|
@ -102,9 +95,7 @@ class IcalEvents(CalendarInterface):
|
||||||
beginAlarmIndex = decode.find(alarm_begin)
|
beginAlarmIndex = decode.find(alarm_begin)
|
||||||
if beginAlarmIndex >= 0:
|
if beginAlarmIndex >= 0:
|
||||||
endAlarmIndex = decode.find(alarm_end, beginAlarmIndex)
|
endAlarmIndex = decode.find(alarm_end, beginAlarmIndex)
|
||||||
decode = decode[:beginAlarmIndex] + \
|
decode = decode[:beginAlarmIndex] + decode[endAlarmIndex + len(alarm_end) + len(lineseparation):]
|
||||||
decode[endAlarmIndex +
|
|
||||||
len(alarm_end) + len(lineseparation):]
|
|
||||||
return decode
|
return decode
|
||||||
|
|
||||||
def __extract_rrule__(self, event):
|
def __extract_rrule__(self, event):
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
from DesignEntity import DesignEntity
|
|
||||||
from Assets import path as application_path
|
|
||||||
from PIL import Image, ExifTags
|
|
||||||
|
|
||||||
|
|
||||||
class ImageDesign (DesignEntity):
|
|
||||||
"""Creates a TableDesign filled with rss post
|
|
||||||
date and title"""
|
|
||||||
|
|
||||||
# fill: "none" : original size, "stretch" : strech to fill, "scale" : scale to fill, "border" : scale until one side touches border
|
|
||||||
def __init__(self, size, path, fill="none", color="RGBA", dither=None):
|
|
||||||
super(ImageDesign, self).__init__(size)
|
|
||||||
self.set_path(path)
|
|
||||||
self.fill = fill
|
|
||||||
self.color = color
|
|
||||||
self.dither = dither
|
|
||||||
|
|
||||||
def set_path(self, path):
|
|
||||||
path = path.replace('\\', '/')
|
|
||||||
if path[0] != '/' and ':' not in path[0:3]:
|
|
||||||
path = application_path + '/' + path
|
|
||||||
self.path = path
|
|
||||||
|
|
||||||
def __finish_image__(self):
|
|
||||||
img = Image.open(self.path)
|
|
||||||
img = img.convert(self.color, dither=self.dither)
|
|
||||||
|
|
||||||
img = self.__fix_orientation__(img)
|
|
||||||
img = self.__resize_image__(img)
|
|
||||||
pos = self.__get_centered_position__(img)
|
|
||||||
|
|
||||||
self.__init_image__("#00000000")
|
|
||||||
self.draw(img, pos)
|
|
||||||
|
|
||||||
def __resize_image__(self, img):
|
|
||||||
if self.fill is "none":
|
|
||||||
return img
|
|
||||||
|
|
||||||
if self.fill is "stretch":
|
|
||||||
img = img.resize(self.size, resample=Image.LANCZOS)
|
|
||||||
|
|
||||||
if self.fill is "scale":
|
|
||||||
size = self.size
|
|
||||||
img_proportions = img.width / img.height
|
|
||||||
if img_proportions < size[0] / size[1]:
|
|
||||||
size = (size[0], int(size[0] * (1 / img_proportions)))
|
|
||||||
else:
|
|
||||||
size = (int(size[1] * img_proportions), size[1])
|
|
||||||
img = img.resize(size, resample=Image.LANCZOS)
|
|
||||||
|
|
||||||
if self.fill is "border":
|
|
||||||
size = self.size
|
|
||||||
img_proportions = img.width / img.height
|
|
||||||
if img_proportions < size[0] / size[1]:
|
|
||||||
size = (int(size[1] * img_proportions), size[1])
|
|
||||||
else:
|
|
||||||
size = (size[0], int(size[0] * (1 / img_proportions)))
|
|
||||||
img = img.resize(size, resample=Image.LANCZOS)
|
|
||||||
|
|
||||||
return img
|
|
||||||
|
|
||||||
def __get_centered_position__(self, img):
|
|
||||||
screen_size = self.size
|
|
||||||
img_size = img.size
|
|
||||||
|
|
||||||
delta_size = [s - i for s, i in zip(screen_size, img_size)]
|
|
||||||
delta_center_pos = [s / 2 for s in delta_size]
|
|
||||||
|
|
||||||
return delta_center_pos
|
|
||||||
|
|
||||||
def __fix_orientation__(self, img):
|
|
||||||
if "parsed_exif" not in img.info.keys():
|
|
||||||
return img
|
|
||||||
|
|
||||||
for orientation in ExifTags.TAGS.keys():
|
|
||||||
if ExifTags.TAGS[orientation] == 'Orientation':
|
|
||||||
break
|
|
||||||
exif = img.info["parsed_exif"]
|
|
||||||
|
|
||||||
if exif[orientation] == 3:
|
|
||||||
img = img.rotate(180, expand=True)
|
|
||||||
elif exif[orientation] == 6:
|
|
||||||
img = img.rotate(270, expand=True)
|
|
||||||
elif exif[orientation] == 8:
|
|
||||||
img = img.rotate(90, expand=True)
|
|
||||||
return img
|
|
|
@ -1,10 +1,8 @@
|
||||||
from DisplayAdapter import DisplayAdapter
|
from DisplayAdapter import DisplayAdapter
|
||||||
from Assets import path
|
from Assets import path
|
||||||
|
|
||||||
|
|
||||||
class ImageFileAdapter (DisplayAdapter):
|
class ImageFileAdapter (DisplayAdapter):
|
||||||
"""Saves design to an image file, can be used for debugging"""
|
"""Saves design to an image file, can be used for debugging"""
|
||||||
|
|
||||||
def __init__ (self, file_path = ""):
|
def __init__ (self, file_path = ""):
|
||||||
super(ImageFileAdapter, self).__init__(384, 640)
|
super(ImageFileAdapter, self).__init__(384, 640)
|
||||||
self.file_path = file_path
|
self.file_path = file_path
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
from PanelDesign import PanelDesign
|
|
||||||
from settings import general_settings
|
|
||||||
from Assets import supported_img_formats, path as application_path
|
|
||||||
from os import listdir
|
|
||||||
from os.path import isfile, join
|
|
||||||
from ImageDesign import ImageDesign
|
|
||||||
from random import choice
|
|
||||||
|
|
||||||
|
|
||||||
class ImageFramePanel (PanelDesign):
|
|
||||||
"""Converts the display into a digital frame and
|
|
||||||
shows a slide show of images, iterating on each update"""
|
|
||||||
|
|
||||||
def __init__(self, size):
|
|
||||||
super(ImageFramePanel, self).__init__(size)
|
|
||||||
self.overlay_path = self.__complete_path__(
|
|
||||||
general_settings["overlay-image"])
|
|
||||||
self.image_folder_path = self.__complete_path__(
|
|
||||||
general_settings["image-folder"])
|
|
||||||
self.images = self.__extract_valid_img_paths__()
|
|
||||||
self.__first_render__()
|
|
||||||
|
|
||||||
def __extract_valid_img_paths__(self):
|
|
||||||
images = []
|
|
||||||
for file in listdir(self.image_folder_path):
|
|
||||||
file_path = join(self.image_folder_path, file).replace('\\', '/')
|
|
||||||
if isfile(file_path) and self.overlay_path != file_path:
|
|
||||||
if file.split('.')[-1].upper() in supported_img_formats:
|
|
||||||
images.append(file_path)
|
|
||||||
return images
|
|
||||||
|
|
||||||
def __complete_path__(self, path):
|
|
||||||
path = path.replace('\\', '/')
|
|
||||||
if path[0] != '/' and ':' not in path[0:3]:
|
|
||||||
path = join(application_path, path)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def __first_render__(self):
|
|
||||||
current_image = choice(self.images)
|
|
||||||
img = ImageDesign(self.size, current_image, fill="scale", color="1")
|
|
||||||
self.draw_design(img)
|
|
||||||
|
|
||||||
if self.overlay_path != "":
|
|
||||||
overlay = ImageDesign(self.size, self.overlay_path)
|
|
||||||
overlay.__finish_image__()
|
|
||||||
self.__image__.alpha_composite(overlay.__image__)
|
|
|
@ -3,21 +3,17 @@ from datetime import datetime, timedelta
|
||||||
min_sleep_minutes = 0
|
min_sleep_minutes = 0
|
||||||
max_history_entries = 25
|
max_history_entries = 25
|
||||||
|
|
||||||
|
|
||||||
class LoopTimer (object):
|
class LoopTimer (object):
|
||||||
"""Manages loop times and sleeps until
|
"""Manages loop times and sleeps until
|
||||||
next loop."""
|
next loop."""
|
||||||
|
def __init__ (self, loop_interval, run_on_hour = False):
|
||||||
def __init__(self, loop_interval, run_on_hour=False, max_loop_count=0):
|
|
||||||
self.interval = int(str(loop_interval))
|
self.interval = int(str(loop_interval))
|
||||||
self.on_hour = run_on_hour
|
self.on_hour = run_on_hour
|
||||||
self.loop_history = []
|
self.loop_history = []
|
||||||
self.loop_count = 0
|
|
||||||
self.max_loop_count = int(str(max_loop_count))
|
|
||||||
|
|
||||||
def begin_loop (self):
|
def begin_loop (self):
|
||||||
begin_time = datetime.now()
|
begin_time = datetime.now()
|
||||||
print('\n__________Starting new loop [' + str(self.loop_count) + ']__________')
|
print('\n__________Starting new loop__________')
|
||||||
print('Datetime: ' + str(begin_time) + '\n')
|
print('Datetime: ' + str(begin_time) + '\n')
|
||||||
self.__add_beginning__(begin_time)
|
self.__add_beginning__(begin_time)
|
||||||
|
|
||||||
|
@ -36,15 +32,6 @@ class LoopTimer (object):
|
||||||
end_time = datetime.now()
|
end_time = datetime.now()
|
||||||
self.__add_ending__(end_time)
|
self.__add_ending__(end_time)
|
||||||
|
|
||||||
self.loop_count += 1
|
|
||||||
while self.loop_count > 86400:
|
|
||||||
self.loop_count -= 86400
|
|
||||||
|
|
||||||
def was_last_loop(self):
|
|
||||||
if self.max_loop_count == 0:
|
|
||||||
return False
|
|
||||||
return self.max_loop_count <= self.loop_count
|
|
||||||
|
|
||||||
def get_current (self):
|
def get_current (self):
|
||||||
return self.loop_history[-1]
|
return self.loop_history[-1]
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,9 @@ dayhighlightboxsize = (0.143, 0.14)
|
||||||
daynumbersize = daynumberboxsize[0] * 0.45
|
daynumbersize = daynumberboxsize[0] * 0.45
|
||||||
day_number_ypadding = -0.002
|
day_number_ypadding = -0.002
|
||||||
|
|
||||||
|
|
||||||
class MonthBlockDesign (DesignEntity):
|
class MonthBlockDesign (DesignEntity):
|
||||||
"""Creates a view containing one week of the month in
|
"""Creates a view containing one week of the month in
|
||||||
one row"""
|
one row"""
|
||||||
|
|
||||||
def __init__(self, size, datetime_month, highlight_today = False):
|
def __init__(self, size, datetime_month, highlight_today = False):
|
||||||
super(MonthBlockDesign, self).__init__(size, mask=True)
|
super(MonthBlockDesign, self).__init__(size, mask=True)
|
||||||
self.month = datetime_month.month
|
self.month = datetime_month.month
|
||||||
|
@ -32,12 +30,10 @@ class MonthBlockDesign (DesignEntity):
|
||||||
cal = callib.monthcalendar(self.year, self.month)
|
cal = callib.monthcalendar(self.year, self.month)
|
||||||
for week in cal:
|
for week in cal:
|
||||||
for numbers in week:
|
for numbers in week:
|
||||||
self.__draw_day_number__(numbers, self.get_day_pos(
|
self.__draw_day_number__(numbers, self.get_day_pos(cal.index(week), week.index(numbers)))
|
||||||
cal.index(week), week.index(numbers)))
|
|
||||||
|
|
||||||
if self.highlight_today:
|
if self.highlight_today:
|
||||||
self.__draw_highlight_box__(self.__abs_pos__(
|
self.__draw_highlight_box__(self.__abs_pos__(dayhighlightboxsize), self.__get_today_box_pos__(), width=3)
|
||||||
dayhighlightboxsize), self.__get_today_box_pos__(), width=3)
|
|
||||||
|
|
||||||
def __draw_highlight_box__ (self, size, pos, color=colors["fg"], width=1):
|
def __draw_highlight_box__ (self, size, pos, color=colors["fg"], width=1):
|
||||||
design = BoxDesign(size, outline=color, width = width)
|
design = BoxDesign(size, outline=color, width = width)
|
||||||
|
@ -47,8 +43,7 @@ class MonthBlockDesign (DesignEntity):
|
||||||
def __draw_day_number__ (self, number, pos):
|
def __draw_day_number__ (self, number, pos):
|
||||||
if number <= 0:
|
if number <= 0:
|
||||||
return
|
return
|
||||||
txt = TextDesign(self.__abs_pos__(daynumberboxsize), fontsize=daynumbersize *
|
txt = TextDesign(self.__abs_pos__(daynumberboxsize), fontsize=daynumbersize * self.size[0], text=str(number), verticalalignment="center", horizontalalignment="center")
|
||||||
self.size[0], text=str(number), verticalalignment="center", horizontalalignment="center")
|
|
||||||
txt.pos = (pos[0], pos[1] + day_number_ypadding * self.size[1])
|
txt.pos = (pos[0], pos[1] + day_number_ypadding * self.size[1])
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
|
@ -59,8 +54,7 @@ class MonthBlockDesign (DesignEntity):
|
||||||
return (int(rel_pos[0] + day_of_week * partialwidth), int(rel_pos[1] + week_in_month * partialheight))
|
return (int(rel_pos[0] + day_of_week * partialwidth), int(rel_pos[1] + week_in_month * partialheight))
|
||||||
|
|
||||||
def __get_today_box_pos__ (self):
|
def __get_today_box_pos__ (self):
|
||||||
x, y = self.get_day_pos(self.__get_week_of_month__(
|
x, y = self.get_day_pos(self.__get_week_of_month__(datetime.now()), self.__get_day_of_week__(datetime.now()))
|
||||||
datetime.now()), self.__get_day_of_week__(datetime.now()))
|
|
||||||
return (x, int(y + (self.__abs_pos__(daynumberboxsize)[1] - self.__abs_pos__(dayhighlightboxsize)[1]) / 2))
|
return (x, int(y + (self.__abs_pos__(daynumberboxsize)[1] - self.__abs_pos__(dayhighlightboxsize)[1]) / 2))
|
||||||
|
|
||||||
def __get_week_of_month__ (self, date):
|
def __get_week_of_month__ (self, date):
|
||||||
|
@ -80,8 +74,7 @@ class MonthBlockDesign (DesignEntity):
|
||||||
|
|
||||||
weekdays = []
|
weekdays = []
|
||||||
for i in range(7):
|
for i in range(7):
|
||||||
weekdays.append(
|
weekdays.append((datetime.now() + timedelta(days=(i + correction))).strftime("%a"))
|
||||||
(datetime.now() + timedelta(days=(i + correction))).strftime("%a"))
|
|
||||||
|
|
||||||
return weekdays
|
return weekdays
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,9 @@ weekdaytextpadding = -0.001
|
||||||
weekrownameboxsize = (0.143, 0.044)
|
weekrownameboxsize = (0.143, 0.044)
|
||||||
eventcirclehorizontalsize = 0.100
|
eventcirclehorizontalsize = 0.100
|
||||||
|
|
||||||
|
|
||||||
class MonthOvPanel (PanelDesign):
|
class MonthOvPanel (PanelDesign):
|
||||||
"""Overview that focuses on the current month and
|
"""Overview that focuses on the current month and
|
||||||
some additional information in the bottom."""
|
some additional information in the bottom."""
|
||||||
|
|
||||||
def __init__ (self, size):
|
def __init__ (self, size):
|
||||||
super(MonthOvPanel, self).__init__(size)
|
super(MonthOvPanel, self).__init__(size)
|
||||||
self.weather_header_height = 0
|
self.weather_header_height = 0
|
||||||
|
@ -54,8 +52,7 @@ class MonthOvPanel (PanelDesign):
|
||||||
if general_settings["weather-info"]:
|
if general_settings["weather-info"]:
|
||||||
self.__draw_seperator__()
|
self.__draw_seperator__()
|
||||||
|
|
||||||
self.month_block = MonthBlockDesign(self.__abs_pos__(
|
self.month_block = MonthBlockDesign(self.__abs_pos__(monthovsize), datetime.now(), highlight_today = True)
|
||||||
monthovsize), datetime.now(), highlight_today=True)
|
|
||||||
pos = self.__abs_pos__(monthovposition)
|
pos = self.__abs_pos__(monthovposition)
|
||||||
pos = (pos[0], pos[1] + self.weather_header_height)
|
pos = (pos[0], pos[1] + self.weather_header_height)
|
||||||
self.month_block.pos = pos
|
self.month_block.pos = pos
|
||||||
|
@ -64,8 +61,7 @@ class MonthOvPanel (PanelDesign):
|
||||||
def add_weather (self, weather):
|
def add_weather (self, weather):
|
||||||
if general_settings["weather-info"] == False:
|
if general_settings["weather-info"] == False:
|
||||||
return
|
return
|
||||||
self.draw_design(WeatherHeaderDesign(
|
self.draw_design(WeatherHeaderDesign(self.__abs_pos__(weatherheadersize), weather))
|
||||||
self.__abs_pos__(weatherheadersize), weather))
|
|
||||||
|
|
||||||
def add_rssfeed (self, rss):
|
def add_rssfeed (self, rss):
|
||||||
if general_settings["info-area"] is "rss":
|
if general_settings["info-area"] is "rss":
|
||||||
|
@ -80,8 +76,7 @@ class MonthOvPanel (PanelDesign):
|
||||||
|
|
||||||
def add_calendar (self, calendar):
|
def add_calendar (self, calendar):
|
||||||
if general_settings["highlight-event-days"]:
|
if general_settings["highlight-event-days"]:
|
||||||
month_events = list(set([(event.begin_datetime.day, event.begin_datetime.month,
|
month_events = list(set([ (event.begin_datetime.day, event.begin_datetime.month, event.begin_datetime.year) for event in calendar.get_month_events()]))
|
||||||
event.begin_datetime.year) for event in calendar.get_month_events()]))
|
|
||||||
for event in month_events:
|
for event in month_events:
|
||||||
self.__draw_highlight_event_day__(event)
|
self.__draw_highlight_event_day__(event)
|
||||||
|
|
||||||
|
@ -91,35 +86,29 @@ class MonthOvPanel (PanelDesign):
|
||||||
def __draw_rss_post_list_to_bottom__ (self, rss):
|
def __draw_rss_post_list_to_bottom__ (self, rss):
|
||||||
month_pos = self.__abs_pos__(monthovposition)
|
month_pos = self.__abs_pos__(monthovposition)
|
||||||
month_height = self.month_block.get_real_height()
|
month_height = self.month_block.get_real_height()
|
||||||
size = (self.size[0], self.size[1] - (month_pos[1] +
|
size = (self.size[0], self.size[1] - (month_pos[1] + month_height + self.weather_header_height))
|
||||||
month_height + self.weather_header_height))
|
|
||||||
info_list = RssPostListDesign(size, rss)
|
info_list = RssPostListDesign(size, rss)
|
||||||
info_list.pos = (
|
info_list.pos = (int(month_pos[0]), month_pos[1] + month_height + self.weather_header_height)
|
||||||
int(month_pos[0]), month_pos[1] + month_height + self.weather_header_height)
|
|
||||||
self.draw_design(info_list)
|
self.draw_design(info_list)
|
||||||
|
|
||||||
def __draw_crypto_post_list_to_bottom__ (self, crypto):
|
def __draw_crypto_post_list_to_bottom__ (self, crypto):
|
||||||
month_pos = self.__abs_pos__(monthovposition)
|
month_pos = self.__abs_pos__(monthovposition)
|
||||||
month_height = self.month_block.get_real_height()
|
month_height = self.month_block.get_real_height()
|
||||||
size = (self.size[0], self.size[1] - (month_pos[1] +
|
size = (self.size[0], self.size[1] - (month_pos[1] + month_height + self.weather_header_height))
|
||||||
month_height + self.weather_header_height))
|
|
||||||
|
|
||||||
info_list = CryptoListDesign(size, crypto)
|
info_list = CryptoListDesign(size, crypto)
|
||||||
list_height = info_list.get_estimated_height()
|
list_height = info_list.get_estimated_height()
|
||||||
info_list.pos = (int(month_pos[0]), month_pos[1] + month_height +
|
info_list.pos = (int(month_pos[0]), month_pos[1] + month_height + self.weather_header_height + (size[1] - list_height))
|
||||||
self.weather_header_height + (size[1] - list_height))
|
|
||||||
self.draw_design(info_list)
|
self.draw_design(info_list)
|
||||||
|
|
||||||
def __draw_event_list_to_bottom__ (self, calendar):
|
def __draw_event_list_to_bottom__ (self, calendar):
|
||||||
month_pos = self.__abs_pos__(monthovposition)
|
month_pos = self.__abs_pos__(monthovposition)
|
||||||
month_height = self.month_block.get_real_height()
|
month_height = self.month_block.get_real_height()
|
||||||
size = (self.size[0], self.size[1] - (month_pos[1] +
|
size = (self.size[0], self.size[1] - (month_pos[1] + month_height + self.weather_header_height))
|
||||||
month_height + self.weather_header_height))
|
|
||||||
|
|
||||||
events = calendar.get_upcoming_events()
|
events = calendar.get_upcoming_events()
|
||||||
info_list = EventListDesign(size, events)
|
info_list = EventListDesign(size, events)
|
||||||
info_list.pos = (int(month_pos[0]), int(
|
info_list.pos = (int(month_pos[0]), int(month_pos[1] + month_height + self.weather_header_height))
|
||||||
month_pos[1] + month_height + self.weather_header_height))
|
|
||||||
self.draw_design(info_list)
|
self.draw_design(info_list)
|
||||||
|
|
||||||
def __draw_highlight_event_day__ (self, date):
|
def __draw_highlight_event_day__ (self, date):
|
||||||
|
@ -128,12 +117,9 @@ class MonthOvPanel (PanelDesign):
|
||||||
|
|
||||||
side_length = int(eventcirclehorizontalsize * self.size[0])
|
side_length = int(eventcirclehorizontalsize * self.size[0])
|
||||||
circle_size = (side_length,side_length)
|
circle_size = (side_length,side_length)
|
||||||
pos = self.month_block.get_day_pos(cur_date.isocalendar(
|
pos = self.month_block.get_day_pos(cur_date.isocalendar()[1] - first_month_week, self.__get_day_of_week__(cur_date), rel_pos = self.month_block.pos)
|
||||||
)[1] - first_month_week, self.__get_day_of_week__(cur_date), rel_pos=self.month_block.pos)
|
place_size = (self.month_block.size[0] * daynumberboxsize[0], self.month_block.size[1] * daynumberboxsize[1])
|
||||||
place_size = (self.month_block.size[0] * daynumberboxsize[0],
|
pos = (int(pos[0] + (place_size[0] - circle_size[0]) / 2), int(pos[1] + (place_size[1] - circle_size[1]) / 2))
|
||||||
self.month_block.size[1] * daynumberboxsize[1])
|
|
||||||
pos = (int(pos[0] + (place_size[0] - circle_size[0]) / 2),
|
|
||||||
int(pos[1] + (place_size[1] - circle_size[1]) / 2))
|
|
||||||
self.__draw_highlight_circle__(circle_size, pos, 'red', width=2)
|
self.__draw_highlight_circle__(circle_size, pos, 'red', width=2)
|
||||||
|
|
||||||
def __abs_pos__ (self, pos, size = None):
|
def __abs_pos__ (self, pos, size = None):
|
||||||
|
@ -143,28 +129,24 @@ class MonthOvPanel (PanelDesign):
|
||||||
|
|
||||||
def __draw_seperator__ (self):
|
def __draw_seperator__ (self):
|
||||||
"""Draw a line seperating the weather and Calendar section"""
|
"""Draw a line seperating the weather and Calendar section"""
|
||||||
ImageDraw.Draw(self.__image__).line([self.__abs_pos__(
|
ImageDraw.Draw(self.__image__).line([ self.__abs_pos__(seperatorplace), self.__abs_pos__((1, seperatorplace[1])) ], fill='red', width=5)
|
||||||
seperatorplace), self.__abs_pos__((1, seperatorplace[1]))], fill='red', width=5)
|
|
||||||
|
|
||||||
def __draw_month_name__ (self):
|
def __draw_month_name__ (self):
|
||||||
"""Draw the icon with the current month's name"""
|
"""Draw the icon with the current month's name"""
|
||||||
month = datetime.now().strftime("%B")
|
month = datetime.now().strftime("%B")
|
||||||
txt = TextDesign(self.__abs_pos__(monthboxsize), fontsize=monthtextsize *
|
txt = TextDesign(self.__abs_pos__(monthboxsize), fontsize=monthtextsize * self.size[1], text=month, verticalalignment="center", horizontalalignment="center")
|
||||||
self.size[1], text=month, verticalalignment="center", horizontalalignment="center")
|
|
||||||
pos = self.__abs_pos__(monthplace)
|
pos = self.__abs_pos__(monthplace)
|
||||||
txt.pos = (pos[0], pos[1] + self.weather_header_height)
|
txt.pos = (pos[0], pos[1] + self.weather_header_height)
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
def __draw_week_row__ (self):
|
def __draw_week_row__ (self):
|
||||||
for day_of_week, day in enumerate(self.__week_days__):
|
for day_of_week, day in enumerate(self.__week_days__):
|
||||||
txt = TextDesign(self.__abs_pos__(weekrownameboxsize), fontsize=weekdaytextsize *
|
txt = TextDesign(self.__abs_pos__(weekrownameboxsize), fontsize=weekdaytextsize * self.size[1], text=str(day), verticalalignment="center", horizontalalignment="center")
|
||||||
self.size[1], text=str(day), verticalalignment="center", horizontalalignment="center")
|
|
||||||
pos = self.__get_week_day_pos__(day_of_week)
|
pos = self.__get_week_day_pos__(day_of_week)
|
||||||
txt.pos = (pos[0], pos[1] + weekdaytextpadding * self.size[1])
|
txt.pos = (pos[0], pos[1] + weekdaytextpadding * self.size[1])
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
self.__draw_highlight_box__(self.__abs_pos__(weekrownameboxsize), self.__get_week_day_pos__(
|
self.__draw_highlight_box__(self.__abs_pos__(weekrownameboxsize), self.__get_week_day_pos__(self.__get_day_of_week__(datetime.now())), width=1)
|
||||||
self.__get_day_of_week__(datetime.now())), width=1)
|
|
||||||
|
|
||||||
def __get_week_day_pos__ (self, day_of_week):
|
def __get_week_day_pos__ (self, day_of_week):
|
||||||
maxwidth, _ = self.__abs_pos__(monthovsize)
|
maxwidth, _ = self.__abs_pos__(monthovsize)
|
||||||
|
@ -190,8 +172,7 @@ class MonthOvPanel (PanelDesign):
|
||||||
|
|
||||||
weekdays = []
|
weekdays = []
|
||||||
for i in range(7):
|
for i in range(7):
|
||||||
weekdays.append(
|
weekdays.append((datetime.now() + timedelta(days=(i + correction))).strftime("%a"))
|
||||||
(datetime.now() + timedelta(days=(i + correction))).strftime("%a"))
|
|
||||||
|
|
||||||
return weekdays
|
return weekdays
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,9 @@ info_height = 0.25
|
||||||
info_padding = 5
|
info_padding = 5
|
||||||
seperator_width = line_thickness
|
seperator_width = line_thickness
|
||||||
|
|
||||||
|
|
||||||
class MonthViewPanel (PanelDesign):
|
class MonthViewPanel (PanelDesign):
|
||||||
"""Displays a grid of the day of the current month
|
"""Displays a grid of the day of the current month
|
||||||
with detailed event descriptions."""
|
with detailed event descriptions."""
|
||||||
|
|
||||||
def __init__(self, size, month = None, year = None):
|
def __init__(self, size, month = None, year = None):
|
||||||
super(MonthViewPanel, self).__init__(size)
|
super(MonthViewPanel, self).__init__(size)
|
||||||
self.day_table = []
|
self.day_table = []
|
||||||
|
@ -89,8 +87,7 @@ class MonthViewPanel (PanelDesign):
|
||||||
pos = (0, self.size[1] - size[1])
|
pos = (0, self.size[1] - size[1])
|
||||||
|
|
||||||
crypto = CryptoListDesign(size, crypto)
|
crypto = CryptoListDesign(size, crypto)
|
||||||
crypto.pos = (pos[0], pos[1] + (size[1] -
|
crypto.pos = (pos[0],pos[1] + (size[1] - crypto.get_estimated_height()))
|
||||||
crypto.get_estimated_height()))
|
|
||||||
self.draw_design(crypto)
|
self.draw_design(crypto)
|
||||||
|
|
||||||
def __finish_panel__(self):
|
def __finish_panel__(self):
|
||||||
|
@ -105,8 +102,7 @@ class MonthViewPanel (PanelDesign):
|
||||||
self.draw_design(table)
|
self.draw_design(table)
|
||||||
|
|
||||||
def __draw_seperator__ (self, height, color):
|
def __draw_seperator__ (self, height, color):
|
||||||
ImageDraw.Draw(self.__image__).line(
|
ImageDraw.Draw(self.__image__).line([ (0, height * self.size[1]), (self.size[0], height * self.size[1]) ], fill=color, width=seperator_width)
|
||||||
[(0, height * self.size[1]), (self.size[0], height * self.size[1])], fill=color, width=seperator_width)
|
|
||||||
|
|
||||||
def __init_day_boxes__(self):
|
def __init_day_boxes__(self):
|
||||||
if week_starts_on == "Monday":
|
if week_starts_on == "Monday":
|
||||||
|
@ -124,8 +120,7 @@ class MonthViewPanel (PanelDesign):
|
||||||
if day == None or day == 0:
|
if day == None or day == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
design = DayBoxDesign(self.day_box_size, date(
|
design = DayBoxDesign(self.day_box_size, date(self.year, self.month, int(day)))
|
||||||
self.year, self.month, int(day)))
|
|
||||||
|
|
||||||
return design
|
return design
|
||||||
|
|
||||||
|
|
|
@ -5,17 +5,14 @@ from datetime import datetime
|
||||||
from settings import units, language
|
from settings import units, language
|
||||||
from Translator import translate
|
from Translator import translate
|
||||||
|
|
||||||
|
|
||||||
class OwmForecasts (WeatherInterface):
|
class OwmForecasts (WeatherInterface):
|
||||||
"""Fetches weather through the Openweathermap-api."""
|
"""Fetches weather through the Openweathermap-api."""
|
||||||
|
|
||||||
def __init__ (self, location, api_key, paid_api=False):
|
def __init__ (self, location, api_key, paid_api=False):
|
||||||
self.subscription = "pro" if paid_api else None
|
self.subscription = "pro" if paid_api else None
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
self.units = units
|
self.units = units
|
||||||
self.location = location
|
self.location = location
|
||||||
self.api = pyowm.OWM(
|
self.api = pyowm.OWM(self.api_key, subscription_type=self.subscription, language=language)
|
||||||
self.api_key, subscription_type=self.subscription, language=language)
|
|
||||||
|
|
||||||
def is_available (self):
|
def is_available (self):
|
||||||
try:
|
try:
|
||||||
|
@ -61,41 +58,30 @@ class OwmForecasts (WeatherInterface):
|
||||||
forecast_object.units = self.units
|
forecast_object.units = self.units
|
||||||
forecast_object.fetch_datetime = datetime.now()
|
forecast_object.fetch_datetime = datetime.now()
|
||||||
forecast_object.location = location
|
forecast_object.location = location
|
||||||
forecast_object.datetime = weather.get_reference_time(
|
forecast_object.datetime = weather.get_reference_time(timeformat='date')
|
||||||
timeformat='date')
|
|
||||||
|
|
||||||
forecast_object.icon = weather.get_weather_icon_name()
|
forecast_object.icon = weather.get_weather_icon_name()
|
||||||
forecast_object.air_humidity = str(weather.get_humidity())
|
forecast_object.air_humidity = str(weather.get_humidity())
|
||||||
forecast_object.clouds = str(weather.get_clouds())
|
forecast_object.clouds = str(weather.get_clouds())
|
||||||
forecast_object.short_description = translate(
|
forecast_object.short_description = translate(str(weather.get_status()))
|
||||||
str(weather.get_status()))
|
forecast_object.detailed_description = str(weather.get_detailed_status())
|
||||||
forecast_object.detailed_description = str(
|
|
||||||
weather.get_detailed_status())
|
|
||||||
forecast_object.air_pressure = str(weather.get_pressure()['press'])
|
forecast_object.air_pressure = str(weather.get_pressure()['press'])
|
||||||
if 'deg' in weather.get_wind().keys():
|
if 'deg' in weather.get_wind().keys():
|
||||||
forecast_object.wind_deg = str(int(weather.get_wind()['deg']))
|
forecast_object.wind_deg = str(int(weather.get_wind()['deg']))
|
||||||
|
|
||||||
if forecast_object.units == "metric":
|
if forecast_object.units == "metric":
|
||||||
forecast_object.air_temperature = str(
|
forecast_object.air_temperature = str(int(weather.get_temperature(unit='celsius')['temp']))
|
||||||
int(weather.get_temperature(unit='celsius')['temp']))
|
forecast_object.wind_speed = str(int(weather.get_wind()['speed'])) #kmh
|
||||||
forecast_object.wind_speed = str(
|
|
||||||
int(weather.get_wind()['speed'])) # kmh
|
|
||||||
|
|
||||||
if forecast_object.units == "aviation":
|
if forecast_object.units == "aviation":
|
||||||
forecast_object.air_temperature = str(
|
forecast_object.air_temperature = str(int(weather.get_temperature(unit='celsius')['temp']))
|
||||||
int(weather.get_temperature(unit='celsius')['temp']))
|
forecast_object.wind_speed = str(int(weather.get_wind()['speed'] * 1.94384)) #knots
|
||||||
forecast_object.wind_speed = str(
|
|
||||||
int(weather.get_wind()['speed'] * 1.94384)) # knots
|
|
||||||
|
|
||||||
if forecast_object.units == "imperial":
|
if forecast_object.units == "imperial":
|
||||||
forecast_object.air_temperature = str(
|
forecast_object.air_temperature = str(int(weather.get_temperature('fahrenheit')['temp']))
|
||||||
int(weather.get_temperature('fahrenheit')['temp']))
|
forecast_object.wind_speed = str(int(weather.get_wind()['speed'] * 0.621)) #mph
|
||||||
forecast_object.wind_speed = str(
|
|
||||||
int(weather.get_wind()['speed'] * 0.621)) # mph
|
|
||||||
|
|
||||||
forecast_object.sunrise = datetime.fromtimestamp(
|
forecast_object.sunrise = datetime.fromtimestamp(int(weather.get_sunrise_time(timeformat='unix')))
|
||||||
int(weather.get_sunrise_time(timeformat='unix')))
|
forecast_object.sunset = datetime.fromtimestamp(int(weather.get_sunset_time(timeformat='unix')))
|
||||||
forecast_object.sunset = datetime.fromtimestamp(
|
|
||||||
int(weather.get_sunset_time(timeformat='unix')))
|
|
||||||
|
|
||||||
return forecast_object
|
return forecast_object
|
||||||
|
|
|
@ -3,28 +3,26 @@ from TechnicalDataDesign import TechnicalDataDesign
|
||||||
from settings import print_technical_data
|
from settings import print_technical_data
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class PanelDesign (DesignEntity):
|
class PanelDesign (DesignEntity):
|
||||||
"""Defined general interface for panel designs."""
|
"""Defined general interface for panel designs."""
|
||||||
|
|
||||||
def __init__ (self, size):
|
def __init__ (self, size):
|
||||||
super(PanelDesign, self).__init__(size)
|
super(PanelDesign, self).__init__(size)
|
||||||
self.start_timestamp = datetime.now()
|
self.start_timestamp = datetime.now()
|
||||||
|
|
||||||
def add_weather (self, weather):
|
def add_weather (self, weather):
|
||||||
pass
|
raise NotImplementedError("Function needs to be implemented")
|
||||||
|
|
||||||
def add_calendar (self, calendar):
|
def add_calendar (self, calendar):
|
||||||
pass
|
raise NotImplementedError("Function needs to be implemented")
|
||||||
|
|
||||||
def add_rssfeed (self, rss):
|
def add_rssfeed (self, rss):
|
||||||
pass
|
raise NotImplementedError("Function needs to be implemented")
|
||||||
|
|
||||||
def add_tasks (self, tasks):
|
def add_tasks (self, tasks):
|
||||||
pass
|
raise NotImplementedError("Function needs to be implemented")
|
||||||
|
|
||||||
def add_crypto (self, crypto):
|
def add_crypto (self, crypto):
|
||||||
pass
|
raise NotImplementedError("Function needs to be implemented")
|
||||||
|
|
||||||
def __finish_panel__(self):
|
def __finish_panel__(self):
|
||||||
pass
|
pass
|
||||||
|
@ -33,7 +31,6 @@ class PanelDesign (DesignEntity):
|
||||||
self.__finish_panel__()
|
self.__finish_panel__()
|
||||||
|
|
||||||
if print_technical_data:
|
if print_technical_data:
|
||||||
td = TechnicalDataDesign(
|
td = TechnicalDataDesign(self.size, self.start_timestamp, datetime.now())
|
||||||
self.size, self.start_timestamp, datetime.now())
|
|
||||||
td.mask = True
|
td.mask = True
|
||||||
self.draw_design(td)
|
self.draw_design(td)
|
|
@ -1,10 +1,8 @@
|
||||||
from DataSourceInterface import DataSourceInterface
|
from DataSourceInterface import DataSourceInterface
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
|
||||||
class RssInterface(DataSourceInterface):
|
class RssInterface(DataSourceInterface):
|
||||||
"""Interface for fetching and processing rss post information."""
|
"""Interface for fetching and processing rss post information."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.loaded_posts = []
|
self.loaded_posts = []
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,8 @@ from urllib.request import urlopen
|
||||||
|
|
||||||
max_range_days = 14
|
max_range_days = 14
|
||||||
|
|
||||||
|
|
||||||
class RssParserPosts (RssInterface):
|
class RssParserPosts (RssInterface):
|
||||||
"""Fetches posts from url-addresses via rss parser."""
|
"""Fetches posts from url-addresses via rss parser."""
|
||||||
|
|
||||||
def __init__(self, urls):
|
def __init__(self, urls):
|
||||||
self.urls = urls
|
self.urls = urls
|
||||||
super(RssParserPosts, self).__init__()
|
super(RssParserPosts, self).__init__()
|
||||||
|
@ -55,3 +53,4 @@ class RssParserPosts (RssInterface):
|
||||||
start_index = link.find('://') + 3
|
start_index = link.find('://') + 3
|
||||||
end_index = link[start_index:].find('/') + start_index
|
end_index = link[start_index:].find('/') + start_index
|
||||||
return link[start_index : end_index]
|
return link[start_index : end_index]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class RssPost(object):
|
class RssPost(object):
|
||||||
"""Defines a rss post, independent of any implementation"""
|
"""Defines a rss post, independent of any implementation"""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
self.title = None
|
self.title = None
|
||||||
self.description = None
|
self.description = None
|
||||||
|
|
|
@ -2,11 +2,9 @@ from DesignEntity import DesignEntity
|
||||||
from TableDesign import TableDesign
|
from TableDesign import TableDesign
|
||||||
from Assets import defaultfontsize
|
from Assets import defaultfontsize
|
||||||
|
|
||||||
|
|
||||||
class RssPostListDesign (DesignEntity):
|
class RssPostListDesign (DesignEntity):
|
||||||
"""Creates a TableDesign filled with rss post
|
"""Creates a TableDesign filled with rss post
|
||||||
date and title"""
|
date and title"""
|
||||||
|
|
||||||
def __init__ (self, size, rssfeed, text_size = defaultfontsize):
|
def __init__ (self, size, rssfeed, text_size = defaultfontsize):
|
||||||
super(RssPostListDesign, self).__init__(size)
|
super(RssPostListDesign, self).__init__(size)
|
||||||
self.rssfeed = rssfeed
|
self.rssfeed = rssfeed
|
||||||
|
@ -16,8 +14,7 @@ class RssPostListDesign (DesignEntity):
|
||||||
def __finish_image__ (self):
|
def __finish_image__ (self):
|
||||||
self.__fill_post_matrix__()
|
self.__fill_post_matrix__()
|
||||||
|
|
||||||
table_design = TableDesign(self.size, line_spacing=2, col_spacing=3, matrix=self.__post_matrix__,
|
table_design = TableDesign(self.size, line_spacing=2, col_spacing=3, matrix=self.__post_matrix__, fontsize = self.text_size, mask=False, wrap=True, truncate_rows=True)
|
||||||
fontsize=self.text_size, mask=False, wrap=True, truncate_rows=True)
|
|
||||||
self.draw_design(table_design)
|
self.draw_design(table_design)
|
||||||
|
|
||||||
def __get_formatted_post__ (self, post):
|
def __get_formatted_post__ (self, post):
|
||||||
|
|
|
@ -5,11 +5,8 @@ from TextFormatter import event_prefix_str_sum
|
||||||
|
|
||||||
font = fonts["regular"]
|
font = fonts["regular"]
|
||||||
|
|
||||||
|
|
||||||
class SingelDayEventListDesign (EventListDesign):
|
class SingelDayEventListDesign (EventListDesign):
|
||||||
"""Specialized event list for day list design."""
|
"""Specialized event list for day list design."""
|
||||||
|
|
||||||
def __init__ (self, size, events, font_size = defaultfontsize, line_spacing=0, event_prefix_rel_dates = [], col_spacing=5, general_color=colors["fg"], background_color=colors["bg"], highlight_color=colors["hl"]):
|
def __init__ (self, size, events, font_size = defaultfontsize, line_spacing=0, event_prefix_rel_dates = [], col_spacing=5, general_color=colors["fg"], background_color=colors["bg"], highlight_color=colors["hl"]):
|
||||||
def prefix_func(x, rel_date): return event_prefix_str_sum(x, rel_date)
|
prefix_func = lambda x, rel_date : event_prefix_str_sum(x, rel_date)
|
||||||
super().__init__(size, events, text_size=font_size, line_spacing=line_spacing, col_spacing=col_spacing, event_prefix_rel_dates=event_prefix_rel_dates,
|
super().__init__(size, events, text_size=font_size, line_spacing=line_spacing, col_spacing=col_spacing, event_prefix_rel_dates = event_prefix_rel_dates, event_prefix_func=prefix_func, font_family=font, show_more_info=True, general_color=general_color, background_color=background_color, highlight_color = highlight_color)
|
||||||
event_prefix_func=prefix_func, font_family=font, show_more_info=True, general_color=general_color, background_color=background_color, highlight_color=highlight_color)
|
|
|
@ -10,14 +10,11 @@ default_props = {
|
||||||
"font-size" : defaultfontsize
|
"font-size" : defaultfontsize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TableDesign (TextDesign):
|
class TableDesign (TextDesign):
|
||||||
"""Gets a matrix with text or designs that is than
|
"""Gets a matrix with text or designs that is than
|
||||||
displayed in a table without borders."""
|
displayed in a table without borders."""
|
||||||
|
|
||||||
def __init__ (self, size, matrix, max_col_size = None, max_row_size = None, font = None, fontsize = defaultfontsize, column_horizontal_alignments = [], mask = True, line_spacing = 0, col_spacing = 0, truncate_rows = True, truncate_cols = True, wrap = False, truncate_text=True, truncate_suffix="...", cell_properties=None, background_color = colors["bg"]):
|
def __init__ (self, size, matrix, max_col_size = None, max_row_size = None, font = None, fontsize = defaultfontsize, column_horizontal_alignments = [], mask = True, line_spacing = 0, col_spacing = 0, truncate_rows = True, truncate_cols = True, wrap = False, truncate_text=True, truncate_suffix="...", cell_properties=None, background_color = colors["bg"]):
|
||||||
super(TableDesign, self).__init__(
|
super(TableDesign, self).__init__(size, font=font, fontsize=fontsize, mask=mask)
|
||||||
size, font=font, fontsize=fontsize, mask=mask)
|
|
||||||
self.__init_image__(background_color)
|
self.__init_image__(background_color)
|
||||||
self.matrix = matrix
|
self.matrix = matrix
|
||||||
self.max_col_size = max_col_size
|
self.max_col_size = max_col_size
|
||||||
|
@ -88,11 +85,9 @@ class TableDesign (TextDesign):
|
||||||
return size
|
return size
|
||||||
elif type(content) == str:
|
elif type(content) == str:
|
||||||
font = self.__get_font__()
|
font = self.__get_font__()
|
||||||
# get width of text in that row/col
|
width = font.getsize(self.matrix[r][c])[0] #get width of text in that row/col
|
||||||
width = font.getsize(self.matrix[r][c])[0]
|
|
||||||
if self.wrap and self.max_col_size != None:
|
if self.wrap and self.max_col_size != None:
|
||||||
content = wrap_text_with_font(
|
content = wrap_text_with_font(content, self.max_col_size[c], font)
|
||||||
content, self.max_col_size[c], font)
|
|
||||||
line_count = content.count('\n') + 1
|
line_count = content.count('\n') + 1
|
||||||
height = font.font.height * line_count #get height of text in that col/row
|
height = font.font.height * line_count #get height of text in that col/row
|
||||||
size = (width, height)
|
size = (width, height)
|
||||||
|
@ -128,8 +123,7 @@ class TableDesign (TextDesign):
|
||||||
bg_color = self.__get_cell_prop__(row, col, "background_color")
|
bg_color = self.__get_cell_prop__(row, col, "background_color")
|
||||||
fontsize = self.__get_cell_prop__(row, col, "font-size")
|
fontsize = self.__get_cell_prop__(row, col, "font-size")
|
||||||
|
|
||||||
design = TextDesign(size, text=self.matrix[row][col], font=self.font_family, color=color, background_color=bg_color, fontsize=fontsize,
|
design = TextDesign(size, text=self.matrix[row][col], font=self.font_family, color=color, background_color=bg_color, fontsize=fontsize, horizontalalignment=self.__get_col_hori_alignment__(col), wrap=self.wrap, truncate=self.truncate_text, truncate_suffix=self.truncate_suffix)
|
||||||
horizontalalignment=self.__get_col_hori_alignment__(col), wrap=self.wrap, truncate=self.truncate_text, truncate_suffix=self.truncate_suffix)
|
|
||||||
design.pos = pos
|
design.pos = pos
|
||||||
design.mask = False
|
design.mask = False
|
||||||
self.draw_design(design)
|
self.draw_design(design)
|
||||||
|
|
|
@ -4,10 +4,8 @@ from Assets import colors
|
||||||
|
|
||||||
font_size = 20
|
font_size = 20
|
||||||
|
|
||||||
|
|
||||||
class TechnicalDataDesign(DesignEntity):
|
class TechnicalDataDesign(DesignEntity):
|
||||||
'''Prints data about the current loop ontop of the panel'''
|
'''Prints data about the current loop ontop of the panel'''
|
||||||
|
|
||||||
def __init__(self, size, start, stop):
|
def __init__(self, size, start, stop):
|
||||||
super(TechnicalDataDesign, self).__init__(size, mask = True)
|
super(TechnicalDataDesign, self).__init__(size, mask = True)
|
||||||
self.start = start
|
self.start = start
|
||||||
|
|
|
@ -5,11 +5,9 @@ from TextWraper import wrap_text_with_font
|
||||||
|
|
||||||
truncateerror_fontsize = 0.5
|
truncateerror_fontsize = 0.5
|
||||||
|
|
||||||
|
|
||||||
class TextDesign (DesignEntity):
|
class TextDesign (DesignEntity):
|
||||||
"""Object that manages all information relevant to text
|
"""Object that manages all information relevant to text
|
||||||
and prints it to an image"""
|
and prints it to an image"""
|
||||||
|
|
||||||
def __init__ (self, size, color=colors["fg"], background_color=colors["bg"], font = None, fontsize = defaultfontsize, text = "", horizontalalignment = "left", verticalalignment = "top", mask=True, truncate=False, truncate_suffix = '...', wrap=False):
|
def __init__ (self, size, color=colors["fg"], background_color=colors["bg"], font = None, fontsize = defaultfontsize, text = "", horizontalalignment = "left", verticalalignment = "top", mask=True, truncate=False, truncate_suffix = '...', wrap=False):
|
||||||
super(TextDesign, self).__init__(size, mask = mask)
|
super(TextDesign, self).__init__(size, mask = mask)
|
||||||
if font is None:
|
if font is None:
|
||||||
|
@ -38,15 +36,12 @@ class TextDesign (DesignEntity):
|
||||||
if self.wrap:
|
if self.wrap:
|
||||||
self.__wrap_text__()
|
self.__wrap_text__()
|
||||||
pos = self.__pos_from_alignment__()
|
pos = self.__pos_from_alignment__()
|
||||||
ImageDraw.Draw(self.__image__).text(
|
ImageDraw.Draw(self.__image__).text(pos, self.text, fill=self.color, font=self.__font__)
|
||||||
pos, self.text, fill=self.color, font=self.__font__)
|
|
||||||
|
|
||||||
def __truncate_text__ (self):
|
def __truncate_text__ (self):
|
||||||
# does not need truncating
|
if self.__font__.getsize_multiline(self.text)[0] <= self.size[0]: #does not need truncating
|
||||||
if self.__font__.getsize_multiline(self.text)[0] <= self.size[0]:
|
|
||||||
return
|
return
|
||||||
suffix_length = self.__font__.getsize_multiline(
|
suffix_length = self.__font__.getsize_multiline(self.truncate_suffix)[0]
|
||||||
self.truncate_suffix)[0]
|
|
||||||
while len(self.text) > 1 and self.__font__.getsize_multiline(self.text)[0] + suffix_length >= self.size[0]:
|
while len(self.text) > 1 and self.__font__.getsize_multiline(self.text)[0] + suffix_length >= self.size[0]:
|
||||||
self.text = self.text[0:-1]
|
self.text = self.text[0:-1]
|
||||||
self.text = self.text.rstrip(' ')
|
self.text = self.text.rstrip(' ')
|
||||||
|
|
|
@ -12,7 +12,6 @@ until_character = ' - '
|
||||||
allday_character = "•"
|
allday_character = "•"
|
||||||
multiday_character = allday_character + allday_character
|
multiday_character = allday_character + allday_character
|
||||||
|
|
||||||
|
|
||||||
def time_str (dt):
|
def time_str (dt):
|
||||||
if hours is "12":
|
if hours is "12":
|
||||||
return dt.strftime("%I:%M%p")
|
return dt.strftime("%I:%M%p")
|
||||||
|
@ -21,7 +20,6 @@ def time_str(dt):
|
||||||
else:
|
else:
|
||||||
return str(dt)
|
return str(dt)
|
||||||
|
|
||||||
|
|
||||||
def event_prefix_str_md_dif (event, relative_date=None):
|
def event_prefix_str_md_dif (event, relative_date=None):
|
||||||
if relative_date is None:
|
if relative_date is None:
|
||||||
relative_date = event.begin_datetime.date()
|
relative_date = event.begin_datetime.date()
|
||||||
|
@ -44,7 +42,6 @@ def event_prefix_str_md_dif(event, relative_date=None):
|
||||||
event.allday = True
|
event.allday = True
|
||||||
return multiday_end_character + event_time_summary(event) + multiday_begin_character
|
return multiday_end_character + event_time_summary(event) + multiday_begin_character
|
||||||
|
|
||||||
|
|
||||||
def event_prefix_str (event, relative_date=None):
|
def event_prefix_str (event, relative_date=None):
|
||||||
if relative_date is None:
|
if relative_date is None:
|
||||||
relative_date = event.begin_datetime.date()
|
relative_date = event.begin_datetime.date()
|
||||||
|
@ -54,7 +51,6 @@ def event_prefix_str(event, relative_date=None):
|
||||||
else:
|
else:
|
||||||
return event_time_detailed(event)
|
return event_time_detailed(event)
|
||||||
|
|
||||||
|
|
||||||
def event_prefix_str_sum (event, relative_date=None):
|
def event_prefix_str_sum (event, relative_date=None):
|
||||||
if relative_date is None:
|
if relative_date is None:
|
||||||
relative_date = event.begin_datetime.date()
|
relative_date = event.begin_datetime.date()
|
||||||
|
@ -64,31 +60,26 @@ def event_prefix_str_sum(event, relative_date=None):
|
||||||
else:
|
else:
|
||||||
return event_time_summary(event)
|
return event_time_summary(event)
|
||||||
|
|
||||||
|
|
||||||
def event_time_summary (event):
|
def event_time_summary (event):
|
||||||
if event.allday:
|
if event.allday:
|
||||||
return allday_character
|
return allday_character
|
||||||
else:
|
else:
|
||||||
return time_str(event.begin_datetime)
|
return time_str(event.begin_datetime)
|
||||||
|
|
||||||
|
|
||||||
def event_time_detailed (event):
|
def event_time_detailed (event):
|
||||||
if event.allday:
|
if event.allday:
|
||||||
return get_text(allday_events)
|
return get_text(allday_events)
|
||||||
else:
|
else:
|
||||||
return time_str(event.begin_datetime) + until_character + time_str(event.end_datetime)
|
return time_str(event.begin_datetime) + until_character + time_str(event.end_datetime)
|
||||||
|
|
||||||
|
|
||||||
def date_str(dt):
|
def date_str(dt):
|
||||||
return remove_leading_zero(dt.strftime('%d. %b'))
|
return remove_leading_zero(dt.strftime('%d. %b'))
|
||||||
|
|
||||||
|
|
||||||
def remove_leading_zero (text):
|
def remove_leading_zero (text):
|
||||||
while text[0] is '0':
|
while text[0] is '0':
|
||||||
text = text[1:]
|
text = text[1:]
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def date_summary_str(dt):
|
def date_summary_str(dt):
|
||||||
day = remove_leading_zero(dt.strftime("%d"))
|
day = remove_leading_zero(dt.strftime("%d"))
|
||||||
if language is "en":
|
if language is "en":
|
||||||
|
@ -98,13 +89,11 @@ def date_summary_str(dt):
|
||||||
else:
|
else:
|
||||||
return dt.strftime('%a ' + day + '. %b')
|
return dt.strftime('%a ' + day + '. %b')
|
||||||
|
|
||||||
|
|
||||||
def __equal__(dt1, dt2):
|
def __equal__(dt1, dt2):
|
||||||
return dt1.day == dt2.day and \
|
return dt1.day == dt2.day and \
|
||||||
dt1.month == dt2.month and \
|
dt1.month == dt2.month and \
|
||||||
dt1.year == dt2.year
|
dt1.year == dt2.year
|
||||||
|
|
||||||
|
|
||||||
def __day_duration__(dt):
|
def __day_duration__(dt):
|
||||||
day_begin = datetime(dt.year, dt.month, dt.day, 0, 0, 0, 0, timezone.utc)
|
day_begin = datetime(dt.year, dt.month, dt.day, 0, 0, 0, 0, timezone.utc)
|
||||||
return dt - day_begin
|
return dt - day_begin
|
|
@ -1,11 +1,10 @@
|
||||||
from Assets import path, defaultfont
|
from Assets import path, defaultfont
|
||||||
from PIL import ImageFont
|
from PIL import ImageFont
|
||||||
|
|
||||||
|
|
||||||
def wrap_text_with_font (text, width, font):
|
def wrap_text_with_font (text, width, font):
|
||||||
words = text.split(' ')
|
words = text.split(' ')
|
||||||
result = ""
|
result = ""
|
||||||
for word in words:
|
for index, word in enumerate(words):
|
||||||
until_current = (result + " " + word).strip()
|
until_current = (result + " " + word).strip()
|
||||||
txt_width, _ = font.getsize_multiline(until_current)
|
txt_width, _ = font.getsize_multiline(until_current)
|
||||||
if txt_width > width:
|
if txt_width > width:
|
||||||
|
@ -15,6 +14,5 @@ def wrap_text_with_font(text, width, font):
|
||||||
result += word
|
result += word
|
||||||
return result.strip()
|
return result.strip()
|
||||||
|
|
||||||
|
|
||||||
def wrap_text (text, width, font_size, font_family = defaultfont):
|
def wrap_text (text, width, font_size, font_family = defaultfont):
|
||||||
return wrap_text_with_font(text, width, ImageFont.truetype(path + font_family, int(font_size)))
|
return wrap_text_with_font(text, width, ImageFont.truetype(path + font_family, int(font_size)))
|
|
@ -4,7 +4,6 @@ from settings import language
|
||||||
'''Looks up a phrase in a given dictionary-collection
|
'''Looks up a phrase in a given dictionary-collection
|
||||||
and returns the translated phrase'''
|
and returns the translated phrase'''
|
||||||
|
|
||||||
|
|
||||||
def translate(phrase, target_lang = language, dictionary_collection = dictionary_collection) :
|
def translate(phrase, target_lang = language, dictionary_collection = dictionary_collection) :
|
||||||
dictionary = find_dictionary(phrase, dictionary_collection)
|
dictionary = find_dictionary(phrase, dictionary_collection)
|
||||||
|
|
||||||
|
@ -18,7 +17,6 @@ def translate(phrase, target_lang=language, dictionary_collection=dictionary_col
|
||||||
else:
|
else:
|
||||||
return dictionary[default_language]
|
return dictionary[default_language]
|
||||||
|
|
||||||
|
|
||||||
def find_dictionary(phrase, dictionary_collection = dictionary_collection):
|
def find_dictionary(phrase, dictionary_collection = dictionary_collection):
|
||||||
for dictionary in dictionary_collection:
|
for dictionary in dictionary_collection:
|
||||||
if phrase in dictionary.values():
|
if phrase in dictionary.values():
|
||||||
|
|
|
@ -13,10 +13,8 @@ info_yresize = -0.05
|
||||||
fontsize_static = defaultfontsize
|
fontsize_static = defaultfontsize
|
||||||
max_symbol_y_width = 0.15
|
max_symbol_y_width = 0.15
|
||||||
|
|
||||||
|
|
||||||
class WeatherColumnDesign (DesignEntity):
|
class WeatherColumnDesign (DesignEntity):
|
||||||
"""Displays weather information in a column"""
|
"""Displays weather information in a column"""
|
||||||
|
|
||||||
def __init__ (self, size, forecast):
|
def __init__ (self, size, forecast):
|
||||||
super().__init__(size)
|
super().__init__(size)
|
||||||
self.forecast = forecast
|
self.forecast = forecast
|
||||||
|
@ -30,8 +28,7 @@ class WeatherColumnDesign (DesignEntity):
|
||||||
self.__draw_infos__(self.forecast)
|
self.__draw_infos__(self.forecast)
|
||||||
|
|
||||||
def __draw_infos__ (self, forecast):
|
def __draw_infos__ (self, forecast):
|
||||||
temperature = forecast.air_temperature + \
|
temperature = forecast.air_temperature + " " + self.__get_unit__(("°C", "°F"))
|
||||||
" " + self.__get_unit__(("°C", "°F"))
|
|
||||||
humidity = forecast.air_humidity + "%"
|
humidity = forecast.air_humidity + "%"
|
||||||
if forecast.units== "aviation":
|
if forecast.units== "aviation":
|
||||||
if forecast.wind_deg == None:
|
if forecast.wind_deg == None:
|
||||||
|
@ -41,15 +38,11 @@ class WeatherColumnDesign (DesignEntity):
|
||||||
elif len(forecast.wind_deg)==2:
|
elif len(forecast.wind_deg)==2:
|
||||||
forecast.wind_deg = "0" + forecast.wind_deg
|
forecast.wind_deg = "0" + forecast.wind_deg
|
||||||
if int(forecast.wind_speed)<10:
|
if int(forecast.wind_speed)<10:
|
||||||
windspeed = forecast.wind_deg + "@" + "0" + forecast.wind_speed + \
|
windspeed = forecast.wind_deg + "@" + "0" + forecast.wind_speed + self.__get_unit__(("", "")) #added degrees, if wind<10 add a 0 to make two digit
|
||||||
self.__get_unit__(
|
|
||||||
("", "")) # added degrees, if wind<10 add a 0 to make two digit
|
|
||||||
else:
|
else:
|
||||||
windspeed = forecast.wind_deg + "@" + forecast.wind_speed + \
|
windspeed = forecast.wind_deg + "@" + forecast.wind_speed + self.__get_unit__(("", "")) #added degrees
|
||||||
self.__get_unit__(("", "")) # added degrees
|
|
||||||
else:
|
else:
|
||||||
windspeed = forecast.wind_speed + " " + \
|
windspeed = forecast.wind_speed + " " + self.__get_unit__(("km/h", "mph"))
|
||||||
self.__get_unit__(("km/h", "mph"))
|
|
||||||
|
|
||||||
numbers_list = [ [ forecast.short_description ],
|
numbers_list = [ [ forecast.short_description ],
|
||||||
[ temperature ],
|
[ temperature ],
|
||||||
|
@ -58,13 +51,10 @@ class WeatherColumnDesign (DesignEntity):
|
||||||
|
|
||||||
ypos = info_x_ypos * self.size[0]
|
ypos = info_x_ypos * self.size[0]
|
||||||
pos = (0, ypos)
|
pos = (0, ypos)
|
||||||
size = (self.size[0], self.size[1] +
|
size = (self.size[0], self.size[1] + info_yresize * self.size[1] - pos[1])
|
||||||
info_yresize * self.size[1] - pos[1])
|
line_spacing = (size[1] - len(numbers_list) * fontsize_static) / (len(numbers_list) + 1)
|
||||||
line_spacing = (size[1] - len(numbers_list) *
|
|
||||||
fontsize_static) / (len(numbers_list) + 1)
|
|
||||||
|
|
||||||
table = TableDesign(size, numbers_list, fontsize=fontsize_static, line_spacing=line_spacing, column_horizontal_alignments=[
|
table = TableDesign(size, numbers_list, fontsize=fontsize_static, line_spacing=line_spacing, column_horizontal_alignments=[ "center" ], max_col_size=[ size[0] ], truncate_text=False, truncate_rows=False)
|
||||||
"center"], max_col_size=[size[0]], truncate_text=False, truncate_rows=False)
|
|
||||||
table.pos = pos
|
table.pos = pos
|
||||||
self.draw_design(table)
|
self.draw_design(table)
|
||||||
|
|
||||||
|
@ -75,8 +65,7 @@ class WeatherColumnDesign (DesignEntity):
|
||||||
ypos = icon_x_ypos * self.size[0]
|
ypos = icon_x_ypos * self.size[0]
|
||||||
pos = (xpos, ypos)
|
pos = (xpos, ypos)
|
||||||
|
|
||||||
self.__draw_resized_path_at__(
|
self.__draw_resized_path_at__(wpath + weathericons[icon_id] + ".jpeg", pos, size)
|
||||||
wpath + weathericons[icon_id] + ".jpeg", pos, size)
|
|
||||||
|
|
||||||
def __draw_no_response__ (self):
|
def __draw_no_response__ (self):
|
||||||
width = int(icon_width * self.size[0])
|
width = int(icon_width * self.size[0])
|
||||||
|
@ -96,6 +85,7 @@ class WeatherColumnDesign (DesignEntity):
|
||||||
resized_img = img.resize(size, resample=Image.LANCZOS)
|
resized_img = img.resize(size, resample=Image.LANCZOS)
|
||||||
self.draw(resized_img, pos)
|
self.draw(resized_img, pos)
|
||||||
|
|
||||||
|
|
||||||
def __get_unit__ (self, tuple):
|
def __get_unit__ (self, tuple):
|
||||||
if self.forecast.units == "metric" or self.forecast.units == "aviation":
|
if self.forecast.units == "metric" or self.forecast.units == "aviation":
|
||||||
return tuple[0]
|
return tuple[0]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class WeatherForecast (object):
|
class WeatherForecast (object):
|
||||||
"""Defines a weather forecast, independent of any implementation"""
|
"""Defines a weather forecast, independent of any implementation"""
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
self.air_temperature = None
|
self.air_temperature = None
|
||||||
self.air_pressure = None
|
self.air_pressure = None
|
||||||
|
|
|
@ -10,10 +10,8 @@ windiconspace = (0.206, 0)
|
||||||
sunriseplace = (0.55, 0)
|
sunriseplace = (0.55, 0)
|
||||||
sunsetplace = (0.55, 0.486)
|
sunsetplace = (0.55, 0.486)
|
||||||
|
|
||||||
|
|
||||||
class WeatherHeaderDesign (DesignEntity):
|
class WeatherHeaderDesign (DesignEntity):
|
||||||
"""Defines a top area that displays basic weather information"""
|
"""Defines a top area that displays basic weather information"""
|
||||||
|
|
||||||
def __init__ (self, size, weather):
|
def __init__ (self, size, weather):
|
||||||
super(WeatherHeaderDesign, self).__init__(size)
|
super(WeatherHeaderDesign, self).__init__(size)
|
||||||
self.__weather__ = weather
|
self.__weather__ = weather
|
||||||
|
@ -30,8 +28,7 @@ class WeatherHeaderDesign (DesignEntity):
|
||||||
self.__render_missing_connection__()
|
self.__render_missing_connection__()
|
||||||
return
|
return
|
||||||
|
|
||||||
temperature = cur_weather.air_temperature + \
|
temperature = cur_weather.air_temperature + " " + self.__get_unit__(("°C", "°F"))
|
||||||
" " + self.__get_unit__(("°C", "°F"))
|
|
||||||
if units== "aviation": #pick up aviation
|
if units== "aviation": #pick up aviation
|
||||||
if cur_weather.wind_deg == None:
|
if cur_weather.wind_deg == None:
|
||||||
cur_weather.wind_deg = ""
|
cur_weather.wind_deg = ""
|
||||||
|
@ -40,35 +37,25 @@ class WeatherHeaderDesign (DesignEntity):
|
||||||
elif len(cur_weather.wind_deg)==2:
|
elif len(cur_weather.wind_deg)==2:
|
||||||
cur_weather.wind_deg = "0" + cur_weather.wind_deg
|
cur_weather.wind_deg = "0" + cur_weather.wind_deg
|
||||||
if int(cur_weather.wind_speed)<10:
|
if int(cur_weather.wind_speed)<10:
|
||||||
windspeed = cur_weather.wind_deg + "@" + "0" + cur_weather.wind_speed + \
|
windspeed = cur_weather.wind_deg + "@" + "0" + cur_weather.wind_speed + self.__get_unit__(("", "")) #added degrees, if wind<10 add a 0 to make two digit
|
||||||
self.__get_unit__(
|
|
||||||
("", "")) # added degrees, if wind<10 add a 0 to make two digit
|
|
||||||
else:
|
else:
|
||||||
windspeed = cur_weather.wind_deg + "@" + cur_weather.wind_speed + \
|
windspeed = cur_weather.wind_deg + "@" + cur_weather.wind_speed + self.__get_unit__(("", "")) #added degrees
|
||||||
self.__get_unit__(("", "")) # added degrees
|
|
||||||
else:
|
else:
|
||||||
windspeed = cur_weather.wind_speed + " " + \
|
windspeed = cur_weather.wind_speed + " " + self.__get_unit__(("km/h", "mph"))
|
||||||
self.__get_unit__(("km/h", "mph"))
|
|
||||||
|
|
||||||
self.__draw_text__(temperature, self.__abs_pos__((0.87, 0)), (50,35))
|
self.__draw_text__(temperature, self.__abs_pos__((0.87, 0)), (50,35))
|
||||||
self.__draw_text__(windspeed, self.__abs_pos__(
|
self.__draw_text__(windspeed, self.__abs_pos__((0.297, 0.05)), (100,35))
|
||||||
(0.297, 0.05)), (100, 35))
|
self.__draw_text__(self.__get_time__(cur_weather.sunrise), self.__abs_pos__((0.64,0)), (50,35))
|
||||||
self.__draw_text__(self.__get_time__(cur_weather.sunrise),
|
self.__draw_text__(self.__get_time__(cur_weather.sunset), self.__abs_pos__((0.64,0.486)), (50,35))
|
||||||
self.__abs_pos__((0.64, 0)), (50, 35))
|
self.__draw_text__(cur_weather.air_humidity + " %", self.__abs_pos__((0.87,0.486)), (50,35))
|
||||||
self.__draw_text__(self.__get_time__(cur_weather.sunset),
|
self.__draw_text__(cur_weather.short_description, self.__abs_pos__((0.182,0.486)), (144,35))
|
||||||
self.__abs_pos__((0.64, 0.486)), (50, 35))
|
|
||||||
self.__draw_text__(cur_weather.air_humidity + " %",
|
|
||||||
self.__abs_pos__((0.87, 0.486)), (50, 35))
|
|
||||||
self.__draw_text__(cur_weather.short_description,
|
|
||||||
self.__abs_pos__((0.182, 0.486)), (144, 35))
|
|
||||||
|
|
||||||
self.draw(windicon, self.__abs_pos__(windiconspace))
|
self.draw(windicon, self.__abs_pos__(windiconspace))
|
||||||
self.draw(sunseticon, self.__abs_pos__(sunsetplace))
|
self.draw(sunseticon, self.__abs_pos__(sunsetplace))
|
||||||
self.draw(sunriseicon, self.__abs_pos__(sunriseplace))
|
self.draw(sunriseicon, self.__abs_pos__(sunriseplace))
|
||||||
self.draw(humicon, self.__abs_pos__(humplace))
|
self.draw(humicon, self.__abs_pos__(humplace))
|
||||||
self.draw(tempicon, self.__abs_pos__(tempplace))
|
self.draw(tempicon, self.__abs_pos__(tempplace))
|
||||||
self.draw_image(
|
self.draw_image(wpath + weathericons[cur_weather.icon] + '.jpeg', self.__abs_pos__(wiconplace))
|
||||||
wpath + weathericons[cur_weather.icon] + '.jpeg', self.__abs_pos__(wiconplace))
|
|
||||||
|
|
||||||
def __render_missing_connection__ (self):
|
def __render_missing_connection__ (self):
|
||||||
self.draw(no_response, self.__abs_pos__(wiconplace))
|
self.draw(no_response, self.__abs_pos__(wiconplace))
|
||||||
|
@ -77,8 +64,7 @@ class WeatherHeaderDesign (DesignEntity):
|
||||||
return (int(pos[0] * self.size[0]), int(pos[1] * self.size[1]))
|
return (int(pos[0] * self.size[0]), int(pos[1] * self.size[1]))
|
||||||
|
|
||||||
def __draw_text__ (self, text, pos, size):
|
def __draw_text__ (self, text, pos, size):
|
||||||
txt = TextDesign(size, fontsize=18, text=text,
|
txt = TextDesign(size, fontsize=18, text=text, verticalalignment="center", horizontalalignment="center")
|
||||||
verticalalignment="center", horizontalalignment="center")
|
|
||||||
txt.pos = pos
|
txt.pos = pos
|
||||||
self.draw_design(txt)
|
self.draw_design(txt)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
from DataSourceInterface import DataSourceInterface
|
from DataSourceInterface import DataSourceInterface
|
||||||
|
|
||||||
|
|
||||||
class WeatherInterface (DataSourceInterface):
|
class WeatherInterface (DataSourceInterface):
|
||||||
"""Interface for fetching and processing weather forecast information."""
|
"""Interface for fetching and processing weather forecast information."""
|
||||||
|
|
||||||
def get_forecast_in_days (self, offset_by_days, location=None):
|
def get_forecast_in_days (self, offset_by_days, location=None):
|
||||||
raise NotImplementedError("Functions needs to be implemented")
|
raise NotImplementedError("Functions needs to be implemented")
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
""" To quickly get started, fill in the following details:"""
|
""" To quickly get started, fill in the following details:"""
|
||||||
|
|
||||||
ical_urls = [
|
ical_urls = [
|
||||||
|
"https://calendar.google.com/calendar/ical/en.usa%23holiday%40group.v.calendar.google.com/public/basic.ics"
|
||||||
]
|
]
|
||||||
|
|
||||||
highlighted_ical_urls = [
|
highlighted_ical_urls = [
|
||||||
]
|
]
|
||||||
|
|
||||||
rss_feeds = [
|
rss_feeds = [
|
||||||
|
"http://feeds.bbci.co.uk/news/world/rss.xml#"
|
||||||
]
|
]
|
||||||
|
|
||||||
crypto_coins = [
|
crypto_coins = [
|
||||||
|
"bitcoin",
|
||||||
|
"litecoin",
|
||||||
|
"ethereum",
|
||||||
|
"binancecoin"
|
||||||
]
|
]
|
||||||
|
|
||||||
api_key = ""
|
api_key = ""
|
||||||
owm_paid_subscription = False
|
owm_paid_subscription = False
|
||||||
location = "Berlin, DE"
|
location = "Julich, DE"
|
||||||
week_starts_on = "Monday"
|
week_starts_on = "Monday"
|
||||||
display_colours = "bwr"
|
display_colours = "bwr"
|
||||||
language = "en"
|
language = "en"
|
||||||
|
@ -22,22 +28,17 @@ datetime_encoding = "UTF-8" # UTF-8
|
||||||
units = "metric" #aviation (celcius,degrees/knots), metric (celcius,kmh), imperial(f,mph)
|
units = "metric" #aviation (celcius,degrees/knots), metric (celcius,kmh), imperial(f,mph)
|
||||||
hours = "24"
|
hours = "24"
|
||||||
update_interval = 60
|
update_interval = 60
|
||||||
max_loop_count = 0 # From 0 to 86400 representing the maximum number the loop is executed, with 0 being unlimited
|
|
||||||
run_on_hour = True # If True, updates calendar every full hour, ignoring differing update_interval
|
|
||||||
|
|
||||||
|
|
||||||
"""DESIGN"""
|
"""DESIGN"""
|
||||||
font_size = 14 # does not affect every text
|
font_size = 14 # does not affect every text
|
||||||
font_boldness = "semibold" # extralight, light, regular, semibold, bold, extrabold
|
font_boldness = "semibold" # extralight, light, regular, semibold, bold, extrabold
|
||||||
line_thickness = 1 # 1-3 Thickness advised
|
line_thickness = 1 # 1-3 Thickness advised
|
||||||
choosen_design = "month-overview" # month-overview, day-list, day-view, day-focus-list, agenda-list, month-view, image-frame
|
choosen_design = "month-overview" # month-overview, day-list, day-view, agenda-list, month-view
|
||||||
general_settings = { # General settings that designs may use
|
general_settings = { # General settings that designs may use
|
||||||
"info-area" : "rss", # empty, events, rss, crypto
|
"info-area" : "rss", # empty, events, rss, crypto
|
||||||
"highlight-event-days" : True,
|
"highlight-event-days" : True,
|
||||||
"weather-info" : True,
|
"weather-info" : True
|
||||||
"image-folder" : "",
|
|
||||||
"overlay-image" : "", # Size must be 384x640px with default display
|
|
||||||
"extra-excluded-urls" : []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
|
@ -21,7 +21,7 @@ if [ -z "$option" ]; then
|
||||||
fi
|
fi
|
||||||
if [ "$option" = 3 ]; then
|
if [ "$option" = 3 ]; then
|
||||||
echo -e "Removing the E-Paper software now..."
|
echo -e "Removing the E-Paper software now..."
|
||||||
pip3 uninstall feedparser -y && sudo pip3 uninstall feedparser -y && pip3 uninstall numpy -y && sudo pip3 uninstall numpy -y && pip3 uninstall Pillow -y && sudo pip3 uninstall Pillow -y && sudo pip3 uninstall pyowm -y&& sudo pip3 uninstall ics -y && pip3 uninstall pyowm -y && pip3 uninstall ics -y && sudo apt-get remove supervisor -y && sudo apt-get clean && sudo apt-get autoremove -y
|
pip3 uninstall json -y && sudo pip3 uninstall json -y && pip3 uninstall feedparser -y && sudo pip3 uninstall feedparser -y && pip3 uninstall numpy -y && sudo pip3 uninstall numpy -y && pip3 uninstall Pillow -y && sudo pip3 uninstall Pillow -y && sudo pip3 uninstall pyowm -y&& sudo pip3 uninstall ics -y && pip3 uninstall pyowm -y && pip3 uninstall ics -y && sudo apt-get remove supervisor -y && sudo apt-get clean && sudo apt-get autoremove -y
|
||||||
if [ -e /etc/supervisor/conf.d/E-Paper.conf ]; then
|
if [ -e /etc/supervisor/conf.d/E-Paper.conf ]; then
|
||||||
sudo rm /etc/supervisor/conf.d/E-Paper.conf
|
sudo rm /etc/supervisor/conf.d/E-Paper.conf
|
||||||
fi
|
fi
|
||||||
|
@ -83,10 +83,12 @@ if [ "$option" = 2 ]; then
|
||||||
sudo pip3 install ics
|
sudo pip3 install ics
|
||||||
sudo pip3 install feedparser
|
sudo pip3 install feedparser
|
||||||
sudo pip3 install numpy
|
sudo pip3 install numpy
|
||||||
|
sudo pip3 install json
|
||||||
pip3 install pyowm
|
pip3 install pyowm
|
||||||
pip3 install ics
|
pip3 install ics
|
||||||
pip3 install feedparser
|
pip3 install feedparser
|
||||||
pip3 install numpy
|
pip3 install numpy
|
||||||
|
pip3 install json
|
||||||
echo -e "\e[1;36m"Finished installing libraries"\e[0m"
|
echo -e "\e[1;36m"Finished installing libraries"\e[0m"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
33
README.md
33
README.md
|
@ -1,9 +1,8 @@
|
||||||
# NOT ACTIVELY MAINTAINED ANY LONGER
|
|
||||||
Since [aceisace/Inkycal](https://github.com/aceisace/Inkycal) has grown significantly and is far superior in many ways, I will no longer focus on mainting my own fork. The ical implementation might also not be super reliable in this project.
|
|
||||||
|
|
||||||
# E-Paper Calendar
|
# E-Paper Calendar
|
||||||
|
|
||||||
This is a software written in python3 that allows you to transform an E-Paper display (like the kindle) into an information display. It fetches live data from Openweathermap (a weather info provider), rss-feeds and your Online Calendar (Google/Yahoo Calendar/...) and displays them on a large, beautiful and ultra-low power E-Paper display. It's ideal for staying organised and keeping track of important details without having to check them up online. It can also be used as an image frame.
|
[](https://app.codacy.com/app/m.giller.dev/E-Paper-Calendar?utm_source=github.com&utm_medium=referral&utm_content=mgfcf/E-Paper-Calendar&utm_campaign=Badge_Grade_Dashboard)
|
||||||
|
|
||||||
|
This is a software written in python3 that allows you to transform an E-Paper display (like the kindle) into an information display. It fetches live data from Openweathermap (a weather info provider), rss-feeds and your Online Calendar (Google/Yahoo Calendar/...) and displays them on a large, beautiful and ultra-low power E-Paper display. It's ideal for staying organised and keeping track of important details without having to check them up online.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
|
@ -16,21 +15,18 @@ This software fully supports the 3-Colour **and** 2-Colour version of the 7.5" E
|
||||||
* **Added Support for the 2-Colour E-Paper Display as well!** (Late September 2018)
|
* **Added Support for the 2-Colour E-Paper Display as well!** (Late September 2018)
|
||||||
* **Added Support for Raspbian Stretch lite.** (Late September 2018)
|
* **Added Support for Raspbian Stretch lite.** (Late September 2018)
|
||||||
|
|
||||||
<img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/day-list_example.png" width="270"><img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/month-overview_example.png" width="270"><img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/agenda-list_example.png" width="270"><img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/day-view_example.png" width="270"><img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/day-focus-list_example.png" width="270"><img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/image-frame_example.png" width="270">
|
<img src="https://github.com/mgfcf/E-Paper-Calendar/blob/master/Gallery/day-list_example.png" width="270"><img src="https://github.com/mgfcf/E-Paper-Calendar/blob/master/Gallery/month-overview_example.png" width="270"><img src="https://github.com/mgfcf/E-Paper-Calendar/blob/master/Gallery/agenda-list_example.png" width="270"><img src="https://github.com/mgfcf/E-Paper-Calendar/blob/master/Gallery/day-view_example.png" width="270">
|
||||||
|
|
||||||
1.: Day-List Panel 
|
1.: Day-List Panel
|
||||||
2.: Month-Overview Panel 
|
2.: Month-Overview Panel
|
||||||
3.: Agenda-List Panel<br>
|
3.: Agenda-List Panel
|
||||||
4.: Day-View Panel 
|
4.: Day-View Panel
|
||||||
5.: Day-Focus-List Panel 
|
|
||||||
6.: Image-Frame Panel
|
|
||||||
|
|
||||||
## Main features
|
## Main features
|
||||||
* Display a calendar with one of multiple designes
|
* Display a calendar with one of multiple designes
|
||||||
* Optionally get RSS-Feed fetched and shown
|
* Optionally get RSS-Feed fetched and shown
|
||||||
* Syncronise events from any online calendar (like google, yahoo, etc.)
|
* Syncronise events from any online calendar (like google, yahoo, etc.)
|
||||||
* Get live weather data (including temperature, humidity, etc.) using openweathermap api
|
* Get live weather data (including temperature, humidity, etc.) using openweathermap api
|
||||||
* Only show a slideshow of images
|
|
||||||
|
|
||||||
## Hardware required
|
## Hardware required
|
||||||
* 7.5" 3-Colour E-Paper Display (Black, White, Red/Yellow) with driver hat from [waveshare](https://www.waveshare.com/product/7.5inch-e-paper-hat-b.htm)
|
* 7.5" 3-Colour E-Paper Display (Black, White, Red/Yellow) with driver hat from [waveshare](https://www.waveshare.com/product/7.5inch-e-paper-hat-b.htm)
|
||||||
|
@ -63,7 +59,7 @@ If the Installer should fail for any reason, kindly open an issue and paste the
|
||||||
|
|
||||||
This is how the installer will run:
|
This is how the installer will run:
|
||||||
|
|
||||||
<img src="https://code.giller.dev/m.giller/E-Paper-Calendar/raw/branch/master/Gallery/Installer-v1.2-screenshot.png" width="700">
|
<img src="https://github.com/mgfcf/E-Paper-Calendar/blob/master/Gallery/Installer-v1.2-screenshot.png" width="700">
|
||||||
|
|
||||||
## Adding details to the programm
|
## Adding details to the programm
|
||||||
Once the packages are installed, navigate to the home directory, open 'E-Paper-Master' and open the file 'settings.py' inside the Calendar folder. Adjust the values as needed. You can use the table below as a reference.
|
Once the packages are installed, navigate to the home directory, open 'E-Paper-Master' and open the file 'settings.py' inside the Calendar folder. Adjust the values as needed. You can use the table below as a reference.
|
||||||
|
@ -72,20 +68,18 @@ Once the packages are installed, navigate to the home directory, open 'E-Paper-M
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| ical_urls | Your iCalendar URL/s. To add more than one URL, seperate each with a comma. |
|
| ical_urls | Your iCalendar URL/s. To add more than one URL, seperate each with a comma. |
|
||||||
| highlighted_ical_urls | Your iCalendar URL/s that should be higlighted in comparison to the ical_urls. To add more than one URL, seperate each with a comma. |
|
| highlighted_ical_urls | Your iCalendar URL/s that should be higlighted in comparison the ical_urls. To add more than one URL, seperate each with a comma. |
|
||||||
| rss_feeds | All the sources for your rss-feed. To add more than one URL, seperate each with a comma. |
|
| rss_feeds | All the sources for your rss-feed. To add more than one URL, seperate each with a comma. |
|
||||||
| api_key | Your __personal__ openweathermap API-key which you can generate and find in your Account info. |
|
| api_key | Your __personal__ openweathermap API-key which you can generate and find in your Account info. |
|
||||||
| owm_paid_subscription | If you have a paid owm subscription you can set it to `True` and in some panels receive forecast information. |
|
| owm_paid_subscription | If you have a paid owm subscription you can set it to `True` and in some panels receive forecast information. |
|
||||||
| 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/). |
|
| 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 work start in your region? Possible options are `"Monday"` or `"Sunday"`. |
|
| week_starts_on | When does the work start on your Region? Possible options are `"Monday"` or `"Sunday"`. |
|
||||||
| 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.|
|
| 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 | Sets the language and the related locale for datetime-information. Some texts depend on additional translations that can be added to the dictionary-file.|
|
| language | Sets the language and the related locale for datetime-information. Some texts depend on additional translations that can be added to the dictionary-file.|
|
||||||
| datetime_encoding | Sets the encoding that will be used in the datetime-texts (month, weekday, ...). Default is `"UTF-8"`. Not to confuse with the full locale, this is only the encoding. |
|
| datetime_encoding | Sets the encoding that will be used in the datetime-texts (month, weekday, ...). Default is `"UTF-8"`. Not to confuse with the full locale, this is only the encoding. |
|
||||||
|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"`, `"imperial"` or `"aviation"` (Celsius, Degree/Knots). |
|
|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"`, `"imperial"` or `"aviation"` (Celsius, Degree/Knots). |
|
||||||
|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.|
|
|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.|
|
||||||
|update_interval | The update delay between two updates in minutes. By default there is always an update on a full hour.|
|
|update_interval | The update delay between two updates in minutes. By default there is always an update on a full hour.|
|
||||||
|max_loop_count | From 0 to 86400 representing the maximum number of times the loop is going to be executed, with `0` being unlimited.|
|
|
||||||
|run_on_hour | If `True`, updates calendar every full hour, ignoring differing update_interval.|
|
|
||||||
|
|
||||||
### Design
|
### Design
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|
@ -93,14 +87,11 @@ Once the packages are installed, navigate to the home directory, open 'E-Paper-M
|
||||||
| font_size | Varies the size of the font. Can be any number >0. In some cases the size of the font is fixed by the design. Default value is `14`. |
|
| font_size | Varies the size of the font. Can be any number >0. In some cases the size of the font is fixed by the design. Default value is `14`. |
|
||||||
| font_boldness | Varies the boldness of the font. Can be one of `extralight`, `light`, `regular`, `semibold`, `bold` or `extrabold`. In some cases the boldness of the font is fixed by the design. Default value is `regular`. |
|
| font_boldness | Varies the boldness of the font. Can be one of `extralight`, `light`, `regular`, `semibold`, `bold` or `extrabold`. In some cases the boldness of the font is fixed by the design. Default value is `regular`. |
|
||||||
| choosen_design | Sets the desired panel design. |
|
| choosen_design | Sets the desired panel design. |
|
||||||
| line_thickness | Varies the boldness of separation lines in the chosen design. |
|
| line_thickness | Varies the boldness of separation lines in the choosen design. |
|
||||||
| general_settings | A dictionary containing options that some designs use to optimize there design. Possible options are as follows: |
|
| general_settings | A dictionary containing options that some designs use to optimize there design. Possible options are as follows: |
|
||||||
| `"info-area"` | Defines the content type of an additionaly info area on the design. Can be one of `"rss"`, `"events"` or empty, to remove this area or keep it clean. |
|
| `"info-area"` | Defines the content type of an additionaly info area on the design. Can be one of `"rss"`, `"events"` or empty, to remove this area or keep it clean. |
|
||||||
| `"highlight-event-days"` | If set to `True`, days with events are highlighted in contrast to days without events. |
|
| `"highlight-event-days"` | If set to `True`, days with events are highlighted in contrast to days without events. |
|
||||||
| `"weather-info"` | If set to `False`, weather info areas disappear and make room for events/rss/etc. (depends on the design). |
|
| `"weather-info"` | If set to `False`, weather info areas disappear and make room for events/rss/etc. (depends on the design). |
|
||||||
| `"image-folder"` | Set a relative or absolute path to a folder containing images that you want to see fullscreen with the `"image-frame"` design activated. |
|
|
||||||
| `"overlay-image"` | Set a relative or absolute path to an image with the same size as the screen (default: 384x640px) to show some static information over every image shown in the `"image-frame"` design. If the overlay image is contained within the `"image-folder"`, it will not be included into the slideshow. |
|
|
||||||
| `"extra-excluded-urls"` | A list of calendar urls that may be excluded in some panels in certain areas. |
|
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|
|
Loading…
Reference in a new issue