diff --git a/README.md b/README.md index 5aa4e3e..e1ea9fc 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,33 @@ Example: } ``` +### Execute command on state change + +Pomo will execute an arbitrary command specified in the array argument `onEvent` +when the state changes. The first element of this array should be the +executable to run while the remaining elements are space delimited arguments. +The new state will be exported as an environment variable `POMO_STATE` for this +command. Possible state values are `RUNNING`, `PAUSED`, `BREAKING`, or +`COMPLETE`. + +For example, to trigger a terminal bell when a session completes, add the +following to `config.json`: +```json +... +"onEvent": ["/bin/sh", "/path/to/script/my_script.sh"], +... +``` +where the contents of `my_script.sh` are +```bash +#!/bin/sh + +if [ "$POMO_STATE" == "COMPLETE" ] ; then + echo -e '\a' +fi +``` + +See the `contrib` directory for user contributed scripts for use with `onEvent`. + ## Integrations By default pomo will setup a Unix socket and serve it's status there. diff --git a/contrib/.gitkeep b/contrib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/contrib/bell b/contrib/bell new file mode 100755 index 0000000..b4c6e8f --- /dev/null +++ b/contrib/bell @@ -0,0 +1,3 @@ +#!/bin/sh + +echo -e '\e' diff --git a/contrib/pomonag b/contrib/pomonag new file mode 100755 index 0000000..4259c6e --- /dev/null +++ b/contrib/pomonag @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ "$POMO_STATE" == "BREAKING" ] ; then + swaynag -m "It's time to take a break!" +fi diff --git a/pkg/internal/config.go b/pkg/internal/config.go index d4ecf3c..85033f8 100644 --- a/pkg/internal/config.go +++ b/pkg/internal/config.go @@ -23,6 +23,7 @@ type Config struct { DBPath string `json:"dbPath"` SocketPath string `json:"socketPath"` IconPath string `json:"iconPath"` + OnEvent []string `json:"onEvent"` // Publish pushes updates to the configured // SocketPath rather than listening for requests Publish bool `json:"publish"` diff --git a/pkg/internal/runner.go b/pkg/internal/runner.go index 58a0039..46a802c 100644 --- a/pkg/internal/runner.go +++ b/pkg/internal/runner.go @@ -2,6 +2,9 @@ package pomo import ( "database/sql" + "fmt" + "os" + "os/exec" "sync" "time" ) @@ -21,6 +24,7 @@ type TaskRunner struct { notifier Notifier duration time.Duration mu sync.Mutex + onEvent []string } func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) { @@ -55,6 +59,7 @@ func NewTaskRunner(task *Task, config *Config) (*TaskRunner, error) { toggle: make(chan bool), notifier: NewXnotifier(config.IconPath), duration: task.Duration, + onEvent: config.OnEvent, } return tr, nil } @@ -73,6 +78,34 @@ func (t *TaskRunner) TimePauseDuration() time.Duration { func (t *TaskRunner) SetState(state State) { t.state = state + // execute onEvent command if variable is set + if t.onEvent != nil { + go t.runOnEvent() + } +} + +// execute script command specified by `onEvent` on state change +func (t *TaskRunner) runOnEvent() error { + var cmd *exec.Cmd + // parse command arguments + numArgs := len(t.onEvent) + app := t.onEvent[0] + if numArgs > 1 { + args := t.onEvent[1:(numArgs + 1)] + cmd = exec.Command(app, args...) + } else { + cmd = exec.Command(app) + } + // set state in command environment + cmd.Env = append(os.Environ(), + fmt.Sprintf("POMO_STATE=%s", t.state), + ) + // run command + err := cmd.Run() + if err != nil { + return err + } + return nil } func (t *TaskRunner) run() error {