From 3d3a2bc152388ee8cefbf88b754822549e0ab63a Mon Sep 17 00:00:00 2001 From: Sam Boysel Date: Mon, 30 May 2022 23:20:58 -0700 Subject: [PATCH] execute command on state change --- README.md | 21 +++++++++++++++++++++ pkg/internal/config.go | 1 + pkg/internal/runner.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/README.md b/README.md index 5aa4e3e..f5a216e 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,27 @@ Example: } ``` +### Execute command on state change + +Pomo will execute the command specified in the array argument `onEvent` when the +state changes. The new state will be exported as an environment variable +`POMO_STATE` for this command. For example, to trigger a terminal bell when a +session complete, add the following to `config.json` +``` +... +"onEvent": ["/bin/sh", "/path/to/script/my_script.sh"] +... +``` +where the contents of `my_script.sh` are +``` +#!/bin/sh + +if [ "$POMO_STATE" == "COMPLETE" ] ; then + echo -e '\a' +fi +``` +Possible state values are `RUNNING`, `PAUSED`, `BREAKING`, or `COMPLETE`. + ## Integrations By default pomo will setup a Unix socket and serve it's status there. 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..9a83fef 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 } @@ -75,6 +80,20 @@ func (t *TaskRunner) SetState(state State) { t.state = state } +// execute script command specified by `onEvent` on state change +func (t *TaskRunner) OnEvent() error { + app, args := t.onEvent[0], t.onEvent[1:len(t.onEvent)] + cmd := exec.Command(app, args...) + cmd.Env = append(os.Environ(), + fmt.Sprintf("POMO_STATE=%s", t.state), + ) + err := cmd.Run() + if err != nil { + return err + } + return nil +} + func (t *TaskRunner) run() error { for t.count < t.nPomodoros { // Create a new pomodoro where we @@ -85,6 +104,8 @@ func (t *TaskRunner) run() error { pomodoro.Start = time.Now() // Set state to RUNNIN t.SetState(RUNNING) + // Execute onEvent command + t.OnEvent() // Create a new timer timer := time.NewTimer(t.duration) // Record our started time @@ -104,6 +125,8 @@ func (t *TaskRunner) run() error { remaining := t.TimeRemaining() // Change state to PAUSED t.SetState(PAUSED) + // Execute onEvent command + t.OnEvent() // Wait for the user to press [p] <-t.pause // Resume the timer with previous @@ -114,6 +137,8 @@ func (t *TaskRunner) run() error { t.duration = remaining // Restore state to RUNNING t.SetState(RUNNING) + // Execute onEvent command + t.OnEvent() goto loop } pomodoro.End = time.Now() @@ -128,6 +153,8 @@ func (t *TaskRunner) run() error { break } t.SetState(BREAKING) + // Execute onEvent command + t.OnEvent() t.notifier.Notify("Pomo", "It is time to take a break!") // Reset the duration incase it // was paused. @@ -138,6 +165,8 @@ func (t *TaskRunner) run() error { } t.notifier.Notify("Pomo", "Pomo session has completed!") t.SetState(COMPLETE) + // Execute onEvent command + t.OnEvent() return nil }