A lightweight, standalone scheduling board built with Python, Kivy, and SQLite (via SQLAlchemy), designed for a Raspberry Pi connected to a touchscreen. The app displays monthly and weekly calendar views with tap-to-edit events, optimized for visibility and finger input. Events sync two-way with iCloud Calendar on your iPhone via CalDAV.
Great for home offices, studios, family schedules, or small business front desks.
- 📆 Monthly and weekly calendar views with tap-to-edit daily events
- 📱 Touchscreen-optimized UI using Kivy (large buttons, clean layout)
- 💾 Persistent event storage via SQLite and SQLAlchemy ORM
- 🔄 iCloud Calendar sync — events added on the Pi appear on your iPhone and vice versa
- 🌗 Themed UI with automatic light/dark switching
- 🌤️ Weather display via OpenWeatherMap
- 🤖 AI calendar agent (optional) — natural-language event management via FastAPI
- ⚡ Auto-start on boot for kiosk-style use on Raspberry Pi
Full-screen calendar on a Raspberry Pi touchscreen with highlighted dates and event popups.
| Layer | Tech |
|---|---|
| UI | Kivy |
| Database | SQLite via SQLAlchemy |
| iCloud sync | caldav + icalendar |
| Agent API | FastAPI + LangChain (optional) |
| Hardware | Raspberry Pi 4/5 + Touchscreen |
| Language | Python 3 |
EventCalendar/
├── main.py # Kivy app entry point
├── calendar.db # SQLite DB (auto-generated)
├── requirements.txt
├── .env.example # Environment variable template
├── app/ # Theme manager, weather, sync runner
├── UI/ # Calendar views, popups, components
├── storage/ # SQLAlchemy models, CRUD, migrations
│ ├── db_manager.py
│ └── migration.py # Adds iCal/sync columns to existing DBs
├── sync/ # iCloud CalDAV sync
│ ├── icloud_sync.py # Connection test / fetch preview
│ ├── ical_mapper.py # iCal ↔ Event field mapping
│ ├── sync_job.py # Full pull / push / delete sync
│ └── sync_state.json # Last sync result (auto-generated)
├── agent_service/ # Optional FastAPI AI agent
├── deploy/systemd/ # Daily sync timer unit files
└── assets/ # Images, fonts, icons
- Raspberry Pi OS (Lite or Full) or any Linux/macOS machine for development
- Python 3.8+
- An Apple ID with iCloud Calendar enabled
git clone https://github.com/bordanattila/EventCalendar.git
cd EventCalendarCreate and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activateInstall dependencies:
pip install --upgrade pip
pip install -r requirements.txtConfigure environment variables:
cp .env.example .env
# Edit .env with your API keys and iCloud credentials (see below)If you have an existing calendar.db, run the migration to add sync columns:
python storage/migration.pyRun the app:
python main.pySync keeps your Pi calendar and a chosen iCloud calendar in sync. Changes made on the touchscreen are pushed to iCloud; events created or edited on your iPhone are pulled down on the next sync.
Apple requires an app-specific password for CalDAV access (not your regular Apple ID password):
- Go to appleid.apple.com → Sign-In and Security → App-Specific Passwords
- Generate a password (e.g. "EventCalendar Pi")
- Add it to
.env:
ICLOUD_APPLE_ID=your@email.com
ICLOUD_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
ICLOUD_CALENDAR_NAME=Family
DEFAULT_TIMEZONE=America/New_YorkICLOUD_CALENDAR_NAME must match the calendar name as shown in the iOS Calendar app. If multiple calendars share the same name, set ICLOUD_CALENDAR_URL to the specific CalDAV URL instead (see .env.example).
Preview today's events from iCloud:
python sync/icloud_sync.pyFetch series masters (for debugging recurrence mapping):
python sync/icloud_sync.py --masters --verbosepython sync/sync_job.pyIndividual steps:
python sync/sync_job.py --pull-only # iCloud → calendar.db
python sync/sync_job.py --push-only # calendar.db → iCloud
python sync/sync_job.py --delete-only # Remove deleted events on both sidesOpen Settings in the app and tap Sync Now. The last sync time is shown below the button. The calendar view refreshes automatically after a background sync completes.
Copy the systemd unit files and adjust paths for your install:
sudo cp deploy/systemd/eventcalendar-sync.service /etc/systemd/system/
sudo cp deploy/systemd/eventcalendar-sync.timer /etc/systemd/system/
# Edit User, WorkingDirectory, and ExecStart paths in the service file
sudo systemctl daemon-reload
sudo systemctl enable --now eventcalendar-sync.timerThis runs a full sync every day at 6:00 AM. Check status with:
systemctl status eventcalendar-sync.timer
journalctl -u eventcalendar-sync.service| Direction | Behavior |
|---|---|
| Pull | Fetches events from iCloud and inserts or updates rows in calendar.db |
| Push | Sends local events marked pending_push to iCloud |
| Delete | Removes events deleted on either side |
Each event stores an ical_uid and ical_etag for stable identity and change detection. Conflicts are resolved with last-write-wins; details are logged to sync/sync.log.
Recurrence (daily, weekly, monthly, yearly) is mapped to and from iCal RRULE in both directions.
The agent_service/ directory contains a FastAPI service that manages the same calendar.db using natural language. See agent_service/README.md for setup.
Sync can also be triggered via the agent API:
# Check last sync status
curl http://localhost:8000/sync/status
# Run a full sync
curl -X POST http://localhost:8000/syncTo launch the Kivy app on boot:
nano ~/.config/lxsession/LXDE-pi/autostartAdd:
@/usr/bin/python3 /home/pi/EventCalendar/main.py
- ✅ Themed UI (light, dark, custom)
- ✅ Weekly calendar view
- ✅ iCloud Calendar sync (CalDAV, two-way)
- ✅ Natural language input via AI agent
- 🔔 Event reminders / notifications
- 🔄 Google Calendar sync (OAuth)
MIT License — use it, fork it, build on it.
Attila Bordán
Engineer. Builder.

