chopstick
All widgets

Timer

Comes with Chopstick

Countdown timer. Pick a length or start it for any length (25 min, 90 seconds, 1h30); it shows the time left on the island and alerts when done.

Already installed? It's in your widget library.

Type it in the notch's search

  • Timer for length
  • Cancel the timer

What it runs

A widget is a folder of plain files. Here is all of its code.

widget.json
{
  "id": "timer",
  "name": "Timer",
  "icon": "icon.png",
  "description": "Countdown timer. Pick a length or start it for any length (25 min, 90 seconds, 1h30); it shows the time left on the island and alerts when done.",
  "initial": "running",
  "opens_at": "pick",
  "show_when": {
    "live.running": true
  },
  "watch": [
    {
      "script": "state.sh",
      "every": 1,
      "on": [
        "file:end"
      ],
      "as": "live"
    }
  ],
  "triggers": [
    {
      "when": {
        "live.finished": true
      },
      "open": "done"
    }
  ],
  "commands": {
    "start": {
      "phrase": "Timer for {length}",
      "description": "Start a countdown timer.",
      "inputs": {
        "length": {
          "type": "duration",
          "description": "How long: minutes (25), or with units (90 seconds, 1h30)"
        }
      },
      "do": [
        {
          "set": {
            "minutes": "{{input.length.minutes}}"
          }
        },
        {
          "run": "start.sh",
          "then": "running"
        },
        {
          "refresh": "live"
        }
      ]
    },
    "cancel": {
      "phrase": "Cancel the timer",
      "description": "Cancel the running timer.",
      "do": [
        {
          "run": "clear.sh"
        },
        {
          "refresh": "live"
        },
        "dismiss"
      ]
    }
  },
  "states": {
    "pick": {
      "open": true,
      "on_enter": [
        {
          "run": "running.sh",
          "then": "running"
        }
      ],
      "compact": {
        "leading": {
          "symbol": "timer",
          "tint": "orange"
        }
      },
      "expanded": {
        "size": "compact",
        "header": {
          "symbol": "timer",
          "title": "Timer",
          "tint": "orange"
        },
        "blocks": [
          {
            "row": [
              {
                "button": {
                  "label": "5 min",
                  "do": [
                    {
                      "set": {
                        "minutes": "5"
                      }
                    },
                    {
                      "run": "start.sh",
                      "then": "running"
                    },
                    {
                      "refresh": "live"
                    }
                  ]
                }
              },
              {
                "button": {
                  "label": "10 min",
                  "do": [
                    {
                      "set": {
                        "minutes": "10"
                      }
                    },
                    {
                      "run": "start.sh",
                      "then": "running"
                    },
                    {
                      "refresh": "live"
                    }
                  ]
                }
              },
              {
                "button": {
                  "label": "25 min",
                  "do": [
                    {
                      "set": {
                        "minutes": "25"
                      }
                    },
                    {
                      "run": "start.sh",
                      "then": "running"
                    },
                    {
                      "refresh": "live"
                    }
                  ]
                }
              }
            ],
            "spacing": "tight"
          },
          {
            "input": {
              "placeholder": "Minutes, or 90 s, 1h30",
              "do": [
                {
                  "command": "start",
                  "with": {
                    "length": "{{item.text}}"
                  }
                }
              ]
            }
          }
        ]
      },
      "keys": [
        {
          "key": "esc",
          "do": [
            "dismiss"
          ]
        }
      ]
    },
    "running": {
      "compact": {
        "leading": {
          "symbol": "timer",
          "tint": "orange"
        },
        "trailing": {
          "text": "{{live.label}}"
        }
      },
      "expanded": {
        "size": "compact",
        "header": {
          "symbol": "timer",
          "title": "Timer",
          "tint": "orange"
        },
        "blocks": [
          {
            "text": "{{live.label}}",
            "style": "title"
          },
          {
            "button": {
              "label": "Cancel",
              "do": [
                {
                  "run": "clear.sh"
                },
                {
                  "refresh": "live"
                },
                "dismiss"
              ]
            }
          }
        ]
      },
      "keys": [
        {
          "key": "esc",
          "do": [
            "dismiss"
          ]
        }
      ]
    },
    "done": {
      "task": true,
      "open": true,
      "compact": {
        "leading": {
          "symbol": "bell.fill",
          "tint": "green"
        },
        "trailing": {
          "text": "Done"
        }
      },
      "expanded": {
        "size": "compact",
        "header": {
          "symbol": "bell.fill",
          "title": "Time's up",
          "tint": "green"
        },
        "blocks": [
          {
            "button": {
              "label": "Dismiss",
              "do": [
                {
                  "run": "clear.sh"
                },
                {
                  "refresh": "live"
                },
                "dismiss"
              ]
            }
          }
        ]
      },
      "keys": [
        {
          "key": "esc",
          "do": [
            {
              "run": "clear.sh"
            },
            {
              "refresh": "live"
            },
            "dismiss"
          ]
        }
      ]
    }
  },
  "preview": {
    "states": [
      "pick",
      "running",
      "done"
    ],
    "data": {
      "live": {
        "label": "4:32",
        "running": true
      }
    }
  }
}
clear.sh
#!/bin/sh
rm -f "$CHOPSTICK_STORAGE/end"
echo '{"ok":true}'
running.sh
#!/bin/sh
# Succeeds while a timer is counting down: opened then, the widget shows it instead of the picker.
F="$CHOPSTICK_STORAGE/end"
[ -f "$F" ] && [ "$(cat "$F")" -gt "$(date +%s)" ]
start.sh
#!/bin/sh
F="$CHOPSTICK_STORAGE/end"
M="${CHOPSTICK_MINUTES:-5}"
echo $(( $(date +%s) + $(printf '%.0f' "$(echo "$M * 60" | bc -l)") )) > "$F"
echo '{"ok":true}'
state.sh
#!/bin/sh
F="$CHOPSTICK_STORAGE/end"
if [ ! -f "$F" ]; then echo '{"running":false,"finished":false,"label":"","remaining":0}'; exit 0; fi
E=$(cat "$F"); N=$(date +%s); R=$((E-N))
if [ $R -le 0 ]; then echo '{"running":false,"finished":true,"label":"0:00","remaining":0}'; exit 0; fi
printf '{"running":true,"finished":false,"remaining":%d,"label":"%d:%02d"}\n' $R $((R/60)) $((R%60))