from flask import Flask, render_template, jsonify import requests import os import random from datetime import datetime, timedelta from config import Config app = Flask(__name__) app.config.from_object(Config) # Cache for API responses to avoid rate limiting weather_cache = {'data': None, 'timestamp': None} calendar_cache = {'data': None, 'timestamp': None} joke_cache = {'data': None, 'timestamp': None} @app.route('/') def index(): """Render the main display page.""" return render_template('index.html') @app.route('/api/weather') def get_weather(): """Fetch weather data from OpenWeatherMap API.""" global weather_cache # Check cache now = datetime.now() if (weather_cache['data'] and weather_cache['timestamp'] and (now - weather_cache['timestamp']).total_seconds() < app.config['WEATHER_UPDATE_INTERVAL']): return jsonify(weather_cache['data']) try: # Fetch current weather current_url = f"https://api.openweathermap.org/data/2.5/weather" params = { 'lat': app.config['WEATHER_LAT'], 'lon': app.config['WEATHER_LON'], 'appid': app.config['OPENWEATHER_API_KEY'], 'units': app.config['WEATHER_UNITS'] } current_response = requests.get(current_url, params=params, timeout=10) current_response.raise_for_status() current_data = current_response.json() # Fetch 5-day forecast forecast_url = f"https://api.openweathermap.org/data/2.5/forecast" forecast_response = requests.get(forecast_url, params=params, timeout=10) forecast_response.raise_for_status() forecast_data = forecast_response.json() # Process forecast to get daily summaries daily_forecast = [] seen_dates = set() for item in forecast_data['list'][:40]: # Next 5 days (8 forecasts per day) date = datetime.fromtimestamp(item['dt']).date() if date not in seen_dates and len(daily_forecast) < 5: seen_dates.add(date) daily_forecast.append({ 'date': date.strftime('%a'), 'temp_max': round(item['main']['temp_max']), 'temp_min': round(item['main']['temp_min']), 'description': item['weather'][0]['description'], 'icon': item['weather'][0]['icon'] }) weather_data = { 'current': { 'temp': round(current_data['main']['temp']), 'feels_like': round(current_data['main']['feels_like']), 'description': current_data['weather'][0]['description'], 'icon': current_data['weather'][0]['icon'], 'humidity': current_data['main']['humidity'], 'wind_speed': round(current_data['wind']['speed'] * 3.6, 1) # Convert m/s to km/h }, 'forecast': daily_forecast } # Update cache weather_cache = {'data': weather_data, 'timestamp': now} return jsonify(weather_data) except Exception as e: app.logger.error(f"Error fetching weather: {str(e)}") # Return cached data if available, otherwise return error if weather_cache['data']: return jsonify(weather_cache['data']) return jsonify({'error': 'Unable to fetch weather data'}), 500 @app.route('/api/calendar') def get_calendar(): """Fetch Google Calendar events.""" global calendar_cache # Check cache now = datetime.now() if (calendar_cache['data'] and calendar_cache['timestamp'] and (now - calendar_cache['timestamp']).total_seconds() < app.config['CALENDAR_UPDATE_INTERVAL']): return jsonify(calendar_cache['data']) # TODO: Implement Google Calendar API integration # For now, return placeholder data try: # This is placeholder data - will be replaced with actual Google Calendar API events = [ { 'title': 'Setup Google Calendar API', 'start': (datetime.now() + timedelta(hours=2)).isoformat(), 'end': (datetime.now() + timedelta(hours=3)).isoformat(), 'location': '' } ] calendar_cache = {'data': events, 'timestamp': now} return jsonify(events) except Exception as e: app.logger.error(f"Error fetching calendar: {str(e)}") if calendar_cache['data']: return jsonify(calendar_cache['data']) return jsonify({'error': 'Unable to fetch calendar data'}), 500 @app.route('/api/background') def get_background(): """Get a random background image.""" try: backgrounds_dir = app.config['BACKGROUNDS_DIR'] # Get list of image files if os.path.exists(backgrounds_dir): image_files = [f for f in os.listdir(backgrounds_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp'))] if image_files: random_image = random.choice(image_files) return jsonify({'image': f'/static/backgrounds/{random_image}'}) # Return a default color if no images return jsonify({'image': None, 'color': '#1a1a2e'}) except Exception as e: app.logger.error(f"Error getting background: {str(e)}") return jsonify({'image': None, 'color': '#1a1a2e'}) @app.route('/api/joke') def get_joke(): """Fetch a dad joke.""" global joke_cache # Check cache now = datetime.now() if (joke_cache['data'] and joke_cache['timestamp'] and (now - joke_cache['timestamp']).total_seconds() < app.config['JOKE_UPDATE_INTERVAL']): return jsonify(joke_cache['data']) try: response = requests.get( 'https://icanhazdadjoke.com/', headers={'Accept': 'application/json'}, timeout=10 ) response.raise_for_status() joke_data = response.json() joke_cache = {'data': {'joke': joke_data['joke']}, 'timestamp': now} return jsonify(joke_cache['data']) except Exception as e: app.logger.error(f"Error fetching joke: {str(e)}") if joke_cache['data']: return jsonify(joke_cache['data']) return jsonify({'joke': 'Why did the developer go broke? Because he used up all his cache!'}) if __name__ == '__main__': # Create backgrounds directory if it doesn't exist os.makedirs(app.config['BACKGROUNDS_DIR'], exist_ok=True) os.makedirs(app.config['CREDENTIALS_DIR'], exist_ok=True) # Run the app app.run(host='0.0.0.0', port=5000, debug=True)