#!/bin/bash # # gromo - To count your daily Gromodoros :thumbsup. A Gromo is a short unit of # time, usually 20 minutes, in which you are committed to do your work # and only your work. It's basically the Pomodoro technique, but more # opinionated. In particular, it's designed to implement the advice an # optician once gave me, the "20/20/20" rule, which is: for each 20 # minutes looking at the screen, look away at least 20 meters away for # at least 20 seconds. Also, not every type of work benefits from the # interruptions 5-minute rests provoke. So, if the mainstream is not # enough for you, enter the all-flexible, eye-careful, non-interrupting # Gromodoros. # # Dependencies: curl and mpv for the 'ding' sound; slock to force you to rest :) ################################################################################ shopt -s nullglob DEFAULT_DING=https://gramos.me/ding.opus GROMO_SECONDS=$((60 * 20)) STOP_SECONDS=20 DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/gromo CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/gromo TODAY_DIR=$DATA_DIR/$(date -I) DING_FILE=$CACHE_DIR/ding.opus STATE_FILE=${XDG_STATE_HOME:-$HOME/.local/state}/gromo cmd_help() { cmd=$(basename "$0") echo -e "Usage: $cmd -h \tShow this help $cmd -s \tShow status $cmd -l \tList past gromos $cmd -x \tReturn state formatted for display in xmobar $cmd [task]\tStart task Tasks can be divided in subtasks by using dots (.), e.g., 'gromo work.retro'. Only the first level is taken into account for organization purposes: the top-level task is considered the 'main task' and a file is created after it to account for its subtasks. " } cmd_list() { find "$DATA_DIR/" -type f -print0 | xargs -0 -n 1 basename | sort -u } cmd_status() { format=$1 state=idle [ -f "$STATE_FILE" ] && state=$(cat "$STATE_FILE") if [ "$format" = "xmobar" ]; then color=red [ "$state" = "idle" ] && color=green if [ -d "$TODAY_DIR" ]; then today="" # needs nullglob for task_file in "$TODAY_DIR"/*; do today+="$(basename "$task_file"): $(sum_subtasks "$task_file"), " done [ -n "$today" ] && today=" (${today%, })" fi echo "$state$today" else echo -e "state: $state\n" if [ -d "$TODAY_DIR" ]; then # needs nullglob for task_file in "$TODAY_DIR"/*; do sort < "$task_file" done fi fi } sum_subtasks() { file=$1 awk -F: '{ sum += $2 } END { print sum }' "$file" } inc_subtask() { task=$1 maintask="${fulltask%%.*}" task_file="$TODAY_DIR/$maintask" if [ -f "$task_file" ]; then awk -F: "\$1 == \"$task\" { \$2 += 1; added=1 } { print } END { if (!added) { print \"$task\" FS 1 } }" "$task_file" > tmp mv tmp "$task_file" else echo "$task 1" > "$task_file" fi } pp_seconds() { seconds=$1 if [ "$seconds" -lt 60 ]; then echo "$seconds seconds" elif [ "$seconds" -lt 3600 ]; then echo "$((seconds / 60)) minutes" else echo "$((seconds / 3600)) hours" fi } optspec="hslx" while getopts "$optspec" optchar; do case "$optchar" in h) cmd_help exit 0 ;; s) cmd_status exit 0 ;; l) cmd_list exit 0 ;; x) cmd_status xmobar exit 0 ;; *) cmd_help exit 0 ;; esac done if [ "$#" -ge 1 ]; then fulltask="$1" else cmd_help exit 0 fi mkdir -p "$DATA_DIR" "$TODAY_DIR" [ -f "$DING_FILE" ] || curl $DEFAULT_DING --create-dirs -so "$DING_FILE" if [ -f "$STATE_FILE" ]; then echo "Another instance is currently running; exiting (remove $STATE_FILE to override)" exit 1 fi echo idle > "$STATE_FILE" trap 'rm $STATE_FILE 2> /dev/null; exit' INT TERM EXIT while true; do echo -ne "\r\033[K[** IN PROGRESS: $fulltask **] " echo "$fulltask" > "$STATE_FILE" sleep $GROMO_SECONDS echo idle > "$STATE_FILE" inc_subtask "$fulltask" (sleep $STOP_SECONDS && mpv --no-terminal "$DING_FILE") & slock || exit done