Sync private iCloud calendar with MagicMirror

posted in: Allgemein | 0

Get app-specific password from iCloud

vdirsyncer needs an app-specific password to connect to iCloud. You can create one in your AppleID settings like descibed in Apple’s support document for using app-specific passwords: https://support.apple.com/en-us/HT204397

Create one and write it down for later.

Install and set up vdirsyncer

vdirsyncer needs to be installed on the machine that hosts your mirror software. On my raspbian system, vdirsyncer was not available via apt. So I hat to install it via pip.

 

STEP 1: Have you installed this repository?

If not, run this installation script command:

curl -s https://packagecloud.io/install/repositories/pimutils/vdirsyncer/script.deb.sh | sudo bash

STEP 2: Install the package
sudo apt-get install vdirsyncer-latest=0.19.3~19+gb50f9def.d20231217

Installation of vdirsyncer

Now we get vdirsyncer and it’s dependencies:

sudo apt-get install libxml2 libxslt1.1 zlib1g python3

sudo apt-get -y install vdirsyncer

pip3 install --user --ignore-installed vdirsyncer

(This is the quick and easy way, it should suffice for most users. For a more clean way to install it, please refer to https://vdirsyncer.pimutils.org/en/stable/installation.html#the-clean-hard-way)

Now, change the first line of your vdirsyncer executable to use only python3:

nano ~/.local/bin/vdirsyncer


And change the first line to #!/usr/bin/python3

I needed to create a symlink to the vdirsyncer executable. So we’re doing this:

sudo ln -s /home/pi/.local/bin/vdirsyncer /usr/bin/vdirsyncer

Now you should be able to use the vdirsyncer command from the command line.

Create a folder for the calendar file

As mentioned in this post, we can put the calendar file into the modules folder to access it via the calendar module. So we create a folder for our calendars:

mkdir /home/pi/MagicMirror/modules/calendars

Configure vdirsyncer

Create a config file in ~/.vdirsyncer/config and open it with nano:

mkdir ~/.vdirsyncer
touch ~/.vdirsyncer/config
nano ~/.vdirsyncer/config

Here’s an example configuration to use with iCloud. Just copy it into your opened file in nano and enter your iCloud credentials.

# vdirsyncer configuration for MagicMirror.
#
# Move it to ~/.vdirsyncer/config or ~/.config/vdirsyncer/config and edit it.
# Run `vdirsyncer --help` for CLI usage.
#
# Optional parameters are commented out.
# This file doesn't document all available parameters, see
# http://vdirsyncer.pimutils.org/ for the rest of them.

[general]
# A folder where vdirsyncer can store some metadata about each pair.
status_path = "~/.vdirsyncer/status/"

# CALDAV Sync
[pair iCloud_to_MagicMirror]
a = "Mirror"
b = "iCloud"
collections = ["HERE-GOES-THE-UUID-OF-THE-CALENDAR-YOU-WANT-TO-SYNC"]

# Calendars also have a color property
metadata = ["displayname", "color"]

[storage Mirror]
# We need a single .ics file for use with the mirror (Attention! This is really slow on big amounts of events.)
type = "singlefile"
# We'll put the calendar file to a readable location for the calendar module
path = "/home/pi/MagicMirror/modules/calendars/%s.ics"

[storage iCloud]
type = "caldav"
url = "https://caldav.icloud.com/"
# Authentication credentials
username = "YOUR-ICLOUD-EMAIL-ADDRESS"
password = "HERE-GOES-YOUR-APP-SPECIFIC-ICLOUD-PASSWORD"
# We only want to sync in the direction TO the mirror, so we make iCloud readonly
read_only = true
# We only want to sync events
item_types = ["VEVENT"]
# We need to keep the number of events low, so we'll just sync the next month
# Adjust this to your needs
start_date = "datetime.now() - timedelta(days=1)"
end_date = "datetime.now() + timedelta(days=30)"


Make sure, you use the start_date and end_date parameters to keep the number of calendar events low. I tried syncing with 700+ events and it is really really sloooooow! Especially when using the singlefile option for the mirror.

Add your iCloud credentials like shown in the config file.

Running vdirsyncer as a systemd.timer for automatic sync

Install the vdirsyncer.service and vdirsyncer.timer files to /etc/systemd/user:

curl https://raw.githubusercontent.com/pimutils/vdirsyncer/master/contrib/vdirsyncer.service | sudo tee /etc/systemd/user/vdirsyncer.service
curl https://raw.githubusercontent.com/pimutils/vdirsyncer/master/contrib/vdirsyncer.timer | sudo tee /etc/systemd/user/vdirsyncer.timer

By default, the syncer is started every 15 minutes. You can change it by configuring the times in the vdirsyncer.timer file:

sudo nano /etc/systemd/user/vdirsyncer.timer

Now we activate the timer in systemd:

systemctl --user enable vdirsyncer.timer


Let vdirsyncer discover the collections and do the inital sync

Now, we have to find out, which collection we want to sync. Let vdirsyncer discover everything:

vdirsyncer discover

You should get an output like this (depending on the number of calendars you have in iCloud):

Discovering collections for pair iCloud_to_MagicMirror
Mirror:
iCloud:
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 1")
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 2")
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 3")
warning: No collection "HERE-GOES-THE-UUID-OF-THE-CALENDAR-YOU-WANT-TO-SYNC" found for storage Mirror.
Should vdirsyncer attempt to create it? [y/N]:

Choose N for now and press return.
Now replace the HERE-GOES-THE-UUID-OF-THE-CALENDAR-YOU-WANT-TO-SYNC in the config with the UUID of the calendar you want to sync.
We assume, we want to sync “Calendar 2”.

So line 19 of your config should look like this:

collections = ["xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx"] 

Now, we can run the discover again and make the first sync:

vdirsyncer discover

You should get something loke this:

Discovering collections for pair iCloud_to_MagicMirror
Mirror:
iCloud:
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 1")
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 2")
  - "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" ("Calendar 3")
warning: No collection "xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx" found for storage Mirror.
Should vdirsyncer attempt to create it? [y/N]:

Choose y and press enter. Now we can start the sync with:

vdirsyncer sync

You should find a single calendar file in /home/pi/MagicMirror/modules/calendars/ with the UUID of your calendar as filename like 221FEE8-E8B4-4D07-9402-8638529919EC.ics in this example.

Add the calendar file to the calendar module of the mirror

Now, we just have to add the URL to the calendar file to our calendar module of the mirror like this:

{
    module: 'calendar',
    position: 'top_left',   // This can be any of the regions. Best results in left or right regions.
    config: {
        maximumNumberOfDays: 10,
        maximumEntries: 7,
        calendars: [
                {
                        url: 'http://localhost:8080/modules/calendars/xxxxxxxxxxxx-A1234-B12345-C12345-xxxxxxxxxxxx.ics', symbol: 'calendar' } ] } }, 

If everything went well, your MagicMirror is now automatically syncing your iCloud calendar with it’s calendar module

 

  1. Go to the Google API Manager
  2. Create a new project under any name.
  1. Within that project, enable the “CalDAV” and “CardDAV” APIs (not the Calendar and Contacts APIs, those are different and won’t work). There should be a search box where you can just enter those terms.

  2. In the sidebar, select “Credentials”, then “Create Credentials” and create a new “OAuth Client ID”.

    You’ll be prompted to create a OAuth consent screen first. Fill out that form however you like.

    After setting up the consent screen, finish creating the new “OAuth Client ID’. The correct application type is “Desktop application”.

  3. Finally you should have a Client ID and a Client secret. Provide these in your storage config.

The token_file parameter should be a path to a file where vdirsyncer can later store authentication-related data. You do not need to create the file itself or write anything to it.

[googleterms] See ToS, section “Confidential Matters”.

Note

You need to configure which calendars Google should offer vdirsyncer using a secret settings page.

google_calendar

Google calendar.

[storage example_for_google_calendar]
type = "google_calendar"
token_file = "..."
client_id = "..."
client_secret = "..."
#start_date = null
#end_date = null
#item_types = []

crontab -e

@reboot /home/pi/MagicMirror/start.sh #!/bin/bash sleep 20 cd /home/pi/MagicMirror/ /usr/lib/node_modules/pm2/bin/pm2 resurrect && /usr/lib/node_modules/pm2/bin/pm2 start all