Update calendar manager to use TTF font and improve display handling

This commit is contained in:
ChuckBuilds
2025-04-21 09:28:07 -05:00
parent c02f80ead7
commit 734376ee52

View File

@@ -1,9 +1,14 @@
import os
import json
import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pickle
import os.path
from google.oauth2.credentials import Credentials from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request from google.auth.transport.requests import Request
from googleapiclient.discovery import build from googleapiclient.discovery import build
import pickle
from PIL import Image, ImageDraw, ImageFont
import numpy as np
from rgbmatrix import graphics from rgbmatrix import graphics
import pytz import pytz
@@ -11,60 +16,58 @@ class CalendarManager:
def __init__(self, matrix, canvas, config): def __init__(self, matrix, canvas, config):
self.matrix = matrix self.matrix = matrix
self.canvas = canvas self.canvas = canvas
self.config = config.get('calendar', {}) self.config = config
self.enabled = self.config.get('enabled', False) self.calendar_config = config.get('calendar', {})
self.update_interval = self.config.get('update_interval', 300) self.enabled = self.calendar_config.get('enabled', False)
self.max_events = self.config.get('max_events', 3) self.update_interval = self.calendar_config.get('update_interval', 300)
self.token_file = self.config.get('token_file', 'token.pickle') self.max_events = self.calendar_config.get('max_events', 3)
self.calendars = self.calendar_config.get('calendars', ['primary'])
self.last_update = 0
self.events = []
self.service = None
# Load font
self.font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
if self.enabled:
self.authenticate()
# Display properties # Display properties
self.font = graphics.Font()
self.font.LoadFont("assets/fonts/7x13.bdf")
self.text_color = graphics.Color(255, 255, 255) self.text_color = graphics.Color(255, 255, 255)
self.date_color = graphics.Color(0, 255, 0) self.date_color = graphics.Color(0, 255, 0)
# State management # State management
self.last_update = None
self.events = []
self.service = None
self.current_event_index = 0 self.current_event_index = 0
def authenticate(self):
"""Authenticate with Google Calendar API."""
creds = None
token_file = self.calendar_config.get('token_file', 'token.pickle')
# Initialize the calendar service if os.path.exists(token_file):
self._initialize_service() with open(token_file, 'rb') as token:
def _initialize_service(self):
"""Initialize the Google Calendar service with stored credentials"""
if not os.path.exists(self.token_file):
print(f"No token file found at {self.token_file}")
print("Please run calendar_registration.py first")
self.enabled = False
return
try:
with open(self.token_file, 'rb') as token:
creds = pickle.load(token) creds = pickle.load(token)
if not creds or not creds.valid: if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token: if creds and creds.expired and creds.refresh_token:
creds.refresh(Request()) creds.refresh(Request())
# Save the refreshed credentials else:
with open(self.token_file, 'wb') as token: logging.error("Calendar credentials not found or invalid. Please run calendar_registration.py first.")
pickle.dump(creds, token) self.enabled = False
else: return
print("Invalid credentials. Please run calendar_registration.py")
self.enabled = False try:
return
self.service = build('calendar', 'v3', credentials=creds) self.service = build('calendar', 'v3', credentials=creds)
logging.info("Successfully authenticated with Google Calendar")
except Exception as e: except Exception as e:
print(f"Error initializing calendar service: {e}") logging.error(f"Error building calendar service: {str(e)}")
self.enabled = False self.enabled = False
def _fetch_events(self): def get_events(self):
"""Fetch upcoming events from Google Calendar""" """Fetch upcoming calendar events."""
if not self.service: if not self.enabled or not self.service:
return [] return []
try: try:
now = datetime.utcnow().isoformat() + 'Z' now = datetime.utcnow().isoformat() + 'Z'
events_result = self.service.events().list( events_result = self.service.events().list(
@@ -74,21 +77,58 @@ class CalendarManager:
singleEvents=True, singleEvents=True,
orderBy='startTime' orderBy='startTime'
).execute() ).execute()
return events_result.get('items', [])
events = events_result.get('items', [])
return events
except Exception as e: except Exception as e:
print(f"Error fetching calendar events: {e}") logging.error(f"Error fetching calendar events: {str(e)}")
return [] return []
def update(self): def draw_event(self, event, y_position):
"""Update calendar events if needed""" """Draw a single calendar event on the canvas."""
try:
# Get event details
summary = event.get('summary', 'No Title')
start = event.get('start', {}).get('dateTime', event.get('start', {}).get('date'))
if start:
start_time = datetime.fromisoformat(start.replace('Z', '+00:00'))
time_str = start_time.strftime('%H:%M')
else:
time_str = 'All Day'
# Create text to display
text = f"{time_str} {summary}"
# Draw text
draw = ImageDraw.Draw(self.canvas)
draw.text((1, y_position), text, font=self.font, fill=(255, 255, 255))
return y_position + 8 # Return next y position
except Exception as e:
logging.error(f"Error drawing calendar event: {str(e)}")
return y_position
def update(self, current_time):
"""Update calendar display if needed."""
if not self.enabled: if not self.enabled:
return return
current_time = datetime.now() if current_time - self.last_update >= self.update_interval:
if (self.last_update is None or self.events = self.get_events()
(current_time - self.last_update).seconds > self.update_interval):
self.events = self._fetch_events()
self.last_update = current_time self.last_update = current_time
# Clear the canvas
self.canvas.Clear()
# Draw each event
y_pos = 1
for event in self.events:
y_pos = self.draw_event(event, y_pos)
if y_pos >= self.matrix.height - 8: # Leave some space at the bottom
break
# Update the display
self.matrix.SwapOnVSync(self.canvas)
def _format_event_time(self, event): def _format_event_time(self, event):
"""Format event time for display""" """Format event time for display"""