How to sync and manage your CalDAV-calendars and your CardDAV-addressbooks via the terminal
Published: Wednesday, August 12, 2020
If you want to sync and manage both your CalDAV-calendars and your CardDAV-addressbooks via the command-line you’re in luck! There’s three pieces of software called vdirsyncer, Khal and Khard that does just that for you. It happen to be how I’ve been doing it for several years now and I think it’s time to write about it.
Setting it all up is pretty easy and straightforward, as long as you know what to do. You can either read the documentation or just be happily lazy and copy my pretty generic setup that I use with both my Nextcloud account and a public read-only calendar from Google, so I can keep myself à jour with our holidays here.
How to get started with vdirsyncer
It’s wise to start with the backend. The file file you need to edit is
~/.config/vdirsyncer and this is what my configuration file looks like (It should be safe to copy this and the other configuration files and just make minor obvious adjustments to them):
[general] # A folder where vdirsyncer can store some metadata about each pair. status_path = "~/.config/vdirsyncer/status/" # CardDAV # ======= # Operation Tulip # --------------- [pair nextcloud_contacts] # A `[pair <name>]` block defines two storages `a` and `b` that should be # synchronized. The definition of these storages follows in `[storage <name>]` # blocks. This is similar to accounts in OfflineIMAP. a = "nextcloud_contacts_local" b = "nextcloud_contacts_remote" # Synchronize all collections that can be found. # You need to run `vdirsyncer discover` if new calendars/addressbooks are added # on the server. collections = ["from a", "from b"] # Synchronize the "display name" property into a local file (~/.contacts/displayname). metadata = ["displayname"] # To resolve a conflict the following values are possible: # `null` - abort when collisions occur (default) # `"a wins"` - assume a's items to be more up-to-date # `"b wins"` - assume b's items to be more up-to-date #conflict_resolution = null [storage nextcloud_contacts_local] # A storage references actual data on a remote server or on the local disk. # Similar to repositories in OfflineIMAP. type = "filesystem" path = "~/.config/vdirsyncer/contacts/" fileext = ".vcf" [storage nextcloud_contacts_remote] type = "carddav" url = "https://cloud.operationtulip.com/remote.php/dav/addressbooks/users/<USERNAME>/contacts/" username = "<USERNAME>" password = "<PASSWORD>" # CalDAV # ====== # Operation Tulip # --------------- [pair nextcloud_calendar] a = "nextcloud_calendar_local" b = "nextcloud_calendar_remote" collections = ["personligt", "fdelsedagar", "temadagar"] # Calendars also have a color property metadata = ["displayname", "color"] [storage nextcloud_calendar_local] type = "filesystem" path = "~/.config/vdirsyncer/calendars/" fileext = ".ics" [storage nextcloud_calendar_remote] type = "caldav" url = "https://cloud.operationtulip.com/remote.php/dav/" username = "<USERNAME>" password = "<PASSWORD>" # Helgdagar # --------- [pair helgdagar] a = "helgdagar_local" b = "helgdagar_remote" collections = null [storage helgdagar_local] type = "filesystem" path = "~/.config/vdirsyncer/calendars/helgdagar/" fileext = ".ics" [storage helgdagar_remote] type = "http" url = "https://calendar.google.com/calendar/ical/sv.swedish%23holiday%40group.v.calendar.google.com/public/basic.ics" read_only = "true"
My calendar “fdelsedagar” is actually named “Födelsedagar”, vdirsyncer just removed the letter “ö” for some reason.
When you’re done setting up vdirsyncer you need to ‘discover’ both the calendars and the addressbooks with the command:
$ vdirsyncer discover
You can then synchronize everything with the command:
$ vdirsyncer sync
And you’re done! I highly recommend you scheduling the command using Cron. I have this in my configuration file for it to sync every three hours:
*/180 * * * * vdirsyncer sync
How to get started with Khal
The config is located at
~/.config/khal/config and my configuration file looks like this:
[default] default_calendar = Personligt highlight_event_days = true [view] dynamic_days= false [locale] unicode_symbols = false local_timezone = Europe/Stockholm default_timezone = Europe/Stockholm timeformat = %H:%M dateformat = %d.%m longdateformat = %d.%m.%Y [calendars] [[Personligt]] path = ~/.config/vdirsyncer/calendars/personligt/ color = light cyan [[Födelsedagar]] path = ~/.config/vdirsyncer/calendars/fdelsedagar/ color = dark blue [[Helgdagar]] path = ~/.config/vdirsyncer/calendars/helgdagar color = yellow
Once it’s setup you can start using Khal. It comes with both a text-based user interface as well as a command-line interface. The syntax for the commands can be a bit tricky to figure out if you’re new and clueless, but the interactive mode is really simple.
When I was new I had issues remembering the syntax for the time and date. For some reason you format the syntax like this:
<DD>.<MM>. <HH>:<MM> <HH>:<MM>, for an example:
12.08. 18:00 22:00. Which doesn’t make much sense to me. Something more sensible would be something like
Anyway. Here’s a few examples of what you can do via the command-line:
Print all available calendars
$ khal printcalendars
Print upcoming events
List all events between today and 30 days forward:
$ khal list today 30d Tuesday, 25.08.2020 Linux födelsedag (R)
Adding a new entry
On a specific date (in this example it’s October 12 between the clock 18:00 and 22:00):
$ khal new 12.08. 18:00 22:00 "Laundry"
You can also specify dynamic dates, alarms, a specific calendar and a description:
$ khal new tomorrow 18:00 22:00 -alarm 10m -a <CALENDAR> "<TITLE> :: <DESCRIPTION>"
If you want it to be a recurring event:
$ khal new 12.08. "<EVENT>" -r <yearly/monthly/weekly/daily>
There’s also the flag
-u, –until if you want it to be for a limited time only.
Searching for an event
$ khal search <KEYWORD>
Editing an event
When you want to edit an event you simply use a search keyword (like “Linus”):
$ khal edit --show-past "Linus" 28.12-28.12 Linus Torvalds födelsedag (R) Edit? [n]o [q]uit [s]ummary [d]escription da[t]etime range re[p]eat [l]ocation [c]ategories [a]larm [D]elete:
Importing a calendar from a file
It’s also possible to import calendars from ICS-files:
$ khal import -a <CALENDAR NAME> <FILE>
The interactive mode
And if you want to use the interactive mode:
$ khal interactive
You can add a new event using the interactive mode without first running Khal, even though this method can be a bit annoying if you do it wrong:
$ khal new -i summary: Test datetime range: 12.08 13.08 13:00 15:00 critical: Could not parse "12.08 13.08 13:00 15:00". critical: Please check your configuration or run `khal printformats` to see if this does match your configured [long](date|time|datetime)format. critical: If you suspect a bug, please file an issue at https://github.com/pimutils/khal/issues/
To be honest I have no idea how the “datetime” syntax work here. I only use the shell commands myself, but I highly recommend using the proper interactive interface with
khal interactive and then pressing the key
I also highly recommend you reading the documentation for Khal regarding all the available commands and how they all work.
How to get started with Khard
For some reason the name scheme doesn’t follow its sibling. For Khard the configuration file is
~/.config/khard/khard.config. Here’s my configuration file:
[general] debug = no default_action = list editor = nvim merge_editor = colordiff [addressbooks] [[Personal]] path = ~/.config/vdirsyncer/contacts/contacts [contact table] # display names by first or last name: first_name / last_name display = first_name # group by address book: yes / no group_by_addressbook = no # reverse table ordering: yes / no reverse = no # append nicknames to name column: yes / no show_nicknames = no # show uid table column: yes / no show_uids = yes # sort by first or last name: first_name / last_name sort = first_name [vcard] # extend contacts with your own private objects # these objects are stored with a leading "X-" before the object name in the vcard files # every object label may only contain letters, digits and the - character # example: # private_objects = Jabber, Skype, Twitter private_objects = Jabber # preferred vcard version: 3.0 / 4.0 preferred_version = 3.0 # Look into source vcf files to speed up search queries: yes / no search_in_source_files = no # skip unparsable vcard files: yes / no skip_unparsable = no
khard prints all the contacts like this:
$ khard <INDEX NUMBER> <NAME> cell: <PHONE NUMBER> home: <E-MAIL ADRESS> <ID>
You can show an contact with the argument
$ khard show alex Name: Alexander Address book: Personal Phone cell: <PHONE NUMBER> E-Mail home: <E-MAIL ADRESSS> Address home: <ADRESS> Miscellaneous UID: <UUID>
If you want to edit a contact using your editor:
$ khard edit alex
The file is to large to show here, but it contains a lot of sane syntax and comments to help you understand how it works. Here’s the part with “important dates”:
# important dates # Formats: # vcard 3.0 and 4.0: yyyy-mm-dd or yyyy-mm-ddTHH:MM:SS # vcard 4.0 only: --mm-dd or text= string value # anniversary Anniversary : # birthday Birthday : 1970-01-01
I once again recommend you reading the documentation for Khard regarding all the available commands and how they all work.
I didn’t go thought every possible bit, but it should be enough to get you started and going! I hope you enjoy this software as much as I do.
If you want to leave feedback you can do so by either sending me a message via e-mail or by commenting on my post for this article on the fediverse.