# Family Calendar Display A smart display application for Raspberry Pi that shows a color-coded, week-view family calendar with live weather, styled after wall-mounted family calendar devices (e.g. Skylight). **Project created:** February 14, 2026 **UI redesigned:** June 30, 2026 β€” switched from a scrolling event list over a photo background to a light-themed week grid with per-person event colors. ## Features - πŸ“… **Week-View Calendar** - Sunday–Saturday grid with a "Next Week" preview column, Prev/Next navigation - πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ **Color-Coded Family Members** - Events tagged `[Name]` are shown in that person's color (see below) - πŸ”Ž **Filter by Person** - Dropdown to show only one family member's events - 🌀️ **Weather** - Current temperature and icon in the header (OpenWeatherMap) - πŸ• **Real-time Clock** - Current time and date display - πŸ“Œ **Read-only by design** - The "+" button is a visual affordance only; this app reads a public iCal feed and cannot write events back to Google Calendar. Add/edit events directly in Google Calendar. > The previous rotating-background-photo and dad-joke features were dropped in the redesign to match the cleaner device look. The `/api/background` and `/api/joke` endpoints still exist server-side if you want to bring them back. ## Family Member Colors Prefix an event title in Google Calendar with `[Name]` (case-insensitive) and the app will strip the prefix and color the event card accordingly. Unprefixed events fall back to a neutral gray "Family" color. | Name / alias | Color | |---|---| | `[Ludwig]` or `[Dad]` | Blue | | `[Michelle]` or `[Mom]` | Pink | | `[Jason]` | Green | | `[Daniel]` | Yellow | Example: an event titled `[Daniel] Football Practice` displays as **Football Practice** in Daniel's yellow card. Member names/aliases/colors are configured in `config.py` (`FAMILY_MEMBERS`). ## Requirements - Python 3.8+ - Raspberry Pi (tested on Pi 3/4) or any Linux system - Google Calendar API credentials - OpenWeatherMap API key (free tier) ## Quick Start ### 1. Clone the Repository ```bash git clone ssh://git@gitea.meyfamily.co.nz:2222/luddie/Calender.git cd Calender ``` ### 2. Install Dependencies ```bash python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt ``` ### 3. Configure Environment Variables ```bash cp .env.example .env ``` Edit [.env](.env) and add your API keys: ```bash # Get your OpenWeatherMap API key from: https://openweathermap.org/api OPENWEATHER_API_KEY=your_api_key_here # Your Google Calendar ID (found in Google Calendar settings) GOOGLE_CALENDAR_ID=your_calendar_id@group.calendar.google.com # Generate a secret key for Flask FLASK_SECRET_KEY=your_random_secret_key ``` ### 4. Get OpenWeatherMap API Key 1. Go to [OpenWeatherMap](https://openweathermap.org/api) 2. Sign up for a free account 3. Navigate to "API keys" section 4. Copy your API key 5. Add it to your `.env` file as `OPENWEATHER_API_KEY` **Find Your Location Coordinates:** 1. Go to [LatLong.net](https://www.latlong.net/) 2. Search for your city 3. Copy the latitude and longitude values 4. Add them to your `.env` file as `WEATHER_LAT` and `WEATHER_LON` ### 5. Set Up Google Calendar **Option 1: Public iCal Feed (Easiest - No Authentication)** 1. Open [Google Calendar](https://calendar.google.com/) 2. Go to Settings (βš™οΈ gear icon) 3. Select the calendar you want to display 4. Scroll down to "Integrate calendar" 5. Copy the **"Public URL to this calendar"** in iCal format - The URL looks like: `https://calendar.google.com/calendar/ical/...@group.calendar.google.com/public/basic.ics` 6. Add this URL to your `.env` file as `GOOGLE_CALENDAR_ICAL_URL` 7. Also copy the Calendar ID (looks like `abc123...@group.calendar.google.com`) 8. Add it to your `.env` file as `GOOGLE_CALENDAR_ID` **Note:** Your calendar must be set to "Public" for this method to work. **Option 2: Google Calendar API with Credentials (Advanced)** If you prefer to use private calendars with authentication: 1. Go to [Google Cloud Console](https://console.cloud.google.com/) 2. Create a new project or select existing one 3. Enable the Google Calendar API 4. Create credentials (Service Account) 5. Download the credentials JSON file 6. Save it as `credentials/google_calendar_credentials.json` 7. Share your calendar with the service account email ### 6. Set Up Family Member Colors (optional) Prefix event titles in your calendar with `[Name]` to color-code them per person β€” see [Family Member Colors](#family-member-colors) above. Edit `FAMILY_MEMBERS` in `config.py` to change names, aliases, or colors. ### 7. Run the Application ```bash python app.py ``` Visit `http://localhost:5000` in your browser. ## Raspberry Pi Deployment ### Auto-start Flask Application Create a systemd service: ```bash sudo nano /etc/systemd/system/calendar-display.service ``` Add the following content: ```ini [Unit] Description=Calendar Display Flask App After=network.target [Service] User=pi WorkingDirectory=/home/pi/Calender ExecStart=/home/pi/Calender/venv/bin/python app.py Restart=always Environment="PATH=/home/pi/Calender/venv/bin" [Install] WantedBy=multi-user.target ``` Enable and start the service: ```bash sudo systemctl daemon-reload sudo systemctl enable calendar-display.service sudo systemctl start calendar-display.service ``` ### Configure Chromium Kiosk Mode The Pi uses **labwc** (Wayland). Create the autostart entry: ```bash mkdir -p ~/.config/autostart nano ~/.config/autostart/chromium-kiosk.desktop ``` Add the following: ```ini [Desktop Entry] Type=Application Name=Chromium Kiosk Exec=chromium-browser --ozone-platform=wayland --kiosk --noerrdialogs --incognito --disable-infobars --simulate-touch-screen --force-show-cursor http://localhost:5002 X-GNOME-Autostart-enabled=true ``` ### Reboot ```bash sudo reboot ``` The display should now start automatically on boot! ### Restarting Without Rebooting A helper script is included to restart both the Flask service and the Chromium kiosk without rebooting: ```bash bash ~/restart-calendar.sh ``` This will: 1. Restart the `calendar-display` systemd service 2. Kill and relaunch Chromium in the correct Wayland session ## Configuration ### Environment Variables (`.env` file) **Weather Settings:** - `OPENWEATHER_API_KEY` - Your OpenWeatherMap API key - `WEATHER_LOCATION` - City name and country code (e.g., "London,UK") - `WEATHER_LAT` - Latitude of your location - `WEATHER_LON` - Longitude of your location **Calendar Settings:** - `GOOGLE_CALENDAR_ID` - Your calendar ID - `GOOGLE_CALENDAR_ICAL_URL` - Public iCal feed URL (easiest method) - `CALENDAR_DAYS_AHEAD` - Used only by the legacy events fetch; the week view always fetches two weeks at a time **Update Intervals (in seconds):** - `WEATHER_UPDATE_INTERVAL` - Weather refresh interval (default: 900) - `CALENDAR_UPDATE_INTERVAL` - Calendar refresh interval (default: 300) **Other:** - `FLASK_SECRET_KEY` - Flask session secret key ### JavaScript Configuration (`static/js/app.js`) For more precise control over update intervals, edit the `INTERVALS` object: ```javascript const INTERVALS = { TIME: 1000, // 1 second WEATHER: 900000, // 15 minutes CALENDAR: 300000 // 5 minutes }; ``` ## Project Structure ``` Calender/ β”œβ”€β”€ app.py # Main Flask application β”œβ”€β”€ config.py # Configuration management β”œβ”€β”€ requirements.txt # Python dependencies β”œβ”€β”€ .env # Environment variables (create from .env.example) β”œβ”€β”€ .env.example # Environment template β”œβ”€β”€ static/ β”‚ β”œβ”€β”€ css/style.css # Styling β”‚ β”œβ”€β”€ js/app.js # Frontend JavaScript β”‚ └── backgrounds/ # Background images directory β”œβ”€β”€ templates/ β”‚ └── index.html # Main display template └── credentials/ └── google_calendar_credentials.json # Google API credentials ``` ## API Endpoints - `GET /` - Main display page - `GET /api/weather` - Weather data (cached for 15 min) - `GET /api/calendar?start=YYYY-MM-DD` - Sunday-starting week of events plus a "Next Week" preview, color-coded by family member (cached per-week for 5 min). `start` defaults to the current week if omitted. - `GET /api/background` - Random background image (no longer used by the UI, kept for future use) - `GET /api/joke` - Dad joke (no longer used by the UI, kept for future use) ## Troubleshooting ### Weather not loading - Check your OpenWeatherMap API key is valid - Ensure you're not exceeding the free tier rate limits (60 calls/min) ### Calendar not loading - Verify the iCal URL in `.env` is correct and the calendar is set to Public - Check the calendar ID is correct ### Events showing the wrong color / "Family" instead of a person - The event title must start with `[Name]` exactly, e.g. `[Daniel] Football Practice` - Check `FAMILY_MEMBERS` in `config.py` for the recognized aliases ### Display not starting on boot - Check systemd service status: `sudo systemctl status calendar-display.service` - View logs: `sudo journalctl -u calendar-display.service -f` ## Contributing Feel free to submit issues or pull requests! ## License MIT License - Feel free to use and modify for personal use. ## Credits Built with: - [Flask](https://flask.palletsprojects.com/) - Web framework - [OpenWeatherMap API](https://openweathermap.org/api) - Weather data - [Google Calendar API](https://developers.google.com/calendar) - Calendar integration - [icanhazdadjoke](https://icanhazdadjoke.com/) - Dad jokes API