Skip to content

Commit 654724f

Browse files
authored
Merge pull request #1411 from brentru/add-pyportal-gcal
Add PyPortal GCal
2 parents 5b776cf + 5c87b81 commit 654724f

File tree

5 files changed

+429
-0
lines changed

5 files changed

+429
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# SPDX-FileCopyrightText: 2021 Brent Rubell, written for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
import board
5+
import busio
6+
from digitalio import DigitalInOut
7+
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
8+
from adafruit_esp32spi import adafruit_esp32spi
9+
import adafruit_requests as requests
10+
import displayio
11+
from adafruit_display_text.label import Label
12+
from adafruit_bitmap_font import bitmap_font
13+
import adafruit_miniqr
14+
from adafruit_oauth2 import OAuth2
15+
16+
17+
# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and
18+
# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other
19+
# source control.
20+
# pylint: disable=no-name-in-module,wrong-import-order
21+
try:
22+
from secrets import secrets
23+
except ImportError:
24+
print("WiFi secrets are kept in secrets.py, please add them there!")
25+
raise
26+
27+
esp32_cs = DigitalInOut(board.ESP_CS)
28+
esp32_ready = DigitalInOut(board.ESP_BUSY)
29+
esp32_reset = DigitalInOut(board.ESP_RESET)
30+
31+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
32+
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
33+
34+
print("Connecting to AP...")
35+
while not esp.is_connected:
36+
try:
37+
esp.connect_AP(secrets["ssid"], secrets["password"])
38+
except RuntimeError as e:
39+
print("could not connect to AP, retrying: ", e)
40+
continue
41+
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
42+
43+
# Initialize a requests object with a socket and esp32spi interface
44+
socket.set_interface(esp)
45+
requests.set_socket(socket, esp)
46+
47+
# DisplayIO Setup
48+
# Set up fonts
49+
font_small = bitmap_font.load_font("/fonts/Arial-12.pcf")
50+
font_large = bitmap_font.load_font("/fonts/Arial-14.pcf")
51+
# preload fonts
52+
glyphs = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: "
53+
font_small.load_glyphs(glyphs)
54+
font_large.load_glyphs(glyphs)
55+
56+
group_verification = displayio.Group(max_size=25)
57+
label_overview_text = Label(
58+
font_large, x=0, y=45, text="To authorize this device with Google:"
59+
)
60+
group_verification.append(label_overview_text)
61+
62+
label_verification_url = Label(font_small, x=0, y=100, line_spacing=1, max_glyphs=90)
63+
group_verification.append(label_verification_url)
64+
65+
label_user_code = Label(font_small, x=0, y=150, max_glyphs=50)
66+
group_verification.append(label_user_code)
67+
68+
label_qr_code = Label(font_small, x=0, y=190, text="Or scan the QR code:")
69+
group_verification.append(label_qr_code)
70+
71+
72+
### helper methods ###
73+
def bitmap_QR(matrix):
74+
# monochome (2 color) palette
75+
BORDER_PIXELS = 2
76+
77+
# bitmap the size of the screen, monochrome (2 colors)
78+
bitmap = displayio.Bitmap(
79+
matrix.width + 2 * BORDER_PIXELS, matrix.height + 2 * BORDER_PIXELS, 2
80+
)
81+
# raster the QR code
82+
for y in range(matrix.height): # each scanline in the height
83+
for x in range(matrix.width):
84+
if matrix[x, y]:
85+
bitmap[x + BORDER_PIXELS, y + BORDER_PIXELS] = 1
86+
else:
87+
bitmap[x + BORDER_PIXELS, y + BORDER_PIXELS] = 0
88+
return bitmap
89+
90+
91+
# Set scope(s) of access required by the API you're using
92+
scopes = ["https://www.googleapis.com/auth/calendar.readonly"]
93+
94+
# Initialize an oauth2 object
95+
google_auth = OAuth2(
96+
requests, secrets["google_client_id"], secrets["google_client_secret"], scopes
97+
)
98+
99+
100+
# Request device and user codes
101+
# https://developers.google.com/identity/protocols/oauth2/limited-input-device#step-1:-request-device-and-user-codes
102+
google_auth.request_codes()
103+
104+
# Display user code and verification url
105+
# NOTE: If you are displaying this on a screen, ensure the text label fields are
106+
# long enough to handle the user_code and verification_url.
107+
# Details in link below:
108+
# https://developers.google.com/identity/protocols/oauth2/limited-input-device#displayingthecode
109+
print(
110+
"1) Navigate to the following URL in a web browser:", google_auth.verification_url
111+
)
112+
print("2) Enter the following code:", google_auth.user_code)
113+
114+
# modify display labels to show verification URL and user code
115+
label_verification_url.text = (
116+
"1. On your computer or mobile device,\n go to: %s"
117+
% google_auth.verification_url
118+
)
119+
label_user_code.text = "2. Enter code: %s" % google_auth.user_code
120+
121+
# Create a QR code
122+
qr = adafruit_miniqr.QRCode(qr_type=3, error_correct=adafruit_miniqr.L)
123+
qr.add_data(google_auth.verification_url.encode())
124+
qr.make()
125+
126+
# generate the 1-pixel-per-bit bitmap
127+
qr_bitmap = bitmap_QR(qr.matrix)
128+
# we'll draw with a classic black/white palette
129+
palette = displayio.Palette(2)
130+
palette[0] = 0xFFFFFF
131+
palette[1] = 0x000000
132+
# we'll scale the QR code as big as the display can handle
133+
scale = 15
134+
# then center it!
135+
qr_img = displayio.TileGrid(qr_bitmap, pixel_shader=palette, x=170, y=165)
136+
group_verification.append(qr_img)
137+
# show the group
138+
board.DISPLAY.show(group_verification)
139+
140+
141+
# Poll Google's authorization server
142+
print("Waiting for browser authorization...")
143+
if not google_auth.wait_for_authorization():
144+
raise RuntimeError("Timed out waiting for browser response!")
145+
146+
print("Successfully Authenticated with Google!")
147+
148+
# print formatted keys for adding to secrets.py
149+
print("Add the following lines to your secrets.py file:")
150+
print("\t'google_access_token' " + ":" + " '%s'," % google_auth.access_token)
151+
print("\t'google_refresh_token' " + ":" + " '%s'" % google_auth.refresh_token)
152+
# Remove QR code and code/verification labels
153+
group_verification.pop()
154+
group_verification.pop()
155+
group_verification.pop()
156+
157+
label_overview_text.text = "Successfully Authenticated!"
158+
label_verification_url.text = (
159+
"Check the REPL for tokens to add\n\tto your secrets.py file"
160+
)
161+
162+
# prevent exit
163+
while True:
164+
pass

0 commit comments

Comments
 (0)