WallFlower is a Python-based electronic wall calendar designed for Raspberry Pi displays. It provides a full-screen calendar interface for portrait-mode, wall-mounted displays, with Google Calendar and CalDAV synchronization.
What It Does
- Shows a detailed week view with event times, locations, descriptions, and calendar colors.
- Provides a month overview or configurable next-week preview.
- Syncs Google Calendar and CalDAV sources, including providers such as iCloud and Nextcloud.
- Stores events locally in SQLite for fast display and offline-friendly rendering.
- Includes a web configuration interface for calendar sources, themes, display settings, and manual sync.
- Supports kiosk-mode deployment with Chromium on Raspberry Pi hardware.
Display And Customization
WallFlower is tuned for always-on wall displays. It includes multiple visual themes, several event display styles, configurable week start day, 12-hour or 24-hour time formats, calendar visibility controls, and per-calendar colors.
The project is intended for local-network use. Calendar credentials and CalDAV passwords are stored locally, with CalDAV passwords encrypted using Fernet.
Repository and release metadata are refreshed from Gitea during the static site build.
Repository README
WallFlower - Electronic Wall Calendar
A Python-based electronic wall calendar designed for Raspberry Pi displays, featuring Google Calendar and CalDAV integration with a beautiful, customizable interface optimized for portrait-mode wall-mounted displays.
Features
Display Features
- Dual View Display:
- Week view with detailed event information (times, locations, descriptions)
- Month calendar with color-coded event indicators
- Optional next week preview (configurable)
- Calendar legend showing all enabled calendars
- Auto-Refresh: Display automatically refreshes every 60 seconds (configurable)
- Portrait Mode Optimized: Perfect for wall-mounted displays
Calendar Integration
- Multiple Calendar Sources: Connect unlimited Google Calendar and CalDAV calendars
- Google Calendar: Full OAuth 2.0 integration with calendar selection
- CalDAV Support: Compatible with iCloud, Nextcloud, and any CalDAV server
- Auto-Sync: Background daemon syncs events every 15 minutes
- Smart Caching: Events stored locally in SQLite for fast display
Customization
- 7 Color Themes: Dark, Light, Blue Ocean, Purple Dream, Green Forest, Warm Sunset, Minimal Gray
- 3 Event Display Styles:
- Left Border (minimal)
- Full Border (outlined)
- Filled Background (bold)
- Flexible Configuration:
- Week start day (Sunday/Monday)
- Time format (12/24 hour)
- Date format (multiple options)
- Bottom view (Month calendar or Next Week preview)
- Per-calendar color customization
- Calendar naming and visibility
Management
- Web Configuration Interface: Easy-to-use web UI for all settings
- Calendar Management: Add, edit, enable/disable, or delete calendars
- Manual Sync: Trigger immediate sync from the dashboard
- Secure: Encrypted storage for CalDAV passwords using Fernet
Screenshots
The display shows a full-screen calendar with:
- Current week events with times, titles, and locations
- Color-coded calendars with legend
- Month overview or next week preview
- Auto-refreshing content
- Beautiful themes suitable for any room
Requirements
Hardware
- Raspberry Pi (Zero 2 W, 3, 4, or 5)
- Display (portrait orientation recommended, 1080x1920 or similar)
- MicroSD card (8GB minimum, 16GB recommended)
- Power supply
- Internet connection (WiFi or Ethernet)
Software
- Raspberry Pi OS (Bullseye or newer)
- Python 3.9 or higher
- Chromium browser (for kiosk mode)
- Git (for installation)
Quick Start
# Clone the repository
git clone https://github.com/yourusername/WallFlower.git
cd WallFlower
# Create virtual environment and install dependencies
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Run the application
python run.py
Then open http://localhost:5000/config to configure your calendars.
Full Installation Guide
1. System Preparation
Update your Raspberry Pi and install required packages:
sudo apt update
sudo apt upgrade -y
sudo apt install -y python3-pip python3-venv git chromium-browser sqlite3 unclutter
2. Clone the Repository
cd /home/pi
git clone https://github.com/yourusername/WallFlower.git
cd WallFlower
3. Set Up Python Environment
# Create virtual environment
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate
# Install Python dependencies
pip install -r requirements.txt
4. Google Calendar Setup (Optional)
To connect Google Calendar, you need OAuth credentials:
Step 1: Create Google Cloud Project
- Go to Google Cloud Console
- Click “Select a project” > “New Project”
- Name it “WallFlower Calendar” and click “Create”
Step 2: Enable Google Calendar API
- In the console, go to “APIs & Services” > “Library”
- Search for “Google Calendar API”
- Click on it and click “Enable”
Step 3: Configure OAuth Consent Screen
- Go to “APIs & Services” > “OAuth consent screen”
- Select “External” user type
- Click “Create”
- Fill in the required fields:
- App name: “WallFlower Calendar”
- User support email: Your email
- Developer contact: Your email
- Click “Save and Continue”
- Skip “Scopes” (click “Save and Continue”)
- Add Test Users:
- Click “Add Users”
- Add your Gmail address
- Click “Add”
- Click “Save and Continue”
Step 4: Create OAuth Credentials
- Go to “APIs & Services” > “Credentials”
- Click “Create Credentials” > “OAuth client ID”
- Application type: “Web application”
- Name: “WallFlower”
- Authorized redirect URIs:
- Click “Add URI”
- Enter:
http://localhost:5000/config/google/callback
- Click “Create”
Step 5: Download Credentials
- Click the download icon (⬇) next to your newly created OAuth client
- Save the file to your computer
- Copy it to your Raspberry Pi:
# From your computer (not on the Pi):
scp ~/Downloads/client_secret_*.json pi@<raspberry-pi-ip>:/home/pi/WallFlower/credentials.json
# Or if you downloaded directly on the Pi, rename it:
mv ~/Downloads/client_secret_*.json /home/pi/WallFlower/credentials.json
Verify the file is in place:
cd /home/pi/WallFlower
ls -la credentials.json
5. Initial Configuration
Start the application manually for first-time setup:
cd /home/pi/WallFlower
source venv/bin/activate
python run.py
The web server will start on port 5000. Open a browser and navigate to:
- From another computer:
http://<raspberry-pi-ip>:5000/config - From the Pi:
http://localhost:5000/config
Add Calendar Sources
For Google Calendar:
- Click “Manage Calendars”
- Click “Connect Google Calendar”
- Authorize the application in your browser
- Select which calendar to add
- Choose a color for the calendar
- Click “Add Calendar”
For CalDAV (iCloud, Nextcloud, etc.):
- Click “Manage Calendars”
- Click “Add CalDAV Calendar”
- Fill in the form:
- Name: Display name for the calendar
- CalDAV URL: See CalDAV URLs below
- Username: Your username or email
- Password: Your password (for iCloud, use app-specific password)
- Color: Choose a color
- Click “Add Calendar”
Configure Appearance
- Click “Appearance” in the config interface
- Set your preferences:
- Week Start Day: Sunday or Monday
- Time Format: 12-hour or 24-hour
- Date Format: Various options
- Color Theme: Choose from 7 themes
- Event Color Style: Left Border, Full Border, or Filled Background
- Bottom View Display: Month Calendar or Next Week View
Test the Display
Navigate to http://localhost:5000/display to see your calendar.
Once everything looks good, press Ctrl+C to stop the server and proceed to deployment.
Deployment
Set Up Systemd Services
For production use, configure systemd to run the services automatically.
1. Create Web Server Service
sudo nano /etc/systemd/system/wallflower-web.service
Paste the following (adjust the User if not using pi):
[Unit]
Description=WallFlower Web Server
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/WallFlower
Environment="PATH=/home/pi/WallFlower/venv/bin"
Environment="DISPLAY_REFRESH=60"
Environment="SYNC_INTERVAL=900"
ExecStart=/home/pi/WallFlower/venv/bin/python /home/pi/WallFlower/run.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
2. Create Sync Daemon Service
sudo nano /etc/systemd/system/wallflower-sync.service
Paste the following:
[Unit]
Description=WallFlower Calendar Sync Daemon
After=network.target wallflower-web.service
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/WallFlower
Environment="PATH=/home/pi/WallFlower/venv/bin"
Environment="SYNC_INTERVAL=900"
ExecStart=/home/pi/WallFlower/venv/bin/python /home/pi/WallFlower/sync_daemon.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
3. Enable and Start Services
# Reload systemd to recognize new services
sudo systemctl daemon-reload
# Enable services to start on boot
sudo systemctl enable wallflower-web.service
sudo systemctl enable wallflower-sync.service
# Start services now
sudo systemctl start wallflower-web.service
sudo systemctl start wallflower-sync.service
# Verify services are running
sudo systemctl status wallflower-web.service
sudo systemctl status wallflower-sync.service
Expected output should show “active (running)” in green.
4. View Service Logs
# Follow web server logs in real-time
sudo journalctl -u wallflower-web.service -f
# Follow sync daemon logs in real-time
sudo journalctl -u wallflower-sync.service -f
# View last 50 lines of logs
sudo journalctl -u wallflower-web.service -n 50
sudo journalctl -u wallflower-sync.service -n 50
# View application log file
tail -f /home/pi/WallFlower/data/calendar.log
Configure Kiosk Mode
To display the calendar full-screen automatically on boot:
1. Create Kiosk Startup Script
nano /home/pi/WallFlower/start_kiosk.sh
Paste the following:
#!/bin/bash
# Wait for services to start
sleep 10
# Hide mouse cursor
unclutter -idle 0.1 &
# Disable screen blanking
xset s off
xset -dpms
xset s noblank
# Start Chromium in kiosk mode
chromium-browser \
--kiosk \
--noerrdialogs \
--disable-infobars \
--no-first-run \
--disable-features=TranslateUI \
--disable-session-crashed-bubble \
--disable-component-update \
--check-for-update-interval=31536000 \
http://localhost:5000/display
Make it executable:
chmod +x /home/pi/WallFlower/start_kiosk.sh
2. Create Autostart Entry
mkdir -p ~/.config/autostart
nano ~/.config/autostart/wallflower.desktop
Paste the following:
[Desktop Entry]
Type=Application
Name=WallFlower Calendar
Exec=/home/pi/WallFlower/start_kiosk.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
3. Disable Screen Blanking System-Wide
Edit LXDE autostart:
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart
Add these lines at the end:
@xset s off
@xset -dpms
@xset s noblank
4. Configure Auto-Login (Optional)
To automatically log in to the desktop on boot:
sudo raspi-config
Navigate to:
- System Options > Boot / Auto Login > Desktop Autologin
Select and confirm.
Screen Rotation (Portrait Mode)
To rotate the display for portrait orientation:
sudo nano /boot/config.txt
Add at the end of the file:
# Rotate display to portrait mode
# Use display_rotate=1 for 90° clockwise
# Use display_rotate=3 for 90° counter-clockwise (270°)
display_rotate=1
Save and reboot:
sudo reboot
CalDAV URLs
iCloud Calendar
- Log in to iCloud.com
- Open Calendar
- Click the share icon next to the calendar name
- Check “Public Calendar”
- Copy the URL and modify it:
- Change
webcal://tohttps:// - The URL should look like:
https://caldav.icloud.com/published/2/...
- Change
For private calendars, use:
https://caldav.icloud.com/[calendar-id]
Important: For iCloud, you must use an app-specific password:
- Go to appleid.apple.com
- Sign in
- Go to “Security” > “App-Specific Passwords”
- Click “Generate Password”
- Name it “WallFlower”
- Use this password (not your Apple ID password)
Nextcloud
https://your-nextcloud-domain.com/remote.php/dav/calendars/USERNAME/CALENDAR-NAME/
Example:
https://cloud.example.com/remote.php/dav/calendars/john/personal/
Other CalDAV Servers
Consult your calendar provider’s documentation for the CalDAV URL.
Configuration Options
Environment Variables
You can customize behavior by adding environment variables to the systemd service files:
[Service]
Environment="DISPLAY_REFRESH=60" # Display refresh interval (seconds)
Environment="SYNC_INTERVAL=900" # Sync interval (seconds)
Environment="SYNC_PAST_DAYS=7" # Days in the past to sync
Environment="SYNC_FUTURE_DAYS=90" # Days in the future to sync
Environment="TZ=America/New_York" # Timezone
Environment="FLASK_HOST=0.0.0.0" # Flask bind address
Environment="FLASK_PORT=5000" # Flask port
Environment="LOG_LEVEL=INFO" # Logging level
After editing service files, reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart wallflower-web.service
sudo systemctl restart wallflower-sync.service
Database Location
All data is stored in:
/home/pi/WallFlower/data/calendar.db
To reset/start fresh:
# Stop services
sudo systemctl stop wallflower-web.service
sudo systemctl stop wallflower-sync.service
# Remove database
rm /home/pi/WallFlower/data/calendar.db
# Restart services (database will be recreated)
sudo systemctl start wallflower-web.service
sudo systemctl start wallflower-sync.service
Usage
Web Interface Pages
/display- Main calendar display (for kiosk mode)/config- Configuration dashboard/config/calendars- Manage calendar sources/config/appearance- Customize display settings
Manual Sync Trigger
From the web interface:
- Go to
/config - Click “Sync Now”
From command line:
cd /home/pi/WallFlower
source venv/bin/activate
python -c "from app.sync import SyncManager; SyncManager().sync_all_sources()"
Restart Services
# Restart both services
sudo systemctl restart wallflower-web.service
sudo systemctl restart wallflower-sync.service
# Or restart individually
sudo systemctl restart wallflower-web.service
sudo systemctl restart wallflower-sync.service
Stop Services
sudo systemctl stop wallflower-web.service
sudo systemctl stop wallflower-sync.service
Disable Services (prevent auto-start)
sudo systemctl disable wallflower-web.service
sudo systemctl disable wallflower-sync.service
Troubleshooting
Services Won’t Start
Check service status:
sudo systemctl status wallflower-web.service
sudo systemctl status wallflower-sync.service
View error logs:
sudo journalctl -u wallflower-web.service -n 100 --no-pager
sudo journalctl -u wallflower-sync.service -n 100 --no-pager
Common issues:
- Incorrect paths: Verify paths in service files match your installation
- Missing dependencies: Ensure all packages are installed in venv
- Permission issues: Check file permissions in the WallFlower directory
- Port already in use: Another service might be using port 5000
Google Calendar Not Syncing
-
Verify
credentials.jsonexists:ls -la /home/pi/WallFlower/credentials.json -
Check if you’re added as a test user in Google Cloud Console
-
View sync logs for errors:
sudo journalctl -u wallflower-sync.service -f -
Remove and re-add the calendar:
- Go to
/config/calendars - Delete the Google calendar
- Re-add it through the OAuth flow
- Go to
CalDAV Connection Errors
For iCloud:
- Use an app-specific password (not your Apple ID password)
- Verify the CalDAV URL is correct
- Check if Calendar access is enabled for app-specific passwords
For Nextcloud:
- Verify the URL format includes
/remote.php/dav/ - Check username and password
- Ensure calendar name in URL is correct
General:
- Test connectivity:
curl -I <caldav-url> - Check firewall rules
- View sync logs:
sudo journalctl -u wallflower-sync.service -f
Display Not Showing/Chromium Not Starting
-
Check if services are running:
sudo systemctl status wallflower-web.service ps aux | grep chromium -
Verify autostart configuration:
ls -la ~/.config/autostart/wallflower.desktop cat ~/.config/autostart/wallflower.desktop -
Check kiosk script is executable:
ls -la /home/pi/WallFlower/start_kiosk.sh -
Test kiosk script manually:
/home/pi/WallFlower/start_kiosk.sh
Screen Keeps Blanking
If screen still blanks despite configuration:
Edit lightdm configuration:
sudo nano /etc/lightdm/lightdm.conf
Add in the [Seat:*] section:
[Seat:*]
xserver-command=X -s 0 -dpms
Reboot:
sudo reboot
Display Not Refreshing
-
Check the refresh interval setting:
grep DISPLAY_REFRESH /home/pi/WallFlower/config.py -
Verify the meta refresh tag in browser (View Source on display page)
-
Restart web service:
sudo systemctl restart wallflower-web.service
Database Corruption
If the database becomes corrupted:
# Stop services
sudo systemctl stop wallflower-web.service
sudo systemctl stop wallflower-sync.service
# Backup current database (optional)
cp /home/pi/WallFlower/data/calendar.db /home/pi/WallFlower/data/calendar.db.backup
# Remove database
rm /home/pi/WallFlower/data/calendar.db
# Restart services (will recreate database)
sudo systemctl start wallflower-web.service
sudo systemctl start wallflower-sync.service
Then re-add all calendars through the web interface.
File Structure
WallFlower/
├── app/
│ ├── __init__.py # Flask app initialization
│ ├── routes.py # Web routes and view controllers
│ ├── models/ # Database models
│ │ ├── __init__.py
│ │ ├── database.py # Database connection and initialization
│ │ ├── calendar_source.py # CalendarSource model
│ │ ├── event.py # Event model
│ │ └── setting.py # Setting model
│ ├── sync/ # Calendar sync modules
│ │ ├── __init__.py
│ │ ├── manager.py # SyncManager orchestration
│ │ ├── google_cal.py # Google Calendar integration
│ │ └── caldav_client.py # CalDAV client integration
│ └── utils/ # Utility functions
│ ├── __init__.py
│ └── helpers.py # Date, time, and encryption helpers
├── templates/ # Jinja2 HTML templates
│ ├── base.html # Base template
│ ├── display.html # Main calendar display
│ └── config/ # Configuration pages
│ ├── index.html # Config dashboard
│ ├── calendars.html # Calendar management
│ ├── appearance.html # Appearance settings
│ └── google_select.html # Google calendar selection
├── static/ # Static assets
│ ├── css/
│ │ ├── display.css # Main stylesheet
│ │ └── themes.css # Color themes
│ └── js/
│ └── display.js # Display page JavaScript
├── data/ # Runtime data (auto-created, gitignored)
│ ├── calendar.db # SQLite database
│ ├── encryption.key # Fernet encryption key
│ └── calendar.log # Application logs
├── venv/ # Python virtual environment (gitignored)
├── config.py # Application configuration
├── run.py # Web server entry point
├── sync_daemon.py # Background sync daemon
├── requirements.txt # Python package dependencies
├── start_kiosk.sh # Chromium kiosk mode script
├── .gitignore # Git ignore rules
└── README.md # This file
Security Considerations
- CalDAV Passwords: Encrypted using Fernet (symmetric encryption)
- Encryption Key: Stored in
data/encryption.keywith restrictive permissions - Google OAuth Tokens: Stored in the database
- No Authentication: The web interface has no login - designed for local network only
- Network Exposure: Do not expose the configuration interface to the internet without adding authentication
- Credentials File: Keep
credentials.jsonsecure and never commit to version control
Recommendations for Production
- Run behind a reverse proxy (nginx) with authentication if accessing remotely
- Use firewall rules to restrict access to port 5000
- Keep the Raspberry Pi OS updated
- Regular backups of the
data/directory - Use strong WiFi password and network security
Performance Tips
- Raspberry Pi Zero: Works but may be slow; consider reducing number of events
- Raspberry Pi 3/4/5: Recommended for smooth performance
- Sync Interval: Default 15 minutes is good balance; reduce for more frequent updates
- Display Refresh: Default 60 seconds; increase to reduce CPU usage
- Event Range: Default syncs 7 days past, 90 days future; adjust in config.py
Advanced Customization
Changing Display Layout
Edit /home/pi/WallFlower/templates/display.html:
- Modify week/month view proportions (flex values in CSS)
- Add/remove event information fields
- Change grid layouts
Custom Themes
Edit /home/pi/WallFlower/static/css/themes.css:
- Create new theme by copying an existing theme block
- Modify CSS custom properties (—bg-primary, —accent-primary, etc.)
- Add theme to appearance settings in
templates/config/appearance.html
Additional Calendar Integrations
To add support for other calendar types:
- Create new sync module in
app/sync/ - Implement sync methods
- Add database support in
app/models/calendar_source.py - Create route handlers in
app/routes.py - Add UI templates in
templates/config/
API Reference
REST API
Get events for a date range:
GET /api/events?start=<ISO-datetime>&end=<ISO-datetime>
Example:
curl "http://localhost:5000/api/events?start=2024-01-01T00:00:00&end=2024-01-31T23:59:59"
Returns JSON array of events.
Contributing
Contributions are welcome! Please feel free to submit pull requests or open issues.
Areas for improvement:
- Additional calendar source integrations
- Mobile-responsive configuration interface
- Touch screen support for display interaction
- Weather widget integration
- Task/todo list integration
- Recurring event exception handling
- Multi-timezone support
- Event creation/editing from display
License
This project is open source and available under the MIT License.
Support
For issues, questions, or feature requests:
- Open an issue on GitHub
- Check the Troubleshooting section
- Review application logs
Acknowledgments
Built with:
- Flask - Web framework
- Google Calendar API v3 - Google Calendar integration
- caldav - CalDAV library
- cryptography - Password encryption
- SQLite - Database
- Chromium - Display rendering
Changelog
Version 1.0.0
- Initial release
- Google Calendar and CalDAV support
- 7 color themes
- 3 event display styles
- Configurable bottom view (month or next week)
- Web-based configuration
- Auto-sync and auto-refresh
- Kiosk mode support
- Dark mode for configuration interface