|
| 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