summaryrefslogtreecommitdiffstats
path: root/assistant.sh
blob: e288afd87496b23bdee0e0cea362d0fe219e31df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/bin/bash
# desktop-assistant: start, stop, restart or check the voice loop.
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# assistant.sh start|stop|restart|status
#
# stop: SIGTERM any running assistant.py (whisper-server and amixer die
# with it through PDEATHSIG), SIGKILL after 10 s; same for the quickshell
# overlay (OVERLAY, default the quickshell repo next to this one).
# start: run both in their own session, detached from the terminal,
# logging where the Hyprland autostart does; does nothing for one already
# running. restart: stop, then start. status: processes, children, stray
# recorders, socket, pause flags and the log's tail; exit 1 if either is
# down. Run it from the login session: it needs its
# audio, bus, Wayland display and XDG_RUNTIME_DIR.

set -eu

dir=$(cd "$(dirname "$0")" && pwd)
log="${XDG_RUNTIME_DIR:?not in a login session}/desktop-assistant.log"
overlay=$(realpath -m "${OVERLAY:-$dir/../quickshell/assistant}")
pat='^\S*python3 (-u )?\S*assistant\.py'
qpat="^qs -p $overlay\$"

kill_wait() {
    pkill -u "$USER" -f "$1" || return 0
    for _ in $(seq 50); do
        pgrep -u "$USER" -f "$1" >/dev/null || return 0
        sleep 0.2
    done
    pkill -KILL -u "$USER" -f "$1" && sleep 0.5 || true
}

running() { pgrep -u "$USER" -f "$1" >/dev/null; }

stop() {
    kill_wait "$pat"
    kill_wait "$qpat"
}

start() {
    cd "$dir"
    running "$pat" || setsid -f python3 -u "$dir/assistant.py" >>"$log" 2>&1 </dev/null
    running "$qpat" || setsid -f qs -p "$overlay" >/dev/null 2>&1 </dev/null
    sleep 0.5
    status
}

show() {
    local pid
    pid=$(pgrep -u "$USER" -of "$2") || { echo "$1: not running"; return 1; }
    echo "$1: running"
    ps -o pid=,etime=,rss=,args= -p "$pid" |
        awk '{ printf "  pid %s, up %s, %d MB\n  %s", $1, $2, $3 / 1024, $4
               for (i = 5; i <= NF; i++) printf " %s", $i; print "" }'
    [ "$1" = assistant ] || return 0
    pgrep -a -P "$pid" | sed 's/^/  child /'
}

status() {
    local rc=0 f
    show assistant "$pat" || rc=1
    show overlay "$qpat" || rc=1
    # Children get PDEATHSIG; one left over means the assistant died under it.
    pgrep -u "$USER" -af "^arecord .*desktop-assistant\.raw" | sed 's/^/stray: /' || true
    [ -S "$XDG_RUNTIME_DIR/desktop-assistant.sock" ] && echo "socket: present" || echo "socket: missing"
    for f in presentation gaming; do
        [ "$(cat "$XDG_RUNTIME_DIR/status.$f" 2>/dev/null)" = 1 ] && echo "paused: $f mode on"
    done
    echo "log: $log"
    tail -n 5 "$log" 2>/dev/null | sed 's/^/  /'
    return $rc
}

case "${1:-}" in
    start) start ;;
    stop) stop ;;
    restart) stop; start ;;
    status) status ;;
    *) echo "usage: $0 start|stop|restart|status" >&2; exit 2 ;;
esac