Last active
January 10, 2022 05:22
-
-
Save JannieT/1e589b215ec05118ab78ec5d441c69e7 to your computer and use it in GitHub Desktop.
MagTag Calendar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import rtc | |
import time | |
import board | |
from adafruit_magtag.magtag import MagTag | |
from adafruit_oauth2 import OAuth2 | |
from adafruit_bitmap_font import bitmap_font | |
from adafruit_display_text import label | |
class Calendar: | |
def __init__(self, refresh_time: int = 15) -> None: | |
self.sleep = refresh_time | |
self.magtag = MagTag() | |
self.clock = rtc.RTC() | |
self.token_obtained_at = 0 | |
self.timezone = "+00:00" | |
self.secrets = {} | |
self.google_auth: OAuth2 | |
def setup(self) -> None: | |
# Load our sensitive credentials and tokens | |
try: | |
from secrets import secrets | |
self.secrets = secrets | |
except ImportError: | |
print("Credentials and tokens are kept in secrets.py, please add them there!") | |
raise | |
# Connect to the configured wifi network | |
self.magtag.network.connect() | |
# Initialize an OAuth2 object with GCal API scope | |
scopes = ["https://www.googleapis.com/auth/calendar.readonly"] | |
self.google_auth = OAuth2( | |
self.magtag.network.requests, | |
secrets["google_client_id"], | |
secrets["google_client_secret"], | |
scopes, | |
secrets["google_access_token"], | |
secrets["google_refresh_token"], | |
) | |
def draw(self) -> None: | |
self._set_local_time() | |
events = self._fetch_events(3) | |
self._show_events(events) | |
print("Sleeping for %d minutes" % self.sleep) | |
self.magtag.exit_and_deep_sleep(self.sleep * 60) | |
# ------------------------------- | |
# Non-API member functions | |
# ------------------------------- | |
def _set_local_time(self) -> None: | |
raw = self.magtag.get_local_time(self.secrets["timezone"]) | |
# 2022-01-01 09:10:03.370 001 6 +0200 SAST | |
zone = raw.split(" ")[4] | |
self.timezone = zone[:3] + ":" + zone[3:] | |
def _refresh_token(self) -> None: | |
# Check if we have a token, if not we need to get one | |
if (self.google_auth.access_token_expiration is None | |
or int(time.monotonic()) - self.token_obtained_at | |
>= self.google_auth.access_token_expiration): | |
print("Fetching a new access token ...") | |
if not self.google_auth.refresh_access_token(): | |
raise RuntimeError( | |
"Unable to refresh access token - has the token been revoked?" | |
) | |
self.token_obtained_at = int(time.monotonic()) | |
def _iso_date(self, date: struct_time) -> str: | |
return "{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}{:s}".format( | |
date.tm_year, date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec, self.timezone) | |
def _url_encode(self, raw: str) -> str: | |
return raw.replace(":", "%3A").replace("+", "%2B").replace(" ", "%20").replace("/", "%2F") | |
def _fetch_events(self, event_count: int = 3) -> list: | |
# see: https://developers.google.com/calendar/api/v3/reference/events/list | |
self._refresh_token() | |
now = self._iso_date(self.clock.datetime) | |
minTime = self._url_encode(now) | |
url = ("https://www.googleapis.com/calendar/v3/calendars/{0}" | |
"/events?maxResults={1}&timeMin={2}&orderBy=startTime" | |
"&singleEvents=true").format(self.secrets['calendar_id'], event_count, minTime) | |
headers = { | |
"Authorization": "Bearer " + self.google_auth.access_token, | |
"Accept": "application/json", | |
"Content-Length": "0", | |
} | |
print("Fetching events since " + now) | |
response = self.magtag.network.requests.get(url, headers=headers) | |
parsed = response.json() | |
if "error" in parsed: | |
raise RuntimeError("Error:", parsed) | |
response.close() | |
if not parsed["items"]: | |
print("No upcoming events found.") | |
return [] | |
return parsed["items"] | |
def _event_summary(self, event: dict) -> str: | |
if "summary" not in event: | |
return "No summary" | |
# wrap event name around second line if necessary | |
lines = self.magtag.wrap_nicely(event["summary"], 25) | |
# only wrap 2 lines, truncate third.. | |
return "\n".join(lines[0:2]) | |
def _event_time(self, event: dict) -> str: | |
if "start" not in event: | |
return "---" | |
# all-day events | |
if "date" in event["start"]: | |
return self._human_date(event["start"]["date"]) | |
if "dateTime" in event["start"]: | |
return self._human_time(event["start"]["dateTime"]) | |
return "---" | |
def _human_date(self, date: str) -> str: | |
(year, month, day) = date.split("-") | |
months = { | |
"01": "JAN", | |
"02": "FEB", | |
"03": "MAR", | |
"04": "APR", | |
"05": "MAY", | |
"06": "JUN", | |
"07": "JUL", | |
"08": "AUG", | |
"09": "SEP", | |
"10": "OCT", | |
"11": "NOV", | |
"12": "DEC", | |
} | |
return "{} {}".format(int(day), months[month]) | |
def _human_time(self, iso_date: str) -> str: | |
(date_part, time_part) = iso_date.split("T") | |
return "{} - {}".format(self._human_date(date_part), time_part[:5]) | |
def _show_events(self, events: list) -> None: | |
self.magtag.set_background(0xFFFFFF) | |
summary_font = bitmap_font.load_font("fonts/Arial-12.pcf") | |
time_font = bitmap_font.load_font("fonts/BenchNine-Bold-20.bdf") | |
for i in range(len(events)): | |
event = events[i] | |
label_time = label.Label( | |
time_font, | |
x=7, | |
y=14 + (i * 40), | |
color=0x000000, | |
text=self._event_time(event), | |
) | |
self.magtag.splash.append(label_time) | |
label_summary = label.Label( | |
summary_font, | |
x=105, | |
y=14 + (i * 40), | |
color=0x000000, | |
line_spacing=0.8, | |
text=self._event_summary(event), | |
) | |
self.magtag.splash.append(label_summary) | |
board.DISPLAY.show(self.magtag.splash) | |
board.DISPLAY.refresh() | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from calendar import Calendar | |
calendar = Calendar(refresh_time=15) | |
calendar.setup() | |
while True: | |
calendar.draw() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
secrets = { | |
'ssid': <redacted>, | |
'password': <redacted>, | |
'aio_username': <redacted>, | |
'aio_key': <redacted>, | |
'timezone': "Africa/Johannesburg", # http://worldtimeapi.org/timezones | |
'calendar_id': "[email protected]", | |
'google_client_id': '<redacted>.apps.googleusercontent.com', | |
'google_client_secret': <redacted>, | |
'google_access_token': <redacted>, | |
'google_refresh_token': <redacted> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment