forked from lee-pham/mcal
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaginkcal.py
More file actions
executable file
·137 lines (121 loc) · 6.51 KB
/
maginkcal.py
File metadata and controls
executable file
·137 lines (121 loc) · 6.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
This project is designed for the WaveShare 12.48" eInk display. Modifications will be needed for other displays,
especially the display drivers and how the image is being rendered on the display. Also, this is the first project that
I posted on GitHub so please go easy on me. There are still many parts of the code (especially with timezone
conversions) that are not tested comprehensively, since my calendar/events are largely based on the timezone I'm in.
There will also be work needed to adjust the calendar rendering for different screen sizes, such as modifying of the
CSS stylesheets in the "render" folder.
"""
import datetime as dt
import json
import logging
import platform
import sys
import time
from pytz import timezone
from gcal.gcal import GcalHelper
from render.render import RenderHelper
from utils.date_parse import enumerate_multiday_event
def main():
# Only drive the EPD when running on Raspberry Pi
is_rpi = platform.system() == "Linux"
# Basic configuration settings (user replaceable)
configFile = open('config.json')
config = json.load(configFile)
# list of timezones - print(pytz.all_timezones)
displayTZ = timezone(config['displayTZ'])
# considers events updated within last 12 hours as recently updated
thresholdHours = config['thresholdHours']
# limits number of events to display (remainder displayed as '+X more')
maxEventsPerDay = config['maxEventsPerDay']
# set to true when debugging rendering without displaying to screen
isDisplayToScreen = config['isDisplayToScreen']
# set to true to conserve power, false if in debugging mode
isShutdownOnComplete = config['isShutdownOnComplete']
# 0: do not show / 1: always show / 2: show when battery is low
batteryDisplayMode = config['batteryDisplayMode']
weekStartDay = config['weekStartDay'] # Monday = 0, Sunday = 6
dayOfWeekText = config['dayOfWeekText'] # Monday as first item in list
# Width of E-Ink display. Default is landscape. Need to rotate image to fit.
screenWidth = config['screenWidth']
# Height of E-Ink display. Default is landscape. Need to rotate image to fit.
screenHeight = config['screenHeight']
# Width of image to be generated for display.
imageWidth = config['imageWidth']
# Height of image to be generated for display.
imageHeight = config['imageHeight']
# If image is rendered in portrait orientation, angle to rotate to fit screen
rotateAngle = config['rotateAngle']
calendars = config['calendars'] # Google calendar ids
is24hour = config['is24h'] # set 24 hour time
weeks_to_display = config["weeks_to_display"] # num of weeks to display after the previous sunday of the month
# Create and configure logger
logging.basicConfig(filename="logfile.log",
format='%(asctime)s %(levelname)s - %(message)s', filemode='a')
logger = logging.getLogger('maginkcal')
logger.addHandler(logging.StreamHandler(
sys.stdout)) # print logger to stdout
logger.setLevel(logging.INFO)
logger.info("Starting daily calendar update")
try:
# Establish current date and time information
# Note: For Python datetime.weekday() - Monday = 0, Sunday = 6
# For this implementation, each week starts on a Sunday and the calendar begins on the nearest elapsed Sunday
# The calendar will also display 5 weeks of events to cover the upcoming month, ending on a Saturday
currDatetime = dt.datetime.now(displayTZ)
current_time = currDatetime.strftime("%H:%M")
logger.info("Time synchronised to {}".format(currDatetime))
currDate = currDatetime.date()
current_month_date = dt.datetime(currDate.year, currDate.month, 1).date()
# Commented out for use with small display since only 4 weeks can be displayed
# calStartDate = current_month_date - dt.timedelta(days=((current_month_date.weekday() + (7 - weekStartDay)) % 7))
calStartDate = currDate - dt.timedelta(days=((currDate.weekday() + (7 - weekStartDay)) % 7))
calEndDate = calStartDate + dt.timedelta(days=(weeks_to_display * 7 - 1))
# Dates used for testing ##########################
# calStartDate = dt.datetime(2022, 3, 27).date() #
# calEndDate = dt.datetime(2022, 4, 30).date() #
###################################################
calStartDatetime = displayTZ.localize(
dt.datetime.combine(calStartDate, dt.datetime.min.time()))
calEndDatetime = displayTZ.localize(
dt.datetime.combine(calEndDate, dt.datetime.max.time()))
# Using Google Calendar to retrieve all events within start and end date (inclusive)
start = dt.datetime.now()
gcalService = GcalHelper()
eventList = gcalService.retrieve_events(
calendars,
calStartDatetime,
calEndDatetime,
displayTZ,
thresholdHours)
enumerated_event_list = enumerate_multiday_event(eventList)
# enumerated_event_list = eventList
logger.info("Calendar events retrieved in " +
str(dt.datetime.now() - start))
# Populate dictionary with information to be rendered on e-ink display
calDict = {'events': enumerated_event_list, 'calStartDate': calStartDate, 'calEndDate': calEndDate,
'today': currDate, 'lastRefresh': currDatetime,
'batteryDisplayMode': batteryDisplayMode,
'dayOfWeekText': dayOfWeekText, 'weekStartDay': weekStartDay, 'maxEventsPerDay': maxEventsPerDay,
'is24hour': is24hour, "current_time": current_time, "weeks_to_display": weeks_to_display}
renderService = RenderHelper(imageWidth, imageHeight, rotateAngle)
calBlackImage, calRedImage = renderService.process_inputs(calDict)
if is_rpi:
from display.display import DisplayHelper
displayService = DisplayHelper(screenWidth, screenHeight)
if currDate.weekday() == weekStartDay:
# calibrate display once a week to prevent ghosting
# to calibrate in production
displayService.calibrate(cycles=0)
displayService.update(calBlackImage, calRedImage)
displayService.sleep()
except Exception as e:
logger.error(e, exc_info=True)
logger.info("Completed daily calendar update")
if __name__ == "__main__":
main()
# start_time = time.time()
# while True:
# if __name__ == "__main__":
# main()
# time.sleep(60.0 - ((time.time() - start_time) % 60.0))