Automated schedule synchronization that actually works
Extracts your schedule from WebUntis and syncs it to Google Calendar every 30 minutes.
Features • Installation • Usage • Showcase • Setup Guide
Note from the Developer
POV: Me at 2 AM building this instead of studying...
Also me at 6 AM writing a Chemistry exam... YES, I did this. Worth it? Absolutely.
- Automated Sync: Updates every 30 minutes via cron
- Duplicate Prevention: UID-based tracking prevents duplicate entries
- Room Change Detection: Handles room changes and updates
- Note Extraction: Separates exam notes from regular room information
- Live Dashboard: Web interface showing sync status and history
- Headless Operation: Runs on servers without GUI using Selenium
- Python 3.8+
- Google Calendar API credentials
- WebUntis account
- Chrome/Chromium browser (for Selenium)
git clone https://github.com/YOUR_USERNAME/untis-calendar-sync.git
cd untis-calendar-syncpython3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtCreate .env file:
UNTIS_SCHOOL='YourSchoolName'
UNTIS_USERNAME='your.username'
UNTIS_PASSWORD='your_password'
UNTIS_WEEKS=4
UNTIS_HEADLESS=true- Go to Google Cloud Console
- Create a new project
- Enable Google Calendar API
- Create OAuth 2.0 credentials (Desktop app)
- Download credentials and save as
credentials.json
./run_full_sync.shThis will:
- Authenticate with Google (opens browser)
- Extract 4 weeks of schedule data
- Create calendar events
# Full sync (extract from WebUntis + sync to Calendar)
./run_full_sync.sh
# Status check
./check_status.shAdd to crontab:
*/30 * * * * /path/to/untis-calendar-sync/auto_sync.sh >> /path/to/logs/cron.log 2>&1This runs a full sync every 30 minutes, detecting changes, cancellations, and room updates.
Start the web server:
python3 status_server.pyAccess dashboard at http://localhost:8080/dashboard
Features:
- Last sync time
- Next sync countdown
- Changes today/this week
- Event statistics
Automatic Holiday Detection:
- Gesetzliche Feiertage werden automatisch für dein Bundesland erkannt
- Standard: Bayern (
BY). Ändere inuntis_sync_improved.py:
parser = ImprovedUntisParser('weekly_data/week_1.json', bundesland='BY')
# BY=Bayern, NW=NRW, BW=Baden-Württemberg, HE=Hessen, SN=Sachsen, etc.Manual School Holidays:
Schulfreie Tage die keine gesetzlichen Feiertage sind (z.B. Buß- und Bettag außerhalb Sachsen, Brückentage, pädagogische Tage) in school_holidays.json eintragen:
{
"custom_holidays": [
"2025-11-19",
"2025-05-30",
"2025-03-15"
]
}Das System erkennt automatisch:
- ✅ Fehlende Wochentage in WebUntis-Daten
- ✅ Ordnet Lessons korrekt zu (ohne Day-Shift)
- ✅ Funktioniert für jeden Wochentag (nicht nur Mittwoch)
Events are color-coded in Google Calendar (default: Orange).
Change in untis_sync_improved.py:
'colorId': '6', # 1=Blue, 3=Purple, 6=Orange, 11=RedAdjust in .env:
UNTIS_WEEKS=4 # Extract 1-8 weeks aheadEdit crontab entry:
*/30 * * * * # Every 30 minutes
*/15 * * * * # Every 15 minutes
0 * * * * # Every houruntis-calendar-sync/
├── extractor.py # WebUntis scraper (Selenium)
├── untis_sync_improved.py # Parser & Calendar sync logic
├── sync_all_weeks.py # Multi-week sync orchestrator
├── auto_sync.sh # Main cron script
├── run_full_sync.sh # Manual full sync script
├── status_server.py # Web dashboard server
├── status_api.py # CLI status tool
├── check_status.sh # Quick status script
├── cleanup_calendar.py # Remove all Untis events
├── remove_duplicates.py # Find & remove duplicates
└── quick_sync.py # Sync without extraction
- Uses Selenium WebDriver (headless Chrome)
- Logs into WebUntis
- Navigates through weeks
- Scrapes lesson data (subject, time, room, teacher)
- Saves to JSON files (
weekly_data/week_*.json)
- Reads JSON files
- Detects day boundaries (time resets)
- Extracts room information (handles changes with +)
- Separates notes (exams, special events)
- Generates unique IDs (MD5 hash)
- Loads existing Calendar events
- Compares UIDs to prevent duplicates
- Creates new events
- Skips unchanged events
- Updates
sync_status.json
Check credentials in .env:
source .env
echo $UNTIS_USERNAME
echo $UNTIS_SCHOOLCheck parsed data:
cat parsed_lessons_all_weeks.json | python3 -m json.tool | lessRemove duplicates:
python3 remove_duplicates.pyCheck logs:
tail -f logs/auto_sync_*.log
tail -f logs/cron.logInstall/update Chrome:
apt update
apt install chromium-browserEndpoint: http://localhost:8080/status
Response:
{
"current_time": "2025-10-23T23:00:00",
"last_sync": "2025-10-23T22:30:00",
"next_sync": "2025-10-23T23:00:00",
"weeks_extracted": 4,
"total_lessons": 67,
"changes_today": [...],
"changes_this_week": [...]
}Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
MIT License - See LICENSE file for details
This tool:
- Stores credentials locally in
.env - Uses OAuth2 for Google Calendar (token.pickle)
- Does not send data to third parties
- Runs entirely on your infrastructure
Keep sensitive files secure and never commit them to version control.
- Issues: GitHub Issues
- Documentation: This README
- Status: Check
./check_status.sh
- Initial release
- Automated 30-minute sync
- Duplicate prevention
- Room change detection
- Web dashboard
- Note extraction




