Tools to Focus
Introduction
You’ve probably heard of the Pomodoro Technique, where you work in discrete time segments, with similarly discrete break intervals. Not that I’m an ageist, but I assumed this technique more useful for people who haven’t had enough time working. I do believe we can train our brains, so perhaps making the work/relax cycle more specific and focused may convert the cycle into practice, and condition our brains.
while I would like to focus my thoughts in discrete bursts, I don’t want to interrupt the flow. You know, that mental groove where productivity flows. I certainly don’t want a jarring ding to shatter that.
I’ve decided to try an experiment to see if I can make the cycle changes noticeable, but not disturbing. What follows is the code I’m using. I’m not supplying it as a package, as I haven’t figured out how to write this more general. I can explain my idea (and the code) easy enough to allow you to steal, adapt, and make something more personal. However, if stealing the details is too much, you should take advantage of org-pomodoro, as this seems to be well designed and customizable package.
My goals are:
- Start an unobtrusive, but obvious timer. This requires it to not be visual.
- Stop the timer, but again, noticeable but not mentally interrupting (just in case I’m in the flow).
- Need full control for starting.
- Directly tie a work session with my TODO list in an org-mode formatted file.
Oh, and it needs to be controllable from Emacs. But that is a given.
Here’s my idea, I play/pause my music as the notifications. While this may (will) change in the future, the music player will be Spotify, and this code will currently be Mac-specific. When you steal this code to make your own version, you should be able to swap out the command line calls to VLC or something else.
Setting a Timer
Of course, this Pomodoro-technique is all about the timers. This global variable holds a reference to the focus timer. Keep in mind, you don’t have to store this, but I figured I might want to query it at times.
(defvar ha-focus-timer nil "A timer reference for the ha-focus functions")
Timers, in Emacs, are pretty easy; call the flexible function, run-at-time
, give it some time specification and a function that acts like a callback. I’m going wrap this function to make the interface simpler, and allows me to expand it later.
(defun ha-focus-countdown-timer (minutes fun) (let ((the-future (* minutes 60))) (setq ha-focus-timer (run-at-time the-future nil fun))))
Controlling the Music
I need to make commands that can control the music, and use that as the indicators of my state. This may also work, like Pavlov’s beagle, to train myself to enter into the flow quicker.
I will assume that I already have work-focus appropriate music loaded and ready to go. If so, the following AppleScript starts the music, waits for 25 minutes, and then starts a break:
tell application "Spotify" next track delay 1 play end tell delay 25 * 60 -- Wait for 25 minutes tell application "Spotify" to pause delay 5 * 60 -- Wait 5 minutes set v to output volume of (get volume settings) set volume output volume 1 -- Slightly above a whisper say "Break time over. Back on your head." - set volume output volume v
Perhaps we should wrap this in a Shell Script:
osascript ~/Library/Scripts/focus-begin.scpt
When a break period completes, I don’t want to automatically start the next session. I want to start it deliberately. I do want to know when the five minute break is over. I’m okay with the announcement using the say
command being more noticeable.
Emacs Interface
While this isn’t for everyone, I do want to tie my work session with an Org file heading, so I want to clock in (as that also makes my text capturing easier). So my ha-focus-begin
function needs to:
- starts a timer
- starts the music
- clocks in
This function is the primary interface and the function call (and bind to a global key):
(defun ha-focus-begin () "Start a concerted, focused effort, ala Pomodoro Technique. We first clock into the current org-mode header (or last one), start some music to indicate we are working, and set a timer. Call `ha-focus-break' when finished." (interactive) (ha-focus-countdown-timer 25 'ha-focus-break) (ha-focus--command "tell application \"Spotify\" to play") (if (eq major-mode 'org-mode) (org-clock-in) (org-clock-in-last)) (message "🍅 Started clocked %s" (substring-no-properties (org-clock-get-clock-string))))
When the focused time is over, we have a callback, ha-focus-break
, which stops the music, clocks out, and starts a new timer for the break period:
(defun ha-focus-break () "Stop the focused time by stopping the music. This also starts another break timer, that calls `ha-focus-break-over' when finished." (interactive) (run-with-idle-timer 30 nil 'ha-focus-capture) (ha-focus--command "tell application \"Spotify\" to pause") (ha-focus-countdown-timer 5 'ha-focus-break-over) (message "🍅 Time to take a break."))
Why is ha-focus-break
interactive? Well, I may need to leave, and this stops the music and clocks out.
Yeah, both the Emacs message as well as the cessation of music is pretty subtle, but it allows me to finish my efforts and my thoughts. The run-with-idle-timer
waits until I’ve paused working, and it queries me to summarize my 25 minute pomodoro (yeah, I want to make sure I keep taking notes about my progress).
(defun ha-focus-capture () "Spin up a capture window." (ignore-errors (org-capture nil "cc") (sit-for 1) (org-clock-out)))
We need a timer to remind me when the break is over:
(defun ha-focus-break-over () "Message me to know that the break time is over. Notice that this doesn't start anything automatically, as I may have wandered off." (ha-focus--command "set v to output volume of (get volume settings) set volume output volume 1 say \"Break time over. Back on your head.\" set volume output volume v") (message "🍅 Type <f12> to restart the timer."))
Do we want to interrupt a focused activity? This should cancel the timer, and clock out.
(defun ha-focus-interrupt () "Interrupt the current focused timer, if set." (interactive) (when (timerp ha-focus-timer) (cancel-timer ha-focus-timer) (ignore-errors (org-clock-out))))
That is all we need. You will notice that my Emacs functions are self-contained, and don’t call external files. That may change.
Asynchronous Shell Commands
The output from the shell commands shouldn’t return anything, so we don’t need to display their output. If I use shell-command
, the output is too small, and Emacs uses the mini-buffer. Since I don’t want to wait on the AppleScript, we need to use async-shell-command
, but this always displays a buffer of the output.
My ha-focus--command
wrapper function is a little more complicated (but not much) since it uses John Wiegley’s async package’s async-start-process
, so you’ll need something like this (or require
):
(require 'async)
To make future troubleshooting possible, I will give my focused-work process a callback function.
(defun ha-focus--command (osascript) "Runs OSASCRIPT by passing to the `osascript' command asynchronously." (async-start-process "focus-os" "osascript" 'ha-focus--command-callback "-e" osascript))
My callback function doesn’t need to do anything, so I ignore the proc
process object given. As you can see how I might want to display a bit more information when troubleshooting.
(defun ha-focus--command-callback (proc) "Asynchronously called when the `osascript' process finishes." ;; ... )
Minimal viable product.
User Interface
I can’t imagine much more than I need that a simple keybinding to start a session.
(global-set-key (kbd "<f12>") 'ha-focus-begin) (global-set-key (kbd "s-<f12>") 'ha-focus-timer-left) (global-set-key (kbd "S-<f12>") 'ha-focus-interrupt)
I wrote this code one evening, so I’ll let you know if it helps me.