Files
LEDMatrix/src/calendar_manager.py

172 lines
6.2 KiB
Python

import os
import json
import logging
from datetime import datetime, timedelta
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
import pickle
from PIL import Image, ImageDraw, ImageFont
import numpy as np
from rgbmatrix import graphics
import pytz
class CalendarManager:
def __init__(self, matrix, canvas, config):
self.matrix = matrix
self.canvas = canvas
self.config = config
self.calendar_config = config.get('calendar', {})
self.enabled = self.calendar_config.get('enabled', False)
self.update_interval = self.calendar_config.get('update_interval', 300)
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
# Get display manager instance
from src.display_manager import DisplayManager
self.display_manager = DisplayManager._instance
if self.enabled:
self.authenticate()
# Display properties
self.text_color = (255, 255, 255) # White
self.date_color = (0, 255, 0) # Green
# State management
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')
if os.path.exists(token_file):
with open(token_file, 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
logging.error("Calendar credentials not found or invalid. Please run calendar_registration.py first.")
self.enabled = False
return
try:
self.service = build('calendar', 'v3', credentials=creds)
logging.info("Successfully authenticated with Google Calendar")
except Exception as e:
logging.error(f"Error building calendar service: {str(e)}")
self.enabled = False
def get_events(self):
"""Fetch upcoming calendar events."""
if not self.enabled or not self.service:
return []
try:
now = datetime.utcnow().isoformat() + 'Z'
events_result = self.service.events().list(
calendarId='primary',
timeMin=now,
maxResults=self.max_events,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
return events
except Exception as e:
logging.error(f"Error fetching calendar events: {str(e)}")
return []
def draw_event(self, event, y_position):
"""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 using display manager
self.display_manager.draw_text(text, y=y_position, color=self.text_color, small_font=True)
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:
return
if current_time - self.last_update >= self.update_interval:
self.events = self.get_events()
self.last_update = current_time
# Clear the display
self.display_manager.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.display_manager.update_display()
def _format_event_time(self, event):
"""Format event time for display"""
start = event['start'].get('dateTime', event['start'].get('date'))
if 'T' in start: # DateTime
dt = datetime.fromisoformat(start.replace('Z', '+00:00'))
local_dt = dt.astimezone(pytz.local)
return local_dt.strftime("%I:%M %p")
else: # All-day event
return "All Day"
def display(self):
"""Display calendar events on the matrix"""
if not self.enabled or not self.events:
return
# Clear the display
self.display_manager.clear()
# Get current event to display
if self.current_event_index >= len(self.events):
self.current_event_index = 0
event = self.events[self.current_event_index]
# Display event time
time_str = self._format_event_time(event)
self.display_manager.draw_text(time_str, y=12, color=self.date_color, small_font=True)
# Display event title (with scrolling if needed)
title = event['summary']
if len(title) > 10: # Implement scrolling for long titles
# Add scrolling logic here
pass
else:
self.display_manager.draw_text(title, y=25, color=self.text_color, small_font=True)
# Update the display
self.display_manager.update_display()
# Increment event index for next display
self.current_event_index += 1