--- /dev/null
+: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]].