This post summarizes my attempts to manage my CalDav based calendars in Emacs.
Emacs has three main packages for managing a diary:
M-x org-agenda, a command that collects time-stamped information from your OrgMode files and present them in an agenda view. By default OrgMode 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).
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 at least to be able to use Emacs to view your CalDav calendars (this is relatively easy).
Secondary goal: you want to be able to modify your events directly from Emacs and sync them on the server (more difficult).
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 backends, 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
(require 'calfw) (require 'calfw-ical) (cfw:open-ical-calendar "http://SERVER/.../basic.ics")
replacing the URL with the URL of your iCal calendar. (See … for determing the URL of your iCal calendar.)
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 OrgMode 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.
org-caldav provides two-way synchronization with CalDav servers.
org-caldav downloads calendars as OrgMode documents, creating one header per event.
The package uses an
ID property to mantain the correspondence
between the events in the OrgMode files and those in the CalDav
events. This allows to synchronize events between Emacs and a CalDav
For each CalDav calendar, org-caldav allows specify two OrgMode 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 OrgMode. This is a kind of a safety net, so that
org-caldav does not mess up with the OrgMode 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 ArchLinux 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
(setq org-caldav-calendars '((:calendar-id "work@whatever" :files ("~/org/work.org") :inbox "~/org/from_work.org") ... ) )
org-caldav to store events from
~/org/from_work.org and push events in
~/org/work.org to the
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 just uploaded:
* Do something :PROPERTIES: :ID: 905F350D-F437-4AEC-B73C-52A2B72ECBCD :END: <2015-06-29 Mon 19:00-19:25>
One important aspect to remark is that deletions are also synced. If you delete files or events, these changes will be uploaded next time you sync. 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
org-caldav-generate-ics. The problem was due to a call
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.
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
calling the function.
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 OrgMode agenda, add their paths
If you do not know what is the iCal or CalDav URL of your calendar, you can read the post I wrote on the matter: Determining the URL of CalDAV calendars
Get in touch