Files
calender_sync/README.md
T
2026-08-14 23:43:40 +02:00

9.0 KiB
Raw Blame History

📅 calender_sync

Google Calendar (ICS-Feed) → MariaDB Sync fürs Homelab.

Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS-Feed, expandiert RRULE/EXDATE/RECURRENCE-ID und schreibt einzelne Instanzen in eine MariaDB-Datenbank.

Features

  • 🔄 Automatische Synchronisation alle 15 Minuten (konfigurierbar)
  • 📆 Expandiert wiederkehrende Events (RRULE) und Serien-Exceptions (EXDATE, RECURRENCE-ID)
  • 🗑️ Soft-Delete: Entfernte Events werden als deleted=1 markiert, nicht gelöscht
  • 🛠️ Optionales Database-Bootstrap: Erstellt DB und User automatisch bei DB_BOOTSTRAP=true
  • Zeitfenster-konfiguration für Vergangenheit/Future (standardmäßig -90 Tage / +365 Tage)
  • 🌐 Web-UI zur Anzeige anstehender Termine mit Suchfunktion
  • 🔌 REST API für programmsprachigen Zugriff auf Kalenderdaten
  • 📧 Optionale tägliche E-Mail-Benachrichtigung um konfigurierte Uhrzeit
  • 📬 Optionale wöchentliche Vorab-Info (z.B. freitags) mit Termine nach Suchbegriffen

UI

📋 Voraussetzungen

  • 🐳 Docker & Docker Compose
  • 🗃️ MariaDB Instanz (z.B. als Proxmox LXC)
  • 📱 Google Calendar mit privatem ICS-Feed

🚀 Quick Start

  1. 🔧 MariaDB Setup

    Entweder manuell ausführen:

    mysql -u root -p < mariadb-setup.sql
    

    Oder automatisch beim Start (in .env setzen):

    DB_BOOTSTRAP=true
    DB_ROOT_USER=root
    DB_ROOT_PASSWORD=dein_root_passwort
    
  2. 📄 .env anlegen

    cp .env.example .env
    

    Variablen anpassen, insbesondere:

    • ICS_URL: Privater ICS-Link aus den Google Calendar Einstellungen
    • DB_PASSWORD: Sicheres Passwort für den calendar_sync User
  3. ▶️ Starten

    docker compose up -d
    

🎮 Demo / Lokaler Test (ohne Docker)

Schneller Weg um Web-UI und API lokal zu testen, ohne MariaDB oder Docker:

./demo.sh

Das Script erstellt eine virtuelle Python-Umgebung, installiert Dependencies, legt eine SQLite Demo-Datenbank mit 4 Beispiel-Terminen an und startet die API auf Port 8000.

🔗 Endpoints:

🎯 Demo-Events (4 Stück):

Termin Zeit Status
📅 Team Daily heute 14:0015:00 CONFIRMED
🧘 Yoga Kurs heute 18:3020:00 CONFIRMED
⚠️ Projekt-Deadline morgen Ganztag 🟡 TENTATIVE
👨‍⚕️ Arzttermin übermorgen 10:0011:30 CONFIRMED

Die Demo verwendet SQLite (DB_BACKEND=sqlite) statt MariaDB. Die Events werden relativ zum heutigen Datum erstellt.

⚙️ Konfiguration

Variable Default Beschreibung
ICS_URL required Privater ICS-Feed aus Google Calendar
CALENDAR_LABEL default Label für den Kalender (für Multi-Kalender)
DB_HOST mariadb.fritz.box MariaDB Host
DB_PORT 3306 MariaDB Port
DB_NAME calendar_sync Datenbank-Name
DB_USER required Datenbank-User
DB_PASSWORD required Datenbank-Passwort
SYNC_INTERVAL_MINUTES 15 Sync-Intervall in Minuten
WINDOW_PAST_DAYS 90 Wie viele Tage in die Vergangenheit synchronisieren
WINDOW_FUTURE_DAYS 365 Wie viele Tage in die Zukunft synchronisieren
LOG_LEVEL INFO Python Logging Level
DB_BOOTSTRAP false DB + User beim Start erstellen
DB_ROOT_USER - Root-User fürs Bootstrap
DB_ROOT_PASSWORD - Root-Passwort fürs Bootstrap
API_PORT 8000 Port für den API/Web-UI Container
TIMEZONE UTC Zeitzone für API/Web-UI Anzeige (z.B. Europe/Berlin)

📧 Optionale E-Mail-Benachrichtigung

Sendet täglich eine HTML-Email mit den anstehenden Terminen. Wird aktiviert wenn SMTP_HOST und NOTIFY_EMAIL gesetzt sind.

Variable Default Beschreibung
SMTP_HOST - SMTP Server Hostname
SMTP_PORT 587 SMTP Server Port
SMTP_USER - SMTP Login Username
SMTP_PASSWORD - SMTP Login Passwort
SMTP_FROM - Absender E-Mail Adresse
SMTP_USE_TLS true TLS verschlüsselung nutzen
NOTIFY_EMAIL - Empfänger E-Mail Adresse
NOTIFY_TIME 6 Uhrzeit für Benachrichtigung (Stunde, 0-23)
NOTIFY_TIMEZONE Europe/Berlin Zeitzone für die Benachrichtigung

📬 Subject-Logik:

  • 📌 1 Termin: Kalender heute: 09:00 - Meeting mit Team
  • 📌 2+ Termine: Kalender heute: 3 Termine

⚠️ Hinweis: Ganztagstermine werden nicht in der Benachrichtigung berücksichtigt.

📬 Optionale wöchentliche Vorab-Info

Sendet wöchentlich (standardmäßig freitags) eine HTML-Email mit Terminen, die auf konfigurierte Suchbegriffe passen. Zeitraum ist immer Samstag bis Freitag der nächsten Woche. Wird aktiviert wenn WEEKLY_NOTIFY_ENABLED=true und mindestens ein Suchbegriff gesetzt ist.

Variable Default Beschreibung
WEEKLY_NOTIFY_ENABLED false Feature aktivieren
WEEKLY_NOTIFY_DAY 5 Wochentag (0=Mo, 1=Di, ..., 5=Fr)
WEEKLY_NOTIFY_TIME 16 Uhrzeit für Benachrichtigung (Stunde, 0-23)
WEEKLY_NOTIFY_TIMEZONE Europe/Berlin Zeitzone für die Benachrichtigung
WEEKLY_NOTIFY_EMAIL - Empfänger (Fallback: NOTIFY_EMAIL)
WEEKLY_SEARCHWORDS - Komma-separierte Suchbegriffe

📬 Subject-Logik:

  • 📌 1 Termin: Vorab-Info: Termin am Fr, 15.08.
  • 📌 2+ Termine: Vorab-Info: 3 Termine naechste Woche

💡 Beispiel:

WEEKLY_NOTIFY_ENABLED=true
WEEKLY_NOTIFY_DAY=5
WEEKLY_NOTIFY_TIME=16
WEEKLY_SEARCHWORDS=Fussball,Arzttermine

⚠️ Hinweis: Keine Email wenn keine Treffer für die Suchbegriffe im Zeitraum.

🗃️ Datenbank-Schema

Tabelle calendar_events:

Spalte Typ Beschreibung
id 🆔 BIGINT PK Auto-Increment
calendar_label 🏷️ VARCHAR(64) Kalender-Label
instance_key 🔑 VARCHAR(255) SHA1-basierte eindeutige Instanz-ID
uid 📌 VARCHAR(255) ICS UID
recurrence_id 🔄 VARCHAR(64) RECURRENCE-ID bei Serien-Exceptions
summary 📝 VARCHAR(512) Titel
description 📄 TEXT Beschreibung
location 📍 VARCHAR(512) Ort
start_at 🚀 DATETIME Startzeit (UTC)
end_at 🏁 DATETIME Endzeit (UTC)
all_day ☀️ TINYINT(1) Ganz-tagig Flag
status 📊 VARCHAR(32) Event-Status
deleted 🗑️ TINYINT(1) Soft-Delete Flag
last_seen_at 👁️ DATETIME Letzte Synchronisation
created_at 🎨 DATETIME Erstellungszeitpunkt
updated_at 🔄 DATETIME Letzte Änderung

Tabelle daily_notification_log (optional, für E-Mail-Benachrichtigung):

Spalte Typ Beschreibung
id 🆔 INT PK Auto-Increment
notify_date 📅 DATE Datum der Benachrichtigung
sent_at DATETIME Zeitpunkt des Versands
event_count 🔢 INT Anzahl Termine in der Email

Tabelle weekly_notification_log (optional, für wöchentliche Vorab-Info):

Spalte Typ Beschreibung
id 🆔 INT PK Auto-Increment
notify_date 📅 DATE Datum der Benachrichtigung
sent_at DATETIME Zeitpunkt des Versands
event_count 🔢 INT Anzahl Termine in der Email

🌐 Web-UI & API

Das Projekt enthält eine FastAPI-basierte Webanwendung die als separater Container (calendar-api) läuft und auf Port 8000 erreichbar ist.

🔗 Endpoints

Endpoint Beschreibung
GET / 🌐 HTML-Seite mit anstehenden Terminen und Suchfunktion
GET /api/health ❤️ Health Check (gibt {"status": "ok"} zurück)
GET /api/events?limit=10&search=... 📊 JSON-Liste zukünftiger Events (nicht gelöscht)
GET /api/events/{event_id} 🔍 Einzelnes Event als JSON

💡 API Beispiel

# Alle anstehenden Events (max. 10)
curl http://localhost:8000/api/events

# Suche nach Titel
curl "http://localhost:8000/api/events?search=Meeting&limit=5"

# Einzelnes Event
curl http://localhost:8000/api/events/42

📋 JSON Response Format

{
  "events": [
    {
      "id": 1,
      "summary": "Teammeeting",
      "description": "Wöchentliches Teammeeting",
      "location": "Konferenzraum 1",
      "start_at": "2025-01-15T10:00:00",
      "end_at": "2025-01-15T11:00:00",
       "all_day": false,
       "status": "CONFIRMED",
       "timezone": "Europe/Berlin"
     }
  ],
  "count": 1,
  "query_time": "2025-01-15T09:30:00Z",
  "timezone": "Europe/Berlin"
}

🔄 CI/CD

GitHub Actions Workflow (docker-publish.yml) baut und published das Docker Image automatisch nach GitHub Container Registry:

ghcr.io/skoelle/calender_sync:latest

📜 Lizenz

Lizenziert unter der MIT License - Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)