- Replace scrolling event list + photo background with a Sun–Sat week grid plus a "Next Week" preview column; Prev/Next navigation and per-member filter - Add [Name] event title prefix convention for color-coded family member cards (Ludwig/Dad=blue, Michelle/Mom=pink, Jason=green, Daniel=yellow) - /api/calendar now accepts ?start=YYYY-MM-DD, returns bucketed week payload with member/color metadata; fetches 14 days to populate Next Week column - Drop rotating background photo and dad joke from the display (endpoints kept) - Update README with new UI overview, prefix convention, and API docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
298 lines
9.5 KiB
Markdown
298 lines
9.5 KiB
Markdown
# 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
|