Emacs and Google Calendars
Tuesday, March 6, 2007
Last year, I did a weblog posting where I talked about the types of things that I use Emacs for. On the topic of Contacts and Calendar/Diary, I was a bit uncertain as to whether I would use Emacs or not:
Contacts and Calendar/Diary. I'm swaying a bit on using Emacs for this as I use gmail for mail (and gmail does a pretty good job of recording email contacts) and I've just started using Google Calendar. Although I've toyed with the idea of accessing my gmail emails using mutt and Emacs, I'm not sure which way I'll go. On the one hand, I like being able to control the apps from within Emacs; however, on the other hand, there are some good reasons for having these applications available online (and I don't like continual sync-ups). Any thoughts/opinions others have on this would be appreciated!I recently moved over to VM/Emacs for accessing my emails from Gmail; however, I didn't get any good suggestions on how best to integrate online (Google) versions of Contacts, Calendar, and Diary with offline (Emacs) versions of the same data. I like being able to access them from within Emacs; however, I do sometimes travel or work onsite without access to my usual development machine, so it is useful to be able to have some form of online access to these as well. The ideal solution would be to be able to synchronize my Emacs calendar and contacts with the Google ones.
Well, recently, Google announced the Google Data APIs. These APIs "provide a simple standard protocol for reading and writing data on the web". Since one of the APIs is for Google Calendar, I started thinking about writing an Emacs library so that I could write Emacs diary entries to Google Calendar. However, before I had gotten very far, T.V. Raman (the primary developer of Emacspeak [a speech interface for Emacs] announced an early version of his Emacs Client for Google Services - an implementation of the Google Data APIs for Emacs! Needless to say, I was pretty thrilled and immediately downloaded the library to try it out. It is still a "work in progress"; however, it is quite usable in its current state. A number of people had some difficulties getting it set up. Basically, all I did to get it going was:
- Downloaded and unpacked the library
- Create a stub file called "g-cus-load.el" and put a single statement in it:
(provide 'g-cus-load)(this is probably meant to be a customizations file and, while it isn't necessary to have it, the library won't load properly without it)
- Ran "make" to byte-compile everything
- Added the following lines to my .emacs file:
(add-to-list 'load-path "path/to/g-client") (load-library "g") (setq g-user-email "email@example.com") (setq g-html-handler 'w3m-buffer)The w3m line is so that I can display Google Calendar entries inside Emacs using w3m instead of an external browser
So, once you have the library installed, you can start up Calendar in Emacs (using "M-x calendar"), go to a date, press "c a" and you add an entry to your Google Calendar from Emacs. Then, press "c c" and get a listing of the Google Calendar entries that you have for that date (or the number of dates that you've specified that you want displayed):
That's nice; however, it is only showing those entries that have been entered in Google Calendar and there is no "local" update of the Emacs diary when you add a Google Calendar entry in Emacs. So, I modified one of the gcal functions (I initially tried to do it with a defadvice but, in the end, had to resort to re-defining one of the gcal functions) so that an Emacs diary entry is written at the same time that a Google Calendar entry is written. Here's the code that I put in my .emacs:
;; Write to Diary any entry added to Google Calendar (eval-after-load "gcal" '(progn (defun gcal-read-event (title content where start end who transparency status) "Prompt user for event params and return an event structure." (interactive (list (read-from-minibuffer "Title: ") (read-from-minibuffer "Content: ") (read-from-minibuffer "Where: ") (gcal-read-calendar-time "Start Time: ") (gcal-read-calendar-time "End Time: ") (gcal-read-who "Participant: ") (gcal-read-transparency) (gcal-read-status))) (save-excursion (let ((pop-up-frames (window-dedicated-p (selected-window)))) (find-file-other-window (substitute-in-file-name diary-file))) (when (eq major-mode default-major-mode) (diary-mode)) (widen) (diary-unhide-everything) (goto-char (point-max)) (when (let ((case-fold-search t)) (search-backward "Local Variables:" (max (- (point-max) 3000) (point-min)) t)) (beginning-of-line) (insert "n") (forward-line -1)) (let* ((dayname) (day (substring start 8 10)) (month (substring start 5 7)) (year (substring start 0 4)) (starttime (substring start 11 16)) (endtime (substring end 11 16)) (monthname (calendar-month-name (parse-integer month) t)) (date-time-string (concat (mapconcat 'eval calendar-date-display-form "") " " starttime " - " endtime))) (insert (if (bolp) "" "n") "" date-time-string " " title)) (bury-buffer) (declare (special gcal-auth-handle)) (let ((event (make-gcal-event :author-email (g-auth-email gcal-auth-handle)))) (setf (gcal-event-title event) title (gcal-event-content event) content (gcal-event-where event) where (gcal-event-when-start event) start (gcal-event-when-end event) end (gcal-event-who event) who (gcal-event-transparency event) transparency (gcal-event-status event) status) event)))))Now, whenever I add an entry to Google Calendar from the Emacs calendar (using "c a"), the same entry is added to my Emacs diary file. So, if I press "d" on a date in the Emacs calendar, I get the same calendar entries showing up in my Emacs Diary as are in my Google Calendar. Here's a sample of the Emacs diary display:
And the equivalent entries in Google Calendar:
Now, I can use one command in the Emacs Calendar ("c a") to add entries to both my Emacs diary and my Google Calendar. So, when I am at home working in Emacs, I can just quickly refer to the Emacs diary for daily events. When I'm travelling or away from my development machine, I can check my Google Calendar for events. Since The Google Data API and the Emacs Client for Google Services are still in development, there are a number of things that you can't do at the moment (like add alerts or synchronize contacts); however, this should come in a matter of time.
Update-2007-07-26: There have been a number of updates to gcal and some of the above is no longer correct. Here are the things to note:
- When installing g-client, you will need to download the entire
emacspeak project (which includes g-client) as the Makefile for
g-client has some dependencies on objects in emacspeak. This can be
svn checkout http://emacspeak.googlecode.com/svn/trunk emacspeak
- When building g-client, you'll first have to run "make
config" in the "emacspeak/lisp" directory and then run "make" in
the emacspeak/lisp/g-client" directory. If you're using Aquamacs
Emacs on a Mac, you'll have to make certain that you change
"EMACS = emacs"statement in both Makefiles to
"EMACS = /Applications/Aquamacs\ Emacs.app/Contents/MacOS/Aquamacs\ Emacs"
- The setup statements in your .emacs file should be:
(add-to-list 'load-path "path/to/g-client") (load-library "g") (setq g-user-email "firstname.lastname@example.org") (gcal-emacs-calendar-setup) (setq g-html-handler 'w3m-buffer)
- You no longer need the modifications
gcal-read-eventas this is now supported directly in gcal.