From: Frederik Vanrenterghem Date: Wed, 5 Mar 2025 14:12:21 +0000 (+0800) Subject: Voeg post ivm CalDAV en orgmode toe. X-Git-Url: http://git.vanrenterghem.biz/www2.vanrenterghem.biz.git/commitdiff_plain/8189c3b08a7bd316371f9b4dcf49a3dc3875e641?ds=sidebyside Voeg post ivm CalDAV en orgmode toe. --- diff --git a/source/assets/caldav-diagram.png b/source/assets/caldav-diagram.png new file mode 100644 index 0000000..dddb4e9 Binary files /dev/null and b/source/assets/caldav-diagram.png differ diff --git a/source/posts/emacs_calendar.org b/source/posts/emacs_calendar.org new file mode 100644 index 0000000..0b50e9f --- /dev/null +++ b/source/posts/emacs_calendar.org @@ -0,0 +1,109 @@ +:PROPERTIES: +:CREATED: [2025-03-05 Wed 19:57] +:END: +#+date: <2025-03-05 Wed 22:08> +#+title: Using Emacs Org mode to manage my appointments. +#+filetags: :emacs:Linux:automation:elisp: +#+OPTIONS: \n:t + +#+BEGIN_PREVIEW +~Memento, homo, quia pulvis es, et in pulverem reverteris.~ + +After some months of trying out a prototype approach and tweaking it, I believe to have landed on a viable system to manage my appointments seamlessly from within Emacs, on my Android phone and any other calendar application. + +I use a calendar self-hosted on a Nextcloud instance, accessible through the CalDAV protocol - the Distributed Authoring and Versioning protocol for calendar data that came out of Apple in the early 2000s (interesting details on that in [[https://www.researchgate.net/publication/3419713_Open_Calendar_Sharing_and_Scheduling_with_CalDAV][Dusseault, Lisa & Whitehead, J.. (2005). Open Calendar Sharing and Scheduling with CalDAV. Internet Computing, IEEE. 9. 81 - 89. 10.1109/MIC.2005.43. ]]) + +The calendar data from the server is synchronised with my phone using [[https://www.davx5.com/][DAVx5]] where I currently use the [[https://github.com/FossifyOrg/Calendar][Fossify Calendar]] to display and make or alter appointments, and on my laptop using [[https://github.com/dengste/org-caldav][org-caldav]] in GNU Emacs, writing to a Org mode calendar file. I occasionally also use the web-based view from the Nextcloud web client from within a browser. +#+END_PREVIEW + +Graphically, this results in a a system as shown hereunder: + +#+BEGIN_SRC dot :results file :file ../assets/caldav-diagram.png :exports results + digraph G { + server [label="Nextcloud CalDAV server"]; + org [label="Emacs Org mode", color = "purple"]; + server -> laptop; + laptop -> server; + server -> phone [label = "DAVx5"]; + phone -> server; + laptop -> org [label = "org-caldav", fontcolor = "purple"]; + org -> laptop; + laptop -> Firefox; + Firefox -> server; + } +#+END_SRC + +#+RESULTS: +[[file:../assets/caldav-diagram.png]] + +The Org file accessed through Emacs is set up using a /date tree/ as the outline mode of the file. This creates headings by year and by month that can be folded and unfolded using standard functionality from Org mode. That offers a unique way to access appointments - alternatives to have headlines by week exist as well, but I don't have enough appointments to warrant that level of granularity in the outline of the file. + +#+BEGIN_SRC org + ,* 2025 + ,** 2025-01 January + ,*** New Year lunch + ,:PROPERTIES: + ,:location: Our place + ,:END: + Lunch with family to celebrate the new year. + <2025-01-01 Wed 11:30>--<2025-01-01 Wed 15:00> + ,** 2025-02 February + ,*** New Year's resolutions progress review + <2025-02-01 Sat>--<2025-02-02 Sun> +#+END_SRC + +Alternatively, I can view my appointments on a traditional calendar layout using the [[https://github.com/kiwanami/emacs-calfw][Emacs calendar framework or calfw]]. + +2025 / April + [ < ] [ > ] [Today] [Day] [Week] [Two Weeks] [Month] ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| 30 | 31 | 1 April Fools' Day | 2 | 3 | 4 | 5 | +| | | | | | | | +| | | | | | | | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| 6 | 7 | 8 | 9 | 10 | 11 | 12 | +| | | | | | | | +| | | | | | | | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| 13 Passover | 14 | 15 | 16 | 17 | 18 Good Friday | 19 | +| | | | | | | | +| | | | | | | | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| 20 Easter Sunday | 21 First Day of Ridvan | 22 | 23 | 24 | 25 | 26 | +| | | | | | | | +| | | | | | | | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ +| 27 | 28 | 29 Ninth Day of Ridvan | 30 | 1 | 2 Twelfth Day of Ridvan | 3 | +| | | | | | | | +| | | | | | | | ++---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+ + +I also set up a new capture template, allowing the quick creation of an appointment using C-c c followed by a. + +#+BEGIN_SRC emacs-lisp + (org-capture-templates + '(("a" "Appointment" entry + (file+olp+datetree "~/Nextcloud/notes/calendar-nextcloud.org") + "* %?\n :PROPERTIES:\n :location: %^{Location}\n :END:\n%(fv/org-capture-appointment-timestam)\n\n" + :jump-to-captured t + :empty-lines 1 + :tree-type month + :time-prompt t))) + + (defun fv/org-capture-appointment-timestamp (&optional duration) + "Get an Org timestamp for an appointment. + Prompt for a start time, calculate the end time by adding DURATION (default 30 + minutes), and return a formatted Org timestamp with start and end times." + (let* ((duration (or duration 30)) + (start-time (org-read-date t t nil "From:")) + (end-time (time-add start-time (seconds-to-time (* duration 60))))) + (concat (format-time-string (org-time-stamp-format t) start-time) + "--" + (format-time-string (org-time-stamp-format t) end-time)))) +#+END_SRC + +One gripe at the moment: the capture template needs a custom function to properly capture a time range, even though the standard Org timestamp can be formatted in a time range easily using syntax like ~2pm+1h~ to create a 1h block starting at 14:00. When using this within a capture template using the shorthand ~%^T~ which prompts for a date, the timestamp is unfortunately simplified using [[https://github.com/tkf/org-mode/blob/c2ebeea6f68f2ef804d387c238e4acccf655dc64/lisp/org-capture.el#L897-L907][some logic in the template engine]] at the moment. + +Full setup can be found in [[http://git.vanrenterghem.biz/Dotty.git/blob/7c1ae2a1b7c0674664d97d06a61c232920a72b90:/emacs/.emacs.d/init.el][Dotty, my dot files repository]].