A Python tool to synchronize events between multiple calendars using CalDAV protocol with OAuth 2.0 support for Google Calendar. Perfect for automatically creating time blocks on your work calendar to prevent double-booking.
- Universal Calendar Support: Google Calendar (OAuth 2.0), iCloud, and any CalDAV-compatible service
- Multiple Sync Modes: Anonymous placeholders, source identification, or full event details
- Automatic Authentication: Handles multiple Google accounts seamlessly with token management
- Multi-Calendar Support: Sync from/to multiple calendars within the same account
- Smart Filtering: Exclude weekend events and prevent sync loops in bi-directional setups
- All-Day Event Control: Configure all-day events as "free" or "busy" on destination calendar
- Alert Management: Optionally sync alerts/reminders from source events
- Event Preservation: Keeps all synced events for historical reference
- Cron-Friendly: Runs without user interaction after setup for automation
- Dry-Run Mode: Preview changes before applying them
- Comprehensive Logging: Detailed logging for troubleshooting
pip install -r requirements.txt# Copy and edit configuration
cp config.yaml.example config.yaml
# Edit config.yaml with your calendar credentialsGoogle Calendar requires OAuth 2.0 setup:
- Go to Google Cloud Console
- Create a new project or select existing
- Enable TWO APIs:
- Google Calendar API
- CalDAV API
- Create OAuth 2.0 credentials (Desktop Application)
- Download credentials as
credentials.json - Run setup:
python google_auth.py
# See what would be synced (dry-run)
python calendar_sync.py --dry-run
# List available calendars
python calendar_sync.py --list-calendars
# Perform actual sync
python calendar_sync.pysource_calendars:
- name: "Personal Gmail"
type: "oauth"
url: "https://apidata.googleusercontent.com/caldav/v2/your-email%40gmail.com/user/"
# calendar_name: "Personal" # Optional: specific non-default calendarsource_calendars:
- name: "Personal iCloud"
url: "https://caldav.icloud.com/"
username: "your-apple-id@icloud.com"
password: "your-app-specific-password"anon: Anonymous "Existing Event" placeholders (privacy-friendly)source: Shows source calendar namefull: Copies complete event details
sync_settings:
# Exclude weekend events for business-focused syncing
exclude_weekends: true
# Set all-day events as "free" to avoid blocking entire days
allday_events_busy: false
# Copy alerts/reminders from source events
# false = no alerts on synced events (removes any defaults)
# true = copy all alerts from source events
sync_alerts: falseThe tool automatically prevents infinite loops when setting up bi-directional synchronization:
- Loop Prevention: Automatically excludes events created by the sync tool from being synced again
- Safe Bi-Directional Setup: You can safely sync Calendar A → Calendar B and Calendar B → Calendar A
- Intelligent Detection: Identifies synced events by prefix (
[SYNC]) and UID pattern
# Example: Safe bi-directional sync
source_calendars:
- name: "Personal Calendar"
url: "calendar-a-url"
- name: "Work Calendar"
url: "calendar-b-url"
destination_calendars:
- name: "Work Calendar Blocks"
url: "calendar-b-url"
- name: "Personal Calendar Blocks"
url: "calendar-a-url"# Sync with default settings
python calendar_sync.py
# Custom time range
python calendar_sync.py --days-back 14 --days-ahead 180
# Dry run to preview changes
python calendar_sync.py --dry-run
# Verbose logging for troubleshooting
python calendar_sync.py --verbose
# List all available calendars
python calendar_sync.py --list-calendars
# Remove all synced events (within time range)
python calendar_sync.py --reset
# Preview what would be removed
python calendar_sync.py --reset --dry-runThe --reset flag removes all synced events from destination calendars within the specified time range:
# Remove synced events from the last 7 days and next 90 days (default range)
python calendar_sync.py --reset
# Remove synced events from custom time range
python calendar_sync.py --reset --days-back 30 --days-ahead 180
# Preview what would be removed without actually deleting
python calendar_sync.py --reset --dry-run --verboseImportant Notes:
- Reset mode identifies synced events by the tool
- Only removes events within the specified time parameters
- Does not affect source calendars - only removes synced blocks from destination calendars
- Use
--dry-runfirst to verify which events would be removed
Add to crontab for automatic syncing every 15 minutes:
crontab -e
# Add this line:
*/15 * * * * cd /path/to/calendar-sync && python calendar_sync.py >> sync.log 2>&1The tool automatically handles multiple Google accounts:
source_calendars:
- name: "Personal Gmail"
type: "oauth"
url: "https://apidata.googleusercontent.com/caldav/v2/personal%40gmail.com/user/"
- name: "Work Gmail"
type: "oauth"
url: "https://apidata.googleusercontent.com/caldav/v2/work%40gmail.com/user/"
destination_calendars:
- name: "Work Calendar"
type: "oauth"
url: "https://apidata.googleusercontent.com/caldav/v2/work%40gmail.com/user/"- Uses single OAuth app for all Google accounts
- Creates separate token files automatically:
token-personal-gmail-com.json,token-work-gmail-com.json - Prompts for authentication only on first run
- Automatically refreshes tokens
- URL Format:
https://apidata.googleusercontent.com/caldav/v2/EMAIL/user/(Use%40instead of the@character) - Authentication: OAuth 2.0 (required)
- Setup: Requires both Calendar API and CalDAV API enabled
- URL Format:
https://caldav.icloud.com/ - Authentication: Basic auth with app-specific password
- Setup: Generate app password at appleid.apple.com
Not Currently Supported: Microsoft has discontinued CalDAV support for consumer and business accounts. Microsoft calendars require the Microsoft Graph API, which is not implemented in this tool.
For Microsoft calendar integration, consider using a CalDAV-compatible service as an intermediary
# Test OAuth authentication
python test_google_oauth.py
# Common fixes:
# 1. Enable both Calendar API AND CalDAV API in Google Cloud Console
# 2. Use app-specific passwords for 2FA accounts
# 3. Check that credentials.json is in project directory- Google: Ensure CalDAV API is enabled (separate from Calendar API)
- iCloud: Generate app-specific password, don't use main password
# List calendars to verify connectivity
python calendar_sync.py --list-calendars --verbosecalendar-sync/
├── calendar_sync.py # Main synchronization script
├── google_auth.py # Google OAuth 2.0 handler
├── config.yaml.example # Configuration template
├── test_google_oauth.py # OAuth troubleshooting tool
├── requirements.txt # Python dependencies
├── credentials.json # Google OAuth credentials (you create)
├── token-*.json # OAuth tokens (auto-generated)
└── calendar_sync.log # Application logs
- Store
config.yamlsecurely (contains credentials) - Use app-specific passwords instead of main account passwords
credentials.jsoncontains app identity (safe to backup)token-*.jsonfiles contain access tokens (keep private)- Consider running from secure location with restricted permissions