QML bar widget + popout panel (dev.woodard.streamdeck) that drives streamdeck-ctl: key grid shaped by the connected model, per-key editor, brightness slider, and daemon controls. Themed via qs.Commons. Makefile gains build-ctl, install-ctl, install-plugin, validate-plugin and uninstall-plugin targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BjuZBBfzXqZgxRhxzJvkkC
319 lines
14 KiB
Makefile
319 lines
14 KiB
Makefile
BINARY := streamdeck-go
|
|
HELPER := streamdeck-helper
|
|
INIT := streamdeck-init
|
|
CTL := streamdeck-ctl
|
|
PREFIX ?= $(HOME)/.local
|
|
CONFIG_DIR := $(HOME)/.config/streamdeck-go
|
|
GROUP := streamdeck
|
|
|
|
# ── Omarchy shell plugin ──────────────────────────────────────────────────────
|
|
PLUGIN_ID := dev.woodard.streamdeck
|
|
PLUGIN_SRC := $(CURDIR)/omarchy-plugin
|
|
PLUGIN_DIR := $(HOME)/.config/omarchy/plugins/$(PLUGIN_ID)
|
|
|
|
# ── OS detection ──────────────────────────────────────────────────────────────
|
|
OS := $(shell uname -s)
|
|
|
|
ifeq ($(OS),Darwin)
|
|
BIN_DIR := $(HOME)/go/bin
|
|
ETC_DIR := /usr/local/etc/streamdeck-go
|
|
LAUNCHAGENTS := $(HOME)/Library/LaunchAgents
|
|
LAUNCHDAEMONS := /Library/LaunchDaemons
|
|
AGENT_PLIST := $(LAUNCHAGENTS)/com.woodarddigital.streamdeck-go.plist
|
|
DAEMON_PLIST := $(LAUNCHDAEMONS)/com.woodarddigital.streamdeck-go-helper.plist
|
|
else
|
|
BIN_DIR := $(PREFIX)/bin
|
|
SYS_BIN := /usr/local/bin
|
|
ETC_DIR := /etc/streamdeck-go
|
|
SYSTEMD_USER := $(HOME)/.config/systemd/user
|
|
SYSTEMD_SYS := /etc/systemd/system
|
|
UDEV_RULE := /etc/udev/rules.d/99-streamdeck.rules
|
|
endif
|
|
|
|
.PHONY: build build-helper build-init build-ctl install install-ctl install-helper install-watchdog \
|
|
install-plugin uninstall-plugin validate-plugin reinstall uninstall uninstall-helper \
|
|
uninstall-watchdog udev
|
|
|
|
# ── Build ─────────────────────────────────────────────────────────────────────
|
|
|
|
build:
|
|
go build -o $(BINARY) ./cmd/streamdeck/
|
|
|
|
build-helper:
|
|
go build -o $(HELPER) ./cmd/streamdeck-helper/
|
|
|
|
build-init:
|
|
go build -o $(INIT) ./cmd/streamdeck-init/
|
|
|
|
build-ctl:
|
|
go build -o $(CTL) ./cmd/streamdeck-ctl/
|
|
|
|
# ── Install ───────────────────────────────────────────────────────────────────
|
|
|
|
# Interactive install — prompts for dotfiles directory, installs binary + service.
|
|
install:
|
|
@bash install.sh
|
|
|
|
# Install the privileged helper (requires sudo).
|
|
ifeq ($(OS),Darwin)
|
|
# On macOS there is no root daemon — privileged commands run via osascript (admin
|
|
# auth dialog). install-helper just drops the whitelist into the user config dir.
|
|
install-helper:
|
|
mkdir -p $(CONFIG_DIR)
|
|
@if [ ! -f $(CONFIG_DIR)/privileged.yaml ]; then \
|
|
install -m 644 config/privileged.example.yaml $(CONFIG_DIR)/privileged.yaml; \
|
|
echo "Whitelist written to $(CONFIG_DIR)/privileged.yaml"; \
|
|
else \
|
|
echo "Whitelist already exists — not overwriting"; \
|
|
fi
|
|
@echo ""
|
|
@echo "Edit $(CONFIG_DIR)/privileged.yaml to add priv: commands."
|
|
@echo "No sudo or daemon required — macOS will prompt for admin auth on use."
|
|
else
|
|
install-helper: build-helper
|
|
# Create group and add current user (Linux).
|
|
@if ! getent group $(GROUP) > /dev/null; then \
|
|
sudo groupadd $(GROUP); \
|
|
echo "Created group '$(GROUP)'"; \
|
|
fi
|
|
sudo usermod -aG $(GROUP) $(USER)
|
|
# Install helper binary (owned root, not world-executable).
|
|
sudo install -Dm750 $(HELPER) $(SYS_BIN)/$(HELPER)
|
|
sudo chown root:$(GROUP) $(SYS_BIN)/$(HELPER)
|
|
# Install whitelist config.
|
|
sudo mkdir -p $(ETC_DIR)
|
|
@if [ ! -f $(ETC_DIR)/privileged.yaml ]; then \
|
|
sudo install -Dm640 config/privileged.example.yaml $(ETC_DIR)/privileged.yaml; \
|
|
sudo chown root:$(GROUP) $(ETC_DIR)/privileged.yaml; \
|
|
echo "Whitelist written to $(ETC_DIR)/privileged.yaml"; \
|
|
else \
|
|
echo "Whitelist already exists — not overwriting"; \
|
|
fi
|
|
# Install and enable system service.
|
|
sudo install -Dm644 systemd/streamdeck-go-helper.service $(SYSTEMD_SYS)/streamdeck-go-helper.service
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now streamdeck-go-helper.service
|
|
@echo ""
|
|
@echo "Helper installed. Edit $(ETC_DIR)/privileged.yaml (as root) to add commands."
|
|
@echo "NOTE: log out and back in for group membership to take effect,"
|
|
@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)
|
|
@echo "udev is Linux-only — not needed on macOS (IOKit grants HID access directly)."
|
|
else
|
|
@if [ ! -f $(UDEV_RULE) ]; then \
|
|
echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", MODE="0666"' \
|
|
| sudo tee $(UDEV_RULE); \
|
|
sudo udevadm control --reload; \
|
|
sudo udevadm trigger; \
|
|
echo "udev rule installed."; \
|
|
else \
|
|
echo "udev rule already exists."; \
|
|
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
|
|
|
|
# ── streamdeck-ctl + Omarchy shell plugin ─────────────────────────────────────
|
|
|
|
# The CLI the Omarchy plugin drives. Also useful on its own for scripting.
|
|
install-ctl: build-ctl
|
|
mkdir -p $(BIN_DIR)
|
|
install -m 755 $(CTL) $(BIN_DIR)/$(CTL)
|
|
@echo "Installed $(BIN_DIR)/$(CTL)"
|
|
|
|
# Symlink rather than copy so edits to the QML are live after a shell rescan.
|
|
install-plugin: install-ctl
|
|
@if [ ! -d "$(HOME)/.config/omarchy" ]; then \
|
|
echo "Omarchy shell config not found at ~/.config/omarchy — is Omarchy installed?"; \
|
|
exit 1; \
|
|
fi
|
|
mkdir -p $(HOME)/.config/omarchy/plugins
|
|
@if [ -e "$(PLUGIN_DIR)" ] && [ ! -L "$(PLUGIN_DIR)" ]; then \
|
|
echo "$(PLUGIN_DIR) exists and is not a symlink — remove it first."; \
|
|
exit 1; \
|
|
fi
|
|
ln -sfn "$(PLUGIN_SRC)" "$(PLUGIN_DIR)"
|
|
# Validate the source tree, not the link: `omarchy plugin validate` refuses a
|
|
# symlinked plugin folder by design, but the shell resolves it happily.
|
|
@omarchy plugin validate "$(PLUGIN_SRC)" || true
|
|
@omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
|
@echo ""
|
|
@echo "Plugin linked: $(PLUGIN_DIR) -> $(PLUGIN_SRC)"
|
|
@echo "Enable it with: omarchy plugin enable $(PLUGIN_ID) right"
|
|
|
|
validate-plugin:
|
|
omarchy plugin validate "$(PLUGIN_SRC)"
|
|
@command -v qmllint >/dev/null 2>&1 && \
|
|
qmllint -I $${OMARCHY_PATH:-/usr/share/omarchy}/shell "$(PLUGIN_SRC)"/*.qml || \
|
|
echo "qmllint not installed — skipped QML lint"
|
|
|
|
uninstall-plugin:
|
|
omarchy plugin disable $(PLUGIN_ID) 2>/dev/null || true
|
|
rm -f "$(PLUGIN_DIR)"
|
|
rm -f $(BIN_DIR)/$(CTL)
|
|
@omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true
|
|
@echo "Plugin unlinked and $(CTL) removed."
|
|
|
|
# ── Uninstall ─────────────────────────────────────────────────────────────────
|
|
|
|
ifeq ($(OS),Darwin)
|
|
uninstall:
|
|
launchctl unload $(AGENT_PLIST) 2>/dev/null || true
|
|
rm -f $(AGENT_PLIST)
|
|
rm -f $(BIN_DIR)/$(BINARY)
|
|
@echo "Uninstalled. Config at $(CONFIG_DIR) preserved."
|
|
|
|
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
|
|
rm -f $(BIN_DIR)/$(BINARY)
|
|
rm -f $(SYSTEMD_USER)/streamdeck-go.service
|
|
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)
|
|
sudo rm -f $(SYSTEMD_SYS)/streamdeck-go-helper.service
|
|
sudo systemctl daemon-reload
|
|
@echo "Helper uninstalled. Whitelist at $(ETC_DIR)/privileged.yaml preserved."
|
|
endif
|