This commit is contained in:
2026-05-06 08:41:06 +02:00
parent 8f0674b6c2
commit 3d98c9ec9c
38 changed files with 87867 additions and 0 deletions
@@ -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. 36 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—stepbystep 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: costeffective, multimodel 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.
+306
View File
@@ -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.
+229
View File
@@ -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
View File
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. 36 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—stepbystep 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: costeffective, multimodel 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...
+33
View File
@@ -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
# ══════════════════════════════════════════════════════
+151
View File
@@ -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
+581
View File
@@ -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()
+33
View File
@@ -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.
@@ -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.
+306
View File
@@ -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.
+229
View File
@@ -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
+91
View File
@@ -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"
}
}
}
+23
View File
@@ -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
+41
View File
@@ -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"
+8
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
Kalender_url: https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg
Aufgaben_url: https://dav.mailbox.org/caldav/MzU