initial
This commit is contained in:
@@ -0,0 +1,553 @@
|
|||||||
|
# OpenClaw auf Raspberry Pi 4 – Installationsanleitung
|
||||||
|
|
||||||
|
### mit OpenRouter + mailbox.org CalDAV & Tasks
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Übersicht & Voraussetzungen
|
||||||
|
|
||||||
|
Diese Anleitung richtet einen **OpenClaw Gateway** auf einem Raspberry Pi 4 (headless, 64-bit) ein, verbindet ihn mit **OpenRouter** als KI-Provider und integriert **mailbox.org Kalender und Aufgaben** über CalDAV via `vdirsyncer` + `khal`.[^1][^2]
|
||||||
|
|
||||||
|
**Du brauchst:**
|
||||||
|
|
||||||
|
- Raspberry Pi 4 (2 GB+ RAM, 4 GB empfohlen) mit 64-bit Raspberry Pi OS Lite[^2]
|
||||||
|
- MicroSD ≥ 16 GB oder USB-SSD (empfohlen für Stabilität)[^2]
|
||||||
|
- SSH-Zugang
|
||||||
|
- OpenRouter API Key (`sk-or-...`) – holen unter openrouter.ai/keys[^1]
|
||||||
|
- mailbox.org App-Passwort mit CalDAV-Berechtigung (unter Einstellungen → Sicherheit → Applikationspasswörter)[^3][^4]
|
||||||
|
- Deine mailbox.org Kalender-URL(s) aus dem Webinterface (☰ → Eigenschaften → CalDAV URL)[^5][^6]
|
||||||
|
|
||||||
|
> **Wichtig:** Das App-Passwort ist zwingend erforderlich – auch ohne 2FA. Externe Apps wie vdirsyncer werden seit mailbox.org Login 2.0 mit dem normalen Passwort blockiert.[^4][^7]
|
||||||
|
|
||||||
|
### Hinweis: npm ist Pflicht
|
||||||
|
|
||||||
|
OpenClaw wird **ausschließlich als npm-Paket** distribuiert – es gibt keinen Weg daran vorbei. Der offizielle `install.sh`-Einzeiler übernimmt den npm-Aufruf automatisch, prüft Node.js, richtet das npm-Verzeichnis ein und vermeidet Permission-Fehler. Deshalb wird in dieser Anleitung der Einzeiler statt `npm install -g openclaw` direkt bevorzugt.[^8][^9][^10][^11]
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 1: Raspberry Pi vorbereiten
|
||||||
|
|
||||||
|
### 1.1 OS flashen
|
||||||
|
|
||||||
|
Mit **Raspberry Pi Imager** (Raspberry Pi OS Lite, 64-bit) flashen. In den erweiterten Einstellungen vorab konfigurieren:[^2]
|
||||||
|
|
||||||
|
- Hostname: `dumbass`
|
||||||
|
- SSH aktivieren
|
||||||
|
- Benutzername: `hans`, Passwort setzen
|
||||||
|
- WLAN (optional; Ethernet ist stabiler)
|
||||||
|
|
||||||
|
SD-Karte einstecken, Pi starten, per SSH verbinden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh hans@dumbass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 System aktualisieren & Grundpakete installieren
|
||||||
|
|
||||||
|
Der `install.sh` installiert fehlende Pakete zwar selbstständig nach, aber explizite Vorinstallation verhindert `spawn git ENOENT`-Fehler während des npm-Build-Prozesses:[^9][^8]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
sudo apt install -y git curl build-essential python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Warum `build-essential` + `python3-pip`?** Einige npm-Abhängigkeiten von OpenClaw (insb. `sharp` für Bildverarbeitung und `node-gyp` für native Addons) kompilieren C++-Code während der Installation. Auf ARM ohne Build-Tools schlägt `npm install` mit `node-gyp rebuild` Fehler fehl.[^11][^12]
|
||||||
|
|
||||||
|
Zeitzone korrekt setzen (wichtig für Kalenderabfragen und Cron):[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo timedatectl set-timezone Europe/Vienna
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Node.js 22 installieren (Mindestanforderung)
|
||||||
|
|
||||||
|
OpenClaw erfordert **Node.js 22+**. Node.js 24 funktioniert ebenfalls.[^13][^14][^11]
|
||||||
|
|
||||||
|
**Empfohlen – nvm (flexibles Versionsmanagement, einfach aktualisierbar):**[^15]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
||||||
|
source ~/.bashrc
|
||||||
|
nvm install 22
|
||||||
|
nvm alias default 22
|
||||||
|
node --version # → v22.x.x
|
||||||
|
npm --version # → 10.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative – NodeSource (systemweit, ohne nvm):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||||
|
sudo apt install -y nodejs
|
||||||
|
node --version
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Achtung:** Den Standard-`nodejs`-APT-Paket von Raspberry Pi OS **nicht** verwenden – er liefert oft veraltete Versionen (v18 oder älter), die von OpenClaw abgelehnt werden.[^14][^15]
|
||||||
|
|
||||||
|
### 1.4 Swap prüfen (du hast bereits 4 GB Swap)
|
||||||
|
|
||||||
|
Da bereits 4 GB Swap konfiguriert sind, wird zusätzlich die Swappiness reduziert um unnötigen Swap-Druck zu vermeiden:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
free -h # Swap sollte angezeigt werden
|
||||||
|
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
|
||||||
|
sudo sysctl -p
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Pi-spezifische Optimierungen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GPU-Speicher minimieren (kein Display benötigt)
|
||||||
|
echo 'gpu_mem=16' | sudo tee -a /boot/firmware/config.txt
|
||||||
|
|
||||||
|
# Nicht benötigte Dienste deaktivieren
|
||||||
|
sudo systemctl disable bluetooth
|
||||||
|
sudo systemctl disable cups 2>/dev/null || true
|
||||||
|
|
||||||
|
# Node.js Compile Cache (beschleunigt CLI-Aufrufe auf ARM spürbar)
|
||||||
|
grep -q 'NODE_COMPILE_CACHE' ~/.bashrc || cat >> ~/.bashrc <<'EOF'
|
||||||
|
export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
|
||||||
|
mkdir -p /var/tmp/openclaw-compile-cache
|
||||||
|
export OPENCLAW_NO_RESPAWN=1
|
||||||
|
EOF
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
node modules nachinsatlliere n
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 2: OpenClaw installieren & mit OpenRouter verbinden
|
||||||
|
|
||||||
|
### 2.1 OpenClaw installieren
|
||||||
|
|
||||||
|
Der offizielle Einzeiler prüft Node.js, richtet den npm-Prefix korrekt ein (verhindert EACCES-Fehler) und installiert alle Abhängigkeiten:[^16][^9][^11]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Installation dauert auf dem Pi 4 ca. 3–6 Minuten (npm kompiliert native Module für ARM). **Nicht unterbrechen.**[^16]
|
||||||
|
|
||||||
|
Nach Abschluss Umgebung neu laden und verifizieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
openclaw --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Falls `openclaw: command not found`** – npm-Prefix wurde auf `~/.npm-global` gesetzt und ist noch nicht im PATH:[^17][^9]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="$HOME/.npm-global/bin:$PATH"
|
||||||
|
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
openclaw --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Falls `sharp`-Build-Fehler auftreten** (kommt vor bei ARM mit globalem libvips):[^11]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install -g openclaw@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach der Installation immer prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw doctor
|
||||||
|
```
|
||||||
|
|
||||||
|
Dieser Befehl checkt Node.js-Version, npm-Installation, Config-Gültigkeit, Gateway-Status und Permission-Probleme – und sagt genau was fehlt.[^8]
|
||||||
|
|
||||||
|
### 2.2 Onboarding mit OpenRouter
|
||||||
|
|
||||||
|
Der schnellste Weg setzt OpenRouter direkt per CLI:[^18][^1]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --auth-choice openrouter-api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Wizard fragt nach dem API Key. Alternativ als Ein-Zeiler:[^18]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --auth-choice apiKey --token-provider openrouter --token "sk-or-DEINKEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Modell konfigurieren
|
||||||
|
|
||||||
|
Öffne `~/.openclaw/openclaw.json` und trage dein gewünschtes Modell ein. Empfohlene Konfiguration mit Fallback:[^19][^20][^1]
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENROUTER_API_KEY": "sk-or-DEINKEY"
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": {
|
||||||
|
"primary": "openrouter/anthropic/claude-sonnet-4.5",
|
||||||
|
"fallbacks": [
|
||||||
|
"openrouter/anthropic/claude-haiku-3.5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"openrouter/anthropic/claude-sonnet-4.5": {},
|
||||||
|
"openrouter/anthropic/claude-haiku-3.5": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Tipp Kostenoptimierung:** `"primary": "openrouter/openrouter/auto"` lässt OpenRouter automatisch das günstigste Modell für jede Anfrage wählen. Besonders sinnvoll wenn OpenClaw viele einfache Hintergrundaufgaben (Heartbeats, Status-Checks) ausführt.[^20][^1]
|
||||||
|
|
||||||
|
Modellverbindung testen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw models list
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Gateway als Daemon starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --install-daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
Status prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw status
|
||||||
|
systemctl --user status openclaw-gateway.service
|
||||||
|
journalctl --user -u openclaw-gateway.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls der Dienst nach Logout nicht weiterläuft, Lingering aktivieren:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo loginctl enable-linger hans
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 3: vdirsyncer + khal installieren (CalDAV-Stack)
|
||||||
|
|
||||||
|
Der offizielle OpenClaw CalDAV-Skill setzt auf `vdirsyncer` (Sync-Layer) und `khal` (Lese-/Schreibzugriff auf Events und Tasks).[^21][^22]
|
||||||
|
|
||||||
|
### 3.1 Pakete installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y vdirsyncer khal
|
||||||
|
```
|
||||||
|
|
||||||
|
Verzeichnisse anlegen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/vdirsyncer
|
||||||
|
mkdir -p ~/.config/khal
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/status
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/calendars
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 CalDAV URLs von mailbox.org ermitteln
|
||||||
|
|
||||||
|
Öffne mailbox.org im Browser. Für **jeden** Kalender und jede Aufgabenliste:
|
||||||
|
|
||||||
|
1. Kalender anklicken → ☰-Menü → **Eigenschaften**
|
||||||
|
2. Unter **CalDAV URL** die vollständige URL kopieren
|
||||||
|
|
||||||
|
Beispielform: `https://dav.mailbox.org/caldav/Y2FsOi8vMC80Mg`[^6][^5]
|
||||||
|
|
||||||
|
> Die oberste Discovery-URL `https://dav.mailbox.org` funktioniert bei vdirsyncer direkt (automatische Collection-Erkennung), aber **spezifische URLs** sind zuverlässiger und vermeiden, dass ungewollte Shared-Folder auftauchen.[^23][^24]
|
||||||
|
|
||||||
|
### 3.3 App-Passwort sicher speichern
|
||||||
|
|
||||||
|
Das Passwort wird als geschützte Datei abgelegt (einfache, bewährte Methode):[^25]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Datei anlegen (600 = nur für dich lesbar)
|
||||||
|
echo "DEIN_APP_PASSWORT" > ~/.config/vdirsyncer/mailbox_apppassword
|
||||||
|
chmod 600 ~/.config/vdirsyncer/mailbox_apppassword
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 4: vdirsyncer konfigurieren
|
||||||
|
|
||||||
|
Vollständige Konfiguration für **Kalender + Aufgaben** in `~/.config/vdirsyncer/config`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[general]
|
||||||
|
status_path = "~/.local/share/vdirsyncer/status/"
|
||||||
|
|
||||||
|
# ──────────────── KALENDER ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_calendar]
|
||||||
|
a = "mailbox_cal_remote"
|
||||||
|
b = "mailbox_cal_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
metadata = ["displayname", "color"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/DEINE_KALENDER_ID"
|
||||||
|
username = "deine@mailadresse.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/calendars/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
# ──────────────── AUFGABEN (VTODO) ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_tasks]
|
||||||
|
a = "mailbox_tasks_remote"
|
||||||
|
b = "mailbox_tasks_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
|
||||||
|
[storage mailbox_tasks_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/DEINE_TASKS_ID"
|
||||||
|
username = "deine@mailadresse.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_tasks_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/tasks/"
|
||||||
|
fileext = ".ics"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Wichtig:** Ersetze `DEINE_KALENDER_ID` und `DEINE_TASKS_ID` mit den tatsächlichen IDs aus Schritt 3.2. `conflict_resolution = "a wins"` bedeutet: im Konflikt gewinnt der Server (mailbox.org). Das ist die sicherste Einstellung.[^26][^25]
|
||||||
|
|
||||||
|
Ersten Sync durchführen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vdirsyncer discover
|
||||||
|
vdirsyncer sync
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Erwartetes Verhalten:** Beim ersten Durchlauf erscheint eine Bestätigungsabfrage für neue Collections → mit `yes` bestätigen.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 5: khal konfigurieren
|
||||||
|
|
||||||
|
`~/.config/khal/config`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[calendars]
|
||||||
|
|
||||||
|
[[mailbox_kalender]]
|
||||||
|
path = ~/.local/share/vdirsyncer/calendars/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[[mailbox_aufgaben]]
|
||||||
|
path = ~/.local/share/vdirsyncer/tasks/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[default]
|
||||||
|
default_calendar = mailbox_kalender
|
||||||
|
highlight_event_days = True
|
||||||
|
show_todos = True
|
||||||
|
|
||||||
|
[locale]
|
||||||
|
timeformat = %H:%M
|
||||||
|
dateformat = %Y-%m-%d
|
||||||
|
datetimeformat = %Y-%m-%d %H:%M
|
||||||
|
longdateformat = %A, %d. %B %Y
|
||||||
|
longtimeformat = %H:%M
|
||||||
|
firstweekday = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Khal-Setup testen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
khal list today 7d
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 6: OpenClaw CalDAV-Skill installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw skills install caldav-calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder alternativ über den Playbooks-Skill:[^22][^27]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw add @asleep123/caldav-calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach OpenClaw Gateway neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart openclaw-gateway.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Du kannst jetzt z.B. via Telegram oder deinem konfigurierten Channel tippen:
|
||||||
|
|
||||||
|
- *„Was habe ich morgen im Kalender?"*
|
||||||
|
- *„Erstelle einen Termin am 10.6. um 14 Uhr – Zahnarzt"*
|
||||||
|
- *„Zeig mir alle offenen Aufgaben"*
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 7: Automatischer Sync via Cron
|
||||||
|
|
||||||
|
Damit OpenClaw immer aktuelle Kalenderdaten hat, Sync automatisieren:[^28][^26]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Folgende Zeilen einfügen:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Alle 5 Minuten synchronisieren
|
||||||
|
*/5 * * * * /usr/bin/vdirsyncer sync >> ~/.local/share/vdirsyncer/sync.log 2>&1
|
||||||
|
|
||||||
|
# khal-Cache täglich um 03:00 löschen (verhindert veraltete Anzeigen)
|
||||||
|
0 3 * * * rm -f ~/.local/share/khal/khal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 8: Dashboard-Zugriff (optional)
|
||||||
|
|
||||||
|
Das OpenClaw Control UI läuft nur lokal. Zugriff via SSH-Tunnel:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Raspi:
|
||||||
|
openclaw dashboard --no-open
|
||||||
|
# → zeigt lokale URL, z.B. http://127.0.0.1:18789
|
||||||
|
|
||||||
|
# Auf deinem PC (neues Terminal):
|
||||||
|
ssh -N -L 18789:127.0.0.1:18789 hans@dumbass
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann im Browser: `http://localhost:18789`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Bekannte Fallstricke & Workarounds
|
||||||
|
|
||||||
|
| Problem | Ursache | Fix |
|
||||||
|
| ---------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||||
|
| **401-Fehler bei vdirsyncer** | Normales Passwort statt App-Passwort | App-Passwort mit CalDAV-Berechtigung anlegen[^4][^29] |
|
||||||
|
| **Wiederkehrende Aufgaben verschwinden** | Open-Xchange-Bug: RRULE wird entfernt | Wiederkehrende Tasks nicht über CalDAV verwalten; als VEVENT anlegen[^30][^31] |
|
||||||
|
| **Tasks nur in eine Richtung** | Bekannter Sync-Bug | `vdirsyncer discover` erneut ausführen; `conflict_resolution` explizit setzen[^32][^33] |
|
||||||
|
| **khal zeigt veraltete Daten** | Cache-Problem | `rm ~/.local/share/khal/khal.db` und neu abfragen[^21] |
|
||||||
|
| **OpenClaw liefert erfundene Termine** | Kalender nicht verbunden, Agent halluziniert | Bekannte Termine als Probe gegenchecken; Logs prüfen[^34][^35] |
|
||||||
|
| **khal edit schlägt fehl** | Braucht TTY, nicht-interaktiver Kontext | `tmux` verwenden oder Events direkt als `.ics` bearbeiten[^21] |
|
||||||
|
| **Service startet nach Reboot nicht** | Kein User-Lingering | `sudo loginctl enable-linger $(whoami)`[^2] |
|
||||||
|
| **WiFi bricht periodisch weg** | Power Management | `sudo iwconfig wlan0 power off` (nach Reboot nötig → `/etc/rc.local` eintragen)[^2] |
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Schnellreferenz: Wichtige Befehle
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status prüfen
|
||||||
|
openclaw status
|
||||||
|
journalctl --user -u openclaw-gateway.service -n 50
|
||||||
|
|
||||||
|
# Manueller Kalender-Sync
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Heutige Termine anzeigen
|
||||||
|
khal list today
|
||||||
|
|
||||||
|
# Nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Neuen Termin erstellen
|
||||||
|
khal new 2026-06-10 14:00 15:00 "Zahnarzt"
|
||||||
|
vdirsyncer sync # danach immer syncen!
|
||||||
|
|
||||||
|
# Modell wechseln
|
||||||
|
openclaw models set openrouter/anthropic/claude-opus-4.5
|
||||||
|
|
||||||
|
# OpenClaw-Skill aktualisieren
|
||||||
|
openclaw skills update --all
|
||||||
|
|
||||||
|
# Gateway neu starten
|
||||||
|
systemctl --user restart openclaw-gateway.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
1. [Integration with OpenClaw | OpenRouter | Documentation](https://openrouter.ai/docs/guides/coding-agents/openclaw-integration) - Learn how to configure OpenClaw to use OpenRouter for AI agents across multiple messaging platforms.
|
||||||
|
|
||||||
|
2. [Raspberry Pi - OpenClaw Docs](https://docs.openclaw.ai/install/raspberry-pi)
|
||||||
|
|
||||||
|
3. [Application passwords for external programs - Knowledge Base](https://kb.mailbox.org/en/private/security-and-privacy/application-passwords-for-external-programs/) - When using Two-Factor Authentication (2FA) for your account, you must create and use a corresponding...
|
||||||
|
|
||||||
|
4. [Access security for mailbox – 2FA, app passwords, and email ...](https://kb.mailbox.org/en/private/security-and-privacy/access-security-for-mailbox-2fa-app-passwords-and-e-mail-passwords/) - This article provides an overview of the different functions for securing your access to mailbox – i...
|
||||||
|
|
||||||
|
5. [CardDAV for Outlook and Thunderbird – CalDav Synchronizer - Mailbox.org](https://kb.mailbox.org/en/private/addressbook-and-calendar/caldav-carddav-for-outlook-caldav-synchronizer/) - Once you have enabled two-factor authentication, external applications such as Outlook, Thunderbird,...
|
||||||
|
|
||||||
|
6. [CalDAV-Clients – Outlook, Thunderbird, macOS, Evolution, Kontact](https://kb.mailbox.org/de/privat/adressbuch-und-kalender/caldav-clients-outlook-thunderbird-macos-evolution-kontact/) - Im nächsten Schritt wählen Sie als Format CalDAV und als Adresse https://dav.mailbox.org/caldav/XXX ...
|
||||||
|
|
||||||
|
7. [Information zu Nutzung von CalDAV/CardDAV/WebDAV und Drive ...](https://userforum.mailbox.org/topic/9956-information-zu-nutzung-von-caldavcarddavwebdav-und-drive-mit-login-2-0-2fa) - Mit dem neuen Login 2.0 in Kombination mit 2FA müssen für den Zugriff über CalDAV/CardDAV/WebDAV und...
|
||||||
|
|
||||||
|
8. [Fix 'npm install failed for openclaw@latest' and ... - Stack Junkie](https://www.stack-junkie.com/blog/fix-openclaw-installation-errors) - Fix npm install failed for openclaw@latest, Node version mismatches, permission errors, port conflic...
|
||||||
|
|
||||||
|
9. [Installer internals - OpenClaw Docs](https://docs.openclaw.ai/install/installer)
|
||||||
|
|
||||||
|
10. [OpenClaw Install: Complete Installation Guide | macOS, Linux ...](https://openclaw-install.org) - Complete OpenClaw install guide for macOS, Linux, and Windows. Learn how to install OpenClaw in 10 m...
|
||||||
|
|
||||||
|
11. [Install – OpenClaw - Open Source AI Coding Assistant](https://openclawlab.com/en/docs/install/) - Install OpenClaw — installer script, npm/pnpm, from source, Docker, and more
|
||||||
|
|
||||||
|
12. [OpenClaw Installation Error: Complete Fix Guide for All Platforms ...](https://yingtu.ai/en/blog/openclaw-installation-error) - Fix OpenClaw installation errors on macOS, Windows, and Linux. Covers Node.js version issues, Sharp/...
|
||||||
|
|
||||||
|
13. [The Ultimate Guide to Fixing OpenClaw Installer NPM Install Failed ...](https://skywork.ai/skypage/en/fixing-openclaw-npm-install/2048664282507194368) - Fix OpenClaw npm install failed errors fast—step‑by‑step guide, environment checks, and top AI agent...
|
||||||
|
|
||||||
|
14. [How to Run OpenClaw on Raspberry Pi: A Practical Setup Guide](https://www.sunfounder.com/blogs/news/how-to-run-openclaw-on-raspberry-pi-a-practical-setup-guide) - Start by installing Raspberry Pi OS Lite. This version provides a minimal environment without a grap...
|
||||||
|
|
||||||
|
15. [OpenClaw x Raspberry Pi Deployment Guide | MI - 超智諮詢](https://www.meta-intelligence.tech/en/insight-openclaw-raspberry-pi) - A complete tutorial on deploying the OpenClaw AI agent on Raspberry Pi (5/4).
|
||||||
|
|
||||||
|
16. [Can OpenClaw Run on Raspberry Pi? Complete Step-by- ...](https://www.pcbuildadvisor.com/can-openclaw-run-on-raspberry-pi-complete-step-by-step-installation-guide/) - Yes, OpenClaw can absolutely run on a Raspberry Pi — and it runs surprisingly well when set up corre...
|
||||||
|
|
||||||
|
17. [The Complete OpenClaw Setup & Installation Guide - Sphere Partners](https://www.sphereinc.com/blogs/the-complete-openclaw-setup-installation-guide/) - If you're deploying on a VPS, use the npm method or Docker instead. 2.3 Installation Method 2: npm G...
|
||||||
|
|
||||||
|
18. [OpenClaw OpenRouter Setup: One API Key for Hundreds of ...](https://www.stack-junkie.com/blog/openclaw-openrouter-setup-guide) - OpenClaw OpenRouter setup: connect hundreds of LLM models through one API key, configure model routi...
|
||||||
|
|
||||||
|
19. [OpenRouter](https://docs.openclaw.ai/providers/openrouter)
|
||||||
|
|
||||||
|
20. [The Ultimate Guide to OpenClaw OpenRouter Setup](https://skywork.ai/skypage/en/openclaw-openrouter-setup/2037020692520374272) - Optimize AI agents with OpenClaw + OpenRouter: cost‑effective, multi‑model routing, local execution,...
|
||||||
|
|
||||||
|
21. [caldav-calendar skill by openclaw/skills - playbooks](https://playbooks.com/skills/openclaw/skills/caldav-calendar) - This skill synchronizes and queries CalDAV calendars using vdirsyncer and khal, enabling streamlined...
|
||||||
|
|
||||||
|
22. [caldav-calendar - OpenClaw Skills](https://openclawskills.best/skills/asleep123/caldav-calendar/) - How do I install caldav-calendar? Run openclaw add @asleep123/caldav-calendar in your terminal. This...
|
||||||
|
|
||||||
|
23. [mailbox.org - DAVx5](https://www.davx5.com/tested-with/mailboxorg) - WebDAV URL: https://dav.mailbox.org/servlet/webdav.infostore/. User name: your mailbox.org email add...
|
||||||
|
|
||||||
|
24. [Access calendar data from programs using CalDav](https://userforum-en.mailbox.org/topic/2526-access-calendar-data-from-programs-using-caldav) - I was using the wrong url. ``` # Replace with your actual mailbox.org username and password. USERNAM...
|
||||||
|
|
||||||
|
25. [Synchronise CalDAV and CardDAV data for khal and khard with ...](https://www.jan0sch.de/post/synchronise-caldav-and-carddav-data-with-vdirsyncer/) - First we need to setup our synchronisation. The file ~/.config/vdirsyncer/config contains the config...
|
||||||
|
|
||||||
|
26. [Calendar Integration | Dank Linux](https://danklinux.com/docs/dankmaterialshell/calendar-integration) - Setting up khal and vdirsyncer is very user-unfriendly and convoluted, in a future release this inte...
|
||||||
|
|
||||||
|
27. [Commands](https://docs.openclaw.ai/tools/clawhub)
|
||||||
|
|
||||||
|
28. [Tutorial¶](https://vdirsyncer.pimutils.org/en/stable/tutorial.html)
|
||||||
|
|
||||||
|
29. [CardDAV/CalDAV authentication error - mailbox User Forum](https://userforum-en.mailbox.org/topic/3141-carddavcaldav-authentication-error) - Hello since about 24 hours or so ago, I have been consistently receiving error authenticating into m...
|
||||||
|
|
||||||
|
30. [Support for recurring tasks for CalDAV would be really nice](https://userforum-en.mailbox.org/topic/1725-support-for-recurring-tasks-for-caldav-would-be-really-nice) - Right now, if I add a recurring task on a CalDAV client and sync it to Mailbox, the recurring tasks ...
|
||||||
|
|
||||||
|
31. [Are recurring tasks not supported when syncing from Mailbox.org?](https://github.com/TechbeeAT/jtxBoard/discussions/2204) - I am trying to sync my Mailbox.org tasks with my Android phone. Everything works as expected, except...
|
||||||
|
|
||||||
|
32. [mailbox.org synchronization broken · Issue #948 · tasks/tasks](https://github.com/tasks/tasks/issues/948) - CALDAV server: mailbox.org Tasks version: 8.7 (current on F-droid) I first noticed this today, but I...
|
||||||
|
|
||||||
|
33. [Tasks only syncing one way over CalDAV - Reddit](https://www.reddit.com/r/tasks/comments/1daztkg/tasks_only_syncing_one_way_over_caldav/) - I have a problem with the sync between NextCloud and the Tasks Android App which uses CalDAV. This i...
|
||||||
|
|
||||||
|
34. [Calendar tool returns hallucinated data when no calendar connected](https://github.com/openclaw/openclaw/issues/10190) - When using the calendar tool with no calendar integration configured, the tool returns fabricated/ha...
|
||||||
|
|
||||||
|
35. [[Bug]:Agent fabricates tool output for factual queries — calendar tool ...](https://github.com/openclaw/openclaw/issues/63289) - I am a non-developer end user running OpenClaw 2026.4.1 in a Lume VM, and that this was discovered w...
|
||||||
Binary file not shown.
@@ -0,0 +1,306 @@
|
|||||||
|
# openclaw-mailbox-cal
|
||||||
|
|
||||||
|
**OpenClaw Skill-Repository** — mailbox.org Kalender und Aufgaben auf dem Raspberry Pi
|
||||||
|
Telegram-Bot-Befehle für Kalendertermine (khal), Tasks (todoman) und automatischen CalDAV-Sync (vdirsyncer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. .env befüllen
|
||||||
|
cp assets/.env.example .env && nano .env
|
||||||
|
|
||||||
|
# 2. Installation
|
||||||
|
chmod +x scripts/install.sh && bash scripts/install.sh
|
||||||
|
|
||||||
|
# 3. Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
openclaw-mailbox-cal/
|
||||||
|
├── SKILL.md ← OpenClaw Skill-Definition
|
||||||
|
├── README.md ← Diese Datei
|
||||||
|
├── assets/
|
||||||
|
│ └── .env.example ← Umgebungsvariablen-Vorlage
|
||||||
|
├── config/
|
||||||
|
│ ├── vdirsyncer.conf ← vdirsyncer CalDAV-Konfiguration
|
||||||
|
│ ├── khal.conf ← khal Kalender-Konfiguration
|
||||||
|
│ └── todoman.conf ← todoman Aufgaben-Konfiguration
|
||||||
|
├── scripts/
|
||||||
|
│ ├── install.sh ← Installations-Script (Raspi)
|
||||||
|
│ ├── test_setup.sh ← Selbsttest aller Komponenten
|
||||||
|
│ └── openclaw_skill.py ← OpenClaw/Telegram Bot Handler
|
||||||
|
└── systemd/
|
||||||
|
├── vdirsyncer-sync.service ← systemd Service Unit
|
||||||
|
├── vdirsyncer-sync.timer ← systemd Timer (alle 15 Min.)
|
||||||
|
└── vdirsyncer-sync.env ← Umgebungsvariablen für systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup-Schritte (detailliert)
|
||||||
|
|
||||||
|
### Schritt 1 — Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm (64-bit empfohlen) oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto
|
||||||
|
- App-Passwort in mailbox.org anlegen:
|
||||||
|
`Einstellungen → Sicherheit → App-Passwörter → Neu erstellen`
|
||||||
|
- OpenClaw installiert: [openclaw.dev](https://openclaw.dev)
|
||||||
|
- Telegram Bot-Token via [@BotFather](https://t.me/BotFather)
|
||||||
|
|
||||||
|
### Schritt 2 — .env konfigurieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Mindestens diese Werte setzen:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
MAILBOX_USER=vorname.nachname@mailbox.org
|
||||||
|
MAILBOX_PASS=app-passwort-xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3 — install.sh ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script installiert automatisch:
|
||||||
|
- `vdirsyncer` — CalDAV-Sync-Tool
|
||||||
|
- `khal` — CLI-Kalender
|
||||||
|
- `todoman` — CLI-Aufgabenverwaltung
|
||||||
|
- `pass` — GNU Password Store
|
||||||
|
- `python3-keyring` — Python-Keyring-Integration
|
||||||
|
- `gettext-base` — envsubst für Konfigurationen
|
||||||
|
|
||||||
|
Anschließend:
|
||||||
|
- Alle Konfigurationsdateien werden deployed
|
||||||
|
- systemd User-Timer wird aktiviert
|
||||||
|
- Initialer Sync wird durchgeführt
|
||||||
|
|
||||||
|
### Schritt 4 — OpenClaw Skill registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills **automatisch** wenn sie in einem der Skill-Verzeichnisse liegen.
|
||||||
|
Kein manueller Eintrag in `openclaw.json` nötig — das `install.sh` legt bereits einen Symlink an.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manuell (falls install.sh den Symlink nicht angelegt hat):
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional — Zugangsdaten direkt in `~/.openclaw/openclaw.json` hinterlegen
|
||||||
|
(Alternative zur `.env`-Datei). **Immer zuerst Backup anlegen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
Den bestehenden `"skills"`-Block in `openclaw.json` um `"entries"` ergänzen
|
||||||
|
(der Block sieht danach so aus):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach jeder Änderung an `openclaw.json` validieren und Gateway neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw doctor --fix
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5 — Telegram testen
|
||||||
|
|
||||||
|
Sende an deinen Bot:
|
||||||
|
```
|
||||||
|
/termine
|
||||||
|
/aufgaben
|
||||||
|
/sync
|
||||||
|
/status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
### Vollständiger Selbsttest
|
||||||
|
```bash
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### vdirsyncer
|
||||||
|
```bash
|
||||||
|
# Verbindung testen und Sammlungen entdecken
|
||||||
|
vdirsyncer discover calendars
|
||||||
|
vdirsyncer discover tasks
|
||||||
|
|
||||||
|
# Synchronisation manuell starten
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Nur Kalender synchronisieren
|
||||||
|
vdirsyncer sync calendars
|
||||||
|
|
||||||
|
# Verbose-Ausgabe
|
||||||
|
vdirsyncer -v DEBUG sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal
|
||||||
|
```bash
|
||||||
|
# Termine heute
|
||||||
|
khal list
|
||||||
|
|
||||||
|
# Termine nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Agenda-Ansicht
|
||||||
|
khal agenda
|
||||||
|
|
||||||
|
# Interaktiver Kalender
|
||||||
|
ikhal
|
||||||
|
|
||||||
|
# Ereignis hinzufügen (interaktiv)
|
||||||
|
khal new
|
||||||
|
|
||||||
|
# Ereignis direkt anlegen
|
||||||
|
khal new 2025-06-15 14:00 15:00 "Arzttermin" --calendar mailbox_kalender
|
||||||
|
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
|
||||||
|
# Konfiguration anzeigen
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### todoman
|
||||||
|
```bash
|
||||||
|
# Alle offenen Aufgaben
|
||||||
|
todo list
|
||||||
|
|
||||||
|
# Alle Aufgaben inkl. erledigt
|
||||||
|
todo list --all
|
||||||
|
|
||||||
|
# Neue Aufgabe
|
||||||
|
todo new "Bericht schreiben"
|
||||||
|
|
||||||
|
# Aufgabe mit Fälligkeitsdatum
|
||||||
|
todo new --due 2025-06-30 "Bericht abgeben"
|
||||||
|
|
||||||
|
# Aufgabe mit Priorität (1=hoch, 9=niedrig)
|
||||||
|
todo new --priority 1 "Dringend: Server patchen"
|
||||||
|
|
||||||
|
# Aufgabe als erledigt markieren (ID aus 'todo list')
|
||||||
|
todo done 1
|
||||||
|
|
||||||
|
# Aufgabe löschen
|
||||||
|
todo delete 2
|
||||||
|
|
||||||
|
# Aufgaben-Listen anzeigen
|
||||||
|
todo lists
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
```bash
|
||||||
|
# Timer-Status
|
||||||
|
systemctl --user status vdirsyncer-sync.timer
|
||||||
|
|
||||||
|
# Service manuell starten (einmaliger Sync)
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
|
||||||
|
# Alle User-Timer anzeigen
|
||||||
|
systemctl --user list-timers
|
||||||
|
|
||||||
|
# Live-Log verfolgen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f
|
||||||
|
|
||||||
|
# Log der letzten 50 Zeilen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -n 50
|
||||||
|
|
||||||
|
# Timer deaktivieren
|
||||||
|
systemctl --user disable --now vdirsyncer-sync.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python-Skill direkt testen
|
||||||
|
```bash
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## mailbox.org CalDAV-Endpunkte
|
||||||
|
|
||||||
|
| Zweck | URL |
|
||||||
|
|---|---|
|
||||||
|
| CalDAV-Basis | `https://dav.mailbox.org/caldav/<USER>/` |
|
||||||
|
| Kalender-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Kalender/` |
|
||||||
|
| Aufgaben-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Aufgaben/` |
|
||||||
|
| CardDAV (Kontakte) | `https://dav.mailbox.org/carddav/<USER>/` |
|
||||||
|
|
||||||
|
Entdeckung aller Sammlungen:
|
||||||
|
```bash
|
||||||
|
curl -u "USER:PASS" -X PROPFIND https://dav.mailbox.org/caldav/ \
|
||||||
|
-H "Depth: 1" -H "Content-Type: application/xml"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
| Maßnahme | Status |
|
||||||
|
|---|---|
|
||||||
|
| App-Passwort statt Hauptpasswort | ✅ Empfohlen |
|
||||||
|
| vdirsyncer-Config: chmod 600 | ✅ Automatisch via install.sh |
|
||||||
|
| .env nicht in Git | ✅ In .gitignore eingetragen |
|
||||||
|
| HTTPS für CalDAV | ✅ dav.mailbox.org erzwingt TLS |
|
||||||
|
| pass(1)-Integration | Optional via `pass` + `python3-keyring` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
**vdirsyncer: `[Errno 111] Connection refused`**
|
||||||
|
→ Netzwerkverbindung prüfen: `ping dav.mailbox.org`
|
||||||
|
|
||||||
|
**vdirsyncer: `Conflict!`**
|
||||||
|
→ Beide Seiten wurden geändert. Konflikt manuell lösen oder `conflict_resolution = "b wins"` in vdirsyncer.conf setzen.
|
||||||
|
|
||||||
|
**khal: `No calendars configured`**
|
||||||
|
→ `khal rebuild-cache` ausführen; Pfad in `~/.config/khal/config` prüfen.
|
||||||
|
|
||||||
|
**todoman: `Error: No lists found`**
|
||||||
|
→ `path`-Eintrag in `~/.config/todoman/config.cfg` prüfen; Verzeichnis `~/.local/share/vdirsyncer/tasks/` muss existieren und `.ics`-Dateien enthalten.
|
||||||
|
|
||||||
|
**systemd-Timer läuft nach Reboot nicht**
|
||||||
|
→ `sudo loginctl enable-linger $USER` ausführen (User-Session ohne Login).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
MIT — Freie Nutzung, Veränderung und Weitergabe.
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
---
|
||||||
|
name: mailbox-cal
|
||||||
|
description: "OpenClaw Telegram-Bot-Skill für mailbox.org Kalender und Aufgaben auf dem Raspberry Pi. Synchronisiert CalDAV-Kalender und VTODO-Aufgaben via vdirsyncer, zeigt Termine mit khal und verwaltet Tasks mit todoman. Befehle: /termine, /aufgaben, /neu_aufgabe, /sync, /status. Lade diesen Skill wenn: der Nutzer Kalender-Einträge oder Aufgaben aus mailbox.org per Telegram abfragen, anlegen oder synchronisieren möchte."
|
||||||
|
license: MIT
|
||||||
|
compatibility: "Raspberry Pi OS Bookworm / Ubuntu 22.04+ ARM. Benötigt: vdirsyncer >= 0.19, khal >= 0.11, todoman >= 4.3, python3 >= 3.10. mailbox.org CalDAV-Zugang erforderlich."
|
||||||
|
metadata:
|
||||||
|
author: lonely.wolf.64
|
||||||
|
version: '1.0'
|
||||||
|
platform: Raspberry Pi
|
||||||
|
caldav_server: mailbox.org
|
||||||
|
---
|
||||||
|
|
||||||
|
# mailbox.org Kalender + Aufgaben Skill
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieser OpenClaw-Skill synchronisiert deinen mailbox.org-Kalender und deine Aufgaben über CalDAV auf einen Raspberry Pi und stellt sie über Telegram-Bot-Befehle bereit. Die Synchronisation läuft automatisch alle 15 Minuten via systemd-Timer.
|
||||||
|
|
||||||
|
**Architektur:**
|
||||||
|
```
|
||||||
|
mailbox.org (CalDAV)
|
||||||
|
│
|
||||||
|
vdirsyncer (Sync)
|
||||||
|
│
|
||||||
|
┌───┴───────────┐
|
||||||
|
│ │
|
||||||
|
khal todoman
|
||||||
|
(Kalender) (Aufgaben)
|
||||||
|
│ │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
openclaw_skill.py
|
||||||
|
│
|
||||||
|
Telegram Bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unterstützte Befehle
|
||||||
|
|
||||||
|
| Telegram-Befehl | Beschreibung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| `/termine` | Heutige Termine | `/termine heute` |
|
||||||
|
| `/termine morgen` | Morgige Termine | `/termine morgen` |
|
||||||
|
| `/termine woche` | Termine nächste 7 Tage | `/termine woche` |
|
||||||
|
| `/aufgaben` | Offene Aufgaben | `/aufgaben` |
|
||||||
|
| `/aufgaben alle` | Alle Aufgaben inkl. erledigt | `/aufgaben alle` |
|
||||||
|
| `/neu_aufgabe` | Neue Aufgabe anlegen | `/neu_aufgabe Arzt anrufen` |
|
||||||
|
| `/sync` | Manuellen Sync auslösen | `/sync` |
|
||||||
|
| `/status` | Sync-Timer-Status | `/status` |
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto mit CalDAV-Zugang
|
||||||
|
- OpenClaw installiert und konfiguriert
|
||||||
|
- Telegram Bot-Token (via @BotFather)
|
||||||
|
- Internetverbindung vom Raspberry Pi
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Repository klonen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/DEIN_USER/openclaw-mailbox-cal.git
|
||||||
|
cd openclaw-mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. .env befüllen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
# MAILBOX_USER und MAILBOX_PASS setzen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Empfehlung:** Lege in mailbox.org ein App-Passwort an:
|
||||||
|
Einstellungen → Sicherheit → App-Passwörter → Neu erstellen
|
||||||
|
|
||||||
|
### 3. Installation ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script führt folgende Schritte aus:
|
||||||
|
1. Pakete installieren: `vdirsyncer khal todoman pass python3-keyring gettext-base`
|
||||||
|
2. Konfigurationsdateien deployen (mit envsubst)
|
||||||
|
3. systemd User-Timer aktivieren
|
||||||
|
4. Initialen Sync durchführen
|
||||||
|
5. khal-Datenbank aufbauen
|
||||||
|
6. OpenClaw Skill verknüpfen
|
||||||
|
|
||||||
|
### 4. Skill in OpenClaw registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills automatisch aus diesen Verzeichnissen (höchste Priorität zuerst):
|
||||||
|
- `<workspace>/skills/`
|
||||||
|
- `~/.openclaw/skills/`
|
||||||
|
- bundled skills
|
||||||
|
|
||||||
|
Skill-Verzeichnis ins OpenClaw-Skills-Verzeichnis kopieren oder verlinken:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Option A: Symlink (empfohlen — Updates wirken sofort)
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
|
||||||
|
# Option B: Kopieren
|
||||||
|
cp -r ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional: Zugangsdaten direkt in `~/.openclaw/openclaw.json` hinterlegen
|
||||||
|
(nur nötig für env-Variablen oder explizites enable/disable).
|
||||||
|
Den bestehenden `"skills"`-Block um `"entries"` ergänzen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Unbekannte Keys lassen den OpenClaw-Gateway nicht starten.
|
||||||
|
Vor dem Bearbeiten immer ein Backup anlegen:
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
# Nach der Bearbeitung validieren:
|
||||||
|
openclaw doctor --fix
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach Gateway neu starten:
|
||||||
|
```bash
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
Nach der Installation Setup validieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vollständiger Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
|
||||||
|
# Einzeltests
|
||||||
|
vdirsyncer sync # Sync testen
|
||||||
|
khal list today tomorrow # Termine heute + morgen
|
||||||
|
khal list --format "{start} {title}" 2025-01-01 2025-12-31 # Jahresübersicht
|
||||||
|
todo list # Aufgaben anzeigen
|
||||||
|
todo list --all # Alle inkl. erledigt
|
||||||
|
todo new "Test-Aufgabe" # Aufgabe anlegen
|
||||||
|
|
||||||
|
# systemd Timer
|
||||||
|
systemctl --user status vdirsyncer-sync.timer # Timer-Status
|
||||||
|
systemctl --user list-timers # Alle Timer
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f # Live-Log
|
||||||
|
|
||||||
|
# Python-Skill direkt testen (ohne OpenClaw/Telegram)
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfigurationsdateien
|
||||||
|
|
||||||
|
| Datei | Ziel-Pfad | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `config/vdirsyncer.conf` | `~/.config/vdirsyncer/config` | CalDAV-Sync-Konfiguration |
|
||||||
|
| `config/khal.conf` | `~/.config/khal/config` | Kalender-Anzeige-Konfiguration |
|
||||||
|
| `config/todoman.conf` | `~/.config/todoman/config.cfg` | Aufgaben-Konfiguration |
|
||||||
|
| `systemd/vdirsyncer-sync.service` | `~/.config/systemd/user/` | Sync-Service Unit |
|
||||||
|
| `systemd/vdirsyncer-sync.timer` | `~/.config/systemd/user/` | 15-Minuten-Timer |
|
||||||
|
| `scripts/openclaw_skill.py` | (in-place) | OpenClaw/Telegram-Integration |
|
||||||
|
|
||||||
|
## Sicherheitshinweise
|
||||||
|
|
||||||
|
- Die vdirsyncer-Config (`~/.config/vdirsyncer/config`) enthält das Passwort — Berechtigungen sind `chmod 600`
|
||||||
|
- Verwende ein mailbox.org App-Passwort statt des Hauptpassworts
|
||||||
|
- Für produktive Umgebungen: `pass`-Integration (GNU Password Store) via `pass` + `python3-keyring`
|
||||||
|
- `.env`-Datei niemals in Git committen (ist in `.gitignore` eingetragen)
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
### vdirsyncer discover schlägt fehl
|
||||||
|
```bash
|
||||||
|
# Verbindung testen
|
||||||
|
curl -u "USER:PASS" https://dav.mailbox.org/caldav/
|
||||||
|
# SSL-Zertifikate prüfen
|
||||||
|
openssl s_client -connect dav.mailbox.org:443
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal zeigt keine Termine
|
||||||
|
```bash
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
# Config validieren
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd-Timer läuft nicht
|
||||||
|
```bash
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
sudo loginctl enable-linger pi
|
||||||
|
# Timer manuell starten
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Todoman findet keine Aufgaben
|
||||||
|
```bash
|
||||||
|
# Pfad in config prüfen
|
||||||
|
cat ~/.config/todoman/config.cfg
|
||||||
|
# Verzeichnis prüfen
|
||||||
|
ls ~/.local/share/vdirsyncer/tasks/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Erweiterungen
|
||||||
|
|
||||||
|
- **Mehrere Kalender:** Weitere `[[kalender_name]]`-Sektionen in `khal.conf` hinzufügen
|
||||||
|
- **Push-Sync:** mailbox.org unterstützt CalDAV-Push — XMPP-Trigger via `vdirsyncer`
|
||||||
|
- **Erinnerungen:** Integration mit `khal-remind` oder `at`-Daemon
|
||||||
|
- **Verschlüsselung:** `.env` via `gpg-agent` verschlüsseln
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# =============================================================
|
||||||
|
# vdirsyncer Konfiguration für mailbox.org (CalDAV)
|
||||||
|
# Pfad: ~/.config/vdirsyncer/config
|
||||||
|
#
|
||||||
|
# Ersetze MAILBOX_USER / MAILBOX_PASS durch echte Werte
|
||||||
|
# oder nutze das install.sh-Script, das .env einliest und
|
||||||
|
# diese Datei mit envsubst befüllt.
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# Statusverzeichnis für vdirsyncer-Metadaten
|
||||||
|
status_path = "~/.local/share/vdirsyncer/status/"
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# STORAGE: Lokale CalDAV-Verzeichnisse (filesystem)
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[storage local_calendars]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/calendars/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
[storage local_tasks]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/tasks/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# STORAGE: mailbox.org CalDAV-Server (Remote)
|
||||||
|
#
|
||||||
|
# mailbox.org CalDAV-Endpunkte:
|
||||||
|
# Kalender: https://dav.mailbox.org/caldav/<USERNAME>/
|
||||||
|
# Aufgaben: https://dav.mailbox.org/caldav/<USERNAME>/Aufgaben/
|
||||||
|
#
|
||||||
|
# Für die Entdeckung aller Sammlungen:
|
||||||
|
# vdirsyncer discover calendars
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[storage remote_calendars]
|
||||||
|
type = "caldav"
|
||||||
|
# mailbox.org: @ im URL-Pfad muss als %40 kodiert sein
|
||||||
|
# MAILBOX_USER_ENCODED wird von install.sh via envsubst gesetzt
|
||||||
|
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER_ENCODED}/"
|
||||||
|
username = "${MAILBOX_USER}"
|
||||||
|
password = "${MAILBOX_PASS}"
|
||||||
|
|
||||||
|
# Nur VEVENT-Einträge (Kalender-Ereignisse)
|
||||||
|
item_types = ["VEVENT"]
|
||||||
|
|
||||||
|
[storage remote_tasks]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER_ENCODED}/"
|
||||||
|
username = "${MAILBOX_USER}"
|
||||||
|
password = "${MAILBOX_PASS}"
|
||||||
|
|
||||||
|
# Nur VTODO-Einträge (Aufgaben / Tasks)
|
||||||
|
item_types = ["VTODO"]
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# PAIR: Kalender-Synchronisierung
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[pair calendars]
|
||||||
|
a = "local_calendars"
|
||||||
|
b = "remote_calendars"
|
||||||
|
collections = ["from b"]
|
||||||
|
|
||||||
|
# Konflikt-Lösung: Remote gewinnt (mailbox.org ist die Quelle der Wahrheit)
|
||||||
|
conflict_resolution = "b wins"
|
||||||
|
|
||||||
|
# Metadaten-Sync (Kalender-Farbe, Anzeigename)
|
||||||
|
metadata = ["color", "displayname"]
|
||||||
|
|
||||||
|
[pair tasks]
|
||||||
|
a = "local_tasks"
|
||||||
|
b = "remote_tasks"
|
||||||
|
collections = ["from b"]
|
||||||
|
conflict_resolution = "b wins"
|
||||||
|
metadata = ["color", "displayname"]
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# install.sh — OpenClaw mailbox.org Kalender + Aufgaben
|
||||||
|
# Zielplattform: Raspberry Pi (Raspberry Pi OS / Ubuntu ARM)
|
||||||
|
# Ausführen als: pi-Nutzer (sudo-Rechte erforderlich)
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
ENV_FILE="$REPO_ROOT/.env"
|
||||||
|
|
||||||
|
# --- Farben ---
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 1. .env laden
|
||||||
|
# =============================================================
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
warn ".env nicht gefunden — kopiere .env.example nach .env"
|
||||||
|
cp "$REPO_ROOT/assets/.env.example" "$ENV_FILE"
|
||||||
|
error "Bitte .env mit deinen mailbox.org-Zugangsdaten befüllen und install.sh erneut ausführen."
|
||||||
|
fi
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
# set -a exportiert alle Variablen; $HOME wird durch die Shell expandiert
|
||||||
|
set -a
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
info ".env geladen (Benutzer: ${MAILBOX_USER:-NICHT GESETZT})"
|
||||||
|
|
||||||
|
[[ -z "${MAILBOX_USER:-}" ]] && error "MAILBOX_USER in .env nicht gesetzt!"
|
||||||
|
[[ -z "${MAILBOX_PASS:-}" ]] && error "MAILBOX_PASS in .env nicht gesetzt!"
|
||||||
|
|
||||||
|
# @ im URL-Pfad muss als %40 kodiert sein (mailbox.org CalDAV-Anforderung)
|
||||||
|
export MAILBOX_USER_ENCODED="${MAILBOX_USER/@/%40}"
|
||||||
|
info "URL-kodierter Benutzername: ${MAILBOX_USER_ENCODED}"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 2. Systempakete installieren
|
||||||
|
# =============================================================
|
||||||
|
info "Installiere Systempakete..."
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y \
|
||||||
|
vdirsyncer \
|
||||||
|
khal \
|
||||||
|
todoman \
|
||||||
|
pass \
|
||||||
|
python3-keyring \
|
||||||
|
gettext-base \
|
||||||
|
curl \
|
||||||
|
jq \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv
|
||||||
|
|
||||||
|
# Locale sicherstellen
|
||||||
|
sudo locale-gen de_AT.UTF-8 2>/dev/null || true
|
||||||
|
info "Pakete installiert."
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 3. Verzeichnisse anlegen
|
||||||
|
# =============================================================
|
||||||
|
info "Lege Verzeichnisse an..."
|
||||||
|
|
||||||
|
VDIR_BASE="${VDIR_BASE_DIR:-$HOME/.local/share/vdirsyncer}"
|
||||||
|
mkdir -p "$VDIR_BASE/status"
|
||||||
|
mkdir -p "$VDIR_BASE/calendars"
|
||||||
|
mkdir -p "$VDIR_BASE/tasks"
|
||||||
|
mkdir -p "${KHAL_DB_PATH:-$HOME/.local/share/khal}"
|
||||||
|
mkdir -p "$HOME/.config/vdirsyncer"
|
||||||
|
mkdir -p "$HOME/.config/khal"
|
||||||
|
mkdir -p "$HOME/.config/todoman"
|
||||||
|
# Log + .env-Zielverzeichnis im Home (kein sudo nötig, passt zu systemd %h-Specifier)
|
||||||
|
mkdir -p "$HOME/.local/share/openclaw-mailbox-cal"
|
||||||
|
mkdir -p "$HOME/.config/openclaw-mailbox-cal"
|
||||||
|
|
||||||
|
# .env auch nach ~/.config/openclaw-mailbox-cal/.env kopieren
|
||||||
|
# (dort erwartet es die systemd EnvironmentFile-Direktive via %h)
|
||||||
|
cp "$ENV_FILE" "$HOME/.config/openclaw-mailbox-cal/.env"
|
||||||
|
chmod 600 "$HOME/.config/openclaw-mailbox-cal/.env"
|
||||||
|
info ".env nach ~/.config/openclaw-mailbox-cal/.env deployt"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 4. Konfigurationsdateien deployen (envsubst für Variablen)
|
||||||
|
# =============================================================
|
||||||
|
info "Deploye Konfigurationsdateien..."
|
||||||
|
|
||||||
|
# vdirsyncer config — Variablen ersetzen
|
||||||
|
export MAILBOX_USER MAILBOX_USER_ENCODED MAILBOX_PASS
|
||||||
|
envsubst '${MAILBOX_USER} ${MAILBOX_USER_ENCODED} ${MAILBOX_PASS}' \
|
||||||
|
< "$REPO_ROOT/config/vdirsyncer.conf" \
|
||||||
|
> "$HOME/.config/vdirsyncer/config"
|
||||||
|
chmod 600 "$HOME/.config/vdirsyncer/config"
|
||||||
|
info " → ~/.config/vdirsyncer/config"
|
||||||
|
|
||||||
|
# khal config
|
||||||
|
cp "$REPO_ROOT/config/khal.conf" "$HOME/.config/khal/config"
|
||||||
|
info " → ~/.config/khal/config"
|
||||||
|
|
||||||
|
# todoman config
|
||||||
|
cp "$REPO_ROOT/config/todoman.conf" "$HOME/.config/todoman/config.cfg"
|
||||||
|
info " → ~/.config/todoman/config.cfg"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 5. systemd Units installieren (User-Session)
|
||||||
|
# =============================================================
|
||||||
|
info "Installiere systemd Units (User-Session)..."
|
||||||
|
|
||||||
|
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
|
||||||
|
mkdir -p "$SYSTEMD_USER_DIR"
|
||||||
|
|
||||||
|
# Variablen in systemd-Units ersetzen
|
||||||
|
for unit_file in "$REPO_ROOT/systemd/"*.{service,timer}; do
|
||||||
|
[[ -f "$unit_file" ]] || continue
|
||||||
|
unit_name="$(basename "$unit_file")"
|
||||||
|
envsubst '${SYNC_INTERVAL}' < "$unit_file" > "$SYSTEMD_USER_DIR/$unit_name"
|
||||||
|
info " → $SYSTEMD_USER_DIR/$unit_name"
|
||||||
|
done
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now vdirsyncer-sync.timer
|
||||||
|
info "systemd Timer aktiviert."
|
||||||
|
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
loginctl enable-linger "$USER" 2>/dev/null || \
|
||||||
|
warn "loginctl enable-linger fehlgeschlagen — bitte manuell ausführen: sudo loginctl enable-linger $USER"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 6. Erst-Synchronisation
|
||||||
|
# =============================================================
|
||||||
|
info "Führe initialen vdirsyncer discover + sync durch..."
|
||||||
|
vdirsyncer discover calendars || warn "discover calendars hatte Warnungen — prüfe Ausgabe"
|
||||||
|
vdirsyncer discover tasks || warn "discover tasks hatte Warnungen — prüfe Ausgabe"
|
||||||
|
vdirsyncer sync || warn "sync hatte Warnungen — prüfe Ausgabe"
|
||||||
|
info "Erst-Sync abgeschlossen."
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 7. khal-Index aufbauen
|
||||||
|
# =============================================================
|
||||||
|
info "Baue khal-Index auf..."
|
||||||
|
khal --config "$HOME/.config/khal/config" rebuild-cache || true
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 8. OpenClaw Skill-Verknüpfung
|
||||||
|
# =============================================================
|
||||||
|
OPENCLAW_SKILLS_DIR="${OPENCLAW_SKILLS_DIR:-$HOME/.config/openclaw/skills}"
|
||||||
|
if [[ -d "$OPENCLAW_SKILLS_DIR" ]]; then
|
||||||
|
info "Verknüpfe OpenClaw Skill..."
|
||||||
|
ln -sf "$REPO_ROOT" "$OPENCLAW_SKILLS_DIR/mailbox-cal" 2>/dev/null || true
|
||||||
|
info " → $OPENCLAW_SKILLS_DIR/mailbox-cal"
|
||||||
|
else
|
||||||
|
warn "OpenClaw Skills-Verzeichnis nicht gefunden ($OPENCLAW_SKILLS_DIR)."
|
||||||
|
warn "Bitte Skill manuell in OpenClaw registrieren (siehe README.md)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# Fertig
|
||||||
|
# =============================================================
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${GREEN}║ Installation abgeschlossen! ║${NC}"
|
||||||
|
echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Nächste Schritte:"
|
||||||
|
echo " 1. khal agenda # Termine anzeigen"
|
||||||
|
echo " 2. todo list # Aufgaben anzeigen"
|
||||||
|
echo " 3. systemctl --user status vdirsyncer-sync.timer"
|
||||||
|
echo " 4. tail -f ~/.local/share/openclaw-mailbox-cal/sync.log"
|
||||||
|
echo ""
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# test_setup.sh — Überprüft die Installation Schritt für Schritt
|
||||||
|
# Ausführen nach install.sh: bash scripts/test_setup.sh
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Farben
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN} [✓] $*${NC}"; }
|
||||||
|
fail() { echo -e "${RED} [✗] $*${NC}"; FAILED=$((FAILED+1)); }
|
||||||
|
info() { echo -e "${CYAN} [i] $*${NC}"; }
|
||||||
|
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${CYAN} OpenClaw mailbox.org Kalender+Aufgaben — Selbsttest${NC}"
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- 1. Binaries vorhanden? ---
|
||||||
|
echo "▶ 1. Programm-Verfügbarkeit"
|
||||||
|
for bin in vdirsyncer khal todo python3; do
|
||||||
|
if command -v "$bin" &>/dev/null; then
|
||||||
|
ok "$bin → $(command -v $bin)"
|
||||||
|
else
|
||||||
|
fail "$bin nicht gefunden — install.sh ausführen"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 2. Konfigurationsdateien ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 2. Konfigurationsdateien"
|
||||||
|
declare -A CONFIGS=(
|
||||||
|
["vdirsyncer"]="$HOME/.config/vdirsyncer/config"
|
||||||
|
["khal"]="$HOME/.config/khal/config"
|
||||||
|
["todoman"]="$HOME/.config/todoman/config.cfg"
|
||||||
|
)
|
||||||
|
for name in "${!CONFIGS[@]}"; do
|
||||||
|
path="${CONFIGS[$name]}"
|
||||||
|
if [[ -f "$path" ]]; then
|
||||||
|
ok "$name → $path"
|
||||||
|
else
|
||||||
|
fail "$name-Config fehlt: $path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 3. Berechtigungen vdirsyncer-Config (enthält Passwort) ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 3. Sicherheit"
|
||||||
|
VDIR_CONF="$HOME/.config/vdirsyncer/config"
|
||||||
|
if [[ -f "$VDIR_CONF" ]]; then
|
||||||
|
PERMS=$(stat -c "%a" "$VDIR_CONF")
|
||||||
|
if [[ "$PERMS" == "600" ]]; then
|
||||||
|
ok "vdirsyncer config Berechtigungen: $PERMS (korrekt)"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer config Berechtigungen: $PERMS (sollte 600 sein) → chmod 600 $VDIR_CONF"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe ob Klartext-Passwort in Config steht
|
||||||
|
if grep -q 'password.*=.*"[^$]' "$VDIR_CONF" 2>/dev/null; then
|
||||||
|
info "Passwort in Plaintext erkannt — Erwäge pass(1) oder python-keyring als Alternative"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 4. Sync-Verzeichnisse ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 4. Sync-Verzeichnisse"
|
||||||
|
for dir in \
|
||||||
|
"$HOME/.local/share/vdirsyncer/status" \
|
||||||
|
"$HOME/.local/share/vdirsyncer/calendars" \
|
||||||
|
"$HOME/.local/share/vdirsyncer/tasks"; do
|
||||||
|
if [[ -d "$dir" ]]; then
|
||||||
|
COUNT=$(find "$dir" -name "*.ics" 2>/dev/null | wc -l)
|
||||||
|
ok "$dir (${COUNT} .ics Dateien)"
|
||||||
|
else
|
||||||
|
fail "Verzeichnis fehlt: $dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 5. vdirsyncer Verbindungstest ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 5. vdirsyncer — Verbindungstest"
|
||||||
|
|
||||||
|
# Prüfen ob discover bereits gelaufen ist (Status-Dateien vorhanden)
|
||||||
|
STATUS_DIR="$HOME/.local/share/vdirsyncer/status"
|
||||||
|
if [[ -z "$(ls -A "$STATUS_DIR" 2>/dev/null)" ]]; then
|
||||||
|
info "Kein discover-Status gefunden — führe 'vdirsyncer discover' zuerst aus..."
|
||||||
|
if timeout 60 vdirsyncer discover calendars 2>&1 | tail -3 && \
|
||||||
|
timeout 60 vdirsyncer discover tasks 2>&1 | tail -3; then
|
||||||
|
ok "vdirsyncer discover erfolgreich"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer discover fehlgeschlagen — Zugangsdaten und Netzwerk prüfen"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "discover-Status vorhanden — überspringe discover"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Führe 'vdirsyncer sync' aus (Timeout: 60s)..."
|
||||||
|
SYNC_OUT=$(timeout 60 vdirsyncer sync 2>&1)
|
||||||
|
SYNC_RC=$?
|
||||||
|
echo "$SYNC_OUT" | tail -8
|
||||||
|
|
||||||
|
# Erfolg prüfen: Kopier-Aktivität oder kein Fehler = OK
|
||||||
|
if [[ $SYNC_RC -eq 0 ]]; then
|
||||||
|
ok "vdirsyncer sync erfolgreich"
|
||||||
|
elif echo "$SYNC_OUT" | grep -qE "^Copying|^Deleting|Nothing to do"; then
|
||||||
|
# Sync hat Daten übertragen trotz non-zero exit (häufig bei teilweisem discover)
|
||||||
|
ok "vdirsyncer sync erfolgreich (Daten wurden übertragen)"
|
||||||
|
if echo "$SYNC_OUT" | grep -q "discover"; then
|
||||||
|
warn "Hinweis: 'vdirsyncer discover tasks' noch ausführen um alle Pairs zu aktivieren"
|
||||||
|
fi
|
||||||
|
elif echo "$SYNC_OUT" | grep -q "discover"; then
|
||||||
|
fail "vdirsyncer sync: discover noch nicht vollständig — starte: vdirsyncer discover tasks"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer sync fehlgeschlagen — siehe Ausgabe oben"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 6. khal-Test ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 6. khal — Termine heute"
|
||||||
|
KHAL_OUT=$(khal list --format "{start-time} {title}" today today 2>&1)
|
||||||
|
KHAL_RC=$?
|
||||||
|
if [[ $KHAL_RC -eq 0 ]]; then
|
||||||
|
ok "khal list ausgeführt"
|
||||||
|
[[ -n "$KHAL_OUT" ]] && echo "$KHAL_OUT" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
fail "khal list fehlgeschlagen:"
|
||||||
|
echo "$KHAL_OUT" | sed 's/^/ /'
|
||||||
|
info "Tipp: khal rebuild-cache ausführen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 7. todoman-Test ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 7. todoman — Aufgabenliste"
|
||||||
|
TODO_OUT=$(todo list 2>&1)
|
||||||
|
TODO_RC=$?
|
||||||
|
if [[ $TODO_RC -eq 0 ]]; then
|
||||||
|
ok "todo list ausgeführt"
|
||||||
|
[[ -n "$TODO_OUT" ]] && echo "$TODO_OUT" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
fail "todo list fehlgeschlagen:"
|
||||||
|
echo "$TODO_OUT" | sed 's/^/ /'
|
||||||
|
info "Tipp: Pfad in ~/.config/todoman/config.cfg prüfen, 'vdirsyncer discover tasks' ausführen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 8. systemd-Timer ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 8. systemd Timer"
|
||||||
|
if systemctl --user is-active vdirsyncer-sync.timer &>/dev/null; then
|
||||||
|
ok "vdirsyncer-sync.timer: aktiv"
|
||||||
|
NEXT=$(systemctl --user list-timers vdirsyncer-sync.timer --no-legend 2>/dev/null | awk '{print $1, $2}')
|
||||||
|
info "Nächster Sync: $NEXT"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer-sync.timer nicht aktiv → systemctl --user enable --now vdirsyncer-sync.timer"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 9. Log-Datei ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 9. Log-Datei"
|
||||||
|
LOG="$HOME/.local/share/openclaw-mailbox-cal/sync.log"
|
||||||
|
if [[ -f "$LOG" ]]; then
|
||||||
|
SIZE=$(du -sh "$LOG" | cut -f1)
|
||||||
|
ok "Log vorhanden: $LOG ($SIZE)"
|
||||||
|
info "Letzte 5 Zeilen:"
|
||||||
|
tail -5 "$LOG" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
info "Log noch nicht vorhanden (wird beim ersten Sync erstellt)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Zusammenfassung ---
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
if [[ $FAILED -eq 0 ]]; then
|
||||||
|
echo -e "${GREEN} Alle Tests bestanden! Setup ist korrekt.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED} $FAILED Test(s) fehlgeschlagen — siehe Details oben.${NC}"
|
||||||
|
fi
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
exit $FAILED
|
||||||
+82496
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,549 @@
|
|||||||
|
# OpenClaw auf Raspberry Pi 4 – Installationsanleitung
|
||||||
|
### mit OpenRouter + mailbox.org CalDAV & Tasks
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Übersicht & Voraussetzungen
|
||||||
|
|
||||||
|
Diese Anleitung richtet einen **OpenClaw Gateway** auf einem Raspberry Pi 4 (headless, 64-bit) ein, verbindet ihn mit **OpenRouter** als KI-Provider und integriert **mailbox.org Kalender und Aufgaben** über CalDAV via `vdirsyncer` + `khal`.[^1][^2]
|
||||||
|
|
||||||
|
**Du brauchst:**
|
||||||
|
- Raspberry Pi 4 (2 GB+ RAM, 4 GB empfohlen) mit 64-bit Raspberry Pi OS Lite[^2]
|
||||||
|
- MicroSD ≥ 16 GB oder USB-SSD (empfohlen für Stabilität)[^2]
|
||||||
|
- SSH-Zugang
|
||||||
|
- OpenRouter API Key (`sk-or-...`) – holen unter openrouter.ai/keys[^1]
|
||||||
|
- mailbox.org App-Passwort mit CalDAV-Berechtigung (unter Einstellungen → Sicherheit → Applikationspasswörter)[^3][^4]
|
||||||
|
- Deine mailbox.org Kalender-URL(s) aus dem Webinterface (☰ → Eigenschaften → CalDAV URL)[^5][^6]
|
||||||
|
|
||||||
|
> **Wichtig:** Das App-Passwort ist zwingend erforderlich – auch ohne 2FA. Externe Apps wie vdirsyncer werden seit mailbox.org Login 2.0 mit dem normalen Passwort blockiert.[^4][^7]
|
||||||
|
|
||||||
|
### Hinweis: npm ist Pflicht
|
||||||
|
|
||||||
|
OpenClaw wird **ausschließlich als npm-Paket** distribuiert – es gibt keinen Weg daran vorbei. Der offizielle `install.sh`-Einzeiler übernimmt den npm-Aufruf automatisch, prüft Node.js, richtet das npm-Verzeichnis ein und vermeidet Permission-Fehler. Deshalb wird in dieser Anleitung der Einzeiler statt `npm install -g openclaw` direkt bevorzugt.[^8][^9][^10][^11]
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 1: Raspberry Pi vorbereiten
|
||||||
|
|
||||||
|
### 1.1 OS flashen
|
||||||
|
|
||||||
|
Mit **Raspberry Pi Imager** (Raspberry Pi OS Lite, 64-bit) flashen. In den erweiterten Einstellungen vorab konfigurieren:[^2]
|
||||||
|
|
||||||
|
- Hostname: `dumbass`
|
||||||
|
- SSH aktivieren
|
||||||
|
- Benutzername: `hans`, Passwort setzen
|
||||||
|
- WLAN (optional; Ethernet ist stabiler)
|
||||||
|
|
||||||
|
SD-Karte einstecken, Pi starten, per SSH verbinden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh hans@dumbass
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 System aktualisieren & Grundpakete installieren
|
||||||
|
|
||||||
|
Der `install.sh` installiert fehlende Pakete zwar selbstständig nach, aber explizite Vorinstallation verhindert `spawn git ENOENT`-Fehler während des npm-Build-Prozesses:[^9][^8]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
sudo apt install -y git curl build-essential python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Warum `build-essential` + `python3-pip`?** Einige npm-Abhängigkeiten von OpenClaw (insb. `sharp` für Bildverarbeitung und `node-gyp` für native Addons) kompilieren C++-Code während der Installation. Auf ARM ohne Build-Tools schlägt `npm install` mit `node-gyp rebuild` Fehler fehl.[^11][^12]
|
||||||
|
|
||||||
|
Zeitzone korrekt setzen (wichtig für Kalenderabfragen und Cron):[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo timedatectl set-timezone Europe/Vienna
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Node.js 22 installieren (Mindestanforderung)
|
||||||
|
|
||||||
|
OpenClaw erfordert **Node.js 22+**. Node.js 24 funktioniert ebenfalls.[^13][^14][^11]
|
||||||
|
|
||||||
|
**Empfohlen – nvm (flexibles Versionsmanagement, einfach aktualisierbar):**[^15]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
||||||
|
source ~/.bashrc
|
||||||
|
nvm install 22
|
||||||
|
nvm alias default 22
|
||||||
|
node --version # → v22.x.x
|
||||||
|
npm --version # → 10.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alternative – NodeSource (systemweit, ohne nvm):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||||
|
sudo apt install -y nodejs
|
||||||
|
node --version
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Achtung:** Den Standard-`nodejs`-APT-Paket von Raspberry Pi OS **nicht** verwenden – er liefert oft veraltete Versionen (v18 oder älter), die von OpenClaw abgelehnt werden.[^14][^15]
|
||||||
|
|
||||||
|
### 1.4 Swap prüfen (du hast bereits 4 GB Swap)
|
||||||
|
|
||||||
|
Da bereits 4 GB Swap konfiguriert sind, wird zusätzlich die Swappiness reduziert um unnötigen Swap-Druck zu vermeiden:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
free -h # Swap sollte angezeigt werden
|
||||||
|
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
|
||||||
|
sudo sysctl -p
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Pi-spezifische Optimierungen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GPU-Speicher minimieren (kein Display benötigt)
|
||||||
|
echo 'gpu_mem=16' | sudo tee -a /boot/firmware/config.txt
|
||||||
|
|
||||||
|
# Nicht benötigte Dienste deaktivieren
|
||||||
|
sudo systemctl disable bluetooth
|
||||||
|
sudo systemctl disable cups 2>/dev/null || true
|
||||||
|
|
||||||
|
# Node.js Compile Cache (beschleunigt CLI-Aufrufe auf ARM spürbar)
|
||||||
|
grep -q 'NODE_COMPILE_CACHE' ~/.bashrc || cat >> ~/.bashrc <<'EOF'
|
||||||
|
export NODE_COMPILE_CACHE=/var/tmp/openclaw-compile-cache
|
||||||
|
mkdir -p /var/tmp/openclaw-compile-cache
|
||||||
|
export OPENCLAW_NO_RESPAWN=1
|
||||||
|
EOF
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 2: OpenClaw installieren & mit OpenRouter verbinden
|
||||||
|
|
||||||
|
### 2.1 OpenClaw installieren
|
||||||
|
|
||||||
|
Der offizielle Einzeiler prüft Node.js, richtet den npm-Prefix korrekt ein (verhindert EACCES-Fehler) und installiert alle Abhängigkeiten:[^16][^9][^11]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL --proto '=https' --tlsv1.2 https://openclaw.ai/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Installation dauert auf dem Pi 4 ca. 3–6 Minuten (npm kompiliert native Module für ARM). **Nicht unterbrechen.**[^16]
|
||||||
|
|
||||||
|
Nach Abschluss Umgebung neu laden und verifizieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
openclaw --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Falls `openclaw: command not found`** – npm-Prefix wurde auf `~/.npm-global` gesetzt und ist noch nicht im PATH:[^17][^9]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PATH="$HOME/.npm-global/bin:$PATH"
|
||||||
|
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
openclaw --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Falls `sharp`-Build-Fehler auftreten** (kommt vor bei ARM mit globalem libvips):[^11]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install -g openclaw@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach der Installation immer prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw doctor
|
||||||
|
```
|
||||||
|
|
||||||
|
Dieser Befehl checkt Node.js-Version, npm-Installation, Config-Gültigkeit, Gateway-Status und Permission-Probleme – und sagt genau was fehlt.[^8]
|
||||||
|
|
||||||
|
### 2.2 Onboarding mit OpenRouter
|
||||||
|
|
||||||
|
Der schnellste Weg setzt OpenRouter direkt per CLI:[^18][^1]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --auth-choice openrouter-api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Wizard fragt nach dem API Key. Alternativ als Ein-Zeiler:[^18]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --auth-choice apiKey --token-provider openrouter --token "sk-or-DEINKEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Modell konfigurieren
|
||||||
|
|
||||||
|
Öffne `~/.openclaw/openclaw.json` und trage dein gewünschtes Modell ein. Empfohlene Konfiguration mit Fallback:[^19][^20][^1]
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENROUTER_API_KEY": "sk-or-DEINKEY"
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": {
|
||||||
|
"primary": "openrouter/anthropic/claude-sonnet-4.5",
|
||||||
|
"fallbacks": [
|
||||||
|
"openrouter/anthropic/claude-haiku-3.5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"openrouter/anthropic/claude-sonnet-4.5": {},
|
||||||
|
"openrouter/anthropic/claude-haiku-3.5": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Tipp Kostenoptimierung:** `"primary": "openrouter/openrouter/auto"` lässt OpenRouter automatisch das günstigste Modell für jede Anfrage wählen. Besonders sinnvoll wenn OpenClaw viele einfache Hintergrundaufgaben (Heartbeats, Status-Checks) ausführt.[^20][^1]
|
||||||
|
|
||||||
|
Modellverbindung testen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw models list
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Gateway als Daemon starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --install-daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
Status prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw status
|
||||||
|
systemctl --user status openclaw-gateway.service
|
||||||
|
journalctl --user -u openclaw-gateway.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls der Dienst nach Logout nicht weiterläuft, Lingering aktivieren:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo loginctl enable-linger hans
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 3: vdirsyncer + khal installieren (CalDAV-Stack)
|
||||||
|
|
||||||
|
Der offizielle OpenClaw CalDAV-Skill setzt auf `vdirsyncer` (Sync-Layer) und `khal` (Lese-/Schreibzugriff auf Events und Tasks).[^21][^22]
|
||||||
|
|
||||||
|
### 3.1 Pakete installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y vdirsyncer khal
|
||||||
|
```
|
||||||
|
|
||||||
|
Verzeichnisse anlegen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/vdirsyncer
|
||||||
|
mkdir -p ~/.config/khal
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/status
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/calendars
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 CalDAV URLs von mailbox.org ermitteln
|
||||||
|
|
||||||
|
Öffne mailbox.org im Browser. Für **jeden** Kalender und jede Aufgabenliste:
|
||||||
|
|
||||||
|
1. Kalender anklicken → ☰-Menü → **Eigenschaften**
|
||||||
|
2. Unter **CalDAV URL** die vollständige URL kopieren
|
||||||
|
|
||||||
|
Beispielform: `https://dav.mailbox.org/caldav/Y2FsOi8vMC80Mg`[^6][^5]
|
||||||
|
|
||||||
|
> Die oberste Discovery-URL `https://dav.mailbox.org` funktioniert bei vdirsyncer direkt (automatische Collection-Erkennung), aber **spezifische URLs** sind zuverlässiger und vermeiden, dass ungewollte Shared-Folder auftauchen.[^23][^24]
|
||||||
|
|
||||||
|
### 3.3 App-Passwort sicher speichern
|
||||||
|
|
||||||
|
Das Passwort wird als geschützte Datei abgelegt (einfache, bewährte Methode):[^25]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Datei anlegen (600 = nur für dich lesbar)
|
||||||
|
echo "DEIN_APP_PASSWORT" > ~/.config/vdirsyncer/mailbox_apppassword
|
||||||
|
chmod 600 ~/.config/vdirsyncer/mailbox_apppassword
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 4: vdirsyncer konfigurieren
|
||||||
|
|
||||||
|
Vollständige Konfiguration für **Kalender + Aufgaben** in `~/.config/vdirsyncer/config`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[general]
|
||||||
|
status_path = "~/.local/share/vdirsyncer/status/"
|
||||||
|
|
||||||
|
# ──────────────── KALENDER ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_calendar]
|
||||||
|
a = "mailbox_cal_remote"
|
||||||
|
b = "mailbox_cal_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
metadata = ["displayname", "color"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/DEINE_KALENDER_ID"
|
||||||
|
username = "deine@mailadresse.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/calendars/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
# ──────────────── AUFGABEN (VTODO) ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_tasks]
|
||||||
|
a = "mailbox_tasks_remote"
|
||||||
|
b = "mailbox_tasks_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
|
||||||
|
[storage mailbox_tasks_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/DEINE_TASKS_ID"
|
||||||
|
username = "deine@mailadresse.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_tasks_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/tasks/"
|
||||||
|
fileext = ".ics"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Wichtig:** Ersetze `DEINE_KALENDER_ID` und `DEINE_TASKS_ID` mit den tatsächlichen IDs aus Schritt 3.2. `conflict_resolution = "a wins"` bedeutet: im Konflikt gewinnt der Server (mailbox.org). Das ist die sicherste Einstellung.[^26][^25]
|
||||||
|
|
||||||
|
Ersten Sync durchführen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vdirsyncer discover
|
||||||
|
vdirsyncer sync
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Erwartetes Verhalten:** Beim ersten Durchlauf erscheint eine Bestätigungsabfrage für neue Collections → mit `yes` bestätigen.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 5: khal konfigurieren
|
||||||
|
|
||||||
|
`~/.config/khal/config`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[calendars]
|
||||||
|
|
||||||
|
[[mailbox_kalender]]
|
||||||
|
path = ~/.local/share/vdirsyncer/calendars/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[[mailbox_aufgaben]]
|
||||||
|
path = ~/.local/share/vdirsyncer/tasks/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[default]
|
||||||
|
default_calendar = mailbox_kalender
|
||||||
|
highlight_event_days = True
|
||||||
|
show_todos = True
|
||||||
|
|
||||||
|
[locale]
|
||||||
|
timeformat = %H:%M
|
||||||
|
dateformat = %Y-%m-%d
|
||||||
|
datetimeformat = %Y-%m-%d %H:%M
|
||||||
|
longdateformat = %A, %d. %B %Y
|
||||||
|
longtimeformat = %H:%M
|
||||||
|
firstweekday = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Khal-Setup testen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
khal list today 7d
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 6: OpenClaw CalDAV-Skill installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw skills install caldav-calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder alternativ über den Playbooks-Skill:[^22][^27]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw add @asleep123/caldav-calendar
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach OpenClaw Gateway neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart openclaw-gateway.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Du kannst jetzt z.B. via Telegram oder deinem konfigurierten Channel tippen:
|
||||||
|
- *„Was habe ich morgen im Kalender?"*
|
||||||
|
- *„Erstelle einen Termin am 10.6. um 14 Uhr – Zahnarzt"*
|
||||||
|
- *„Zeig mir alle offenen Aufgaben"*
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 7: Automatischer Sync via Cron
|
||||||
|
|
||||||
|
Damit OpenClaw immer aktuelle Kalenderdaten hat, Sync automatisieren:[^28][^26]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Folgende Zeilen einfügen:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Alle 5 Minuten synchronisieren
|
||||||
|
*/5 * * * * /usr/bin/vdirsyncer sync >> ~/.local/share/vdirsyncer/sync.log 2>&1
|
||||||
|
|
||||||
|
# khal-Cache täglich um 03:00 löschen (verhindert veraltete Anzeigen)
|
||||||
|
0 3 * * * rm -f ~/.local/share/khal/khal.db
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Phase 8: Dashboard-Zugriff (optional)
|
||||||
|
|
||||||
|
Das OpenClaw Control UI läuft nur lokal. Zugriff via SSH-Tunnel:[^2]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Raspi:
|
||||||
|
openclaw dashboard --no-open
|
||||||
|
# → zeigt lokale URL, z.B. http://127.0.0.1:18789
|
||||||
|
|
||||||
|
# Auf deinem PC (neues Terminal):
|
||||||
|
ssh -N -L 18789:127.0.0.1:18789 hans@dumbass
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann im Browser: `http://localhost:18789`
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Bekannte Fallstricke & Workarounds
|
||||||
|
|
||||||
|
| Problem | Ursache | Fix |
|
||||||
|
|---|---|---|
|
||||||
|
| **401-Fehler bei vdirsyncer** | Normales Passwort statt App-Passwort | App-Passwort mit CalDAV-Berechtigung anlegen[^4][^29] |
|
||||||
|
| **Wiederkehrende Aufgaben verschwinden** | Open-Xchange-Bug: RRULE wird entfernt | Wiederkehrende Tasks nicht über CalDAV verwalten; als VEVENT anlegen[^30][^31] |
|
||||||
|
| **Tasks nur in eine Richtung** | Bekannter Sync-Bug | `vdirsyncer discover` erneut ausführen; `conflict_resolution` explizit setzen[^32][^33] |
|
||||||
|
| **khal zeigt veraltete Daten** | Cache-Problem | `rm ~/.local/share/khal/khal.db` und neu abfragen[^21] |
|
||||||
|
| **OpenClaw liefert erfundene Termine** | Kalender nicht verbunden, Agent halluziniert | Bekannte Termine als Probe gegenchecken; Logs prüfen[^34][^35] |
|
||||||
|
| **khal edit schlägt fehl** | Braucht TTY, nicht-interaktiver Kontext | `tmux` verwenden oder Events direkt als `.ics` bearbeiten[^21] |
|
||||||
|
| **Service startet nach Reboot nicht** | Kein User-Lingering | `sudo loginctl enable-linger $(whoami)`[^2] |
|
||||||
|
| **WiFi bricht periodisch weg** | Power Management | `sudo iwconfig wlan0 power off` (nach Reboot nötig → `/etc/rc.local` eintragen)[^2] |
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Schnellreferenz: Wichtige Befehle
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status prüfen
|
||||||
|
openclaw status
|
||||||
|
journalctl --user -u openclaw-gateway.service -n 50
|
||||||
|
|
||||||
|
# Manueller Kalender-Sync
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Heutige Termine anzeigen
|
||||||
|
khal list today
|
||||||
|
|
||||||
|
# Nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Neuen Termin erstellen
|
||||||
|
khal new 2026-06-10 14:00 15:00 "Zahnarzt"
|
||||||
|
vdirsyncer sync # danach immer syncen!
|
||||||
|
|
||||||
|
# Modell wechseln
|
||||||
|
openclaw models set openrouter/anthropic/claude-opus-4.5
|
||||||
|
|
||||||
|
# OpenClaw-Skill aktualisieren
|
||||||
|
openclaw skills update --all
|
||||||
|
|
||||||
|
# Gateway neu starten
|
||||||
|
systemctl --user restart openclaw-gateway.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
1. [Integration with OpenClaw | OpenRouter | Documentation](https://openrouter.ai/docs/guides/coding-agents/openclaw-integration) - Learn how to configure OpenClaw to use OpenRouter for AI agents across multiple messaging platforms.
|
||||||
|
|
||||||
|
2. [Raspberry Pi - OpenClaw Docs](https://docs.openclaw.ai/install/raspberry-pi)
|
||||||
|
|
||||||
|
3. [Application passwords for external programs - Knowledge Base](https://kb.mailbox.org/en/private/security-and-privacy/application-passwords-for-external-programs/) - When using Two-Factor Authentication (2FA) for your account, you must create and use a corresponding...
|
||||||
|
|
||||||
|
4. [Access security for mailbox – 2FA, app passwords, and email ...](https://kb.mailbox.org/en/private/security-and-privacy/access-security-for-mailbox-2fa-app-passwords-and-e-mail-passwords/) - This article provides an overview of the different functions for securing your access to mailbox – i...
|
||||||
|
|
||||||
|
5. [CardDAV for Outlook and Thunderbird – CalDav Synchronizer - Mailbox.org](https://kb.mailbox.org/en/private/addressbook-and-calendar/caldav-carddav-for-outlook-caldav-synchronizer/) - Once you have enabled two-factor authentication, external applications such as Outlook, Thunderbird,...
|
||||||
|
|
||||||
|
6. [CalDAV-Clients – Outlook, Thunderbird, macOS, Evolution, Kontact](https://kb.mailbox.org/de/privat/adressbuch-und-kalender/caldav-clients-outlook-thunderbird-macos-evolution-kontact/) - Im nächsten Schritt wählen Sie als Format CalDAV und als Adresse https://dav.mailbox.org/caldav/XXX ...
|
||||||
|
|
||||||
|
7. [Information zu Nutzung von CalDAV/CardDAV/WebDAV und Drive ...](https://userforum.mailbox.org/topic/9956-information-zu-nutzung-von-caldavcarddavwebdav-und-drive-mit-login-2-0-2fa) - Mit dem neuen Login 2.0 in Kombination mit 2FA müssen für den Zugriff über CalDAV/CardDAV/WebDAV und...
|
||||||
|
|
||||||
|
8. [Fix 'npm install failed for openclaw@latest' and ... - Stack Junkie](https://www.stack-junkie.com/blog/fix-openclaw-installation-errors) - Fix npm install failed for openclaw@latest, Node version mismatches, permission errors, port conflic...
|
||||||
|
|
||||||
|
9. [Installer internals - OpenClaw Docs](https://docs.openclaw.ai/install/installer)
|
||||||
|
|
||||||
|
10. [OpenClaw Install: Complete Installation Guide | macOS, Linux ...](https://openclaw-install.org) - Complete OpenClaw install guide for macOS, Linux, and Windows. Learn how to install OpenClaw in 10 m...
|
||||||
|
|
||||||
|
11. [Install – OpenClaw - Open Source AI Coding Assistant](https://openclawlab.com/en/docs/install/) - Install OpenClaw — installer script, npm/pnpm, from source, Docker, and more
|
||||||
|
|
||||||
|
12. [OpenClaw Installation Error: Complete Fix Guide for All Platforms ...](https://yingtu.ai/en/blog/openclaw-installation-error) - Fix OpenClaw installation errors on macOS, Windows, and Linux. Covers Node.js version issues, Sharp/...
|
||||||
|
|
||||||
|
13. [The Ultimate Guide to Fixing OpenClaw Installer NPM Install Failed ...](https://skywork.ai/skypage/en/fixing-openclaw-npm-install/2048664282507194368) - Fix OpenClaw npm install failed errors fast—step‑by‑step guide, environment checks, and top AI agent...
|
||||||
|
|
||||||
|
14. [How to Run OpenClaw on Raspberry Pi: A Practical Setup Guide](https://www.sunfounder.com/blogs/news/how-to-run-openclaw-on-raspberry-pi-a-practical-setup-guide) - Start by installing Raspberry Pi OS Lite. This version provides a minimal environment without a grap...
|
||||||
|
|
||||||
|
15. [OpenClaw x Raspberry Pi Deployment Guide | MI - 超智諮詢](https://www.meta-intelligence.tech/en/insight-openclaw-raspberry-pi) - A complete tutorial on deploying the OpenClaw AI agent on Raspberry Pi (5/4).
|
||||||
|
|
||||||
|
16. [Can OpenClaw Run on Raspberry Pi? Complete Step-by- ...](https://www.pcbuildadvisor.com/can-openclaw-run-on-raspberry-pi-complete-step-by-step-installation-guide/) - Yes, OpenClaw can absolutely run on a Raspberry Pi — and it runs surprisingly well when set up corre...
|
||||||
|
|
||||||
|
17. [The Complete OpenClaw Setup & Installation Guide - Sphere Partners](https://www.sphereinc.com/blogs/the-complete-openclaw-setup-installation-guide/) - If you're deploying on a VPS, use the npm method or Docker instead. 2.3 Installation Method 2: npm G...
|
||||||
|
|
||||||
|
18. [OpenClaw OpenRouter Setup: One API Key for Hundreds of ...](https://www.stack-junkie.com/blog/openclaw-openrouter-setup-guide) - OpenClaw OpenRouter setup: connect hundreds of LLM models through one API key, configure model routi...
|
||||||
|
|
||||||
|
19. [OpenRouter](https://docs.openclaw.ai/providers/openrouter)
|
||||||
|
|
||||||
|
20. [The Ultimate Guide to OpenClaw OpenRouter Setup](https://skywork.ai/skypage/en/openclaw-openrouter-setup/2037020692520374272) - Optimize AI agents with OpenClaw + OpenRouter: cost‑effective, multi‑model routing, local execution,...
|
||||||
|
|
||||||
|
21. [caldav-calendar skill by openclaw/skills - playbooks](https://playbooks.com/skills/openclaw/skills/caldav-calendar) - This skill synchronizes and queries CalDAV calendars using vdirsyncer and khal, enabling streamlined...
|
||||||
|
|
||||||
|
22. [caldav-calendar - OpenClaw Skills](https://openclawskills.best/skills/asleep123/caldav-calendar/) - How do I install caldav-calendar? Run openclaw add @asleep123/caldav-calendar in your terminal. This...
|
||||||
|
|
||||||
|
23. [mailbox.org - DAVx5](https://www.davx5.com/tested-with/mailboxorg) - WebDAV URL: https://dav.mailbox.org/servlet/webdav.infostore/. User name: your mailbox.org email add...
|
||||||
|
|
||||||
|
24. [Access calendar data from programs using CalDav](https://userforum-en.mailbox.org/topic/2526-access-calendar-data-from-programs-using-caldav) - I was using the wrong url. ``` # Replace with your actual mailbox.org username and password. USERNAM...
|
||||||
|
|
||||||
|
25. [Synchronise CalDAV and CardDAV data for khal and khard with ...](https://www.jan0sch.de/post/synchronise-caldav-and-carddav-data-with-vdirsyncer/) - First we need to setup our synchronisation. The file ~/.config/vdirsyncer/config contains the config...
|
||||||
|
|
||||||
|
26. [Calendar Integration | Dank Linux](https://danklinux.com/docs/dankmaterialshell/calendar-integration) - Setting up khal and vdirsyncer is very user-unfriendly and convoluted, in a future release this inte...
|
||||||
|
|
||||||
|
27. [Commands](https://docs.openclaw.ai/tools/clawhub)
|
||||||
|
|
||||||
|
28. [Tutorial¶](https://vdirsyncer.pimutils.org/en/stable/tutorial.html)
|
||||||
|
|
||||||
|
29. [CardDAV/CalDAV authentication error - mailbox User Forum](https://userforum-en.mailbox.org/topic/3141-carddavcaldav-authentication-error) - Hello since about 24 hours or so ago, I have been consistently receiving error authenticating into m...
|
||||||
|
|
||||||
|
30. [Support for recurring tasks for CalDAV would be really nice](https://userforum-en.mailbox.org/topic/1725-support-for-recurring-tasks-for-caldav-would-be-really-nice) - Right now, if I add a recurring task on a CalDAV client and sync it to Mailbox, the recurring tasks ...
|
||||||
|
|
||||||
|
31. [Are recurring tasks not supported when syncing from Mailbox.org?](https://github.com/TechbeeAT/jtxBoard/discussions/2204) - I am trying to sync my Mailbox.org tasks with my Android phone. Everything works as expected, except...
|
||||||
|
|
||||||
|
32. [mailbox.org synchronization broken · Issue #948 · tasks/tasks](https://github.com/tasks/tasks/issues/948) - CALDAV server: mailbox.org Tasks version: 8.7 (current on F-droid) I first noticed this today, but I...
|
||||||
|
|
||||||
|
33. [Tasks only syncing one way over CalDAV - Reddit](https://www.reddit.com/r/tasks/comments/1daztkg/tasks_only_syncing_one_way_over_caldav/) - I have a problem with the sync between NextCloud and the Tasks Android App which uses CalDAV. This i...
|
||||||
|
|
||||||
|
34. [Calendar tool returns hallucinated data when no calendar connected](https://github.com/openclaw/openclaw/issues/10190) - When using the calendar tool with no calendar integration configured, the tool returns fabricated/ha...
|
||||||
|
|
||||||
|
35. [[Bug]:Agent fabricates tool output for factual queries — calendar tool ...](https://github.com/openclaw/openclaw/issues/63289) - I am a non-developer end user running OpenClaw 2026.4.1 in a Lume VM, and that this was discovered w...
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
# caldav_crud.py — Konfigurationsvorlage
|
||||||
|
# Kopieren nach: ~/.openclaw/.env
|
||||||
|
# Berechtigungen setzen: chmod 600 ~/.openclaw/.env
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# CalDAV-Server (mailbox.org Standard — nicht ändern nötig)
|
||||||
|
MAILBOX_DAV_URL="https://dav.mailbox.org"
|
||||||
|
|
||||||
|
# Deine mailbox.org E-Mail-Adresse
|
||||||
|
MAILBOX_DAV_USERNAME="minitux@mailbox.org"
|
||||||
|
|
||||||
|
# App-Passwort (NICHT dein Haupt-Passwort!)
|
||||||
|
# Erstellen unter: mailbox.org → Einstellungen → Sicherheit → App-Passwörter
|
||||||
|
#
|
||||||
|
# EMPFEHLUNG: Dieses Feld leer lassen und stattdessen keyring verwenden:
|
||||||
|
# ./caldav_crud.py store-password
|
||||||
|
#
|
||||||
|
# Nur als Fallback (z.B. auf Headless-Servern ohne keyring-Backend):
|
||||||
|
MAILBOX_DAV_PASSWORD="dbba-guvm-perd-pdhq"
|
||||||
|
|
||||||
|
# Kalenderbezeichnung (optional)
|
||||||
|
# Leer lassen → ersten verfügbaren Kalender verwenden
|
||||||
|
# Exakter Name wie er in mailbox.org angezeigt wird (z.B. "Persönlich")
|
||||||
|
MAILBOX_DAV_CALENDAR=
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
# Sicherheitshinweise:
|
||||||
|
# • Nie diese Datei in git committen (.gitignore eintragen)
|
||||||
|
# • chmod 600 ~/.openclaw/.env (nur Owner lesbar)
|
||||||
|
# • App-Passwort in mailbox.org hat eingeschränkten Scope
|
||||||
|
# • Bevorzuge keyring für interaktive Umgebungen
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
name: caldav
|
||||||
|
description: "CRUD operations on mailbox.org CalDAV calendars. Use when: user asks to list, create, read, update or delete calendar events. NOT for: contacts (CardDAV), tasks/todos, or other CalDAV providers."
|
||||||
|
homepage: https://dav.mailbox.org
|
||||||
|
metadata:
|
||||||
|
{
|
||||||
|
"openclaw":
|
||||||
|
{
|
||||||
|
"emoji": "📅",
|
||||||
|
"requires": { "bins": ["python3"] },
|
||||||
|
"install":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "pip",
|
||||||
|
"kind": "run",
|
||||||
|
"label": "Install Python dependencies",
|
||||||
|
"run": "pip install caldav python-dotenv keyring vobject pytz",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
# CalDAV Skill (mailbox.org)
|
||||||
|
|
||||||
|
Lesen und Verwalten von Kalenderterminen via CalDAV auf mailbox.org.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
✅ **USE this skill when:**
|
||||||
|
|
||||||
|
- "Zeig mir meine Termine"
|
||||||
|
- "Was habe ich heute / diese Woche?"
|
||||||
|
- "Erstelle einen Termin für morgen um 10 Uhr"
|
||||||
|
- "Verschiebe meinen Termin auf Freitag"
|
||||||
|
- "Lösche den Termin Arztbesuch"
|
||||||
|
- "Ändere den Ort des Meetings"
|
||||||
|
|
||||||
|
## When NOT to Use
|
||||||
|
|
||||||
|
❌ **DON'T use this skill when:**
|
||||||
|
|
||||||
|
- Kontakte verwalten → CardDAV verwenden
|
||||||
|
- Aufgaben/Todos → separate Tasks-API
|
||||||
|
- Andere CalDAV-Anbieter (Google, Apple) → andere Skills
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Credentials in `~/.openclaw/.env` eintragen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MAILBOX_DAV_URL=https://dav.mailbox.org
|
||||||
|
MAILBOX_DAV_USERNAME=deine@adresse.mailbox.org
|
||||||
|
# Passwort besser via keyring:
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py store-password
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Alle Termine auflisten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Termin erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py create \
|
||||||
|
--summary "Titel" \
|
||||||
|
--start "2026-04-25T10:00" \
|
||||||
|
--end "2026-04-25T11:00" \
|
||||||
|
--description "Optionale Beschreibung" \
|
||||||
|
--location "Wien"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Termin lesen (Details)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py read --uid "<UID>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Termin aktualisieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Titel ändern
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py update --uid "<UID>" --summary "Neuer Titel"
|
||||||
|
|
||||||
|
# Zeit verschieben
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py update --uid "<UID>" \
|
||||||
|
--start "2026-04-26T14:00" \
|
||||||
|
--end "2026-04-26T15:00"
|
||||||
|
|
||||||
|
# Ort ändern
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py update --uid "<UID>" --location "Graz"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Termin löschen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mit Bestätigungsabfrage
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py delete --uid "<UID>"
|
||||||
|
|
||||||
|
# Ohne Bestätigung (für Agenten-Aufrufe)
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py delete --uid "<UID>" --force
|
||||||
|
```
|
||||||
|
|
||||||
|
### Passwort-Verwaltung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Einmalig im System-Schlüsselbund speichern (empfohlen)
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py store-password
|
||||||
|
|
||||||
|
# Passwort aus Schlüsselbund entfernen
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py delete-password
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow-Beispiele
|
||||||
|
|
||||||
|
**"Zeig mir meine Termine für heute"**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py list
|
||||||
|
# → Ausgabe filtern nach heutigem Datum
|
||||||
|
```
|
||||||
|
|
||||||
|
**"Erstelle morgen um 15 Uhr einen Termin Zahnarzt"**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py create \
|
||||||
|
--summary "Zahnarzt" \
|
||||||
|
--start "2026-04-24T15:00" \
|
||||||
|
--end "2026-04-24T16:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
**"Verschiebe den Termin Meeting auf Freitag 10 Uhr"**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Zuerst UID aus list ermitteln, dann:
|
||||||
|
~/.openclaw/skills/caldav/caldav_crud.py update \
|
||||||
|
--uid "<UID-aus-list>" \
|
||||||
|
--start "2026-04-25T10:00" \
|
||||||
|
--end "2026-04-25T11:00"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Authentifizierung: keyring (bevorzugt) → `.env` → interaktive Eingabe
|
||||||
|
- App-Passwort von mailbox.org verwenden, NICHT das Haupt-Passwort
|
||||||
|
- UID eines Events über `list` ermitteln
|
||||||
|
- `--verbose` Flag für Debug-Ausgabe verfügbar
|
||||||
@@ -0,0 +1,581 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
caldav_crud.py — Sichere CalDAV CRUD-Operationen für mailbox.org
|
||||||
|
=================================================================
|
||||||
|
Authentifizierung: python-keyring (bevorzugt) → .env-Datei → Umgebungsvariablen
|
||||||
|
|
||||||
|
Abhängigkeiten:
|
||||||
|
pip install caldav python-dotenv keyring vobject
|
||||||
|
|
||||||
|
Verwendung:
|
||||||
|
chmod +x caldav_crud.py
|
||||||
|
./caldav_crud.py --help
|
||||||
|
./caldav_crud.py list
|
||||||
|
./caldav_crud.py create --summary "Meeting" --start 2026-04-24T10:00 --end 2026-04-24T11:00
|
||||||
|
./caldav_crud.py read --uid <event-uid>
|
||||||
|
./caldav_crud.py update --uid <event-uid> --summary "Neuer Titel"
|
||||||
|
./caldav_crud.py delete --uid <event-uid>
|
||||||
|
./caldav_crud.py store-password # Passwort einmalig in keyring speichern
|
||||||
|
./caldav_crud.py delete-password # Passwort aus keyring entfernen
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# ── Drittanbieter-Importe mit Fehlerhinweis ──────────────────────────────────
|
||||||
|
try:
|
||||||
|
import caldav
|
||||||
|
except ImportError:
|
||||||
|
sys.exit("Fehlend: caldav → pip install caldav")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
except ImportError:
|
||||||
|
sys.exit("Fehlend: python-dotenv → pip install python-dotenv")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import keyring
|
||||||
|
import keyring.errors
|
||||||
|
HAS_KEYRING = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_KEYRING = False
|
||||||
|
print("Hinweis: keyring nicht verfügbar, falle zurück auf .env / Umgebungsvariablen.",
|
||||||
|
file=sys.stderr)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import vobject
|
||||||
|
except ImportError:
|
||||||
|
sys.exit("Fehlend: vobject → pip install vobject")
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pytz
|
||||||
|
except ImportError:
|
||||||
|
sys.exit("Fehlend: pytz → pip install pytz")
|
||||||
|
|
||||||
|
# ── Konstanten ───────────────────────────────────────────────────────────────
|
||||||
|
KEYRING_SERVICE = "mailbox_caldav"
|
||||||
|
KEYRING_USERNAME = "caldav_password"
|
||||||
|
ENV_FILE_PATHS = [
|
||||||
|
Path.home() / ".openclaw" / ".env", # OpenClaw-Standardpfad
|
||||||
|
Path(".env"), # Aktuelles Verzeichnis (Fallback)
|
||||||
|
]
|
||||||
|
DEFAULT_CALENDAR_NAME = None # None = ersten verfügbaren Kalender nutzen
|
||||||
|
|
||||||
|
# ── Logging ──────────────────────────────────────────────────────────────────
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.WARNING,
|
||||||
|
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||||
|
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||||
|
)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# Authentifizierung & Konfiguration
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def load_env() -> None:
|
||||||
|
"""Lädt .env-Datei aus bekannten Pfaden (erste gefundene gewinnt)."""
|
||||||
|
for path in ENV_FILE_PATHS:
|
||||||
|
if path.exists():
|
||||||
|
load_dotenv(dotenv_path=path, override=False)
|
||||||
|
log.info(f".env geladen von: {path}")
|
||||||
|
return
|
||||||
|
log.info("Keine .env-Datei gefunden — verwende reine Umgebungsvariablen.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_password(username: str) -> str:
|
||||||
|
"""
|
||||||
|
Passwort-Abruf-Reihenfolge (sicherste zuerst):
|
||||||
|
1. python-keyring (systemischer Schlüsselbund / libsecret)
|
||||||
|
2. Umgebungsvariable MAILBOX_DAV_PASSWORD (aus .env oder Shell)
|
||||||
|
3. Interaktive Eingabe (Fallback, kein Echo)
|
||||||
|
"""
|
||||||
|
# 1. Keyring
|
||||||
|
if HAS_KEYRING:
|
||||||
|
try:
|
||||||
|
password = keyring.get_password(KEYRING_SERVICE, KEYRING_USERNAME)
|
||||||
|
if password:
|
||||||
|
log.info("Passwort aus keyring geladen.")
|
||||||
|
return password
|
||||||
|
except keyring.errors.KeyringError as exc:
|
||||||
|
log.warning(f"Keyring-Fehler: {exc} — falle zurück auf .env")
|
||||||
|
|
||||||
|
# 2. Umgebungsvariable
|
||||||
|
password = os.getenv("MAILBOX_DAV_PASSWORD")
|
||||||
|
if password:
|
||||||
|
log.info("Passwort aus Umgebungsvariable MAILBOX_DAV_PASSWORD geladen.")
|
||||||
|
return password
|
||||||
|
|
||||||
|
# 3. Interaktive Eingabe
|
||||||
|
import getpass
|
||||||
|
print(f"Kein gespeichertes Passwort für '{username}' gefunden.")
|
||||||
|
print("Tipp: Einmalig speichern mit: ./caldav_crud.py store-password")
|
||||||
|
return getpass.getpass(f"App-Passwort für {username}: ")
|
||||||
|
|
||||||
|
|
||||||
|
def get_config() -> dict:
|
||||||
|
"""
|
||||||
|
Liest Konfiguration aus Umgebungsvariablen.
|
||||||
|
Priorität: keyring-Passwort > MAILBOX_DAV_PASSWORD > interaktiv.
|
||||||
|
"""
|
||||||
|
load_env()
|
||||||
|
|
||||||
|
url = os.getenv("MAILBOX_DAV_URL", "https://dav.mailbox.org")
|
||||||
|
username = os.getenv("MAILBOX_DAV_USERNAME", "")
|
||||||
|
calendar = os.getenv("MAILBOX_DAV_CALENDAR", "") # optional: Kalenderbezeichnung
|
||||||
|
|
||||||
|
if not username:
|
||||||
|
sys.exit(
|
||||||
|
"Fehler: MAILBOX_DAV_USERNAME ist nicht gesetzt.\n"
|
||||||
|
" → Setze ihn in ~/.openclaw/.env oder als Umgebungsvariable."
|
||||||
|
)
|
||||||
|
|
||||||
|
password = get_password(username)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"url": url,
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"calendar": calendar or None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# CalDAV-Verbindung
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def connect(cfg: dict) -> caldav.DAVClient:
|
||||||
|
"""Baut eine authentifizierte DAV-Verbindung auf."""
|
||||||
|
client = caldav.DAVClient(
|
||||||
|
url=cfg["url"],
|
||||||
|
username=cfg["username"],
|
||||||
|
password=cfg["password"],
|
||||||
|
)
|
||||||
|
# Verbindungstest
|
||||||
|
try:
|
||||||
|
client.principal()
|
||||||
|
log.info(f"Verbunden mit {cfg['url']} als {cfg['username']}")
|
||||||
|
except Exception as exc:
|
||||||
|
sys.exit(f"Verbindungsfehler: {exc}")
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
def get_calendar(client: caldav.DAVClient, calendar_name: str | None = None) -> caldav.Calendar:
|
||||||
|
"""
|
||||||
|
Gibt den gewünschten Kalender zurück.
|
||||||
|
Wenn calendar_name=None → ersten verfügbaren Kalender verwenden.
|
||||||
|
"""
|
||||||
|
principal = client.principal()
|
||||||
|
calendars = principal.calendars()
|
||||||
|
|
||||||
|
if not calendars:
|
||||||
|
sys.exit("Keine Kalender auf dem Server gefunden.")
|
||||||
|
|
||||||
|
if calendar_name:
|
||||||
|
for cal in calendars:
|
||||||
|
if cal.name and cal.name.lower() == calendar_name.lower():
|
||||||
|
return cal
|
||||||
|
available = ", ".join(c.name or "<unnamed>" for c in calendars)
|
||||||
|
sys.exit(
|
||||||
|
f"Kalender '{calendar_name}' nicht gefunden.\n"
|
||||||
|
f"Verfügbar: {available}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return calendars[0]
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# iCalendar-Hilfsfunktionen
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def _to_vobject_dt(dt: datetime) -> datetime:
|
||||||
|
"""
|
||||||
|
vobject kann stdlib timezone.utc nicht erkennen → in pytz.UTC konvertieren.
|
||||||
|
Alle anderen tz-Infos werden ebenfalls in pytz-Objekte überführt.
|
||||||
|
"""
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
return dt.replace(tzinfo=pytz.UTC)
|
||||||
|
if dt.tzinfo is timezone.utc or str(dt.tzinfo) == "UTC":
|
||||||
|
return dt.replace(tzinfo=pytz.UTC)
|
||||||
|
# Versuche, den IANA-Namen zu ermitteln (bei zoneinfo / pytz-Objekten)
|
||||||
|
try:
|
||||||
|
iana_name = dt.tzname()
|
||||||
|
if iana_name and iana_name != "UTC":
|
||||||
|
return dt.astimezone(pytz.timezone(iana_name))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return dt.replace(tzinfo=pytz.UTC)
|
||||||
|
|
||||||
|
|
||||||
|
def make_vcalendar(
|
||||||
|
uid: str,
|
||||||
|
summary: str,
|
||||||
|
start: datetime,
|
||||||
|
end: datetime,
|
||||||
|
description: str = "",
|
||||||
|
location: str = "",
|
||||||
|
) -> str:
|
||||||
|
"""Erstellt einen validen VCALENDAR-String (RFC 5545)."""
|
||||||
|
cal = vobject.iCalendar()
|
||||||
|
cal.add("prodid").value = "-//caldav_crud.py//mailbox.org//DE"
|
||||||
|
cal.add("version").value = "2.0"
|
||||||
|
|
||||||
|
event = cal.add("vevent")
|
||||||
|
event.add("uid").value = uid
|
||||||
|
event.add("summary").value = summary
|
||||||
|
event.add("dtstart").value = _to_vobject_dt(start)
|
||||||
|
event.add("dtend").value = _to_vobject_dt(end)
|
||||||
|
event.add("dtstamp").value = _to_vobject_dt(datetime.now(tz=timezone.utc))
|
||||||
|
if description:
|
||||||
|
event.add("description").value = description
|
||||||
|
if location:
|
||||||
|
event.add("location").value = location
|
||||||
|
|
||||||
|
return cal.serialize()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_datetime(value: str) -> datetime:
|
||||||
|
"""
|
||||||
|
Parst ISO-8601-Datetime-Strings flexibel.
|
||||||
|
Unterstützt: 2026-04-24T10:00, 2026-04-24T10:00:00, 2026-04-24T10:00+02:00
|
||||||
|
"""
|
||||||
|
formats = [
|
||||||
|
"%Y-%m-%dT%H:%M:%S%z",
|
||||||
|
"%Y-%m-%dT%H:%M%z",
|
||||||
|
"%Y-%m-%dT%H:%M:%S",
|
||||||
|
"%Y-%m-%dT%H:%M",
|
||||||
|
"%Y-%m-%d",
|
||||||
|
]
|
||||||
|
for fmt in formats:
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(value, fmt)
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
|
return dt
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
sys.exit(
|
||||||
|
f"Ungültiges Datum/Uhrzeit-Format: '{value}'\n"
|
||||||
|
f"Erwartet z.B.: 2026-04-24T10:00 oder 2026-04-24T10:00:00+02:00"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_event_info(event: caldav.Event) -> dict:
|
||||||
|
"""Extrahiert lesbare Felder aus einem CalDAV-Event-Objekt."""
|
||||||
|
try:
|
||||||
|
vcal = vobject.readOne(event.data)
|
||||||
|
vevent = vcal.vevent
|
||||||
|
return {
|
||||||
|
"uid": getattr(vevent, "uid", None) and vevent.uid.value,
|
||||||
|
"summary": getattr(vevent, "summary", None) and vevent.summary.value,
|
||||||
|
"dtstart": getattr(vevent, "dtstart", None) and str(vevent.dtstart.value),
|
||||||
|
"dtend": getattr(vevent, "dtend", None) and str(vevent.dtend.value),
|
||||||
|
"description": getattr(vevent, "description", None) and vevent.description.value,
|
||||||
|
"location": getattr(vevent, "location", None) and vevent.location.value,
|
||||||
|
}
|
||||||
|
except Exception as exc:
|
||||||
|
log.warning(f"Event-Parsing-Fehler: {exc}")
|
||||||
|
return {"raw": event.data}
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# CRUD-Operationen
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def cmd_list(args, cal: caldav.Calendar) -> None:
|
||||||
|
"""READ-ALL: Alle Events auflisten."""
|
||||||
|
results = cal.events()
|
||||||
|
if not results:
|
||||||
|
print("Keine Events im Kalender gefunden.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n{'UID':<38} {'Start':<22} Titel")
|
||||||
|
print("-" * 90)
|
||||||
|
for ev in results:
|
||||||
|
info = extract_event_info(ev)
|
||||||
|
uid = (info.get("uid") or "")[:36]
|
||||||
|
start = (info.get("dtstart") or "")[:22]
|
||||||
|
summary = (info.get("summary") or "<kein Titel>")[:50]
|
||||||
|
print(f"{uid:<38} {start:<22} {summary}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_create(args, cal: caldav.Calendar) -> None:
|
||||||
|
"""CREATE: Neues Event anlegen."""
|
||||||
|
if not args.summary:
|
||||||
|
sys.exit("Fehler: --summary ist Pflichtfeld für 'create'")
|
||||||
|
if not args.start:
|
||||||
|
sys.exit("Fehler: --start ist Pflichtfeld für 'create'")
|
||||||
|
if not args.end:
|
||||||
|
sys.exit("Fehler: --end ist Pflichtfeld für 'create'")
|
||||||
|
|
||||||
|
event_uid = args.uid or str(uuid.uuid4())
|
||||||
|
start_dt = parse_datetime(args.start)
|
||||||
|
end_dt = parse_datetime(args.end)
|
||||||
|
|
||||||
|
ical_data = make_vcalendar(
|
||||||
|
uid=event_uid,
|
||||||
|
summary=args.summary,
|
||||||
|
start=start_dt,
|
||||||
|
end=end_dt,
|
||||||
|
description=args.description or "",
|
||||||
|
location=args.location or "",
|
||||||
|
)
|
||||||
|
|
||||||
|
cal.save_event(ical_data)
|
||||||
|
print(f"✓ Event erstellt UID: {event_uid}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_read(args, cal: caldav.Calendar) -> None:
|
||||||
|
"""READ: Einzelnes Event anzeigen."""
|
||||||
|
if not args.uid:
|
||||||
|
sys.exit("Fehler: --uid ist Pflichtfeld für 'read'")
|
||||||
|
|
||||||
|
event = _find_event(cal, args.uid)
|
||||||
|
info = extract_event_info(event)
|
||||||
|
|
||||||
|
print(f"\n── Event ──────────────────────────────────")
|
||||||
|
for key, val in info.items():
|
||||||
|
if val:
|
||||||
|
print(f" {key:<12}: {val}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_update(args, cal: caldav.Calendar) -> None:
|
||||||
|
"""UPDATE: Vorhandenes Event bearbeiten."""
|
||||||
|
if not args.uid:
|
||||||
|
sys.exit("Fehler: --uid ist Pflichtfeld für 'update'")
|
||||||
|
|
||||||
|
event = _find_event(cal, args.uid)
|
||||||
|
vcal = vobject.readOne(event.data)
|
||||||
|
vevent = vcal.vevent
|
||||||
|
|
||||||
|
if args.summary:
|
||||||
|
vevent.summary.value = args.summary
|
||||||
|
if args.start:
|
||||||
|
vevent.dtstart.value = _to_vobject_dt(parse_datetime(args.start))
|
||||||
|
if args.end:
|
||||||
|
vevent.dtend.value = _to_vobject_dt(parse_datetime(args.end))
|
||||||
|
if args.description:
|
||||||
|
if hasattr(vevent, "description"):
|
||||||
|
vevent.description.value = args.description
|
||||||
|
else:
|
||||||
|
vevent.add("description").value = args.description
|
||||||
|
if args.location:
|
||||||
|
if hasattr(vevent, "location"):
|
||||||
|
vevent.location.value = args.location
|
||||||
|
else:
|
||||||
|
vevent.add("location").value = args.location
|
||||||
|
|
||||||
|
# DTSTAMP aktualisieren
|
||||||
|
vevent.dtstamp.value = _to_vobject_dt(datetime.now(tz=timezone.utc))
|
||||||
|
|
||||||
|
event.data = vcal.serialize()
|
||||||
|
event.save()
|
||||||
|
print(f"✓ Event aktualisiert UID: {args.uid}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_delete(args, cal: caldav.Calendar) -> None:
|
||||||
|
"""DELETE: Event löschen."""
|
||||||
|
if not args.uid:
|
||||||
|
sys.exit("Fehler: --uid ist Pflichtfeld für 'delete'")
|
||||||
|
|
||||||
|
event = _find_event(cal, args.uid)
|
||||||
|
|
||||||
|
if not args.force:
|
||||||
|
info = extract_event_info(event)
|
||||||
|
print(f"Event zum Löschen: {info.get('summary', '<kein Titel>')} ({args.uid})")
|
||||||
|
confirm = input("Wirklich löschen? [j/N] ").strip().lower()
|
||||||
|
if confirm not in ("j", "ja", "y", "yes"):
|
||||||
|
print("Abgebrochen.")
|
||||||
|
return
|
||||||
|
|
||||||
|
event.delete()
|
||||||
|
print(f"✓ Event gelöscht UID: {args.uid}")
|
||||||
|
|
||||||
|
|
||||||
|
def _find_event(cal: caldav.Calendar, uid: str) -> caldav.Event:
|
||||||
|
"""Sucht ein Event anhand seiner UID — wirft SystemExit wenn nicht gefunden."""
|
||||||
|
results = cal.events()
|
||||||
|
for ev in results:
|
||||||
|
info = extract_event_info(ev)
|
||||||
|
if info.get("uid") == uid:
|
||||||
|
return ev
|
||||||
|
# Fallback: direkte URL-basierte Suche via search
|
||||||
|
try:
|
||||||
|
events = cal.search(uid=uid, event=True)
|
||||||
|
if events:
|
||||||
|
return events[0]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
sys.exit(f"Event mit UID '{uid}' nicht gefunden.")
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# Keyring-Verwaltung
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def cmd_store_password(args) -> None:
|
||||||
|
"""Speichert das App-Passwort einmalig im System-Schlüsselbund."""
|
||||||
|
if not HAS_KEYRING:
|
||||||
|
sys.exit("Fehler: python-keyring ist nicht installiert.\n"
|
||||||
|
" → pip install keyring")
|
||||||
|
import getpass
|
||||||
|
load_env()
|
||||||
|
username = os.getenv("MAILBOX_DAV_USERNAME", "")
|
||||||
|
if not username:
|
||||||
|
username = input("mailbox.org Benutzername (E-Mail): ").strip()
|
||||||
|
|
||||||
|
password = getpass.getpass(f"App-Passwort für '{username}' (wird nicht angezeigt): ")
|
||||||
|
if not password:
|
||||||
|
sys.exit("Abgebrochen — kein Passwort eingegeben.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
keyring.set_password(KEYRING_SERVICE, KEYRING_USERNAME, password)
|
||||||
|
print(f"✓ Passwort sicher im System-Schlüsselbund gespeichert.")
|
||||||
|
print(f" Service : {KEYRING_SERVICE}")
|
||||||
|
print(f" Username : {KEYRING_USERNAME}")
|
||||||
|
print(f" Hinweis : MAILBOX_DAV_PASSWORD in .env kann jetzt entfernt werden.")
|
||||||
|
except keyring.errors.KeyringError as exc:
|
||||||
|
sys.exit(f"Keyring-Fehler: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_delete_password(args) -> None:
|
||||||
|
"""Entfernt das gespeicherte Passwort aus dem System-Schlüsselbund."""
|
||||||
|
if not HAS_KEYRING:
|
||||||
|
sys.exit("Fehler: python-keyring ist nicht installiert.")
|
||||||
|
try:
|
||||||
|
keyring.delete_password(KEYRING_SERVICE, KEYRING_USERNAME)
|
||||||
|
print(f"✓ Passwort aus keyring entfernt.")
|
||||||
|
except keyring.errors.PasswordDeleteError:
|
||||||
|
print("Kein Passwort im keyring gespeichert.")
|
||||||
|
except keyring.errors.KeyringError as exc:
|
||||||
|
sys.exit(f"Keyring-Fehler: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# CLI-Argument-Parser
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="caldav_crud.py",
|
||||||
|
description="Sichere CalDAV CRUD-Operationen für mailbox.org",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Beispiele:
|
||||||
|
./caldav_crud.py store-password
|
||||||
|
./caldav_crud.py list
|
||||||
|
./caldav_crud.py create --summary "Arzttermin" --start 2026-04-25T09:00 --end 2026-04-25T10:00
|
||||||
|
./caldav_crud.py read --uid 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
./caldav_crud.py update --uid 550e8400-e29b-41d4-a716-446655440000 --summary "Verschoben"
|
||||||
|
./caldav_crud.py delete --uid 550e8400-e29b-41d4-a716-446655440000 --force
|
||||||
|
|
||||||
|
Umgebungsvariablen (.env oder Shell):
|
||||||
|
MAILBOX_DAV_URL CalDAV-Server (Standard: https://dav.mailbox.org)
|
||||||
|
MAILBOX_DAV_USERNAME E-Mail-Adresse oder App-Benutzername
|
||||||
|
MAILBOX_DAV_PASSWORD App-Passwort (besser: keyring verwenden)
|
||||||
|
MAILBOX_DAV_CALENDAR Kalenderbezeichnung (optional, Standard: erster Kalender)
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--verbose",
|
||||||
|
action="store_true",
|
||||||
|
help="Ausführliche Ausgabe (DEBUG-Level)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--calendar",
|
||||||
|
metavar="NAME",
|
||||||
|
help="Kalenderbezeichnung (überschreibt MAILBOX_DAV_CALENDAR)"
|
||||||
|
)
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest="command", metavar="BEFEHL")
|
||||||
|
subparsers.required = True
|
||||||
|
|
||||||
|
# ── list ──────────────────────────────────────────────────────────────
|
||||||
|
subparsers.add_parser("list", help="Alle Events auflisten")
|
||||||
|
|
||||||
|
# ── create ────────────────────────────────────────────────────────────
|
||||||
|
p_create = subparsers.add_parser("create", help="Neues Event anlegen")
|
||||||
|
p_create.add_argument("--summary", required=True, help="Titel des Events")
|
||||||
|
p_create.add_argument("--start", required=True, help="Start (ISO-8601, z.B. 2026-04-25T10:00)")
|
||||||
|
p_create.add_argument("--end", required=True, help="Ende (ISO-8601)")
|
||||||
|
p_create.add_argument("--description", default="", help="Beschreibung (optional)")
|
||||||
|
p_create.add_argument("--location", default="", help="Ort (optional)")
|
||||||
|
p_create.add_argument("--uid", default=None, help="Eigene UID (Standard: auto-generiert)")
|
||||||
|
|
||||||
|
# ── read ──────────────────────────────────────────────────────────────
|
||||||
|
p_read = subparsers.add_parser("read", help="Einzelnes Event anzeigen")
|
||||||
|
p_read.add_argument("--uid", required=True, help="UID des Events")
|
||||||
|
|
||||||
|
# ── update ────────────────────────────────────────────────────────────
|
||||||
|
p_update = subparsers.add_parser("update", help="Vorhandenes Event bearbeiten")
|
||||||
|
p_update.add_argument("--uid", required=True, help="UID des Events")
|
||||||
|
p_update.add_argument("--summary", default=None, help="Neuer Titel")
|
||||||
|
p_update.add_argument("--start", default=None, help="Neuer Start (ISO-8601)")
|
||||||
|
p_update.add_argument("--end", default=None, help="Neues Ende (ISO-8601)")
|
||||||
|
p_update.add_argument("--description", default=None, help="Neue Beschreibung")
|
||||||
|
p_update.add_argument("--location", default=None, help="Neuer Ort")
|
||||||
|
|
||||||
|
# ── delete ────────────────────────────────────────────────────────────
|
||||||
|
p_delete = subparsers.add_parser("delete", help="Event löschen")
|
||||||
|
p_delete.add_argument("--uid", required=True, help="UID des Events")
|
||||||
|
p_delete.add_argument("--force", action="store_true", help="Ohne Bestätigungsabfrage löschen")
|
||||||
|
|
||||||
|
# ── Passwort-Verwaltung ───────────────────────────────────────────────
|
||||||
|
subparsers.add_parser("store-password", help="App-Passwort sicher im Schlüsselbund speichern")
|
||||||
|
subparsers.add_parser("delete-password", help="Passwort aus Schlüsselbund entfernen")
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# Einstiegspunkt
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = build_parser()
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Passwort-Verwaltung braucht keine CalDAV-Verbindung
|
||||||
|
if args.command == "store-password":
|
||||||
|
cmd_store_password(args)
|
||||||
|
return
|
||||||
|
if args.command == "delete-password":
|
||||||
|
cmd_delete_password(args)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Konfiguration laden & verbinden
|
||||||
|
cfg = get_config()
|
||||||
|
|
||||||
|
# --calendar überschreibt Env-Variable
|
||||||
|
if args.calendar:
|
||||||
|
cfg["calendar"] = args.calendar
|
||||||
|
|
||||||
|
client = connect(cfg)
|
||||||
|
cal = get_calendar(client, cfg.get("calendar"))
|
||||||
|
|
||||||
|
# CRUD-Dispatcher
|
||||||
|
dispatch = {
|
||||||
|
"list": cmd_list,
|
||||||
|
"create": cmd_create,
|
||||||
|
"read": cmd_read,
|
||||||
|
"update": cmd_update,
|
||||||
|
"delete": cmd_delete,
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch[args.command](args, cal)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
# caldav_crud.py — Konfigurationsvorlage
|
||||||
|
# Kopieren nach: ~/.openclaw/.env
|
||||||
|
# Berechtigungen setzen: chmod 600 ~/.openclaw/.env
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# CalDAV-Server (mailbox.org Standard — nicht ändern nötig)
|
||||||
|
MAILBOX_DAV_URL=https://dav.mailbox.org
|
||||||
|
|
||||||
|
# Deine mailbox.org E-Mail-Adresse
|
||||||
|
MAILBOX_DAV_USERNAME=deine-adresse@mailbox.org
|
||||||
|
|
||||||
|
# App-Passwort (NICHT dein Haupt-Passwort!)
|
||||||
|
# Erstellen unter: mailbox.org → Einstellungen → Sicherheit → App-Passwörter
|
||||||
|
#
|
||||||
|
# EMPFEHLUNG: Dieses Feld leer lassen und stattdessen keyring verwenden:
|
||||||
|
# ./caldav_crud.py store-password
|
||||||
|
#
|
||||||
|
# Nur als Fallback (z.B. auf Headless-Servern ohne keyring-Backend):
|
||||||
|
MAILBOX_DAV_PASSWORD=
|
||||||
|
|
||||||
|
# Kalenderbezeichnung (optional)
|
||||||
|
# Leer lassen → ersten verfügbaren Kalender verwenden
|
||||||
|
# Exakter Name wie er in mailbox.org angezeigt wird (z.B. "Persönlich")
|
||||||
|
MAILBOX_DAV_CALENDAR=
|
||||||
|
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
|
# Sicherheitshinweise:
|
||||||
|
# • Nie diese Datei in git committen (.gitignore eintragen)
|
||||||
|
# • chmod 600 ~/.openclaw/.env (nur Owner lesbar)
|
||||||
|
# • App-Passwort in mailbox.org hat eingeschränkten Scope
|
||||||
|
# • Bevorzuge keyring für interaktive Umgebungen
|
||||||
|
# ══════════════════════════════════════════════════════
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,303 @@
|
|||||||
|
# openclaw-mailbox-cal
|
||||||
|
|
||||||
|
**OpenClaw Skill-Repository** — mailbox.org Kalender und Aufgaben auf dem Raspberry Pi
|
||||||
|
Telegram-Bot-Befehle für Kalendertermine (khal), Tasks (todoman) und automatischen CalDAV-Sync (vdirsyncer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. .env befüllen
|
||||||
|
cp assets/.env.example .env && nano .env
|
||||||
|
|
||||||
|
# 2. Installation
|
||||||
|
chmod +x scripts/install.sh && bash scripts/install.sh
|
||||||
|
|
||||||
|
# 3. Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
openclaw-mailbox-cal/
|
||||||
|
├── SKILL.md ← OpenClaw Skill-Definition
|
||||||
|
├── README.md ← Diese Datei
|
||||||
|
├── assets/
|
||||||
|
│ └── .env.example ← Umgebungsvariablen-Vorlage
|
||||||
|
├── config/
|
||||||
|
│ ├── vdirsyncer.conf ← vdirsyncer CalDAV-Konfiguration
|
||||||
|
│ ├── khal.conf ← khal Kalender-Konfiguration
|
||||||
|
│ └── todoman.conf ← todoman Aufgaben-Konfiguration
|
||||||
|
├── scripts/
|
||||||
|
│ ├── install.sh ← Installations-Script (Raspi)
|
||||||
|
│ ├── test_setup.sh ← Selbsttest aller Komponenten
|
||||||
|
│ └── openclaw_skill.py ← OpenClaw/Telegram Bot Handler
|
||||||
|
└── systemd/
|
||||||
|
├── vdirsyncer-sync.service ← systemd Service Unit
|
||||||
|
├── vdirsyncer-sync.timer ← systemd Timer (alle 15 Min.)
|
||||||
|
└── vdirsyncer-sync.env ← Umgebungsvariablen für systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup-Schritte (detailliert)
|
||||||
|
|
||||||
|
### Schritt 1 — Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm (64-bit empfohlen) oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto
|
||||||
|
- App-Passwort in mailbox.org anlegen:
|
||||||
|
`Einstellungen → Sicherheit → App-Passwörter → Neu erstellen`
|
||||||
|
- OpenClaw installiert: [openclaw.dev](https://openclaw.dev)
|
||||||
|
- Telegram Bot-Token via [@BotFather](https://t.me/BotFather)
|
||||||
|
|
||||||
|
### Schritt 2 — .env konfigurieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Mindestens diese Werte setzen:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
MAILBOX_USER=vorname.nachname@mailbox.org
|
||||||
|
MAILBOX_PASS=app-passwort-xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3 — install.sh ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script installiert automatisch:
|
||||||
|
- `vdirsyncer` — CalDAV-Sync-Tool
|
||||||
|
- `khal` — CLI-Kalender
|
||||||
|
- `todoman` — CLI-Aufgabenverwaltung
|
||||||
|
- `pass` — GNU Password Store
|
||||||
|
- `python3-keyring` — Python-Keyring-Integration
|
||||||
|
- `gettext-base` — envsubst für Konfigurationen
|
||||||
|
|
||||||
|
Anschließend:
|
||||||
|
- Alle Konfigurationsdateien werden deployed
|
||||||
|
- systemd User-Timer wird aktiviert
|
||||||
|
- Initialer Sync wird durchgeführt
|
||||||
|
|
||||||
|
### Schritt 4 — OpenClaw Skill registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills **automatisch** wenn sie in einem der Skill-Verzeichnisse liegen.
|
||||||
|
Kein manueller Eintrag in `openclaw.json` nötig — das `install.sh` legt bereits einen Symlink an.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manuell (falls install.sh den Symlink nicht angelegt hat):
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional — Zugangsdaten direkt in `~/.openclaw/openclaw.json` hinterlegen
|
||||||
|
(Alternative zur `.env`-Datei). **Immer zuerst Backup anlegen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
Dann im `"skills"`-Block den `"entries"`-Unterblock ergänzen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach jeder Änderung an `openclaw.json` validieren und Gateway neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw doctor --fix
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5 — Telegram testen
|
||||||
|
|
||||||
|
Sende an deinen Bot:
|
||||||
|
```
|
||||||
|
/termine
|
||||||
|
/aufgaben
|
||||||
|
/sync
|
||||||
|
/status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
### Vollständiger Selbsttest
|
||||||
|
```bash
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### vdirsyncer
|
||||||
|
```bash
|
||||||
|
# Verbindung testen und Sammlungen entdecken
|
||||||
|
vdirsyncer discover calendars
|
||||||
|
vdirsyncer discover tasks
|
||||||
|
|
||||||
|
# Synchronisation manuell starten
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Nur Kalender synchronisieren
|
||||||
|
vdirsyncer sync calendars
|
||||||
|
|
||||||
|
# Verbose-Ausgabe
|
||||||
|
vdirsyncer -v DEBUG sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal
|
||||||
|
```bash
|
||||||
|
# Termine heute
|
||||||
|
khal list
|
||||||
|
|
||||||
|
# Termine nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Agenda-Ansicht
|
||||||
|
khal agenda
|
||||||
|
|
||||||
|
# Interaktiver Kalender
|
||||||
|
ikhal
|
||||||
|
|
||||||
|
# Ereignis hinzufügen (interaktiv)
|
||||||
|
khal new
|
||||||
|
|
||||||
|
# Ereignis direkt anlegen
|
||||||
|
khal new 2025-06-15 14:00 15:00 "Arzttermin" --calendar mailbox_kalender
|
||||||
|
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
|
||||||
|
# Konfiguration anzeigen
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### todoman
|
||||||
|
```bash
|
||||||
|
# Alle offenen Aufgaben
|
||||||
|
todo list
|
||||||
|
|
||||||
|
# Alle Aufgaben inkl. erledigt
|
||||||
|
todo list --all
|
||||||
|
|
||||||
|
# Neue Aufgabe
|
||||||
|
todo new "Bericht schreiben"
|
||||||
|
|
||||||
|
# Aufgabe mit Fälligkeitsdatum
|
||||||
|
todo new --due 2025-06-30 "Bericht abgeben"
|
||||||
|
|
||||||
|
# Aufgabe mit Priorität (1=hoch, 9=niedrig)
|
||||||
|
todo new --priority 1 "Dringend: Server patchen"
|
||||||
|
|
||||||
|
# Aufgabe als erledigt markieren (ID aus 'todo list')
|
||||||
|
todo done 1
|
||||||
|
|
||||||
|
# Aufgabe löschen
|
||||||
|
todo delete 2
|
||||||
|
|
||||||
|
# Aufgaben-Listen anzeigen
|
||||||
|
todo lists
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
```bash
|
||||||
|
# Timer-Status
|
||||||
|
systemctl --user status vdirsyncer-sync.timer
|
||||||
|
|
||||||
|
# Service manuell starten (einmaliger Sync)
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
|
||||||
|
# Alle User-Timer anzeigen
|
||||||
|
systemctl --user list-timers
|
||||||
|
|
||||||
|
# Live-Log verfolgen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f
|
||||||
|
|
||||||
|
# Log der letzten 50 Zeilen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -n 50
|
||||||
|
|
||||||
|
# Timer deaktivieren
|
||||||
|
systemctl --user disable --now vdirsyncer-sync.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python-Skill direkt testen
|
||||||
|
```bash
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## mailbox.org CalDAV-Endpunkte
|
||||||
|
|
||||||
|
| Zweck | URL |
|
||||||
|
|---|---|
|
||||||
|
| CalDAV-Basis | `https://dav.mailbox.org/caldav/<USER>/` |
|
||||||
|
| Kalender-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Kalender/` |
|
||||||
|
| Aufgaben-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Aufgaben/` |
|
||||||
|
| CardDAV (Kontakte) | `https://dav.mailbox.org/carddav/<USER>/` |
|
||||||
|
|
||||||
|
Entdeckung aller Sammlungen:
|
||||||
|
```bash
|
||||||
|
curl -u "USER:PASS" -X PROPFIND https://dav.mailbox.org/caldav/ \
|
||||||
|
-H "Depth: 1" -H "Content-Type: application/xml"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
| Maßnahme | Status |
|
||||||
|
|---|---|
|
||||||
|
| App-Passwort statt Hauptpasswort | ✅ Empfohlen |
|
||||||
|
| vdirsyncer-Config: chmod 600 | ✅ Automatisch via install.sh |
|
||||||
|
| .env nicht in Git | ✅ In .gitignore eingetragen |
|
||||||
|
| HTTPS für CalDAV | ✅ dav.mailbox.org erzwingt TLS |
|
||||||
|
| pass(1)-Integration | Optional via `pass` + `python3-keyring` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
**vdirsyncer: `[Errno 111] Connection refused`**
|
||||||
|
→ Netzwerkverbindung prüfen: `ping dav.mailbox.org`
|
||||||
|
|
||||||
|
**vdirsyncer: `Conflict!`**
|
||||||
|
→ Beide Seiten wurden geändert. Konflikt manuell lösen oder `conflict_resolution = "b wins"` in vdirsyncer.conf setzen.
|
||||||
|
|
||||||
|
**khal: `No calendars configured`**
|
||||||
|
→ `khal rebuild-cache` ausführen; Pfad in `~/.config/khal/config` prüfen.
|
||||||
|
|
||||||
|
**todoman: `Error: No lists found`**
|
||||||
|
→ `path`-Eintrag in `~/.config/todoman/config.cfg` prüfen; Verzeichnis `~/.local/share/vdirsyncer/tasks/` muss existieren und `.ics`-Dateien enthalten.
|
||||||
|
|
||||||
|
**systemd-Timer läuft nach Reboot nicht**
|
||||||
|
→ `sudo loginctl enable-linger $USER` ausführen (User-Session ohne Login).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
MIT — Freie Nutzung, Veränderung und Weitergabe.
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
---
|
||||||
|
name: mailbox-cal
|
||||||
|
description: "OpenClaw Telegram-Bot-Skill für mailbox.org Kalender und Aufgaben auf dem Raspberry Pi. Synchronisiert CalDAV-Kalender und VTODO-Aufgaben via vdirsyncer, zeigt Termine mit khal und verwaltet Tasks mit todoman. Befehle: /termine, /aufgaben, /neu_aufgabe, /sync, /status. Lade diesen Skill wenn: der Nutzer Kalender-Einträge oder Aufgaben aus mailbox.org per Telegram abfragen, anlegen oder synchronisieren möchte."
|
||||||
|
license: MIT
|
||||||
|
compatibility: "Raspberry Pi OS Bookworm / Ubuntu 22.04+ ARM. Benötigt: vdirsyncer >= 0.19, khal >= 0.11, todoman >= 4.3, python3 >= 3.10. mailbox.org CalDAV-Zugang erforderlich."
|
||||||
|
metadata:
|
||||||
|
author: lonely.wolf.64
|
||||||
|
version: '1.0'
|
||||||
|
platform: Raspberry Pi
|
||||||
|
caldav_server: mailbox.org
|
||||||
|
---
|
||||||
|
|
||||||
|
# mailbox.org Kalender + Aufgaben Skill
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieser OpenClaw-Skill synchronisiert deinen mailbox.org-Kalender und deine Aufgaben über CalDAV auf einen Raspberry Pi und stellt sie über Telegram-Bot-Befehle bereit. Die Synchronisation läuft automatisch alle 15 Minuten via systemd-Timer.
|
||||||
|
|
||||||
|
**Architektur:**
|
||||||
|
```
|
||||||
|
mailbox.org (CalDAV)
|
||||||
|
│
|
||||||
|
vdirsyncer (Sync)
|
||||||
|
│
|
||||||
|
┌───┴───────────┐
|
||||||
|
│ │
|
||||||
|
khal todoman
|
||||||
|
(Kalender) (Aufgaben)
|
||||||
|
│ │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
openclaw_skill.py
|
||||||
|
│
|
||||||
|
Telegram Bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unterstützte Befehle
|
||||||
|
|
||||||
|
| Telegram-Befehl | Beschreibung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| `/termine` | Heutige Termine | `/termine heute` |
|
||||||
|
| `/termine morgen` | Morgige Termine | `/termine morgen` |
|
||||||
|
| `/termine woche` | Termine nächste 7 Tage | `/termine woche` |
|
||||||
|
| `/aufgaben` | Offene Aufgaben | `/aufgaben` |
|
||||||
|
| `/aufgaben alle` | Alle Aufgaben inkl. erledigt | `/aufgaben alle` |
|
||||||
|
| `/neu_aufgabe` | Neue Aufgabe anlegen | `/neu_aufgabe Arzt anrufen` |
|
||||||
|
| `/sync` | Manuellen Sync auslösen | `/sync` |
|
||||||
|
| `/status` | Sync-Timer-Status | `/status` |
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto mit CalDAV-Zugang
|
||||||
|
- OpenClaw installiert und konfiguriert
|
||||||
|
- Telegram Bot-Token (via @BotFather)
|
||||||
|
- Internetverbindung vom Raspberry Pi
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Repository klonen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/DEIN_USER/openclaw-mailbox-cal.git
|
||||||
|
cd openclaw-mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. .env befüllen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
# MAILBOX_USER und MAILBOX_PASS setzen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Empfehlung:** Lege in mailbox.org ein App-Passwort an:
|
||||||
|
Einstellungen → Sicherheit → App-Passwörter → Neu erstellen
|
||||||
|
|
||||||
|
### 3. Installation ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script führt folgende Schritte aus:
|
||||||
|
1. Pakete installieren: `vdirsyncer khal todoman pass python3-keyring gettext-base`
|
||||||
|
2. Konfigurationsdateien deployen (mit envsubst)
|
||||||
|
3. systemd User-Timer aktivieren
|
||||||
|
4. Initialen Sync durchführen
|
||||||
|
5. khal-Datenbank aufbauen
|
||||||
|
6. OpenClaw Skill verknüpfen
|
||||||
|
|
||||||
|
### 4. Skill in OpenClaw registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills automatisch aus diesen Verzeichnissen (höchste Priorität zuerst):
|
||||||
|
- `<workspace>/skills/`
|
||||||
|
- `~/.openclaw/skills/`
|
||||||
|
- bundled skills
|
||||||
|
|
||||||
|
Skill-Verzeichnis ins OpenClaw-Skills-Verzeichnis kopieren oder verlinken:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Option A: Symlink (empfohlen — Updates wirken sofort)
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
|
||||||
|
# Option B: Kopieren
|
||||||
|
cp -r ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional: Skill in `~/.openclaw/openclaw.json` aktivieren/konfigurieren
|
||||||
|
(nur nötig für env-Variablen oder explizites enable/disable):
|
||||||
|
|
||||||
|
```json
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Unbekannte Keys lassen den OpenClaw-Gateway nicht starten.
|
||||||
|
Vor dem Bearbeiten immer ein Backup anlegen:
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
# Nach der Bearbeitung validieren:
|
||||||
|
openclaw doctor --fix
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach Gateway neu starten:
|
||||||
|
```bash
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
Nach der Installation Setup validieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vollständiger Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
|
||||||
|
# Einzeltests
|
||||||
|
vdirsyncer sync # Sync testen
|
||||||
|
khal list today tomorrow # Termine heute + morgen
|
||||||
|
khal list --format "{start} {title}" 2025-01-01 2025-12-31 # Jahresübersicht
|
||||||
|
todo list # Aufgaben anzeigen
|
||||||
|
todo list --all # Alle inkl. erledigt
|
||||||
|
todo new "Test-Aufgabe" # Aufgabe anlegen
|
||||||
|
|
||||||
|
# systemd Timer
|
||||||
|
systemctl --user status vdirsyncer-sync.timer # Timer-Status
|
||||||
|
systemctl --user list-timers # Alle Timer
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f # Live-Log
|
||||||
|
|
||||||
|
# Python-Skill direkt testen (ohne OpenClaw/Telegram)
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfigurationsdateien
|
||||||
|
|
||||||
|
| Datei | Ziel-Pfad | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `config/vdirsyncer.conf` | `~/.config/vdirsyncer/config` | CalDAV-Sync-Konfiguration |
|
||||||
|
| `config/khal.conf` | `~/.config/khal/config` | Kalender-Anzeige-Konfiguration |
|
||||||
|
| `config/todoman.conf` | `~/.config/todoman/config.cfg` | Aufgaben-Konfiguration |
|
||||||
|
| `systemd/vdirsyncer-sync.service` | `~/.config/systemd/user/` | Sync-Service Unit |
|
||||||
|
| `systemd/vdirsyncer-sync.timer` | `~/.config/systemd/user/` | 15-Minuten-Timer |
|
||||||
|
| `scripts/openclaw_skill.py` | (in-place) | OpenClaw/Telegram-Integration |
|
||||||
|
|
||||||
|
## Sicherheitshinweise
|
||||||
|
|
||||||
|
- Die vdirsyncer-Config (`~/.config/vdirsyncer/config`) enthält das Passwort — Berechtigungen sind `chmod 600`
|
||||||
|
- Verwende ein mailbox.org App-Passwort statt des Hauptpassworts
|
||||||
|
- Für produktive Umgebungen: `pass`-Integration (GNU Password Store) via `pass` + `python3-keyring`
|
||||||
|
- `.env`-Datei niemals in Git committen (ist in `.gitignore` eingetragen)
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
### vdirsyncer discover schlägt fehl
|
||||||
|
```bash
|
||||||
|
# Verbindung testen
|
||||||
|
curl -u "USER:PASS" https://dav.mailbox.org/caldav/
|
||||||
|
# SSL-Zertifikate prüfen
|
||||||
|
openssl s_client -connect dav.mailbox.org:443
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal zeigt keine Termine
|
||||||
|
```bash
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
# Config validieren
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd-Timer läuft nicht
|
||||||
|
```bash
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
sudo loginctl enable-linger pi
|
||||||
|
# Timer manuell starten
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Todoman findet keine Aufgaben
|
||||||
|
```bash
|
||||||
|
# Pfad in config prüfen
|
||||||
|
cat ~/.config/todoman/config.cfg
|
||||||
|
# Verzeichnis prüfen
|
||||||
|
ls ~/.local/share/vdirsyncer/tasks/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Erweiterungen
|
||||||
|
|
||||||
|
- **Mehrere Kalender:** Weitere `[[kalender_name]]`-Sektionen in `khal.conf` hinzufügen
|
||||||
|
- **Push-Sync:** mailbox.org unterstützt CalDAV-Push — XMPP-Trigger via `vdirsyncer`
|
||||||
|
- **Erinnerungen:** Integration mit `khal-remind` oder `at`-Daemon
|
||||||
|
- **Verschlüsselung:** `.env` via `gpg-agent` verschlüsseln
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Secrets — niemals committen!
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Editor
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
/logs/
|
||||||
|
|
||||||
|
# vdirsyncer Status (lokal)
|
||||||
|
status/
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
# openclaw-mailbox-cal
|
||||||
|
|
||||||
|
**OpenClaw Skill-Repository** — mailbox.org Kalender und Aufgaben auf dem Raspberry Pi
|
||||||
|
Telegram-Bot-Befehle für Kalendertermine (khal), Tasks (todoman) und automatischen CalDAV-Sync (vdirsyncer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. .env befüllen
|
||||||
|
cp assets/.env.example .env && nano .env
|
||||||
|
|
||||||
|
# 2. Installation
|
||||||
|
chmod +x scripts/install.sh && bash scripts/install.sh
|
||||||
|
|
||||||
|
# 3. Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
openclaw-mailbox-cal/
|
||||||
|
├── SKILL.md ← OpenClaw Skill-Definition
|
||||||
|
├── README.md ← Diese Datei
|
||||||
|
├── assets/
|
||||||
|
│ └── .env.example ← Umgebungsvariablen-Vorlage
|
||||||
|
├── config/
|
||||||
|
│ ├── vdirsyncer.conf ← vdirsyncer CalDAV-Konfiguration
|
||||||
|
│ ├── khal.conf ← khal Kalender-Konfiguration
|
||||||
|
│ └── todoman.conf ← todoman Aufgaben-Konfiguration
|
||||||
|
├── scripts/
|
||||||
|
│ ├── install.sh ← Installations-Script (Raspi)
|
||||||
|
│ ├── test_setup.sh ← Selbsttest aller Komponenten
|
||||||
|
│ └── openclaw_skill.py ← OpenClaw/Telegram Bot Handler
|
||||||
|
└── systemd/
|
||||||
|
├── vdirsyncer-sync.service ← systemd Service Unit
|
||||||
|
├── vdirsyncer-sync.timer ← systemd Timer (alle 15 Min.)
|
||||||
|
└── vdirsyncer-sync.env ← Umgebungsvariablen für systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup-Schritte (detailliert)
|
||||||
|
|
||||||
|
### Schritt 1 — Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm (64-bit empfohlen) oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto
|
||||||
|
- App-Passwort in mailbox.org anlegen:
|
||||||
|
`Einstellungen → Sicherheit → App-Passwörter → Neu erstellen`
|
||||||
|
- OpenClaw installiert: [openclaw.dev](https://openclaw.dev)
|
||||||
|
- Telegram Bot-Token via [@BotFather](https://t.me/BotFather)
|
||||||
|
|
||||||
|
### Schritt 2 — .env konfigurieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Mindestens diese Werte setzen:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
MAILBOX_USER=vorname.nachname@mailbox.org
|
||||||
|
MAILBOX_PASS=app-passwort-xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3 — install.sh ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script installiert automatisch:
|
||||||
|
- `vdirsyncer` — CalDAV-Sync-Tool
|
||||||
|
- `khal` — CLI-Kalender
|
||||||
|
- `todoman` — CLI-Aufgabenverwaltung
|
||||||
|
- `python3-pass` — Passwort-Store-Integration
|
||||||
|
- `gettext-base` — envsubst für Konfigurationen
|
||||||
|
|
||||||
|
Anschließend:
|
||||||
|
- Alle Konfigurationsdateien werden deployed
|
||||||
|
- systemd User-Timer wird aktiviert
|
||||||
|
- Initialer Sync wird durchgeführt
|
||||||
|
|
||||||
|
### Schritt 4 — OpenClaw Skill registrieren
|
||||||
|
|
||||||
|
Füge in `~/.config/openclaw/config.yaml` hinzu:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
skills:
|
||||||
|
- name: mailbox-cal
|
||||||
|
path: ~/openclaw-mailbox-cal
|
||||||
|
entry: scripts/openclaw_skill.py
|
||||||
|
commands:
|
||||||
|
- termine
|
||||||
|
- aufgaben
|
||||||
|
- neu_aufgabe
|
||||||
|
- sync
|
||||||
|
- status
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5 — Telegram testen
|
||||||
|
|
||||||
|
Sende an deinen Bot:
|
||||||
|
```
|
||||||
|
/termine
|
||||||
|
/aufgaben
|
||||||
|
/sync
|
||||||
|
/status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
### Vollständiger Selbsttest
|
||||||
|
```bash
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### vdirsyncer
|
||||||
|
```bash
|
||||||
|
# Verbindung testen und Sammlungen entdecken
|
||||||
|
vdirsyncer discover calendars
|
||||||
|
vdirsyncer discover tasks
|
||||||
|
|
||||||
|
# Synchronisation manuell starten
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Nur Kalender synchronisieren
|
||||||
|
vdirsyncer sync calendars
|
||||||
|
|
||||||
|
# Verbose-Ausgabe
|
||||||
|
vdirsyncer -v DEBUG sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal
|
||||||
|
```bash
|
||||||
|
# Termine heute
|
||||||
|
khal list
|
||||||
|
|
||||||
|
# Termine nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Agenda-Ansicht
|
||||||
|
khal agenda
|
||||||
|
|
||||||
|
# Interaktiver Kalender
|
||||||
|
ikhal
|
||||||
|
|
||||||
|
# Ereignis hinzufügen (interaktiv)
|
||||||
|
khal new
|
||||||
|
|
||||||
|
# Ereignis direkt anlegen
|
||||||
|
khal new 2025-06-15 14:00 15:00 "Arzttermin" --calendar mailbox_kalender
|
||||||
|
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
|
||||||
|
# Konfiguration anzeigen
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### todoman
|
||||||
|
```bash
|
||||||
|
# Alle offenen Aufgaben
|
||||||
|
todo list
|
||||||
|
|
||||||
|
# Alle Aufgaben inkl. erledigt
|
||||||
|
todo list --all
|
||||||
|
|
||||||
|
# Neue Aufgabe
|
||||||
|
todo new "Bericht schreiben"
|
||||||
|
|
||||||
|
# Aufgabe mit Fälligkeitsdatum
|
||||||
|
todo new --due 2025-06-30 "Bericht abgeben"
|
||||||
|
|
||||||
|
# Aufgabe mit Priorität (1=hoch, 9=niedrig)
|
||||||
|
todo new --priority 1 "Dringend: Server patchen"
|
||||||
|
|
||||||
|
# Aufgabe als erledigt markieren (ID aus 'todo list')
|
||||||
|
todo done 1
|
||||||
|
|
||||||
|
# Aufgabe löschen
|
||||||
|
todo delete 2
|
||||||
|
|
||||||
|
# Aufgaben-Listen anzeigen
|
||||||
|
todo lists
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
```bash
|
||||||
|
# Timer-Status
|
||||||
|
systemctl --user status vdirsyncer-sync.timer
|
||||||
|
|
||||||
|
# Service manuell starten (einmaliger Sync)
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
|
||||||
|
# Alle User-Timer anzeigen
|
||||||
|
systemctl --user list-timers
|
||||||
|
|
||||||
|
# Live-Log verfolgen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f
|
||||||
|
|
||||||
|
# Log der letzten 50 Zeilen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -n 50
|
||||||
|
|
||||||
|
# Timer deaktivieren
|
||||||
|
systemctl --user disable --now vdirsyncer-sync.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python-Skill direkt testen
|
||||||
|
```bash
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## mailbox.org CalDAV-Endpunkte
|
||||||
|
|
||||||
|
| Zweck | URL |
|
||||||
|
|---|---|
|
||||||
|
| CalDAV-Basis | `https://dav.mailbox.org/caldav/<USER>/` |
|
||||||
|
| Kalender-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Kalender/` |
|
||||||
|
| Aufgaben-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Aufgaben/` |
|
||||||
|
| CardDAV (Kontakte) | `https://dav.mailbox.org/carddav/<USER>/` |
|
||||||
|
|
||||||
|
Entdeckung aller Sammlungen:
|
||||||
|
```bash
|
||||||
|
curl -u "USER:PASS" -X PROPFIND https://dav.mailbox.org/caldav/ \
|
||||||
|
-H "Depth: 1" -H "Content-Type: application/xml"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
| Maßnahme | Status |
|
||||||
|
|---|---|
|
||||||
|
| App-Passwort statt Hauptpasswort | ✅ Empfohlen |
|
||||||
|
| vdirsyncer-Config: chmod 600 | ✅ Automatisch via install.sh |
|
||||||
|
| .env nicht in Git | ✅ In .gitignore eingetragen |
|
||||||
|
| HTTPS für CalDAV | ✅ dav.mailbox.org erzwingt TLS |
|
||||||
|
| pass(1)-Integration | Optional via python3-pass |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
**vdirsyncer: `[Errno 111] Connection refused`**
|
||||||
|
→ Netzwerkverbindung prüfen: `ping dav.mailbox.org`
|
||||||
|
|
||||||
|
**vdirsyncer: `Conflict!`**
|
||||||
|
→ Beide Seiten wurden geändert. Konflikt manuell lösen oder `conflict_resolution = "b wins"` in vdirsyncer.conf setzen.
|
||||||
|
|
||||||
|
**khal: `No calendars configured`**
|
||||||
|
→ `khal rebuild-cache` ausführen; Pfad in `~/.config/khal/config` prüfen.
|
||||||
|
|
||||||
|
**todoman: `Error: No lists found`**
|
||||||
|
→ `path`-Eintrag in `~/.config/todoman/config.cfg` prüfen; Verzeichnis `~/.local/share/vdirsyncer/tasks/` muss existieren und `.ics`-Dateien enthalten.
|
||||||
|
|
||||||
|
**systemd-Timer läuft nach Reboot nicht**
|
||||||
|
→ `sudo loginctl enable-linger $USER` ausführen (User-Session ohne Login).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
MIT — Freie Nutzung, Veränderung und Weitergabe.
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
---
|
||||||
|
name: mailbox-cal
|
||||||
|
description: "OpenClaw Telegram-Bot-Skill für mailbox.org Kalender und Aufgaben auf dem Raspberry Pi. Synchronisiert CalDAV-Kalender und VTODO-Aufgaben via vdirsyncer, zeigt Termine mit khal und verwaltet Tasks mit todoman. Befehle: /termine, /aufgaben, /neu_aufgabe, /sync, /status. Lade diesen Skill wenn: der Nutzer Kalender-Einträge oder Aufgaben aus mailbox.org per Telegram abfragen, anlegen oder synchronisieren möchte."
|
||||||
|
license: MIT
|
||||||
|
compatibility: "Raspberry Pi OS Bookworm / Ubuntu 22.04+ ARM. Benötigt: vdirsyncer >= 0.19, khal >= 0.11, todoman >= 4.3, python3 >= 3.10. mailbox.org CalDAV-Zugang erforderlich."
|
||||||
|
metadata:
|
||||||
|
author: lonely.wolf.64
|
||||||
|
version: '1.0'
|
||||||
|
platform: Raspberry Pi
|
||||||
|
caldav_server: mailbox.org
|
||||||
|
---
|
||||||
|
|
||||||
|
# mailbox.org Kalender + Aufgaben Skill
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieser OpenClaw-Skill synchronisiert deinen mailbox.org-Kalender und deine Aufgaben über CalDAV auf einen Raspberry Pi und stellt sie über Telegram-Bot-Befehle bereit. Die Synchronisation läuft automatisch alle 15 Minuten via systemd-Timer.
|
||||||
|
|
||||||
|
**Architektur:**
|
||||||
|
```
|
||||||
|
mailbox.org (CalDAV)
|
||||||
|
│
|
||||||
|
vdirsyncer (Sync)
|
||||||
|
│
|
||||||
|
┌───┴───────────┐
|
||||||
|
│ │
|
||||||
|
khal todoman
|
||||||
|
(Kalender) (Aufgaben)
|
||||||
|
│ │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
openclaw_skill.py
|
||||||
|
│
|
||||||
|
Telegram Bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unterstützte Befehle
|
||||||
|
|
||||||
|
| Telegram-Befehl | Beschreibung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| `/termine` | Heutige Termine | `/termine heute` |
|
||||||
|
| `/termine morgen` | Morgige Termine | `/termine morgen` |
|
||||||
|
| `/termine woche` | Termine nächste 7 Tage | `/termine woche` |
|
||||||
|
| `/aufgaben` | Offene Aufgaben | `/aufgaben` |
|
||||||
|
| `/aufgaben alle` | Alle Aufgaben inkl. erledigt | `/aufgaben alle` |
|
||||||
|
| `/neu_aufgabe` | Neue Aufgabe anlegen | `/neu_aufgabe Arzt anrufen` |
|
||||||
|
| `/sync` | Manuellen Sync auslösen | `/sync` |
|
||||||
|
| `/status` | Sync-Timer-Status | `/status` |
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto mit CalDAV-Zugang
|
||||||
|
- OpenClaw installiert und konfiguriert
|
||||||
|
- Telegram Bot-Token (via @BotFather)
|
||||||
|
- Internetverbindung vom Raspberry Pi
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Repository klonen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/DEIN_USER/openclaw-mailbox-cal.git
|
||||||
|
cd openclaw-mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. .env befüllen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
# MAILBOX_USER und MAILBOX_PASS setzen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Empfehlung:** Lege in mailbox.org ein App-Passwort an:
|
||||||
|
Einstellungen → Sicherheit → App-Passwörter → Neu erstellen
|
||||||
|
|
||||||
|
### 3. Installation ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script führt folgende Schritte aus:
|
||||||
|
1. Pakete installieren: `vdirsyncer khal todoman python3-pass gettext-base`
|
||||||
|
2. Konfigurationsdateien deployen (mit envsubst)
|
||||||
|
3. systemd User-Timer aktivieren
|
||||||
|
4. Initialen Sync durchführen
|
||||||
|
5. khal-Datenbank aufbauen
|
||||||
|
6. OpenClaw Skill verknüpfen
|
||||||
|
|
||||||
|
### 4. Skill in OpenClaw registrieren
|
||||||
|
|
||||||
|
In OpenClaw-Konfiguration (`~/.config/openclaw/config.yaml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
skills:
|
||||||
|
- name: mailbox-cal
|
||||||
|
path: /home/pi/openclaw-mailbox-cal
|
||||||
|
entry: scripts/openclaw_skill.py
|
||||||
|
commands:
|
||||||
|
- termine
|
||||||
|
- aufgaben
|
||||||
|
- neu_aufgabe
|
||||||
|
- sync
|
||||||
|
- status
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach OpenClaw neu starten:
|
||||||
|
```bash
|
||||||
|
systemctl --user restart openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
Nach der Installation Setup validieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vollständiger Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
|
||||||
|
# Einzeltests
|
||||||
|
vdirsyncer sync # Sync testen
|
||||||
|
khal list today tomorrow # Termine heute + morgen
|
||||||
|
khal list --format "{start} {title}" 2025-01-01 2025-12-31 # Jahresübersicht
|
||||||
|
todo list # Aufgaben anzeigen
|
||||||
|
todo list --all # Alle inkl. erledigt
|
||||||
|
todo new "Test-Aufgabe" # Aufgabe anlegen
|
||||||
|
|
||||||
|
# systemd Timer
|
||||||
|
systemctl --user status vdirsyncer-sync.timer # Timer-Status
|
||||||
|
systemctl --user list-timers # Alle Timer
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f # Live-Log
|
||||||
|
|
||||||
|
# Python-Skill direkt testen (ohne OpenClaw/Telegram)
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfigurationsdateien
|
||||||
|
|
||||||
|
| Datei | Ziel-Pfad | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `config/vdirsyncer.conf` | `~/.config/vdirsyncer/config` | CalDAV-Sync-Konfiguration |
|
||||||
|
| `config/khal.conf` | `~/.config/khal/config` | Kalender-Anzeige-Konfiguration |
|
||||||
|
| `config/todoman.conf` | `~/.config/todoman/config.cfg` | Aufgaben-Konfiguration |
|
||||||
|
| `systemd/vdirsyncer-sync.service` | `~/.config/systemd/user/` | Sync-Service Unit |
|
||||||
|
| `systemd/vdirsyncer-sync.timer` | `~/.config/systemd/user/` | 15-Minuten-Timer |
|
||||||
|
| `scripts/openclaw_skill.py` | (in-place) | OpenClaw/Telegram-Integration |
|
||||||
|
|
||||||
|
## Sicherheitshinweise
|
||||||
|
|
||||||
|
- Die vdirsyncer-Config (`~/.config/vdirsyncer/config`) enthält das Passwort — Berechtigungen sind `chmod 600`
|
||||||
|
- Verwende ein mailbox.org App-Passwort statt des Hauptpassworts
|
||||||
|
- Für produktive Umgebungen: `pass`-Integration (GNU Password Store) via `python3-pass`
|
||||||
|
- `.env`-Datei niemals in Git committen (ist in `.gitignore` eingetragen)
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
### vdirsyncer discover schlägt fehl
|
||||||
|
```bash
|
||||||
|
# Verbindung testen
|
||||||
|
curl -u "USER:PASS" https://dav.mailbox.org/caldav/
|
||||||
|
# SSL-Zertifikate prüfen
|
||||||
|
openssl s_client -connect dav.mailbox.org:443
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal zeigt keine Termine
|
||||||
|
```bash
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
# Config validieren
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd-Timer läuft nicht
|
||||||
|
```bash
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
sudo loginctl enable-linger pi
|
||||||
|
# Timer manuell starten
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Todoman findet keine Aufgaben
|
||||||
|
```bash
|
||||||
|
# Pfad in config prüfen
|
||||||
|
cat ~/.config/todoman/config.cfg
|
||||||
|
# Verzeichnis prüfen
|
||||||
|
ls ~/.local/share/vdirsyncer/tasks/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Erweiterungen
|
||||||
|
|
||||||
|
- **Mehrere Kalender:** Weitere `[[kalender_name]]`-Sektionen in `khal.conf` hinzufügen
|
||||||
|
- **Push-Sync:** mailbox.org unterstützt CalDAV-Push — XMPP-Trigger via `vdirsyncer`
|
||||||
|
- **Erinnerungen:** Integration mit `khal-remind` oder `at`-Daemon
|
||||||
|
- **Verschlüsselung:** `.env` via `gpg-agent` verschlüsseln
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# ============================================================
|
||||||
|
# OpenClaw mailbox.org Kalender + Aufgaben — Umgebungsvariablen
|
||||||
|
# Kopiere diese Datei nach .env und passe die Werte an.
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# --- mailbox.org Zugangsdaten ---
|
||||||
|
MAILBOX_USER=dein.name@mailbox.org
|
||||||
|
MAILBOX_PASS=dein_passwort_hier
|
||||||
|
|
||||||
|
# Optional: App-Passwort statt Hauptpasswort (empfohlen)
|
||||||
|
# Unter mailbox.org → Einstellungen → Sicherheit → App-Passwörter erstellen
|
||||||
|
# MAILBOX_PASS=app-passwort-xyzabc
|
||||||
|
|
||||||
|
# --- Kalender-Konfiguration ---
|
||||||
|
# Basis-URL des CalDAV-Servers von mailbox.org
|
||||||
|
CALDAV_BASE_URL=https://dav.mailbox.org/caldav/
|
||||||
|
|
||||||
|
# Name des primären Kalenders (wie in mailbox.org angelegt)
|
||||||
|
PRIMARY_CALENDAR=Kalender
|
||||||
|
|
||||||
|
# Name des Aufgaben-Kalenders (VTODO)
|
||||||
|
TASKS_CALENDAR=Aufgaben
|
||||||
|
|
||||||
|
# --- Sync-Intervall (für systemd-Timer) ---
|
||||||
|
# Format: systemd OnCalendar — z. B. *:0/15 = alle 15 Minuten
|
||||||
|
SYNC_INTERVAL=*:0/15
|
||||||
|
|
||||||
|
# --- Lokale Verzeichnisse ---
|
||||||
|
VDIR_BASE_DIR=/home/pi/.local/share/vdirsyncer
|
||||||
|
KHAL_DB_PATH=/home/pi/.local/share/khal/db.db
|
||||||
|
|
||||||
|
# --- OpenClaw / Telegram ---
|
||||||
|
OPENCLAW_SKILL_NAME=mailbox-cal
|
||||||
|
# Telegram Bot Token (aus BotFather)
|
||||||
|
TELEGRAM_BOT_TOKEN=123456789:AABBCCDDEEFFaabbccddeeff1234567890
|
||||||
|
|
||||||
|
# --- Logging ---
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
LOG_FILE=/var/log/openclaw-mailbox-cal/sync.log
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# =============================================================
|
||||||
|
# khal Konfiguration
|
||||||
|
# Pfad: ~/.config/khal/config
|
||||||
|
# Dokumentation: https://lostpackets.de/khal/
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
[calendars]
|
||||||
|
|
||||||
|
# Primärer Kalender (VEVENT)
|
||||||
|
[[mailbox_kalender]]
|
||||||
|
path = ~/.local/share/vdirsyncer/calendars/Kalender/
|
||||||
|
color = light cyan
|
||||||
|
readonly = False
|
||||||
|
|
||||||
|
# Weitere Kalender-Sammlungen — füge bei Bedarf weitere hinzu
|
||||||
|
# [[mailbox_privat]]
|
||||||
|
# path = ~/.local/share/vdirsyncer/calendars/Privat/
|
||||||
|
# color = light blue
|
||||||
|
# readonly = False
|
||||||
|
|
||||||
|
|
||||||
|
[sqlite]
|
||||||
|
# SQLite-Datenbank für khal-Index
|
||||||
|
path = ~/.local/share/khal/db.db
|
||||||
|
|
||||||
|
|
||||||
|
[locale]
|
||||||
|
# Zeitzone und Spracheinstellungen (Österreich/Deutschland)
|
||||||
|
local_timezone = Europe/Vienna
|
||||||
|
default_timezone = Europe/Vienna
|
||||||
|
|
||||||
|
# Datumsformat: DD.MM.YYYY (deutsches Format)
|
||||||
|
dateformat = %d.%m.%Y
|
||||||
|
|
||||||
|
# Zeitformat: HH:MM (24h)
|
||||||
|
timeformat = %H:%M
|
||||||
|
|
||||||
|
# Datum+Zeit-Format
|
||||||
|
datetimeformat = %d.%m.%Y %H:%M
|
||||||
|
|
||||||
|
# Wochenbeginn: Montag
|
||||||
|
firstweekday = 0
|
||||||
|
|
||||||
|
# Sprache (für Ausgabe)
|
||||||
|
# khal nutzt die System-Locale; stelle sicher, dass de_AT.UTF-8 installiert ist:
|
||||||
|
# sudo locale-gen de_AT.UTF-8
|
||||||
|
|
||||||
|
|
||||||
|
[view]
|
||||||
|
# Standard-Ansicht beim Starten von khal
|
||||||
|
default_calendar = mailbox_kalender
|
||||||
|
|
||||||
|
# Agenda-Vorschau: Tage in die Zukunft
|
||||||
|
days = 7
|
||||||
|
|
||||||
|
# Farbige Ausgabe
|
||||||
|
color = True
|
||||||
|
|
||||||
|
# Zeilenumbruch bei langen Titeln
|
||||||
|
event_format = {title} [{calendar}]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# =============================================================
|
||||||
|
# todoman Konfiguration
|
||||||
|
# Pfad: ~/.config/todoman/config.cfg
|
||||||
|
# Dokumentation: https://todoman.readthedocs.io/
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
[main]
|
||||||
|
|
||||||
|
# Verzeichnis mit den synchronisierten VTODO-Dateien (vdirsyncer output)
|
||||||
|
path = ~/.local/share/vdirsyncer/tasks/*
|
||||||
|
|
||||||
|
# Standard-Liste für neue Aufgaben (muss einem Unterverzeichnis entsprechen)
|
||||||
|
default_list = Aufgaben
|
||||||
|
|
||||||
|
# Datum/Zeit-Format (deutsches Format)
|
||||||
|
date_format = %d.%m.%Y
|
||||||
|
time_format = %H:%M
|
||||||
|
datetime_format = %d.%m.%Y %H:%M
|
||||||
|
|
||||||
|
# Farbige Ausgabe aktivieren
|
||||||
|
color = auto
|
||||||
|
|
||||||
|
# Abgeschlossene Aufgaben ausblenden (true = nur offene anzeigen)
|
||||||
|
# Kann via CLI mit --all überschrieben werden
|
||||||
|
humanize = true
|
||||||
|
|
||||||
|
# Priorität anzeigen
|
||||||
|
show_private = true
|
||||||
|
|
||||||
|
# Dateierweiterung für VTODO-Dateien
|
||||||
|
# (muss mit vdirsyncer fileext übereinstimmen)
|
||||||
|
# fileext = .ics ← Standard, nicht ändern
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# =============================================================
|
||||||
|
# vdirsyncer Konfiguration für mailbox.org (CalDAV)
|
||||||
|
# Pfad: ~/.config/vdirsyncer/config
|
||||||
|
#
|
||||||
|
# Ersetze MAILBOX_USER / MAILBOX_PASS durch echte Werte
|
||||||
|
# oder nutze das install.sh-Script, das .env einliest und
|
||||||
|
# diese Datei mit envsubst befüllt.
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# Statusverzeichnis für vdirsyncer-Metadaten
|
||||||
|
status_path = "~/.local/share/vdirsyncer/status/"
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# STORAGE: Lokale CalDAV-Verzeichnisse (filesystem)
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[storage local_calendars]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/calendars/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
[storage local_tasks]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/tasks/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# STORAGE: mailbox.org CalDAV-Server (Remote)
|
||||||
|
#
|
||||||
|
# mailbox.org CalDAV-Endpunkte:
|
||||||
|
# Kalender: https://dav.mailbox.org/caldav/<USERNAME>/
|
||||||
|
# Aufgaben: https://dav.mailbox.org/caldav/<USERNAME>/Aufgaben/
|
||||||
|
#
|
||||||
|
# Für die Entdeckung aller Sammlungen:
|
||||||
|
# vdirsyncer discover calendars
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[storage remote_calendars]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER}/"
|
||||||
|
username = "${MAILBOX_USER}"
|
||||||
|
password = "${MAILBOX_PASS}"
|
||||||
|
|
||||||
|
# Nur VEVENT-Einträge (Kalender-Ereignisse)
|
||||||
|
item_types = ["VEVENT"]
|
||||||
|
|
||||||
|
[storage remote_tasks]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER}/"
|
||||||
|
username = "${MAILBOX_USER}"
|
||||||
|
password = "${MAILBOX_PASS}"
|
||||||
|
|
||||||
|
# Nur VTODO-Einträge (Aufgaben / Tasks)
|
||||||
|
item_types = ["VTODO"]
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# PAIR: Kalender-Synchronisierung
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
[pair calendars]
|
||||||
|
a = "local_calendars"
|
||||||
|
b = "remote_calendars"
|
||||||
|
collections = ["from b"]
|
||||||
|
|
||||||
|
# Konflikt-Lösung: Remote gewinnt (mailbox.org ist die Quelle der Wahrheit)
|
||||||
|
conflict_resolution = "b wins"
|
||||||
|
|
||||||
|
# Metadaten-Sync (Kalender-Farbe, Anzeigename)
|
||||||
|
metadata = ["color", "displayname"]
|
||||||
|
|
||||||
|
[pair tasks]
|
||||||
|
a = "local_tasks"
|
||||||
|
b = "remote_tasks"
|
||||||
|
collections = ["from b"]
|
||||||
|
conflict_resolution = "b wins"
|
||||||
|
metadata = ["color", "displayname"]
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# install.sh — OpenClaw mailbox.org Kalender + Aufgaben
|
||||||
|
# Zielplattform: Raspberry Pi (Raspberry Pi OS / Ubuntu ARM)
|
||||||
|
# Ausführen als: pi-Nutzer (sudo-Rechte erforderlich)
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
ENV_FILE="$REPO_ROOT/.env"
|
||||||
|
|
||||||
|
# --- Farben ---
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 1. .env laden
|
||||||
|
# =============================================================
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
warn ".env nicht gefunden — kopiere .env.example nach .env"
|
||||||
|
cp "$REPO_ROOT/assets/.env.example" "$ENV_FILE"
|
||||||
|
error "Bitte .env mit deinen mailbox.org-Zugangsdaten befüllen und install.sh erneut ausführen."
|
||||||
|
fi
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$ENV_FILE"
|
||||||
|
info ".env geladen (Benutzer: ${MAILBOX_USER:-NICHT GESETZT})"
|
||||||
|
|
||||||
|
[[ -z "${MAILBOX_USER:-}" ]] && error "MAILBOX_USER in .env nicht gesetzt!"
|
||||||
|
[[ -z "${MAILBOX_PASS:-}" ]] && error "MAILBOX_PASS in .env nicht gesetzt!"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 2. Systempakete installieren
|
||||||
|
# =============================================================
|
||||||
|
info "Installiere Systempakete..."
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y \
|
||||||
|
vdirsyncer \
|
||||||
|
khal \
|
||||||
|
todoman \
|
||||||
|
python3-pass \
|
||||||
|
gettext-base \
|
||||||
|
curl \
|
||||||
|
jq \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv
|
||||||
|
|
||||||
|
# Locale sicherstellen
|
||||||
|
sudo locale-gen de_AT.UTF-8 2>/dev/null || true
|
||||||
|
info "Pakete installiert."
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 3. Verzeichnisse anlegen
|
||||||
|
# =============================================================
|
||||||
|
info "Lege Verzeichnisse an..."
|
||||||
|
|
||||||
|
VDIR_BASE="${VDIR_BASE_DIR:-$HOME/.local/share/vdirsyncer}"
|
||||||
|
mkdir -p "$VDIR_BASE/status"
|
||||||
|
mkdir -p "$VDIR_BASE/calendars"
|
||||||
|
mkdir -p "$VDIR_BASE/tasks"
|
||||||
|
mkdir -p "${KHAL_DB_PATH:-$HOME/.local/share/khal}"
|
||||||
|
mkdir -p "$HOME/.config/vdirsyncer"
|
||||||
|
mkdir -p "$HOME/.config/khal"
|
||||||
|
mkdir -p "$HOME/.config/todoman"
|
||||||
|
sudo mkdir -p /var/log/openclaw-mailbox-cal
|
||||||
|
sudo chown "$USER:$USER" /var/log/openclaw-mailbox-cal
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 4. Konfigurationsdateien deployen (envsubst für Variablen)
|
||||||
|
# =============================================================
|
||||||
|
info "Deploye Konfigurationsdateien..."
|
||||||
|
|
||||||
|
# vdirsyncer config — Variablen ersetzen
|
||||||
|
export MAILBOX_USER MAILBOX_PASS
|
||||||
|
envsubst '${MAILBOX_USER} ${MAILBOX_PASS}' \
|
||||||
|
< "$REPO_ROOT/config/vdirsyncer.conf" \
|
||||||
|
> "$HOME/.config/vdirsyncer/config"
|
||||||
|
chmod 600 "$HOME/.config/vdirsyncer/config"
|
||||||
|
info " → ~/.config/vdirsyncer/config"
|
||||||
|
|
||||||
|
# khal config
|
||||||
|
cp "$REPO_ROOT/config/khal.conf" "$HOME/.config/khal/config"
|
||||||
|
info " → ~/.config/khal/config"
|
||||||
|
|
||||||
|
# todoman config
|
||||||
|
cp "$REPO_ROOT/config/todoman.conf" "$HOME/.config/todoman/config.cfg"
|
||||||
|
info " → ~/.config/todoman/config.cfg"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 5. systemd Units installieren (User-Session)
|
||||||
|
# =============================================================
|
||||||
|
info "Installiere systemd Units (User-Session)..."
|
||||||
|
|
||||||
|
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
|
||||||
|
mkdir -p "$SYSTEMD_USER_DIR"
|
||||||
|
|
||||||
|
# Variablen in systemd-Units ersetzen
|
||||||
|
for unit_file in "$REPO_ROOT/systemd/"*.{service,timer}; do
|
||||||
|
[[ -f "$unit_file" ]] || continue
|
||||||
|
unit_name="$(basename "$unit_file")"
|
||||||
|
envsubst '${SYNC_INTERVAL}' < "$unit_file" > "$SYSTEMD_USER_DIR/$unit_name"
|
||||||
|
info " → $SYSTEMD_USER_DIR/$unit_name"
|
||||||
|
done
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now vdirsyncer-sync.timer
|
||||||
|
info "systemd Timer aktiviert."
|
||||||
|
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
loginctl enable-linger "$USER" 2>/dev/null || \
|
||||||
|
warn "loginctl enable-linger fehlgeschlagen — bitte manuell ausführen: sudo loginctl enable-linger $USER"
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 6. Erst-Synchronisation
|
||||||
|
# =============================================================
|
||||||
|
info "Führe initialen vdirsyncer discover + sync durch..."
|
||||||
|
vdirsyncer discover calendars || warn "discover calendars hatte Warnungen — prüfe Ausgabe"
|
||||||
|
vdirsyncer discover tasks || warn "discover tasks hatte Warnungen — prüfe Ausgabe"
|
||||||
|
vdirsyncer sync || warn "sync hatte Warnungen — prüfe Ausgabe"
|
||||||
|
info "Erst-Sync abgeschlossen."
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 7. khal-Index aufbauen
|
||||||
|
# =============================================================
|
||||||
|
info "Baue khal-Index auf..."
|
||||||
|
khal --config "$HOME/.config/khal/config" rebuild-cache || true
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# 8. OpenClaw Skill-Verknüpfung
|
||||||
|
# =============================================================
|
||||||
|
OPENCLAW_SKILLS_DIR="${OPENCLAW_SKILLS_DIR:-$HOME/.config/openclaw/skills}"
|
||||||
|
if [[ -d "$OPENCLAW_SKILLS_DIR" ]]; then
|
||||||
|
info "Verknüpfe OpenClaw Skill..."
|
||||||
|
ln -sf "$REPO_ROOT" "$OPENCLAW_SKILLS_DIR/mailbox-cal" 2>/dev/null || true
|
||||||
|
info " → $OPENCLAW_SKILLS_DIR/mailbox-cal"
|
||||||
|
else
|
||||||
|
warn "OpenClaw Skills-Verzeichnis nicht gefunden ($OPENCLAW_SKILLS_DIR)."
|
||||||
|
warn "Bitte Skill manuell in OpenClaw registrieren (siehe README.md)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================
|
||||||
|
# Fertig
|
||||||
|
# =============================================================
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${GREEN}║ Installation abgeschlossen! ║${NC}"
|
||||||
|
echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Nächste Schritte:"
|
||||||
|
echo " 1. khal agenda # Termine anzeigen"
|
||||||
|
echo " 2. todo list # Aufgaben anzeigen"
|
||||||
|
echo " 3. systemctl --user status vdirsyncer-sync.timer"
|
||||||
|
echo " 4. tail -f /var/log/openclaw-mailbox-cal/sync.log"
|
||||||
|
echo ""
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
openclaw_skill.py — OpenClaw Telegram-Bot-Skill: mailbox.org Kalender + Aufgaben
|
||||||
|
Registrierung in OpenClaw: /register skill mailbox-cal
|
||||||
|
|
||||||
|
Unterstützte Befehle (via Telegram-Chat an den Bot):
|
||||||
|
/termine [heute|morgen|woche] — Kalender-Ereignisse anzeigen
|
||||||
|
/aufgaben [offen|alle] — Aufgabenliste anzeigen
|
||||||
|
/neu_aufgabe <Text> — Neue Aufgabe anlegen
|
||||||
|
/sync — manuellen vdirsyncer-Sync auslösen
|
||||||
|
/status — Sync-Status abfragen
|
||||||
|
|
||||||
|
Voraussetzungen:
|
||||||
|
- khal, todoman, vdirsyncer installiert (install.sh ausgeführt)
|
||||||
|
- OpenClaw mit Python-Skill-API >= 0.4
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, date, timedelta
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Hilfsfunktionen
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _run(cmd: list[str], timeout: int = 30) -> tuple[str, str, int]:
|
||||||
|
"""Führt einen Subprozess aus und gibt (stdout, stderr, returncode) zurück."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
return result.stdout.strip(), result.stderr.strip(), result.returncode
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "", "Timeout nach {}s".format(timeout), 1
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
return "", str(e), 127
|
||||||
|
|
||||||
|
|
||||||
|
def _khal_agenda(days: int = 1, start: Optional[date] = None) -> str:
|
||||||
|
"""Gibt khal-Agenda als String zurück."""
|
||||||
|
start_str = (start or date.today()).strftime("%Y-%m-%d")
|
||||||
|
end_str = (date.today() + timedelta(days=days)).strftime("%Y-%m-%d")
|
||||||
|
stdout, stderr, rc = _run(
|
||||||
|
["khal", "list", "--format", "{start-time} {title}", start_str, end_str]
|
||||||
|
)
|
||||||
|
if rc != 0:
|
||||||
|
logger.warning("khal list Fehler: %s", stderr)
|
||||||
|
return "⚠️ khal Fehler: " + stderr
|
||||||
|
return stdout or "Keine Termine gefunden."
|
||||||
|
|
||||||
|
|
||||||
|
def _todo_list(all_tasks: bool = False) -> str:
|
||||||
|
"""Gibt todoman-Aufgabenliste als String zurück."""
|
||||||
|
cmd = ["todo", "list"]
|
||||||
|
if all_tasks:
|
||||||
|
cmd.append("--all")
|
||||||
|
stdout, stderr, rc = _run(cmd)
|
||||||
|
if rc != 0:
|
||||||
|
return "⚠️ todoman Fehler: " + stderr
|
||||||
|
return stdout or "Keine Aufgaben."
|
||||||
|
|
||||||
|
|
||||||
|
def _todo_new(title: str) -> str:
|
||||||
|
"""Legt eine neue Aufgabe an."""
|
||||||
|
if not title.strip():
|
||||||
|
return "⚠️ Kein Aufgabentitel angegeben."
|
||||||
|
stdout, stderr, rc = _run(["todo", "new", "--", title])
|
||||||
|
if rc != 0:
|
||||||
|
return "⚠️ Fehler beim Anlegen: " + stderr
|
||||||
|
return "✅ Aufgabe angelegt: " + title
|
||||||
|
|
||||||
|
|
||||||
|
def _vdirsyncer_sync() -> str:
|
||||||
|
"""Löst manuellen vdirsyncer-Sync aus."""
|
||||||
|
stdout, stderr, rc = _run(["vdirsyncer", "sync"], timeout=60)
|
||||||
|
if rc != 0:
|
||||||
|
return "⚠️ Sync-Fehler:\n" + stderr
|
||||||
|
return "✅ Sync erfolgreich.\n" + stdout
|
||||||
|
|
||||||
|
|
||||||
|
def _sync_status() -> str:
|
||||||
|
"""Prüft systemd-Timer-Status."""
|
||||||
|
stdout, _, rc = _run(
|
||||||
|
["systemctl", "--user", "status", "vdirsyncer-sync.timer", "--no-pager", "-l"]
|
||||||
|
)
|
||||||
|
if rc not in (0, 3):
|
||||||
|
return "⚠️ Timer nicht gefunden oder Fehler."
|
||||||
|
# Nur relevante Zeilen extrahieren
|
||||||
|
lines = [
|
||||||
|
line for line in stdout.splitlines()
|
||||||
|
if any(kw in line for kw in ("Active:", "Trigger:", "Last trigger:"))
|
||||||
|
]
|
||||||
|
return "\n".join(lines) or stdout[:500]
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# OpenClaw Skill-Handler
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
def handle(command: str, args: str, context: dict) -> str:
|
||||||
|
"""
|
||||||
|
Haupt-Einstiegspunkt für OpenClaw.
|
||||||
|
|
||||||
|
:param command: Befehlsname ohne führenden Slash, z. B. "termine"
|
||||||
|
:param args: Argumente als String, z. B. "woche"
|
||||||
|
:param context: OpenClaw-Kontext (user_id, chat_id, etc.)
|
||||||
|
:return: Antwort-String für Telegram
|
||||||
|
"""
|
||||||
|
cmd = command.lower().strip()
|
||||||
|
arg = args.lower().strip()
|
||||||
|
|
||||||
|
if cmd == "termine":
|
||||||
|
if arg in ("morgen",):
|
||||||
|
start = date.today() + timedelta(days=1)
|
||||||
|
result = _khal_agenda(days=1, start=start)
|
||||||
|
return "📅 *Termine morgen:*\n" + result
|
||||||
|
elif arg in ("woche", "7"):
|
||||||
|
result = _khal_agenda(days=7)
|
||||||
|
return "📅 *Termine diese Woche:*\n" + result
|
||||||
|
else:
|
||||||
|
result = _khal_agenda(days=1)
|
||||||
|
return "📅 *Termine heute:*\n" + result
|
||||||
|
|
||||||
|
elif cmd == "aufgaben":
|
||||||
|
all_tasks = arg in ("alle", "all", "fertig")
|
||||||
|
result = _todo_list(all_tasks=all_tasks)
|
||||||
|
label = "alle Aufgaben" if all_tasks else "offene Aufgaben"
|
||||||
|
return "📝 *{}:*\n{}".format(label.capitalize(), result)
|
||||||
|
|
||||||
|
elif cmd == "neu_aufgabe":
|
||||||
|
return _todo_new(args.strip())
|
||||||
|
|
||||||
|
elif cmd == "sync":
|
||||||
|
return "🔄 Starte Sync...\n" + _vdirsyncer_sync()
|
||||||
|
|
||||||
|
elif cmd == "status":
|
||||||
|
return "⏱ *Sync-Status:*\n" + _sync_status()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
"❓ Unbekannter Befehl. Verfügbare Befehle:\n"
|
||||||
|
" /termine [heute|morgen|woche]\n"
|
||||||
|
" /aufgaben [offen|alle]\n"
|
||||||
|
" /neu_aufgabe <Text>\n"
|
||||||
|
" /sync\n"
|
||||||
|
" /status"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
# Standalone-Test (ohne OpenClaw)
|
||||||
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
tests = [
|
||||||
|
("termine", "heute"),
|
||||||
|
("termine", "woche"),
|
||||||
|
("aufgaben", "offen"),
|
||||||
|
("sync", ""),
|
||||||
|
("status", ""),
|
||||||
|
]
|
||||||
|
for cmd, arg in tests:
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print(f"CMD: {cmd!r} ARG: {arg!r}")
|
||||||
|
print(handle(cmd, arg, {}))
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# test_setup.sh — Überprüft die Installation Schritt für Schritt
|
||||||
|
# Ausführen nach install.sh: bash scripts/test_setup.sh
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Farben
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN} [✓] $*${NC}"; }
|
||||||
|
fail() { echo -e "${RED} [✗] $*${NC}"; FAILED=$((FAILED+1)); }
|
||||||
|
info() { echo -e "${CYAN} [i] $*${NC}"; }
|
||||||
|
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${CYAN} OpenClaw mailbox.org Kalender+Aufgaben — Selbsttest${NC}"
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- 1. Binaries vorhanden? ---
|
||||||
|
echo "▶ 1. Programm-Verfügbarkeit"
|
||||||
|
for bin in vdirsyncer khal todo python3; do
|
||||||
|
if command -v "$bin" &>/dev/null; then
|
||||||
|
ok "$bin → $(command -v $bin)"
|
||||||
|
else
|
||||||
|
fail "$bin nicht gefunden — install.sh ausführen"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 2. Konfigurationsdateien ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 2. Konfigurationsdateien"
|
||||||
|
declare -A CONFIGS=(
|
||||||
|
["vdirsyncer"]="$HOME/.config/vdirsyncer/config"
|
||||||
|
["khal"]="$HOME/.config/khal/config"
|
||||||
|
["todoman"]="$HOME/.config/todoman/config.cfg"
|
||||||
|
)
|
||||||
|
for name in "${!CONFIGS[@]}"; do
|
||||||
|
path="${CONFIGS[$name]}"
|
||||||
|
if [[ -f "$path" ]]; then
|
||||||
|
ok "$name → $path"
|
||||||
|
else
|
||||||
|
fail "$name-Config fehlt: $path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 3. Berechtigungen vdirsyncer-Config (enthält Passwort) ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 3. Sicherheit"
|
||||||
|
VDIR_CONF="$HOME/.config/vdirsyncer/config"
|
||||||
|
if [[ -f "$VDIR_CONF" ]]; then
|
||||||
|
PERMS=$(stat -c "%a" "$VDIR_CONF")
|
||||||
|
if [[ "$PERMS" == "600" ]]; then
|
||||||
|
ok "vdirsyncer config Berechtigungen: $PERMS (korrekt)"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer config Berechtigungen: $PERMS (sollte 600 sein) → chmod 600 $VDIR_CONF"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe ob Klartext-Passwort in Config steht
|
||||||
|
if grep -q 'password.*=.*"[^$]' "$VDIR_CONF" 2>/dev/null; then
|
||||||
|
info "Passwort in Plaintext erkannt — Erwäge pass(1) oder python-keyring als Alternative"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 4. Sync-Verzeichnisse ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 4. Sync-Verzeichnisse"
|
||||||
|
for dir in \
|
||||||
|
"$HOME/.local/share/vdirsyncer/status" \
|
||||||
|
"$HOME/.local/share/vdirsyncer/calendars" \
|
||||||
|
"$HOME/.local/share/vdirsyncer/tasks"; do
|
||||||
|
if [[ -d "$dir" ]]; then
|
||||||
|
COUNT=$(find "$dir" -name "*.ics" 2>/dev/null | wc -l)
|
||||||
|
ok "$dir (${COUNT} .ics Dateien)"
|
||||||
|
else
|
||||||
|
fail "Verzeichnis fehlt: $dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- 5. vdirsyncer Verbindungstest ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 5. vdirsyncer — Verbindungstest"
|
||||||
|
info "Führe 'vdirsyncer sync' aus (Timeout: 30s)..."
|
||||||
|
if timeout 30 vdirsyncer sync 2>&1 | tail -5; then
|
||||||
|
ok "vdirsyncer sync erfolgreich"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer sync fehlgeschlagen — Zugangsdaten und Netzwerk prüfen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 6. khal-Test ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 6. khal — Termine heute"
|
||||||
|
if khal list --format "{start-time} {title}" today today 2>/dev/null; then
|
||||||
|
ok "khal list ausgeführt"
|
||||||
|
else
|
||||||
|
fail "khal list fehlgeschlagen — khal.conf prüfen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 7. todoman-Test ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 7. todoman — Aufgabenliste"
|
||||||
|
if todo list 2>/dev/null; then
|
||||||
|
ok "todo list ausgeführt"
|
||||||
|
else
|
||||||
|
fail "todo list fehlgeschlagen — todoman.conf prüfen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 8. systemd-Timer ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 8. systemd Timer"
|
||||||
|
if systemctl --user is-active vdirsyncer-sync.timer &>/dev/null; then
|
||||||
|
ok "vdirsyncer-sync.timer: aktiv"
|
||||||
|
NEXT=$(systemctl --user list-timers vdirsyncer-sync.timer --no-legend 2>/dev/null | awk '{print $1, $2}')
|
||||||
|
info "Nächster Sync: $NEXT"
|
||||||
|
else
|
||||||
|
fail "vdirsyncer-sync.timer nicht aktiv → systemctl --user enable --now vdirsyncer-sync.timer"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 9. Log-Datei ---
|
||||||
|
echo ""
|
||||||
|
echo "▶ 9. Log-Datei"
|
||||||
|
LOG="/var/log/openclaw-mailbox-cal/sync.log"
|
||||||
|
if [[ -f "$LOG" ]]; then
|
||||||
|
SIZE=$(du -sh "$LOG" | cut -f1)
|
||||||
|
ok "Log vorhanden: $LOG ($SIZE)"
|
||||||
|
info "Letzte 5 Zeilen:"
|
||||||
|
tail -5 "$LOG" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
info "Log noch nicht vorhanden (wird beim ersten Sync erstellt)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Zusammenfassung ---
|
||||||
|
echo ""
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
if [[ $FAILED -eq 0 ]]; then
|
||||||
|
echo -e "${GREEN} Alle Tests bestanden! Setup ist korrekt.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED} $FAILED Test(s) fehlgeschlagen — siehe Details oben.${NC}"
|
||||||
|
fi
|
||||||
|
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
exit $FAILED
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Konfiguration
|
||||||
|
CAL_URL="https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg"
|
||||||
|
USER="minitux@net-so.org"
|
||||||
|
PASS="dbba-guvm-perd-pdhq"
|
||||||
|
OUT_FILE="/home/hans/.openclaw/workspace/mailbox-calendar.ics"
|
||||||
|
|
||||||
|
# Temporäre Datei, um halb geschriebene Files zu vermeiden
|
||||||
|
TMP_FILE="$(mktemp)"
|
||||||
|
|
||||||
|
# ICS/CalDAV-Daten holen
|
||||||
|
curl -sS -u "$USER:$PASS" "$CAL_URL" -o "$TMP_FILE"
|
||||||
|
|
||||||
|
# Wenn curl erfolgreich war, Datei an Ziel verschieben
|
||||||
|
mv "$TMP_FILE" "$OUT_FILE"
|
||||||
|
chmod 644 "$OUT_FILE"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=vdirsyncer — mailbox.org CalDAV Synchronisation
|
||||||
|
Documentation=https://vdirsyncer.pimutils.org/
|
||||||
|
# Netzwerk abwarten
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
|
||||||
|
# Umgebungsvariablen aus .env laden (Pfad anpassen falls nötig)
|
||||||
|
EnvironmentFile=%h/.config/openclaw-mailbox-cal/.env
|
||||||
|
|
||||||
|
# Erst-Sync: discover + sync
|
||||||
|
# Bei erstem Lauf collect = auto, danach nur sync
|
||||||
|
ExecStart=/usr/bin/vdirsyncer sync
|
||||||
|
|
||||||
|
# Fehler loggen
|
||||||
|
StandardOutput=append:/var/log/openclaw-mailbox-cal/sync.log
|
||||||
|
StandardError=append:/var/log/openclaw-mailbox-cal/sync.log
|
||||||
|
|
||||||
|
# Timeout: 120 Sekunden (ausreichend für langsame Verbindungen)
|
||||||
|
TimeoutStartSec=120
|
||||||
|
|
||||||
|
# Nach erfolgreichem Sync khal-Cache neu aufbauen
|
||||||
|
ExecStartPost=/usr/bin/khal rebuild-cache
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=vdirsyncer Sync Timer — mailbox.org (alle 15 Minuten)
|
||||||
|
Documentation=https://vdirsyncer.pimutils.org/
|
||||||
|
Requires=vdirsyncer-sync.service
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
# Sync-Intervall: alle 15 Minuten (aus .env SYNC_INTERVAL oder hier anpassen)
|
||||||
|
# Beispiele:
|
||||||
|
# *:0/15 → alle 15 Minuten
|
||||||
|
# *:0/5 → alle 5 Minuten
|
||||||
|
# hourly → stündlich
|
||||||
|
# *-*-* 06,12,18,22:00:00 → 4x täglich
|
||||||
|
OnCalendar=${SYNC_INTERVAL:-*:0/15}
|
||||||
|
|
||||||
|
# Beim Booten direkt starten (nicht auf den nächsten Interval warten)
|
||||||
|
OnBootSec=60s
|
||||||
|
|
||||||
|
# Zufällige Verzögerung ±30s (verhindert Last-Spitzen)
|
||||||
|
RandomizedDelaySec=30s
|
||||||
|
|
||||||
|
# Verpasste Runs nachholen (z. B. nach Schlafmodus)
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
Unit=vdirsyncer-sync.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Binary file not shown.
@@ -0,0 +1,306 @@
|
|||||||
|
# openclaw-mailbox-cal
|
||||||
|
|
||||||
|
**OpenClaw Skill-Repository** — mailbox.org Kalender und Aufgaben auf dem Raspberry Pi
|
||||||
|
Telegram-Bot-Befehle für Kalendertermine (khal), Tasks (todoman) und automatischen CalDAV-Sync (vdirsyncer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. .env befüllen
|
||||||
|
cp assets/.env.example .env && nano .env
|
||||||
|
|
||||||
|
# 2. Installation
|
||||||
|
chmod +x scripts/install.sh && bash scripts/install.sh
|
||||||
|
|
||||||
|
# 3. Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
openclaw-mailbox-cal/
|
||||||
|
├── SKILL.md ← OpenClaw Skill-Definition
|
||||||
|
├── README.md ← Diese Datei
|
||||||
|
├── assets/
|
||||||
|
│ └── .env.example ← Umgebungsvariablen-Vorlage
|
||||||
|
├── config/
|
||||||
|
│ ├── vdirsyncer.conf ← vdirsyncer CalDAV-Konfiguration
|
||||||
|
│ ├── khal.conf ← khal Kalender-Konfiguration
|
||||||
|
│ └── todoman.conf ← todoman Aufgaben-Konfiguration
|
||||||
|
├── scripts/
|
||||||
|
│ ├── install.sh ← Installations-Script (Raspi)
|
||||||
|
│ ├── test_setup.sh ← Selbsttest aller Komponenten
|
||||||
|
│ └── openclaw_skill.py ← OpenClaw/Telegram Bot Handler
|
||||||
|
└── systemd/
|
||||||
|
├── vdirsyncer-sync.service ← systemd Service Unit
|
||||||
|
├── vdirsyncer-sync.timer ← systemd Timer (alle 15 Min.)
|
||||||
|
└── vdirsyncer-sync.env ← Umgebungsvariablen für systemd
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup-Schritte (detailliert)
|
||||||
|
|
||||||
|
### Schritt 1 — Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm (64-bit empfohlen) oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto
|
||||||
|
- App-Passwort in mailbox.org anlegen:
|
||||||
|
`Einstellungen → Sicherheit → App-Passwörter → Neu erstellen`
|
||||||
|
- OpenClaw installiert: [openclaw.dev](https://openclaw.dev)
|
||||||
|
- Telegram Bot-Token via [@BotFather](https://t.me/BotFather)
|
||||||
|
|
||||||
|
### Schritt 2 — .env konfigurieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Mindestens diese Werte setzen:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
MAILBOX_USER=vorname.nachname@mailbox.org
|
||||||
|
MAILBOX_PASS=app-passwort-xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3 — install.sh ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script installiert automatisch:
|
||||||
|
- `vdirsyncer` — CalDAV-Sync-Tool
|
||||||
|
- `khal` — CLI-Kalender
|
||||||
|
- `todoman` — CLI-Aufgabenverwaltung
|
||||||
|
- `pass` — GNU Password Store
|
||||||
|
- `python3-keyring` — Python-Keyring-Integration
|
||||||
|
- `gettext-base` — envsubst für Konfigurationen
|
||||||
|
|
||||||
|
Anschließend:
|
||||||
|
- Alle Konfigurationsdateien werden deployed
|
||||||
|
- systemd User-Timer wird aktiviert
|
||||||
|
- Initialer Sync wird durchgeführt
|
||||||
|
|
||||||
|
### Schritt 4 — OpenClaw Skill registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills **automatisch** wenn sie in einem der Skill-Verzeichnisse liegen.
|
||||||
|
Kein manueller Eintrag in `openclaw.json` nötig — das `install.sh` legt bereits einen Symlink an.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manuell (falls install.sh den Symlink nicht angelegt hat):
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional — Zugangsdaten direkt in `~/.openclaw/openclaw.json` hinterlegen
|
||||||
|
(Alternative zur `.env`-Datei). **Immer zuerst Backup anlegen:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
Den bestehenden `"skills"`-Block in `openclaw.json` um `"entries"` ergänzen
|
||||||
|
(der Block sieht danach so aus):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach jeder Änderung an `openclaw.json` validieren und Gateway neu starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw doctor --fix
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5 — Telegram testen
|
||||||
|
|
||||||
|
Sende an deinen Bot:
|
||||||
|
```
|
||||||
|
/termine
|
||||||
|
/aufgaben
|
||||||
|
/sync
|
||||||
|
/status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
### Vollständiger Selbsttest
|
||||||
|
```bash
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### vdirsyncer
|
||||||
|
```bash
|
||||||
|
# Verbindung testen und Sammlungen entdecken
|
||||||
|
vdirsyncer discover calendars
|
||||||
|
vdirsyncer discover tasks
|
||||||
|
|
||||||
|
# Synchronisation manuell starten
|
||||||
|
vdirsyncer sync
|
||||||
|
|
||||||
|
# Nur Kalender synchronisieren
|
||||||
|
vdirsyncer sync calendars
|
||||||
|
|
||||||
|
# Verbose-Ausgabe
|
||||||
|
vdirsyncer -v DEBUG sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal
|
||||||
|
```bash
|
||||||
|
# Termine heute
|
||||||
|
khal list
|
||||||
|
|
||||||
|
# Termine nächste 7 Tage
|
||||||
|
khal list today 7d
|
||||||
|
|
||||||
|
# Agenda-Ansicht
|
||||||
|
khal agenda
|
||||||
|
|
||||||
|
# Interaktiver Kalender
|
||||||
|
ikhal
|
||||||
|
|
||||||
|
# Ereignis hinzufügen (interaktiv)
|
||||||
|
khal new
|
||||||
|
|
||||||
|
# Ereignis direkt anlegen
|
||||||
|
khal new 2025-06-15 14:00 15:00 "Arzttermin" --calendar mailbox_kalender
|
||||||
|
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
|
||||||
|
# Konfiguration anzeigen
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### todoman
|
||||||
|
```bash
|
||||||
|
# Alle offenen Aufgaben
|
||||||
|
todo list
|
||||||
|
|
||||||
|
# Alle Aufgaben inkl. erledigt
|
||||||
|
todo list --all
|
||||||
|
|
||||||
|
# Neue Aufgabe
|
||||||
|
todo new "Bericht schreiben"
|
||||||
|
|
||||||
|
# Aufgabe mit Fälligkeitsdatum
|
||||||
|
todo new --due 2025-06-30 "Bericht abgeben"
|
||||||
|
|
||||||
|
# Aufgabe mit Priorität (1=hoch, 9=niedrig)
|
||||||
|
todo new --priority 1 "Dringend: Server patchen"
|
||||||
|
|
||||||
|
# Aufgabe als erledigt markieren (ID aus 'todo list')
|
||||||
|
todo done 1
|
||||||
|
|
||||||
|
# Aufgabe löschen
|
||||||
|
todo delete 2
|
||||||
|
|
||||||
|
# Aufgaben-Listen anzeigen
|
||||||
|
todo lists
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
```bash
|
||||||
|
# Timer-Status
|
||||||
|
systemctl --user status vdirsyncer-sync.timer
|
||||||
|
|
||||||
|
# Service manuell starten (einmaliger Sync)
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
|
||||||
|
# Alle User-Timer anzeigen
|
||||||
|
systemctl --user list-timers
|
||||||
|
|
||||||
|
# Live-Log verfolgen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f
|
||||||
|
|
||||||
|
# Log der letzten 50 Zeilen
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -n 50
|
||||||
|
|
||||||
|
# Timer deaktivieren
|
||||||
|
systemctl --user disable --now vdirsyncer-sync.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python-Skill direkt testen
|
||||||
|
```bash
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## mailbox.org CalDAV-Endpunkte
|
||||||
|
|
||||||
|
| Zweck | URL |
|
||||||
|
|---|---|
|
||||||
|
| CalDAV-Basis | `https://dav.mailbox.org/caldav/<USER>/` |
|
||||||
|
| Kalender-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Kalender/` |
|
||||||
|
| Aufgaben-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Aufgaben/` |
|
||||||
|
| CardDAV (Kontakte) | `https://dav.mailbox.org/carddav/<USER>/` |
|
||||||
|
|
||||||
|
Entdeckung aller Sammlungen:
|
||||||
|
```bash
|
||||||
|
curl -u "USER:PASS" -X PROPFIND https://dav.mailbox.org/caldav/ \
|
||||||
|
-H "Depth: 1" -H "Content-Type: application/xml"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicherheit
|
||||||
|
|
||||||
|
| Maßnahme | Status |
|
||||||
|
|---|---|
|
||||||
|
| App-Passwort statt Hauptpasswort | ✅ Empfohlen |
|
||||||
|
| vdirsyncer-Config: chmod 600 | ✅ Automatisch via install.sh |
|
||||||
|
| .env nicht in Git | ✅ In .gitignore eingetragen |
|
||||||
|
| HTTPS für CalDAV | ✅ dav.mailbox.org erzwingt TLS |
|
||||||
|
| pass(1)-Integration | Optional via `pass` + `python3-keyring` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
**vdirsyncer: `[Errno 111] Connection refused`**
|
||||||
|
→ Netzwerkverbindung prüfen: `ping dav.mailbox.org`
|
||||||
|
|
||||||
|
**vdirsyncer: `Conflict!`**
|
||||||
|
→ Beide Seiten wurden geändert. Konflikt manuell lösen oder `conflict_resolution = "b wins"` in vdirsyncer.conf setzen.
|
||||||
|
|
||||||
|
**khal: `No calendars configured`**
|
||||||
|
→ `khal rebuild-cache` ausführen; Pfad in `~/.config/khal/config` prüfen.
|
||||||
|
|
||||||
|
**todoman: `Error: No lists found`**
|
||||||
|
→ `path`-Eintrag in `~/.config/todoman/config.cfg` prüfen; Verzeichnis `~/.local/share/vdirsyncer/tasks/` muss existieren und `.ics`-Dateien enthalten.
|
||||||
|
|
||||||
|
**systemd-Timer läuft nach Reboot nicht**
|
||||||
|
→ `sudo loginctl enable-linger $USER` ausführen (User-Session ohne Login).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
MIT — Freie Nutzung, Veränderung und Weitergabe.
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
---
|
||||||
|
name: mailbox-cal
|
||||||
|
description: "OpenClaw Telegram-Bot-Skill für mailbox.org Kalender und Aufgaben auf dem Raspberry Pi. Synchronisiert CalDAV-Kalender und VTODO-Aufgaben via vdirsyncer, zeigt Termine mit khal und verwaltet Tasks mit todoman. Befehle: /termine, /aufgaben, /neu_aufgabe, /sync, /status. Lade diesen Skill wenn: der Nutzer Kalender-Einträge oder Aufgaben aus mailbox.org per Telegram abfragen, anlegen oder synchronisieren möchte."
|
||||||
|
license: MIT
|
||||||
|
compatibility: "Raspberry Pi OS Bookworm / Ubuntu 22.04+ ARM. Benötigt: vdirsyncer >= 0.19, khal >= 0.11, todoman >= 4.3, python3 >= 3.10. mailbox.org CalDAV-Zugang erforderlich."
|
||||||
|
metadata:
|
||||||
|
author: lonely.wolf.64
|
||||||
|
version: '1.0'
|
||||||
|
platform: Raspberry Pi
|
||||||
|
caldav_server: mailbox.org
|
||||||
|
---
|
||||||
|
|
||||||
|
# mailbox.org Kalender + Aufgaben Skill
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Dieser OpenClaw-Skill synchronisiert deinen mailbox.org-Kalender und deine Aufgaben über CalDAV auf einen Raspberry Pi und stellt sie über Telegram-Bot-Befehle bereit. Die Synchronisation läuft automatisch alle 15 Minuten via systemd-Timer.
|
||||||
|
|
||||||
|
**Architektur:**
|
||||||
|
```
|
||||||
|
mailbox.org (CalDAV)
|
||||||
|
│
|
||||||
|
vdirsyncer (Sync)
|
||||||
|
│
|
||||||
|
┌───┴───────────┐
|
||||||
|
│ │
|
||||||
|
khal todoman
|
||||||
|
(Kalender) (Aufgaben)
|
||||||
|
│ │
|
||||||
|
└───────┬───────┘
|
||||||
|
│
|
||||||
|
openclaw_skill.py
|
||||||
|
│
|
||||||
|
Telegram Bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unterstützte Befehle
|
||||||
|
|
||||||
|
| Telegram-Befehl | Beschreibung | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| `/termine` | Heutige Termine | `/termine heute` |
|
||||||
|
| `/termine morgen` | Morgige Termine | `/termine morgen` |
|
||||||
|
| `/termine woche` | Termine nächste 7 Tage | `/termine woche` |
|
||||||
|
| `/aufgaben` | Offene Aufgaben | `/aufgaben` |
|
||||||
|
| `/aufgaben alle` | Alle Aufgaben inkl. erledigt | `/aufgaben alle` |
|
||||||
|
| `/neu_aufgabe` | Neue Aufgabe anlegen | `/neu_aufgabe Arzt anrufen` |
|
||||||
|
| `/sync` | Manuellen Sync auslösen | `/sync` |
|
||||||
|
| `/status` | Sync-Timer-Status | `/status` |
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Raspberry Pi mit Raspberry Pi OS Bookworm oder Ubuntu 22.04+ ARM
|
||||||
|
- mailbox.org-Konto mit CalDAV-Zugang
|
||||||
|
- OpenClaw installiert und konfiguriert
|
||||||
|
- Telegram Bot-Token (via @BotFather)
|
||||||
|
- Internetverbindung vom Raspberry Pi
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Repository klonen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/DEIN_USER/openclaw-mailbox-cal.git
|
||||||
|
cd openclaw-mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. .env befüllen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp assets/.env.example .env
|
||||||
|
nano .env
|
||||||
|
# MAILBOX_USER und MAILBOX_PASS setzen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Empfehlung:** Lege in mailbox.org ein App-Passwort an:
|
||||||
|
Einstellungen → Sicherheit → App-Passwörter → Neu erstellen
|
||||||
|
|
||||||
|
### 3. Installation ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/install.sh
|
||||||
|
bash scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script führt folgende Schritte aus:
|
||||||
|
1. Pakete installieren: `vdirsyncer khal todoman pass python3-keyring gettext-base`
|
||||||
|
2. Konfigurationsdateien deployen (mit envsubst)
|
||||||
|
3. systemd User-Timer aktivieren
|
||||||
|
4. Initialen Sync durchführen
|
||||||
|
5. khal-Datenbank aufbauen
|
||||||
|
6. OpenClaw Skill verknüpfen
|
||||||
|
|
||||||
|
### 4. Skill in OpenClaw registrieren
|
||||||
|
|
||||||
|
OpenClaw erkennt Skills automatisch aus diesen Verzeichnissen (höchste Priorität zuerst):
|
||||||
|
- `<workspace>/skills/`
|
||||||
|
- `~/.openclaw/skills/`
|
||||||
|
- bundled skills
|
||||||
|
|
||||||
|
Skill-Verzeichnis ins OpenClaw-Skills-Verzeichnis kopieren oder verlinken:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Option A: Symlink (empfohlen — Updates wirken sofort)
|
||||||
|
mkdir -p ~/.openclaw/skills
|
||||||
|
ln -s ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
|
||||||
|
# Option B: Kopieren
|
||||||
|
cp -r ~/mailbox-cal ~/.openclaw/skills/mailbox-cal
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional: Zugangsdaten direkt in `~/.openclaw/openclaw.json` hinterlegen
|
||||||
|
(nur nötig für env-Variablen oder explizites enable/disable).
|
||||||
|
Den bestehenden `"skills"`-Block um `"entries"` ergänzen:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
},
|
||||||
|
"entries": {
|
||||||
|
"mailbox-cal": {
|
||||||
|
"enabled": true,
|
||||||
|
"env": {
|
||||||
|
"MAILBOX_USER": "dein.name@mailbox.org",
|
||||||
|
"MAILBOX_PASS": "app-passwort-xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Unbekannte Keys lassen den OpenClaw-Gateway nicht starten.
|
||||||
|
Vor dem Bearbeiten immer ein Backup anlegen:
|
||||||
|
```bash
|
||||||
|
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak
|
||||||
|
# Nach der Bearbeitung validieren:
|
||||||
|
openclaw doctor --fix
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach Gateway neu starten:
|
||||||
|
```bash
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Befehle
|
||||||
|
|
||||||
|
Nach der Installation Setup validieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vollständiger Selbsttest
|
||||||
|
bash scripts/test_setup.sh
|
||||||
|
|
||||||
|
# Einzeltests
|
||||||
|
vdirsyncer sync # Sync testen
|
||||||
|
khal list today tomorrow # Termine heute + morgen
|
||||||
|
khal list --format "{start} {title}" 2025-01-01 2025-12-31 # Jahresübersicht
|
||||||
|
todo list # Aufgaben anzeigen
|
||||||
|
todo list --all # Alle inkl. erledigt
|
||||||
|
todo new "Test-Aufgabe" # Aufgabe anlegen
|
||||||
|
|
||||||
|
# systemd Timer
|
||||||
|
systemctl --user status vdirsyncer-sync.timer # Timer-Status
|
||||||
|
systemctl --user list-timers # Alle Timer
|
||||||
|
journalctl --user -u vdirsyncer-sync.service -f # Live-Log
|
||||||
|
|
||||||
|
# Python-Skill direkt testen (ohne OpenClaw/Telegram)
|
||||||
|
python3 scripts/openclaw_skill.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfigurationsdateien
|
||||||
|
|
||||||
|
| Datei | Ziel-Pfad | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `config/vdirsyncer.conf` | `~/.config/vdirsyncer/config` | CalDAV-Sync-Konfiguration |
|
||||||
|
| `config/khal.conf` | `~/.config/khal/config` | Kalender-Anzeige-Konfiguration |
|
||||||
|
| `config/todoman.conf` | `~/.config/todoman/config.cfg` | Aufgaben-Konfiguration |
|
||||||
|
| `systemd/vdirsyncer-sync.service` | `~/.config/systemd/user/` | Sync-Service Unit |
|
||||||
|
| `systemd/vdirsyncer-sync.timer` | `~/.config/systemd/user/` | 15-Minuten-Timer |
|
||||||
|
| `scripts/openclaw_skill.py` | (in-place) | OpenClaw/Telegram-Integration |
|
||||||
|
|
||||||
|
## Sicherheitshinweise
|
||||||
|
|
||||||
|
- Die vdirsyncer-Config (`~/.config/vdirsyncer/config`) enthält das Passwort — Berechtigungen sind `chmod 600`
|
||||||
|
- Verwende ein mailbox.org App-Passwort statt des Hauptpassworts
|
||||||
|
- Für produktive Umgebungen: `pass`-Integration (GNU Password Store) via `pass` + `python3-keyring`
|
||||||
|
- `.env`-Datei niemals in Git committen (ist in `.gitignore` eingetragen)
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
### vdirsyncer discover schlägt fehl
|
||||||
|
```bash
|
||||||
|
# Verbindung testen
|
||||||
|
curl -u "USER:PASS" https://dav.mailbox.org/caldav/
|
||||||
|
# SSL-Zertifikate prüfen
|
||||||
|
openssl s_client -connect dav.mailbox.org:443
|
||||||
|
```
|
||||||
|
|
||||||
|
### khal zeigt keine Termine
|
||||||
|
```bash
|
||||||
|
# Cache neu aufbauen
|
||||||
|
khal rebuild-cache
|
||||||
|
# Config validieren
|
||||||
|
khal printformats
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd-Timer läuft nicht
|
||||||
|
```bash
|
||||||
|
# Lingering aktivieren (damit User-Units ohne Login laufen)
|
||||||
|
sudo loginctl enable-linger pi
|
||||||
|
# Timer manuell starten
|
||||||
|
systemctl --user start vdirsyncer-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Todoman findet keine Aufgaben
|
||||||
|
```bash
|
||||||
|
# Pfad in config prüfen
|
||||||
|
cat ~/.config/todoman/config.cfg
|
||||||
|
# Verzeichnis prüfen
|
||||||
|
ls ~/.local/share/vdirsyncer/tasks/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Erweiterungen
|
||||||
|
|
||||||
|
- **Mehrere Kalender:** Weitere `[[kalender_name]]`-Sektionen in `khal.conf` hinzufügen
|
||||||
|
- **Push-Sync:** mailbox.org unterstützt CalDAV-Push — XMPP-Trigger via `vdirsyncer`
|
||||||
|
- **Erinnerungen:** Integration mit `khal-remind` oder `at`-Daemon
|
||||||
|
- **Verschlüsselung:** `.env` via `gpg-agent` verschlüsseln
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"workspace": "/home/hans/.openclaw/workspace",
|
||||||
|
"models": {
|
||||||
|
"openrouter/auto": {
|
||||||
|
"alias": "OpenRouter"
|
||||||
|
},
|
||||||
|
"openrouter/google/gemini-3.1-flash-lite-preview": {}
|
||||||
|
},
|
||||||
|
"model": {
|
||||||
|
"primary": "openrouter/google/gemini-3.1-flash-lite-preview"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gateway": {
|
||||||
|
"mode": "local",
|
||||||
|
"auth": {
|
||||||
|
"mode": "token",
|
||||||
|
"token": "26974f72b9bbfa03983f340114cd15e92df44bd2d19ca18e"
|
||||||
|
},
|
||||||
|
"port": 18789,
|
||||||
|
"bind": "loopback",
|
||||||
|
"tailscale": {
|
||||||
|
"mode": "off",
|
||||||
|
"resetOnExit": false
|
||||||
|
},
|
||||||
|
"controlUi": {
|
||||||
|
"allowInsecureAuth": true
|
||||||
|
},
|
||||||
|
"nodes": {
|
||||||
|
"denyCommands": [
|
||||||
|
"camera.snap",
|
||||||
|
"camera.clip",
|
||||||
|
"screen.record",
|
||||||
|
"contacts.add",
|
||||||
|
"calendar.add",
|
||||||
|
"reminders.add",
|
||||||
|
"sms.send",
|
||||||
|
"sms.search"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"dmScope": "per-channel-peer"
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"profile": "coding"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"profiles": {
|
||||||
|
"openrouter:default": {
|
||||||
|
"provider": "openrouter",
|
||||||
|
"mode": "api_key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"telegram": {
|
||||||
|
"enabled": true,
|
||||||
|
"groups": {
|
||||||
|
"*": {
|
||||||
|
"requireMention": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"botToken": "8699386665:AAHNDYHdAtWmt1B34U8Rae_7quTfOr6dPBU"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wizard": {
|
||||||
|
"lastRunAt": "2026-04-22T13:48:49.051Z",
|
||||||
|
"lastRunVersion": "2026.4.21",
|
||||||
|
"lastRunCommand": "onboard",
|
||||||
|
"lastRunMode": "local"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"lastTouchedVersion": "2026.4.21",
|
||||||
|
"lastTouchedAt": "2026-04-22T13:48:49.595Z"
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"entries": {
|
||||||
|
"openrouter": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"install": {
|
||||||
|
"nodeManager": "npm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[calendars]
|
||||||
|
|
||||||
|
[[mailbox_kalender]]
|
||||||
|
path = ~/.local/share/vdirsyncer/calendars/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[[mailbox_aufgaben]]
|
||||||
|
path = ~/.local/share/vdirsyncer/tasks/*
|
||||||
|
type = discover
|
||||||
|
|
||||||
|
[default]
|
||||||
|
default_calendar = mailbox_kalender
|
||||||
|
highlight_event_days = True
|
||||||
|
show_todos = True
|
||||||
|
|
||||||
|
[locale]
|
||||||
|
timeformat = %H:%M
|
||||||
|
dateformat = %Y-%m-%d
|
||||||
|
datetimeformat = %Y-%m-%d %H:%M
|
||||||
|
longdateformat = %A, %d. %B %Y
|
||||||
|
longdatetimeformat = %A, %d. %B %Y %H:%M
|
||||||
|
longtimeformat = %H:%M
|
||||||
|
firstweekday = 0
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
[general]
|
||||||
|
status_path = "~/.local/share/vdirsyncer/status/"
|
||||||
|
|
||||||
|
# ──────────────── KALENDER ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_calendar]
|
||||||
|
a = "mailbox_cal_remote"
|
||||||
|
b = "mailbox_cal_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
metadata = ["displayname", "color"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg"
|
||||||
|
username = "minitux@mailbox.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_cal_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/calendars/"
|
||||||
|
fileext = ".ics"
|
||||||
|
|
||||||
|
# ──────────────── AUFGABEN (VTODO) ────────────────
|
||||||
|
|
||||||
|
[pair mailbox_tasks]
|
||||||
|
a = "mailbox_tasks_remote"
|
||||||
|
b = "mailbox_tasks_local"
|
||||||
|
collections = ["from a", "from b"]
|
||||||
|
conflict_resolution = "a wins"
|
||||||
|
|
||||||
|
[storage mailbox_tasks_remote]
|
||||||
|
type = "caldav"
|
||||||
|
url = "https://dav.mailbox.org/caldav/MzU"
|
||||||
|
username = "minitux@mailbox.org"
|
||||||
|
password.fetch = ["command", "cat", "/home/hans/.config/vdirsyncer/mailbox_apppassword"]
|
||||||
|
|
||||||
|
[storage mailbox_tasks_local]
|
||||||
|
type = "filesystem"
|
||||||
|
path = "~/.local/share/vdirsyncer/tasks/"
|
||||||
|
fileext = ".ics"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
mkdir -p ~/.config/vdirsyncer
|
||||||
|
mkdir -p ~/.config/khal
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/status
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/calendars
|
||||||
|
mkdir -p ~/.local/share/vdirsyncer/tasks
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Kalender_url: https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg
|
||||||
|
|
||||||
|
Aufgaben_url: https://dav.mailbox.org/caldav/MzU
|
||||||
Reference in New Issue
Block a user