A simple integration of org capture with Google Calendar
Keywords: org mode org capture Google Calendar Elisp Bash scripting
In this solution I only consider this use case: the user capture an appointment using org capture and the appointment is automatically uploaded to Google Calendar. Therefore, if the event is created in Google Calendar then Emacs is not updated, or if the event is updated or deleted in one them, the other will not be modified.
I just want to capture an appointment from Emacs and have the notification in the mobile phone. To quickly capture things I use org capture using different templates (see the related post ../post-aviso-citas/post.html). The template for appointments creates a new entry at the top of the file citas.org
.
Org capture allows the execution of a hook (an elisp function) when the capture ends, so it is possible from that function to run a script that obtains the data from the first appointment. This data can be parsed to obtain: the title, the date, and the start time of the appointment. These tokens can be passed to a CLI that communicates with Google Calendar, for instance gcalcli.
The first thing is to install gcalcli
and use Google developer console to create a new Client ID OAuth 2.0
with permission to make requests to Google Calendar. This process is described in the gcalcli
page.
The generated Client ID
and the Secret
are needed to make requests. This info can be stored in a plain text file but I don't like that solution.
So I created a file gcal
with the following content (replace with your Client ID
and Secret
):
--client-id=xxxxxxxxxxxxxxx.apps.googleusercontent.com --client-secret=xxxxxxxxxxxxxxxxx
then I encrypted using gpg
gpg -c gcal
this will ask you for a password. Finally remove the original (plain text file)
rm gcal
When I create a new appointment, the capture template adds this info to the file:
** CITA Reunión :LOGBOOK: Created: [2021-01-30 sáb 19:42] :END: <2021-01-30 sáb 20:00>
So, in my case, the info about the appointment starts with CITA
at line \(l\) and ends at line \(l+4\).
The next step is to write a script to get the appointment's info from the file, parse that info, and use gcalcli
to add the event to Google Calendar (you need to customize this script):
#!/bin/bas # Get data from appointments file and create a new event in Google Calendar #Put here the location of your appointment file: appts=PATH # This is the text that I use in the capture template to signal that is an appointment appts_keyword=CITA # Put here the path of the file where the Client ID and secret are encrypted gcalid=PATH # Put here the name of your Google Calendar calendar=NAME # Get the line where the first appointment starts lineS=$(grep -n $appt_keyword $appts | head -n1 | cut -d':' -f1) # Calculate the last line (in my case I have to add 4) lineE=$(echo "$lineS+4" | bc -l) # Get the info data=$(awk "NR >= $lineS && NR <= $lineE" $PATH) # Parse the info to obtain the date and time when=$(echo "$data" | egrep -e "<[1-9].*>" | tr -d '\>' | tr -d '\<' ) # Get the title title="$(echo "$data" | awk -F"$appts_keyword " '{print $2}')" # Get the date date="$(echo "$when" | cut -d' ' -f1)" # Get the time start="$(echo "$when" | cut -d' ' -f3)" # Reminder time before the appointment reminder=20 # If start is empty I put 9:30 if [ -z "$start" ]; then start="9:30" fi # If date is not empty I create the event if [ -n "$date" ]; then # Just to debug echo "Uploading event to Google Calendar" echo "$title" echo "$date" echo "$start" echo "$duration" echo "$reminder" # Call gcalcli with the information extracted # Also decrypt the file with the identity and secret gcalcli $(gpg -d "gcalid") --calendar $calendar add --title "$title" --when "$date $start" --duration '10' --reminder "$reminder" --noprompt fi
The first time you run this script, the browser opens and Google ask for the user's authorization to the application making the request.
The final step is to call this script when a new appointment is captured. In your Emacs configuration file add:
;; Customize this variable (setq gcalScript "Put here the path to the previous script") ;; Org capture integration with Google Calendar (defun gcal () (let ( (desc (plist-get org-capture-plist :description))) ;; I use Cita in the org-capture template (if (string= desc "Cita") (start-process "gcal" nil gcalScript)) )) (add-hook 'org-capture-after-finalize-hook 'gcal)
So when I launch org capture and select c
(my key to capture a new appointment):
provide the information:
and store the appointment with C-c C-c
, the hook is executed and I can see the appointment in my phone: