Evolution calendars in Org Mode's agenda
On a Linux GNOME desktop it’s convenient to use Evolution as a calendar sync tool (for Fastmail in my case). Once a configuration has been created, you can stash away the <mumble>.source
files, drop them in place on another machine and (subject to re-entering your password), everything just works.
Such calendars are also available through GNOME Calendar, which is a little easier on the eye for casual glances.
Spending a lot of time in emacs, and enjoying org-mode more all of the time, I’d really like all of my calendar events shown in the org agenda. Achieving that consists of two parts:
- getting the events out of the Evolution calendar into a more pliable format,
- adding the events to an org file so that they are shown in the agenda.
To simplify the implementation, I use khal as a parser for vCalendar files. It’s definitely the case that you could use vdirsyncer
and avoid Evolution, but given that the GUI is sometimes convenient, this way requires configuring only a single sync tool.
Getting events out of Evolution is achieved by grovelling in the sqlite database that Evolution uses as a cache:
#!/usr/bin/env zsh
set -e
calendir=${HOME}/v/calendar
dblist=(
9ccf564d3aac1c2a02a29be8d44720ec7ea0d5a3
62870b2ad9e6a1e04b800fea354ae44e6b20f754
ca1c1bad361ca6e59b57d86f9e06c3fd5ffd4ccc
)
convert_one_calendar ()
{
# Evolution sqlite db:
local db=$1
# Khal directory to populate:
local dst=$2
[[ -d ${dst} ]] || mkdir -p ${dst}
cd ${dst}
sqlite3 ${db} "select ECacheOBJ from ECacheObjects" |\
csplit \
--quiet \
--elide-empty-files \
--suffix-format "%03d.ics" \
- \
'/BEGIN:VEVENT/' '{*}'
}
# Convert each of the Evolution cache databases to a Khal compatible
# directory of .ics files:
#
for db in ${dblist}; do
rm -r ${calendir}/${db}
convert_one_calendar \
${HOME}/.cache/evolution/calendar/${db}/cache.db \
${calendir}/${db}
done
# Write a khal config to match:
config_dir=${HOME}/.config/khal
config_file=${config_dir}/config
[[ -d ${config_dir} ]] || mkdir ${config_dir}
cat > ${config_file} <<EOF
[calendars]
EOF
for db in ${dblist}; do
cat >> ${config_file} <<EOF
[[${db}]]
path = ${calendir}/${db}
type = calendar
EOF
done
cat >> ${config_file} <<EOF
[locale]
timeformat = %H:%M
dateformat = %Y-%m-%d
longdateformat = %Y-%m-%d
datetimeformat = %Y-%m-%d %H:%M
longdatetimeformat = %Y-%m-%d %H:%M
EOF
In the script, update the dblist
variable to include the list of Evolution calendars that you want to convert - you can see their names in ~/.cache/evolution/calendar
. The outputs of the script end up in ~/v/calendar
, or update the calendir
variable to suit your preferences.
Note that the script will overwrite your current khal configuration file.
Now we have a pile of .ics
files and a khal configuration configured to parse them. Using that in the org agenda requires another conversion:
#!/usr/bin/env zsh
diary=${HOME}/.emacs.d/diary.org
start=$(date --date='-30days' '+%Y-%m-%d')
end=$(date --date='+30days' '+%Y-%m-%d')
khal list \
--day-format "" \
--format "* {title}{repeat-symbol}{alarm-symbol}
<{start-date} {start-end-time-style}>
{description}" \
${start} ${end} \
> ${diary} \
2> /dev/null
This one is less thorough - it only includes events for the previous 30 and next 30 days. You could tweak this, of course, but I find it sufficient for awareness in the org agenda.
The script generates ~/.emacs.d/diary.org
, which you should ensure to include in org-agenda-files
so that the events contained therein are displayed.
That’s it! I run the pair of scripts in sequence every hour, but you can decide what to do about that.
Originally I was generating an emacs diary file, and that works out okay. It means that
org-timeblock
is unaware of the entries, though, which can sometimes be an inconvenience.