+69 -0 +/-6 browse
1 | diff --git a/README.md b/README.md |
2 | index 5aa4e3e..e1ea9fc 100644 |
3 | --- a/README.md |
4 | +++ b/README.md |
5 | @@ -65,6 +65,33 @@ Example: |
6 | } |
7 | ``` |
8 | |
9 | + ### Execute command on state change |
10 | + |
11 | + Pomo will execute an arbitrary command specified in the array argument `onEvent` |
12 | + when the state changes. The first element of this array should be the |
13 | + executable to run while the remaining elements are space delimited arguments. |
14 | + The new state will be exported as an environment variable `POMO_STATE` for this |
15 | + command. Possible state values are `RUNNING`, `PAUSED`, `BREAKING`, or |
16 | + `COMPLETE`. |
17 | + |
18 | + For example, to trigger a terminal bell when a session completes, add the |
19 | + following to `config.json`: |
20 | + ```json |
21 | + ... |
22 | + "onEvent": ["/bin/sh", "/path/to/script/my_script.sh"], |
23 | + ... |
24 | + ``` |
25 | + where the contents of `my_script.sh` are |
26 | + ```bash |
27 | + #!/bin/sh |
28 | + |
29 | + if [ "$POMO_STATE" == "COMPLETE" ] ; then |
30 | + echo -e '\a' |
31 | + fi |
32 | + ``` |
33 | + |
34 | + See the `contrib` directory for user contributed scripts for use with `onEvent`. |
35 | + |
36 | ## Integrations |
37 | |
38 | By default pomo will setup a Unix socket and serve it's status there. |
39 | diff --git a/contrib/.gitkeep b/contrib/.gitkeep |
40 | new file mode 100644 |
41 | index 0000000..e69de29 |
42 | --- /dev/null |
43 | +++ b/contrib/.gitkeep |
44 | diff --git a/contrib/bell b/contrib/bell |
45 | new file mode 100755 |
46 | index 0000000..b4c6e8f |
47 | --- /dev/null |
48 | +++ b/contrib/bell |
49 | @@ -0,0 +1,3 @@ |
50 | + #!/bin/sh |
51 | + |
52 | + echo -e '\e' |
53 | diff --git a/contrib/pomonag b/contrib/pomonag |
54 | new file mode 100755 |
55 | index 0000000..4259c6e |
56 | --- /dev/null |
57 | +++ b/contrib/pomonag |
58 | @@ -0,0 +1,5 @@ |
59 | + #!/bin/sh |
60 | + |
61 | + if [ "$POMO_STATE" == "BREAKING" ] ; then |
62 | + swaynag -m "It's time to take a break!" |
63 | + fi |
64 | diff --git a/pkg/internal/config.go b/pkg/internal/config.go |
65 | index d4ecf3c..85033f8 100644 |
66 | --- a/pkg/internal/config.go |
67 | +++ b/pkg/internal/config.go |
68 | @@ -23,6 +23,7 @@ type Config struct { |
69 | DBPath string `json:"dbPath"` |
70 | SocketPath string `json:"socketPath"` |
71 | IconPath string `json:"iconPath"` |
72 | + OnEvent []string `json:"onEvent"` |
73 | // Publish pushes updates to the configured |
74 | // SocketPath rather than listening for requests |
75 | Publish bool `json:"publish"` |
76 | diff --git a/pkg/internal/runner.go b/pkg/internal/runner.go |
77 | index 58a0039..46a802c 100644 |
78 | --- a/pkg/internal/runner.go |
79 | +++ b/pkg/internal/runner.go |
80 | @@ -2,6 +2,9 @@ package pomo |
81 | |
82 | import ( |
83 | "database/sql" |
84 | + "fmt" |
85 | + "os" |
86 | + "os/exec" |
87 | "sync" |
88 | "time" |
89 | ) |
90 | @@ -21,6 +24,7 @@ type TaskRunner struct { |
91 | notifier Notifier |
92 | duration time.Duration |
93 | mu sync.Mutex |
94 | + onEvent []string |
95 | } |
96 | |
97 | func NewMockedTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) { |
98 | @@ -55,6 +59,7 @@ func NewTaskRunner(task *Task, config *Config) (*TaskRunner, error) { |
99 | toggle: make(chan bool), |
100 | notifier: NewXnotifier(config.IconPath), |
101 | duration: task.Duration, |
102 | + onEvent: config.OnEvent, |
103 | } |
104 | return tr, nil |
105 | } |
106 | @@ -73,6 +78,34 @@ func (t *TaskRunner) TimePauseDuration() time.Duration { |
107 | |
108 | func (t *TaskRunner) SetState(state State) { |
109 | t.state = state |
110 | + // execute onEvent command if variable is set |
111 | + if t.onEvent != nil { |
112 | + go t.runOnEvent() |
113 | + } |
114 | + } |
115 | + |
116 | + // execute script command specified by `onEvent` on state change |
117 | + func (t *TaskRunner) runOnEvent() error { |
118 | + var cmd *exec.Cmd |
119 | + // parse command arguments |
120 | + numArgs := len(t.onEvent) |
121 | + app := t.onEvent[0] |
122 | + if numArgs > 1 { |
123 | + args := t.onEvent[1:(numArgs + 1)] |
124 | + cmd = exec.Command(app, args...) |
125 | + } else { |
126 | + cmd = exec.Command(app) |
127 | + } |
128 | + // set state in command environment |
129 | + cmd.Env = append(os.Environ(), |
130 | + fmt.Sprintf("POMO_STATE=%s", t.state), |
131 | + ) |
132 | + // run command |
133 | + err := cmd.Run() |
134 | + if err != nil { |
135 | + return err |
136 | + } |
137 | + return nil |
138 | } |
139 | |
140 | func (t *TaskRunner) run() error { |