Compare commits
2 Commits
38893cbb84
...
8b6b4d582d
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b6b4d582d | |||
| 212e957f49 |
135
Makefile
135
Makefile
@@ -24,7 +24,7 @@ else
|
||||
UDEV_RULE := /etc/udev/rules.d/99-streamdeck.rules
|
||||
endif
|
||||
|
||||
.PHONY: build build-helper build-init install install-helper uninstall uninstall-helper udev
|
||||
.PHONY: build build-helper build-init install install-helper install-watchdog reinstall uninstall uninstall-helper uninstall-watchdog udev
|
||||
|
||||
# ── Build ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -88,6 +88,37 @@ install-helper: build-helper
|
||||
@echo " or run: newgrp $(GROUP)"
|
||||
endif
|
||||
|
||||
# Install the watchdog timer that detects USB unplug/replug and restarts the
|
||||
# service when the daemon's in-process reconnect misses an event. Linux only.
|
||||
ifeq ($(OS),Darwin)
|
||||
WATCHDOG_PLIST := $(LAUNCHAGENTS)/com.woodarddigital.streamdeck-go-watchdog.plist
|
||||
WATCHDOG_LOG := $(HOME)/Library/Logs/streamdeck-go-watchdog.log
|
||||
install-watchdog:
|
||||
mkdir -p $(BIN_DIR) $(LAUNCHAGENTS) $(HOME)/Library/Logs
|
||||
install -m 755 systemd/streamdeck-go-watchdog.sh $(BIN_DIR)/streamdeck-go-watchdog
|
||||
# Substitute the binary and log paths into the plist.
|
||||
sed -e 's|STREAMDECK_WATCHDOG_PATH|$(BIN_DIR)/streamdeck-go-watchdog|' \
|
||||
-e 's|STREAMDECK_WATCHDOG_LOG_PATH|$(WATCHDOG_LOG)|g' \
|
||||
launchd/com.woodarddigital.streamdeck-go-watchdog.plist > $(WATCHDOG_PLIST)
|
||||
# Reload: bootout (ignore if not loaded) then bootstrap.
|
||||
launchctl bootout gui/$$(id -u)/com.woodarddigital.streamdeck-go-watchdog 2>/dev/null || true
|
||||
launchctl bootstrap gui/$$(id -u) $(WATCHDOG_PLIST)
|
||||
@echo ""
|
||||
@echo "Watchdog installed. Fires every 30s."
|
||||
@echo " Logs: $(WATCHDOG_LOG)"
|
||||
else
|
||||
install-watchdog:
|
||||
install -Dm755 systemd/streamdeck-go-watchdog.sh $(BIN_DIR)/streamdeck-go-watchdog
|
||||
install -Dm644 systemd/streamdeck-go-watchdog.service $(SYSTEMD_USER)/streamdeck-go-watchdog.service
|
||||
install -Dm644 systemd/streamdeck-go-watchdog.timer $(SYSTEMD_USER)/streamdeck-go-watchdog.timer
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now streamdeck-go-watchdog.timer
|
||||
@echo ""
|
||||
@echo "Watchdog timer installed and started."
|
||||
@echo " Status: systemctl --user status streamdeck-go-watchdog.timer"
|
||||
@echo " Logs: journalctl --user -u streamdeck-go-watchdog.service"
|
||||
endif
|
||||
|
||||
# udev: Linux-only device permission rule.
|
||||
udev:
|
||||
ifeq ($(OS),Darwin)
|
||||
@@ -104,6 +135,94 @@ else
|
||||
fi
|
||||
endif
|
||||
|
||||
# ── Reinstall ─────────────────────────────────────────────────────────────────
|
||||
# Refresh only the pieces that are already installed: binary, service unit,
|
||||
# helper, watchdog, and modules.yaml. Skips dependency/dotfile/symlink setup.
|
||||
# Use this after a code change to redeploy without going through install.sh.
|
||||
|
||||
ifeq ($(OS),Darwin)
|
||||
reinstall: build
|
||||
@echo " ━━━ streamdeck-go reinstall ━━━"
|
||||
@if [ -f $(BIN_DIR)/$(BINARY) ]; then \
|
||||
install -m 755 $(BINARY) $(BIN_DIR)/$(BINARY); \
|
||||
echo " ✓ binary → $(BIN_DIR)/$(BINARY)"; \
|
||||
else \
|
||||
echo " · binary not installed (skipping — run 'make install' first)"; \
|
||||
fi
|
||||
@if [ -f $(AGENT_PLIST) ]; then \
|
||||
LOG_PATH="$$HOME/Library/Logs/streamdeck-go.log"; \
|
||||
sed -e "s|STREAMDECK_BINARY_PATH|$(BIN_DIR)/$(BINARY)|g" \
|
||||
-e "s|STREAMDECK_LOG_PATH|$$LOG_PATH|g" \
|
||||
launchd/com.woodarddigital.streamdeck-go.plist > $(AGENT_PLIST); \
|
||||
launchctl unload $(AGENT_PLIST) 2>/dev/null || true; \
|
||||
launchctl load $(AGENT_PLIST); \
|
||||
echo " ✓ launchd agent reloaded"; \
|
||||
else \
|
||||
echo " · launchd agent not installed (skipping)"; \
|
||||
fi
|
||||
@if [ -f $(WATCHDOG_PLIST) ]; then \
|
||||
install -m 755 systemd/streamdeck-go-watchdog.sh $(BIN_DIR)/streamdeck-go-watchdog; \
|
||||
sed -e 's|STREAMDECK_WATCHDOG_PATH|$(BIN_DIR)/streamdeck-go-watchdog|' \
|
||||
-e 's|STREAMDECK_WATCHDOG_LOG_PATH|$(WATCHDOG_LOG)|g' \
|
||||
launchd/com.woodarddigital.streamdeck-go-watchdog.plist > $(WATCHDOG_PLIST); \
|
||||
launchctl bootout gui/$$(id -u)/com.woodarddigital.streamdeck-go-watchdog 2>/dev/null || true; \
|
||||
launchctl bootstrap gui/$$(id -u) $(WATCHDOG_PLIST); \
|
||||
echo " ✓ watchdog refreshed"; \
|
||||
else \
|
||||
echo " · watchdog not installed (skipping)"; \
|
||||
fi
|
||||
@if [ -f $(CONFIG_DIR)/modules.yaml ]; then \
|
||||
install -m 644 modules.example.yaml $(CONFIG_DIR)/modules.yaml; \
|
||||
echo " ✓ modules.yaml updated"; \
|
||||
else \
|
||||
echo " · config dir not set up (skipping modules.yaml)"; \
|
||||
fi
|
||||
else
|
||||
reinstall: build
|
||||
@echo " ━━━ streamdeck-go reinstall ━━━"
|
||||
@if [ -f $(BIN_DIR)/$(BINARY) ]; then \
|
||||
install -m 755 $(BINARY) $(BIN_DIR)/$(BINARY); \
|
||||
echo " ✓ binary → $(BIN_DIR)/$(BINARY)"; \
|
||||
else \
|
||||
echo " · binary not installed (skipping — run 'make install' first)"; \
|
||||
fi
|
||||
@if [ -f $(SYSTEMD_USER)/streamdeck-go.service ]; then \
|
||||
install -m 644 systemd/streamdeck-go.service $(SYSTEMD_USER)/streamdeck-go.service; \
|
||||
systemctl --user daemon-reload; \
|
||||
systemctl --user restart streamdeck-go.service; \
|
||||
echo " ✓ systemd unit refreshed and service restarted"; \
|
||||
else \
|
||||
echo " · systemd user service not installed (skipping)"; \
|
||||
fi
|
||||
@if [ -f $(SYS_BIN)/$(HELPER) ]; then \
|
||||
$(MAKE) -s build-helper; \
|
||||
sudo install -m 750 $(HELPER) $(SYS_BIN)/$(HELPER); \
|
||||
sudo chown root:$(GROUP) $(SYS_BIN)/$(HELPER); \
|
||||
sudo install -m 644 systemd/streamdeck-go-helper.service $(SYSTEMD_SYS)/streamdeck-go-helper.service; \
|
||||
sudo systemctl daemon-reload; \
|
||||
sudo systemctl restart streamdeck-go-helper.service; \
|
||||
echo " ✓ helper refreshed and restarted"; \
|
||||
else \
|
||||
echo " · helper not installed (skipping)"; \
|
||||
fi
|
||||
@if [ -f $(SYSTEMD_USER)/streamdeck-go-watchdog.timer ]; then \
|
||||
install -m 755 systemd/streamdeck-go-watchdog.sh $(BIN_DIR)/streamdeck-go-watchdog; \
|
||||
install -m 644 systemd/streamdeck-go-watchdog.service $(SYSTEMD_USER)/streamdeck-go-watchdog.service; \
|
||||
install -m 644 systemd/streamdeck-go-watchdog.timer $(SYSTEMD_USER)/streamdeck-go-watchdog.timer; \
|
||||
systemctl --user daemon-reload; \
|
||||
systemctl --user restart streamdeck-go-watchdog.timer; \
|
||||
echo " ✓ watchdog refreshed"; \
|
||||
else \
|
||||
echo " · watchdog not installed (skipping)"; \
|
||||
fi
|
||||
@if [ -f $(CONFIG_DIR)/modules.yaml ]; then \
|
||||
install -m 644 modules.example.yaml $(CONFIG_DIR)/modules.yaml; \
|
||||
echo " ✓ modules.yaml updated"; \
|
||||
else \
|
||||
echo " · config dir not set up (skipping modules.yaml)"; \
|
||||
fi
|
||||
endif
|
||||
|
||||
# ── Uninstall ─────────────────────────────────────────────────────────────────
|
||||
|
||||
ifeq ($(OS),Darwin)
|
||||
@@ -116,6 +235,12 @@ uninstall:
|
||||
uninstall-helper:
|
||||
@echo "No helper daemon on macOS — nothing to uninstall."
|
||||
@echo "Whitelist at $(CONFIG_DIR)/privileged.yaml preserved."
|
||||
|
||||
uninstall-watchdog:
|
||||
launchctl bootout gui/$$(id -u)/com.woodarddigital.streamdeck-go-watchdog 2>/dev/null || true
|
||||
rm -f $(WATCHDOG_PLIST)
|
||||
rm -f $(BIN_DIR)/streamdeck-go-watchdog
|
||||
@echo "Watchdog uninstalled."
|
||||
else
|
||||
uninstall:
|
||||
systemctl --user disable --now streamdeck-go.service || true
|
||||
@@ -124,6 +249,14 @@ uninstall:
|
||||
systemctl --user daemon-reload
|
||||
@echo "Uninstalled. Config at $(CONFIG_DIR) preserved."
|
||||
|
||||
uninstall-watchdog:
|
||||
systemctl --user disable --now streamdeck-go-watchdog.timer || true
|
||||
rm -f $(BIN_DIR)/streamdeck-go-watchdog
|
||||
rm -f $(SYSTEMD_USER)/streamdeck-go-watchdog.service
|
||||
rm -f $(SYSTEMD_USER)/streamdeck-go-watchdog.timer
|
||||
systemctl --user daemon-reload
|
||||
@echo "Watchdog uninstalled."
|
||||
|
||||
uninstall-helper:
|
||||
sudo systemctl disable --now streamdeck-go-helper.service || true
|
||||
sudo rm -f $(SYS_BIN)/$(HELPER)
|
||||
|
||||
34
README.md
34
README.md
@@ -126,7 +126,7 @@ interleave partial image data across keys.
|
||||
# Prerequisites
|
||||
brew install go hidapi
|
||||
|
||||
git clone https://github.com/WoodardDigital/streamdeck-go
|
||||
git clone https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
cd streamdeck-go
|
||||
make install
|
||||
```
|
||||
@@ -154,7 +154,7 @@ directory integration.
|
||||
# Prerequisites — Arch example; adjust for your distro (see table above)
|
||||
sudo pacman -S go hidapi
|
||||
|
||||
git clone https://github.com/WoodardDigital/streamdeck-go
|
||||
git clone https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
cd streamdeck-go
|
||||
make install
|
||||
```
|
||||
@@ -218,7 +218,7 @@ sudo udevadm trigger
|
||||
**2. Build and run:**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/WoodardDigital/streamdeck-go
|
||||
git clone https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
cd streamdeck-go
|
||||
|
||||
cp config.example.yaml config.yaml
|
||||
@@ -233,6 +233,33 @@ first (respecting `$XDG_CONFIG_HOME`). The repo's `config.yaml` is gitignored.
|
||||
|
||||
---
|
||||
|
||||
### Updating — `make reinstall`
|
||||
|
||||
After pulling new code or editing a Go source file, refresh whatever's already
|
||||
deployed without going through the full installer:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
make reinstall
|
||||
```
|
||||
|
||||
`reinstall` rebuilds the binary and refreshes only the pieces that are already
|
||||
installed:
|
||||
|
||||
| Component | Action when present |
|
||||
|-------------------|--------------------------------------------------------------------|
|
||||
| Main binary | rebuilt and copied into `~/.local/bin` (Linux) or `~/go/bin` (macOS) |
|
||||
| Service unit | systemd unit / launchd plist re-installed; service restarted |
|
||||
| Helper (Linux) | rebuilt, re-installed under `/usr/local/bin`, helper service restarted (sudo) |
|
||||
| Watchdog | script + unit/plist refreshed and timer restarted |
|
||||
| `modules.yaml` | re-copied from `modules.example.yaml` into the active config dir |
|
||||
|
||||
Anything not currently installed prints `· skipped` instead of failing.
|
||||
No dependency installs, no dotfile prompts, no symlink logic — that's still
|
||||
`make install`'s job.
|
||||
|
||||
---
|
||||
|
||||
### AUR (Arch Linux)
|
||||
|
||||
> AUR package coming soon. Until then, use `make install` above.
|
||||
@@ -791,6 +818,7 @@ keys:
|
||||
function: is_recording_paused
|
||||
match: "Paused: true"
|
||||
interval: 2s
|
||||
|
||||
```
|
||||
|
||||
**Note — absolute paths in modules:** The example templates call `/usr/local/bin/obs-cmd` rather than just `obs-cmd`. This is because launchd (macOS) and systemd (Linux) give the service a minimal `PATH` that doesn't include `/usr/local/bin` or Homebrew. Use the absolute path returned by `which obs-cmd` in your own module templates, or set `PATH` in the launchd plist / systemd unit.
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/config"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/config"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/config"
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/defaults"
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/modules"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/config"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/defaults"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/modules"
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/config"
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/device"
|
||||
"github.com/WoodardDigital/streamdeck-go/internal/modules"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/config"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/device"
|
||||
"git.i0t.app/lwoodard/streamdeck-go/internal/modules"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/srwiley/oksvg"
|
||||
"github.com/srwiley/rasterx"
|
||||
@@ -754,7 +754,7 @@ func loadImage(path string) (image.Image, error) {
|
||||
}
|
||||
|
||||
func loadSVG(path string) (image.Image, error) {
|
||||
icon, err := oksvg.ReadIcon(path, oksvg.WarnErrorMode)
|
||||
icon, err := oksvg.ReadIcon(path, oksvg.IgnoreErrorMode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -921,7 +921,7 @@ func ensureConfigDir(cfgPath string) error {
|
||||
|
||||
func defaultConfig(iconsDir string) string {
|
||||
return `# streamdeck-go configuration
|
||||
# https://github.com/WoodardDigital/streamdeck-go
|
||||
# https://git.i0t.app/lwoodard/streamdeck-go
|
||||
|
||||
icons_dir: ` + iconsDir + `
|
||||
brightness: 70
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
||||
module github.com/WoodardDigital/streamdeck-go
|
||||
module git.i0t.app/lwoodard/streamdeck-go
|
||||
|
||||
go 1.25.0
|
||||
|
||||
|
||||
27
install.sh
27
install.sh
@@ -449,6 +449,33 @@ else
|
||||
ok "Service enabled and started"
|
||||
fi
|
||||
|
||||
# ── 9. Watchdog ────────────────────────────────────────────────────────────────
|
||||
nl
|
||||
step "Installing watchdog (USB unplug/replug recovery)..."
|
||||
if $IS_MAC; then
|
||||
WATCHDOG_BIN="${BIN_DIR}/streamdeck-go-watchdog"
|
||||
WATCHDOG_PLIST_LABEL="com.woodarddigital.streamdeck-go-watchdog"
|
||||
WATCHDOG_PLIST="${LAUNCHAGENTS_DIR}/${WATCHDOG_PLIST_LABEL}.plist"
|
||||
WATCHDOG_LOG="${HOME}/Library/Logs/streamdeck-go-watchdog.log"
|
||||
|
||||
install -m 755 systemd/streamdeck-go-watchdog.sh "${WATCHDOG_BIN}"
|
||||
sed \
|
||||
-e "s|STREAMDECK_WATCHDOG_PATH|${WATCHDOG_BIN}|g" \
|
||||
-e "s|STREAMDECK_WATCHDOG_LOG_PATH|${WATCHDOG_LOG}|g" \
|
||||
launchd/com.woodarddigital.streamdeck-go-watchdog.plist \
|
||||
> "${WATCHDOG_PLIST}"
|
||||
launchctl bootout "gui/$(id -u)/${WATCHDOG_PLIST_LABEL}" 2>/dev/null || true
|
||||
launchctl bootstrap "gui/$(id -u)" "${WATCHDOG_PLIST}"
|
||||
ok "Watchdog loaded — fires every 30s"
|
||||
else
|
||||
install_file 755 systemd/streamdeck-go-watchdog.sh "${BIN_DIR}/streamdeck-go-watchdog"
|
||||
install_file 644 systemd/streamdeck-go-watchdog.service "${SYSTEMD_USER}/streamdeck-go-watchdog.service"
|
||||
install_file 644 systemd/streamdeck-go-watchdog.timer "${SYSTEMD_USER}/streamdeck-go-watchdog.timer"
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now streamdeck-go-watchdog.timer
|
||||
ok "Watchdog timer enabled — fires every 30s"
|
||||
fi
|
||||
|
||||
# ── Done ───────────────────────────────────────────────────────────────────────
|
||||
nl
|
||||
echo -e " ${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
36
launchd/com.woodarddigital.streamdeck-go-watchdog.plist
Normal file
36
launchd/com.woodarddigital.streamdeck-go-watchdog.plist
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!--
|
||||
LaunchAgent for streamdeck-go-watchdog.
|
||||
Installed to: ~/Library/LaunchAgents/com.woodarddigital.streamdeck-go-watchdog.plist
|
||||
|
||||
Fires every 30 seconds. Detects USB unplug/replug events that the daemon's
|
||||
in-process reconnect missed and restarts the streamdeck-go agent.
|
||||
|
||||
The watchdog binary path below is set by the install target at install time.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.woodarddigital.streamdeck-go-watchdog</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>STREAMDECK_WATCHDOG_PATH</string>
|
||||
</array>
|
||||
|
||||
<!-- Fire every 30 seconds. -->
|
||||
<key>StartInterval</key>
|
||||
<integer>30</integer>
|
||||
|
||||
<!-- Don't run at load — let the first interval fire naturally so the
|
||||
device address has a chance to settle after login. -->
|
||||
<key>RunAtLoad</key>
|
||||
<false/>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>STREAMDECK_WATCHDOG_LOG_PATH</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>STREAMDECK_WATCHDOG_LOG_PATH</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Stream Deck privileged command helper
|
||||
Documentation=https://github.com/WoodardDigital/streamdeck-go
|
||||
Documentation=https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
# Start before the user session so the socket is ready when streamdeck-go starts.
|
||||
Before=graphical.target
|
||||
|
||||
|
||||
8
systemd/streamdeck-go-watchdog.service
Normal file
8
systemd/streamdeck-go-watchdog.service
Normal file
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Stream Deck watchdog (one-shot)
|
||||
Documentation=https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
After=streamdeck-go.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=%h/.local/bin/streamdeck-go-watchdog
|
||||
135
systemd/streamdeck-go-watchdog.sh
Normal file
135
systemd/streamdeck-go-watchdog.sh
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env bash
|
||||
# streamdeck-go watchdog — runs every 30s via systemd timer (Linux) or launchd
|
||||
# StartInterval (macOS).
|
||||
#
|
||||
# Why this exists: when the Stream Deck is unplugged and replugged, the daemon's
|
||||
# in-process reconnect logic does not always notice. On Linux, hidraw can keep
|
||||
# returning read timeouts on the now-stale fd instead of surfacing an error, so
|
||||
# the "3 consecutive errors → reconnect" path never triggers, and the service
|
||||
# manager still reports the service as active even though the device is
|
||||
# unreachable.
|
||||
#
|
||||
# Strategy: track the device's transient USB address (Linux: bus:device,
|
||||
# macOS: Location ID). When it changes (unplug/replug) or the service is
|
||||
# inactive while a device is present, restart the service.
|
||||
set -euo pipefail
|
||||
|
||||
# Stream Deck product IDs we support (see internal/device/streamdeck.go).
|
||||
PIDS_RE="00ba|006c|006d"
|
||||
|
||||
OS="$(uname -s)"
|
||||
|
||||
case "$OS" in
|
||||
Linux)
|
||||
STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}"
|
||||
;;
|
||||
Darwin)
|
||||
# No XDG_RUNTIME_DIR on macOS; use the user-private temp dir.
|
||||
STATE_DIR="${TMPDIR:-/tmp}"
|
||||
;;
|
||||
*)
|
||||
echo "watchdog: unsupported OS: $OS" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
STATE_FILE="$STATE_DIR/streamdeck-go-watchdog.state"
|
||||
|
||||
# Print a transient identifier for the first matching Stream Deck on the USB
|
||||
# bus, or empty if none is present. The identifier must change across
|
||||
# unplug/replug so we can detect it.
|
||||
current_addr() {
|
||||
case "$OS" in
|
||||
Linux)
|
||||
# "Bus 003 Device 052: ID 0fd9:00ba ..." → "003:052"
|
||||
lsusb 2>/dev/null | awk -v pids="$PIDS_RE" '
|
||||
$0 ~ ("ID 0fd9:(" pids ")") {
|
||||
gsub(":", "", $4)
|
||||
print $2 ":" $4
|
||||
exit
|
||||
}
|
||||
'
|
||||
;;
|
||||
Darwin)
|
||||
# system_profiler entry per device:
|
||||
# Stream Deck XL:
|
||||
# Product ID: 0x00ba
|
||||
# Vendor ID: 0x0fd9 (Elgato ...)
|
||||
# ...
|
||||
# Location ID: 0x14140000 / 5
|
||||
# The trailing "/ N" is the bus address — it changes on replug.
|
||||
system_profiler SPUSBDataType 2>/dev/null | awk -v pids="$PIDS_RE" '
|
||||
/^[[:space:]]*Product ID:/ { pid = $3 }
|
||||
/^[[:space:]]*Vendor ID:/ { vid = $3 }
|
||||
/^[[:space:]]*Location ID:/ {
|
||||
sub(/^[[:space:]]*Location ID:[[:space:]]*/, "")
|
||||
if (vid == "0x0fd9" && pid ~ ("^0x(" pids ")$")) {
|
||||
print
|
||||
exit
|
||||
}
|
||||
}
|
||||
'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Is the streamdeck-go service currently active?
|
||||
service_active() {
|
||||
case "$OS" in
|
||||
Linux)
|
||||
systemctl --user is-active --quiet streamdeck-go.service
|
||||
;;
|
||||
Darwin)
|
||||
# launchctl list prints "PID Status Label". A PID of "-" means
|
||||
# the agent is loaded but not running.
|
||||
local line
|
||||
line="$(launchctl list 2>/dev/null | awk '$3 == "com.woodarddigital.streamdeck-go" { print $1 }')"
|
||||
[[ -n "$line" && "$line" != "-" ]]
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
restart_service() {
|
||||
case "$OS" in
|
||||
Linux)
|
||||
systemctl --user restart streamdeck-go.service
|
||||
;;
|
||||
Darwin)
|
||||
# kickstart -k stops and restarts; works whether or not it's running.
|
||||
launchctl kickstart -k "gui/$(id -u)/com.woodarddigital.streamdeck-go"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prev=""
|
||||
[[ -f "$STATE_FILE" ]] && prev="$(cat "$STATE_FILE" 2>/dev/null || true)"
|
||||
|
||||
curr="$(current_addr)"
|
||||
|
||||
# Always update the state file so the next run sees a fresh baseline.
|
||||
printf '%s' "$curr" > "$STATE_FILE"
|
||||
|
||||
# No device present — nothing to do. Don't touch the service.
|
||||
if [[ -z "$curr" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
reason=""
|
||||
|
||||
if [[ -z "$prev" ]]; then
|
||||
# First observation (or state file was wiped). Only restart if the service
|
||||
# is also down — if it's already running, assume it's healthy and just
|
||||
# record the baseline.
|
||||
if ! service_active; then
|
||||
reason="device present at $curr but service is not active"
|
||||
fi
|
||||
elif [[ "$curr" != "$prev" ]]; then
|
||||
reason="device address changed: $prev → $curr (likely unplug/replug)"
|
||||
elif ! service_active; then
|
||||
reason="device present at $curr but service is not active"
|
||||
fi
|
||||
|
||||
if [[ -n "$reason" ]]; then
|
||||
echo "watchdog: $reason — restarting streamdeck-go"
|
||||
restart_service
|
||||
fi
|
||||
12
systemd/streamdeck-go-watchdog.timer
Normal file
12
systemd/streamdeck-go-watchdog.timer
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Stream Deck watchdog timer (every 30s)
|
||||
Documentation=https://git.i0t.app/WoodardDigital/streamdeck-go
|
||||
|
||||
[Timer]
|
||||
OnBootSec=30s
|
||||
OnUnitActiveSec=30s
|
||||
AccuracySec=5s
|
||||
Unit=streamdeck-go-watchdog.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Stream Deck controller
|
||||
Documentation=https://github.com/WoodardDigital/streamdeck-go
|
||||
Documentation=https://git.i0t.app/lwoodard/streamdeck-go
|
||||
After=graphical-session.target
|
||||
PartOf=graphical-session.target
|
||||
|
||||
|
||||
Reference in New Issue
Block a user