Emacs and Caldav
This post summarizes my attempts to manage my CalDAV based calendars in Emacs.
Managing your Calendar/Diary in Emacs
Emacs has three main packages for managing a diary:
- Diary Mode ships with Emacs and allows one to manage an agenda stored as a plain text file on the computer. It has many interesting features and I used it in the past, but it lacks any of the sharing and syncing functions we are used to.
- OrgMode ships with
M-x org-agenda, a command that collects time-stamped information from your Org mode files and present them in an agenda view. By default Org mode ships with function to sync files among different devices, but it does not provide any support for external protocols, such as CalDAV (extensions exist, though).
- Calfw is a calendar viewing framework with extensions to sync with CalDAV-based calendars.
Starting Point and Goals
Starting point: you are managing your appointments with Thunderbird Lightning or another CalDAV capable app and store them on various cloud services, such as Google Calendar, NextCloud, and iCloud.
Primary goal: you want to use Emacs to view your CalDAV calendars. (Spoiler: this is easy!)
Secondary goal: you want to be able to modify your events directly from Emacs and sync them on the server (Spoiler: more difficult).
Viewing your CalDAV Calendars in Emacs
Solution 1. Calfw
Calfw is a framework which presents a calendar in a way similar any modern calendaring application. There are of course some limitations in the experience and interaction, mainly related to the Calendar views being rendered in pure text.
The package comes with extensions meant to integrate with different back-ends, including iCal. This makes it possible to download a copy of your CalDAV calendars locally and then display it using Emacs.
To use it, install the required packages:
M-x package-install calfw M-x package-install calfw-ical
and then add to your
.emacs, replacing the URL with the URL of your
iCal calendar; see What is the iCal/CalDav URL of my calendar? for determining the URL of your iCal
(require 'calfw) (require 'calfw-ical) (cfw:open-ical-calendar "http://SERVER/.../basic.ics")
You can now display your calendar with:
The Calfw repository is very well documented and provides detailed instructions on how to configure the package to display different calendars.
In synthesis, it is a very nice solution, but it does not work for me, since I prefer to use Org Mode and Diary to display my agenda in Emacs. Another limitation is that the week and month view might be cumbersome to use, if your calendars have many events, since there is only so much you can do with a textual table. In any other respect, it is impressive.
Solution 2. Org CalDAV
org-caldav provides two-way synchronization with CalDAV servers. It downloads calendars as Org Mode documents, creating one header per event.
The package uses an
ID property to maintain the correspondence between
the events in the Org Mode files and those in the CalDAV events. This
allows to synchronize events between Emacs and a CalDAV server.
For each CalDAV calendar, org-caldav allows specify two Org Mode files:
one for receiving the events downloaded from the server and the other
for uploading events to the server or keeping the events your write
directly in Org Mode. This is a kind of a safety net, so that
org-caldav does not mess up with the Org Mode files you do not want to
be touched by the package.
The package is available on Melpa:
M-x package-install org-caldav
Note: The installation on my machine reported an error, but the package gets installed. I am running Emacs 25.3.1 on a Arch Linux box.
To use the package, you need to specify what files get synced. This is
accomplished by setting the
Quoting from the documentation, you might add something like the
following to your
.emacs, which tells
org-caldav to store events
~/org/from_work.org and push events in
~/org/work.org to the server:
(setq org-caldav-calendars '((:calendar-id "work@whatever" :files ("~/org/work.org") :inbox "~/org/from_work.org") ... ) )
You start the synchronization with:
Notice that synchronization is bi-directional and changes made locally in all files, including those used to receive events, are pushed to the server.
Notice that only basic information is synced (title, description, and date-time); other information is simply discarded (e.g., no location, no reminders).
To add an event, simply add a new time-stamped entry to your org file and sync again. For instance:
* Do something <2015-06-28 Sun 18:00-20:00>
After a successful sync,
org-caldav will set the
ID of the event you
* Do something :PROPERTIES: :ID: 905F350D-F437-4AEC-B73C-52A2B72ECBCD :END: <2015-06-29 Mon 19:00-19:25>
Note that deletions are also synced: if you delete files or events, these changes will be uploaded next time you sync. More important: if the events you delete contain invitations, emails will be sent to the attendees, even for events in the past. Be careful about this point if you want to this package a try (I did it and ended up spamming some of my collaborators, with cancellations of old events.)
One variable worth mentioning is:
that allows you to control whether events on the server are really deleted.
If your sync state somehow gets broken, you can make a clean slate by doing:
C-u M-x org-caldav-delete-everything
The package is very well documented and you can consult it for the finer grained details.
As a final remark I need to mention that in an old configuration of
mine, the package did not work as-is, because of a problem in the
signature of an
org-mode function. To make it work I had to change the
org-caldav-generate-ics. The problem was due to a call to
org-icalendar--combine-files. The fix is relatively simple. Look for
the following piece of code (in
;; New exporter (Org 8) Signature changed in version 8.2.8 (if (version< ;; org-version "8.2.8") (apply 'org-icalendar--combine-files nil orgfiles) (apply 'org-icalendar--combine-files orgfiles)
and replace it with:
(apply 'org-icalendar--combine-files nil orgfiles)
See Adapt to signature change of org-icalendar–combine-files for some notes on the matter.
Solution 3. iCal to Diary
Another solution worth mentioning is using an Emacs Lisp function to import your CalDAV files to Emacs diary files.
These files can be viewed using the diary package or with the agenda provided by org mode.
Add the following code to your
(setq diary-location "a directory where you store your diary files") ; calendars you want to download ; each item links to a remote iCal calendar (setq calendars '(("calendar1" . "http://.../home.ics") ("calendar2" . "http://.../work.ics") ... )) (defun getcal (url file) "Download ics file and add it to file" (let ((tmpfile (url-file-local-copy url))) (icalendar-import-file tmpfile file) (kill-buffer (car (last (split-string tmpfile "/")))))) (defun getcals () "Load a set of ICS calendars into Emacs diary files" (interactive) (mapcar #'(lambda (x) (let ((file (concat diary-location (car x))) (url (cdr x))) (message (concat "Loading " url " into " file)) (find-file file) ;; (flush-lines "^[& ]") ;; if you import ical as non marking (erase-buffer) ;; to avoid duplicating events (getcal url file) )) calendars))
When you invoke the function with
M-x getcals it will start
downloading the iCal files specified in the variable
generate one diary file per calendar. *Notice that the diary files must
exist in the directory specified by
diary-location before calling the
The function erases the files and retrieves all the events every time. It is an effective strategy, since iCals don’t grow much (unless you attach documents to events). As a consequence of the previous point, each iCal must be connected to a different diary file.
To view the files in the diary, your diary file should include all files you download:
#include "calendar1" #include "calendar2" ...
To view the Emacs diary file in your Org Mode agenda, add their paths to