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