diff --git a/calendar_registration.py b/calendar_registration.py index 42d0534d..a88eb558 100644 --- a/calendar_registration.py +++ b/calendar_registration.py @@ -5,8 +5,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow from google.oauth2.credentials import Credentials from google.auth.transport.requests import Request import pickle -import urllib.parse -import socket +import requests # If modifying these scopes, delete the file token.pickle. SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] @@ -20,6 +19,28 @@ def save_credentials(creds, token_path): with open(token_path, 'wb') as token: pickle.dump(creds, token) +def get_device_code(client_id, client_secret): + """Get device code for TV and Limited Input Device flow.""" + url = 'https://oauth2.googleapis.com/device/code' + data = { + 'client_id': client_id, + 'scope': ' '.join(SCOPES) + } + response = requests.post(url, data=data) + return response.json() + +def poll_for_token(client_id, client_secret, device_code): + """Poll for token using device code.""" + url = 'https://oauth2.googleapis.com/token' + data = { + 'client_id': client_id, + 'client_secret': client_secret, + 'device_code': device_code, + 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code' + } + response = requests.post(url, data=data) + return response.json() + def main(): config = load_config() calendar_config = config.get('calendar', {}) @@ -43,34 +64,54 @@ def main(): print("1. Go to https://console.cloud.google.com") print("2. Create a project or select existing project") print("3. Enable the Google Calendar API") - print("4. Configure the OAuth consent screen") - print("5. Create OAuth 2.0 credentials (Desktop application)") + print("4. Configure the OAuth consent screen (select TV and Limited Input Device)") + print("5. Create OAuth 2.0 credentials (TV and Limited Input Device)") print("6. Download the credentials and save as credentials.json") return - # Create the flow using the client secrets file from the Google API Console - flow = InstalledAppFlow.from_client_secrets_file( - creds_file, SCOPES, - redirect_uri='urn:ietf:wg:oauth:2.0:oob' # Use out-of-band flow for headless environment - ) + # Load client credentials + with open(creds_file, 'r') as f: + client_config = json.load(f) + + client_id = client_config['installed']['client_id'] + client_secret = client_config['installed']['client_secret'] - # Generate URL for authorization - auth_url, _ = flow.authorization_url(prompt='consent') + # Get device code + device_info = get_device_code(client_id, client_secret) - print("\nPlease visit this URL to authorize this application:") - print(auth_url) - print("\nAfter authorizing, you will receive a code. Enter that code below:") - - code = input("Enter the authorization code: ") - - # Exchange the authorization code for credentials - flow.fetch_token(code=code) - creds = flow.credentials + print("\nTo authorize this application, visit:") + print(device_info['verification_url']) + print("\nAnd enter the code:") + print(device_info['user_code']) + print("\nWaiting for authorization...") - # Save the credentials - save_credentials(creds, token_file) - print(f"\nCredentials saved successfully to {token_file}") - print("You can now run the LED Matrix display with calendar integration!") + # Poll for token + while True: + token_info = poll_for_token(client_id, client_secret, device_info['device_code']) + + if 'access_token' in token_info: + # Create credentials object + creds = Credentials( + token=token_info['access_token'], + refresh_token=token_info.get('refresh_token'), + token_uri="https://oauth2.googleapis.com/token", + client_id=client_id, + client_secret=client_secret, + scopes=SCOPES + ) + + # Save the credentials + save_credentials(creds, token_file) + print(f"\nCredentials saved successfully to {token_file}") + print("You can now run the LED Matrix display with calendar integration!") + break + elif token_info.get('error') == 'authorization_pending': + import time + time.sleep(device_info['interval']) + else: + print(f"\nError during authorization: {token_info.get('error')}") + print("Please try again.") + return if __name__ == '__main__': main() \ No newline at end of file