Org-Agenda als ICS-Datei
Heute habe ich zusammen mit einer KI ein wenig gebastelt. Ich habe begonnen, meine Termine und Todos in einer Agenda-Datei in Emacs zu verwalten. Der berühmte Org-Mode wird jetzt genutzt. Mein Problem ist, dass es unterwegs auf dem Handy eher mühsam ist, eine org-Datei zu öffnen und zu lesen. Es gibt zwar das Programm orgzly-revived, welches org-Dateien einlesen und darstellen kann, aber ich werde mit dem Programm nicht so ganz warm. Meine Idee: Ich möchte einfach aus der org-Agenda für die nächsten 360 Tage eine ics-Datei exportieren lassen. Diese Datei soll auf einen meiner V-Server hochgeladen werden. Auf dem Smartphone soll dann ein Programm regelmäßig die Datei abrufen und in der Kalender-App zur Verfügung stellen. Dank KI war eine Funktion in Lisp recht zügig erstellt. Ich hatte noch mit einem HTML-Export experimentiert. Daher sind wohl einige Lisp-Funktionen unnötig. Aber vielleicht ist es für den ein oder anderen interessant.
Schritt 1 - Erstelle eine ICS-Datei
Das ist die Methode, die jetzt in meiner Emacs-Konfiguration vorhanden ist. Das Umkopieren der Dateien in meinen org-Ordner habe ich auskommentiert. Scheinbar kommt mein Sync-Client für meine NAS damit nicht klar und landet in einem endlosen Sync-Prozess. Einiges an Code ist also überflüssig. Gemini hat den soweit generiert und er funktioniert. Es werden in /tmp Dateien mit der exportieren HTML- und ICS-Agenda angelegt. Wie gesagt, die HTML-Agenda ist für mich jetzt überflüssig.
(defvar my/agenda-export-running nil
"Verhindert, dass der Export mehrfach gleichzeitig startet.")
(with-eval-after-load 'org-agenda
;; subr-x stellt den 'dlet' Befehl bereit
(require 'subr-x)
(defun my/export-and-push-agenda ()
"Exportiert die Agenda NAS-schonend und lädt sie auf den v-Server hoch."
(interactive)
(unless my/agenda-export-running
(setq my/agenda-export-running t)
(unwind-protect
(dlet ((org-agenda-span 365)
(org-agenda-sticky nil)
(org-agenda-tags-column -1)
(org-agenda-prefix-format '((agenda . " %-10:c ")))
(org-icalendar-include-todo 'all))
(let ((tmp-html "/tmp/agenda.html")
(tmp-ics "/tmp/agenda.ics")
(final-html (expand-file-name "~/org/agenda.html"))
(final-ics (expand-file-name "~/org/agenda.ics")))
;; 1. Export in den Temp-Ordner (unsichtbar für das NAS)
(save-window-excursion
(org-agenda-list)
(with-current-buffer "*Org Agenda*"
(let ((inhibit-read-only t))
(delete-trailing-whitespace))
(org-agenda-write tmp-html)
(org-agenda-write tmp-ics)
(kill-buffer "*Org Agenda*")))
;; 2. HTML-Design anpassen (Schriftgröße & Umbruch)
(with-current-buffer (find-file-noselect tmp-html)
(goto-char (point-min))
(when (search-forward "</style>" nil t)
(goto-char (match-beginning 0))
(insert " body { font-size: 20pt; font-family: sans-serif; line-height: 1.4; padding: 20px; }\n")
(insert " pre { white-space: pre-wrap; }\n"))
(basic-save-buffer)
(kill-buffer))
;; 3. Dateien atomar an das NAS übergeben
;;(rename-file tmp-html final-html t)
;;(rename-file tmp-ics final-ics t)
;; 4. Geräuschloser Upload zum v-Server (jan-iversen.de)
(let ((default-directory "~/"))
(start-process "agenda-upload" nil
"scp" tmp-ics "xxx@yyy:/var/www/ics/agenda.ics"))
(message "Agenda-Export erfolgreich abgeschlossen.")))
;; Diese Zeile MUSS innerhalb der unwind-protect Klammern stehen
(setq my/agenda-export-running nil)))))
Schritt 2 - Vorbereiten des Servers
Ich habe auf meinen virtuellen Server bereits NGINX im Einsatz. Ich habe mir dort lediglich unter /var/www den Ordner ics angelegt. Danach habe ich die Rechte an dem Ordner auf meinen Benutzer geändert. So kann ich einfach per scp eine Datei auf den Server kopieren.
Eine neue Konfiguration für die Seite wurde angelegt. Und ich habe die Seite per htpasswd abgesichert. So kann niemand die Datei einfach herunterladen.
Schritt 3 - DNS-Eintrag
Bei Netcup habe ich einen neuen DNS-Eintrag gesetzt. Diesmal hat es nur wenige Minuten gedauert, bis der DNS-Eintrag auch überall bekannt war. Ich konnte also zügig weitermachen.
Schritt 4 - Erster Test
In Emacs habe ich die neue Methode das erste Mal gestartet. Die ICS-Datei wurde korrekt auf den Server kopiert. Über die URL konnte ich im Browser die Passwortabfrage testen und danach die Datei erfolgreich herunterladen. Hat also wunderbar geklappt.
Schritt 5 - Einrichtung auf dem Handy
Für Android gibt es das Programm ICSx⁵. Dieses Programm macht genau das, was ich wollte. Es lädt regelmäßig eine ICS-Datei herunter. In der Kalenderapp wird dann ein neuer Kalender angezeigt. Man kann noch Optionen wie Erinnerungszeiten setzen. Für mich reicht es aber, wenn ich die Einträge erst einmal im Kalender sehen kann.
Schritt 6 - Automatischer Hook in Emacs
Wenn ich etwas in Emacs an meiner Agenda ändere und speichere, soll ein Hook automatisch ausgeführt werden. Dieser Hook soll dann die Methode von oben aufrufen. Der ganze Vorgang geht erstaunlich schnell. Vielleicht liegt es daran, dass ich noch eine überschaubare Agenda habe.
(defun my/org-agenda-maybe-push ()
"Prüft beim Speichern, ob es die Agenda-Datei ist und startet dann den Export."
(when (and (buffer-file-name)
(string= (file-truename (buffer-file-name))
(file-truename (expand-file-name "~/org/Agenda.org"))))
;; Wir stellen sicher, dass die Export-Funktion geladen ist
(unless (fboundp 'my/export-and-push-agenda)
(require 'org-agenda))
(my/export-and-push-agenda)))
;; Den Hook global registrieren
(add-hook 'after-save-hook #'my/org-agenda-maybe-push)
Fazit
Um meine Termine unterwegs schnell einsehen zu können, reicht dieser Ansatz bisher. Es ist genügend automatisiert, damit ich mich auf den Prozess soweit verlassen kann. In meinem Backlog liegt aber als Todo bereits die Idee, eine richtige Caldav-Synchronisation mit Emacs gegen einen Caldav-Server einzurichten. Aber das ist noch Zukunftsmusik.