Pomodoro Timer with Slack and Alfred

by Jan-Hendrik Kuperus

Are you looking for a way to integrate that “awesome productivity hack” with the rest of your workflow, but just can’t seem to get it right? Have you tried a ton of these timers, apps, websites and whatnot, but they all just feel like another thing to remember or like two (comfy) left shoes? That’s how I felt when I tried to integrate the Pomodoro-technique into my workflow.

I use Slack a lot and I absolutely love it. At some point, I had a trial for a plugin that allows you to work on a task and have your status be set to 🍅 — Until 20:33. This would then also block notifications apart from direct mentions. This was great, but the rest of the integration just didn’t fit. Then I found a great app for my phone, where I could start timers for 25 minutes and then extend it by 5 minutes as needed, or go for a long/short break and then return to the next 25 minutes. Fantastic. It just didn’t integrate with Slack.

It was time to build something myself. The new feature would have to meet the following requirements:

  • Be keyboard-activated
  • Integrate with Slack
  • Audibly and visibly remind me of the end of a focus-session
  • Preferably not require new software

Integrating with Slack

The first thing I needed to figure out, was how to do things in Slack. I figured as long as I can at least post that I’m in a Pomodoro session until a specified time, I would already be half-way there. That’s when I found out about a little tool called slack-cli. It can do pretty much anything with Slack, right from the command-line. The best part is, it’s 100% bash-scripting and available through brew :

$ brew tap rockymadden/rockymadden
$ brew install rockymadden/rockymadden/slack-cli

Now you can send messages, set/update/remove reminders, work with files and update your presence! After some playing around with the various commands and arguments, I found this to do exactly what I wanted:

$ slack status edit --text="Pomodoro - In cycle" --emoji=":tomato:" --expiration=$expiryValue &&
  slack snooze start --minutes="25"

It sets a temporary status on me, with a 🍅-emoji behind my name and then activates snooze, which turns most notifications off for 25 minutes. The value in $expiryValue is the result of adding 25 minutes to the current timestamp using date -v +25M "+%s".

Creating the Workflow

TLDR: Just download the workflow and import it.

If you’ve read other stories of mine, you will not be surprised to find out I used Alfred to create my pomodoro-workflow. It allows me to tick off the other three requirements I had for my new “tool”. The workflow offers two new inputs, chosen from a List Filter block. In my setup, the List Filter uses the keyword .pd after which I can type either start or stop. The workflow then looks like this:

The Pomodoro Workflow

The List Filter block is pretty straight forward with its configuration. The only thing it needs are two entries which have start and stop as their value in the Arg-field:

Configuration of the List Filter block

The second block from the left is a Conditional block (found under Utilities). When you double click it, you can define multiple conditions, each of which will get its own output for the remainder of the workflow. Here we just need one condition, to check whether you typed start, otherwise we will just assume you typed stop. The labels on the outputs are configured in this dialog as well:

Configuration for the Conditional block

After the conditional block, the workflow continues in one of the two paths. The scripts blocks either enable your pomodor-mode (top flow) or disables it (bottom flow) in Slack. The script to enable your pomodoro status in Slack looks like this:

expiryStatus=$(date -v +25M "+%H:%M")
expiryValue=$(date -v +25M "+%s")
slack status edit --text="Pomodoro - In cycle" --emoji=":tomato:" --expiration=$expiryValue &&
  slack snooze start --minutes="25"
slack reminder add --text="Pomodoro done. Time to slack off." --time=$expiryValue

First, we calculate the time 25 minutes in the future. These values can then be given to the two slack commands. The first edits your status to 🍅 with a hover-text of Pomodoro — In cycle Until 14:32. It also enables the snooze-status, which suppresses most notifications. Finally, it also creates a reminder for when the pomodor-cycle is over.

The script for the bottom flow basically cancels everything:

pomodoroReminder=$(slack reminder list | jq -r '.reminders[] | select(.text | startswith("Pomodoro done")).id')
if [[ -z $pomodoroReminder ]];
then
  echo "Reminder was already deleted, skipping..."
else
  slack reminder delete $pomodoroReminder
fi
slack status clear
slack snooze end

At the top it tries to find any reminder that might still be active with the Pomodoro done text and then deletes that reminder if it exists. At the bottom, your Slack status is cleared and the snooze flag is also removed, returning Slack into its normal online state for you.

Next up in both the top and bottom flow is a Arg and Vars block. These are blocks where you can modify the value flowing into the block and assign variables. In this workflow, we will leave the value of {query} unchanged, as we’re not using it here. We do however set a variable to either true or false to indicate whether the pomodoro is still active or not. We need to do this, because the Delay block cannot be cancelled once activated. The top block sets the variable cancelled to false:

Configuration for the Arg and Vars block

The bottom one sets it to true, to indicate the current pomodoro-cycle has ended prematurely.

The bottom flow is basically done after this, it just shows a MacOS Notification telling you the pomodoro was cancelled. The top flow has a few things left to do: wait 25 minutes and then let you know the pomodoro is done. The delay block does exactly what it sounds like: wait for a specified time and then continue the workflow. In this flow, I’ve set the time to 1500 seconds, which is equivalent to 25 minutes. After the Delay, there is another Conditional block, checking to see if perhaps the pomodoro was cancelled. Here, if cancelled == true holds, the workflow ends and nothing happens. If cancelled == false holds, it will instead execute one last script before posting a notification that the pomodoro is done:

slack status clear && say 'Pomodoro done'

This resets your status in Slack and uses MacOS’ speech synthesis to audibly tell you “Pomodoro done”. I added this one because I am usually listening to music during a pomodoro and this awkward voice that interrupts your music never fails to snag my attention; exactly what it needs to do.


That’s it. Now every time you want to start a cycle of 25 minutes of deep focus and let your team know about it, just activate Alfred and type .pd start and you’re off.

If you make any custom tweaks to this workflow, be sure to comment on this story and share it! Thanks for making it all the way down here 😅.

Cheers!

— JH