#!/bin/zsh # install_bridge.sh — install the Resi -> ProPresenter bridge as a login service on THIS Mac. # # Put this folder on the Mac that should run the bridge (one that stays on and logged in), # then run: zsh install_bridge.sh # # First run asks for the Resi login, the decoder, ProPresenter's address and the timer. # Safe to run again: it keeps those answers, replaces the script and restarts the service. # zsh install_bridge.sh --setup ask the questions again # zsh install_bridge.sh --uninstall stop it and remove it from this Mac set -e HERE="${0:A:h}" DEST="$HOME/Library/Application Support/resi-propresenter" LABEL="org.waterschurch.resi-propresenter" PLIST="$HOME/Library/LaunchAgents/$LABEL.plist" LOG="$HOME/Library/Logs/resi-propresenter.log" if [[ "$1" == "--uninstall" ]]; then launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true rm -f "$PLIST" echo "Stopped and removed the service. Settings are kept in $DEST (delete it to forget them)." exit 0 fi # python3 ships with the Command Line Tools; /usr/bin/python3 is only a stub without them if ! /usr/bin/python3 -c 'import sys' 2>/dev/null; then echo "python3 isn't usable yet. Run: xcode-select --install then run this again." exit 1 fi # Outside ~/Documents: macOS blocks background services from reading it. mkdir -p "$DEST" "$HOME/Library/LaunchAgents" "$HOME/Library/Logs" if [[ ! "$HERE/resi_to_propresenter.py" -ef "$DEST/resi_to_propresenter.py" ]]; then cat "$HERE/resi_to_propresenter.py" > "$DEST/resi_to_propresenter.py.new" mv "$DEST/resi_to_propresenter.py.new" "$DEST/resi_to_propresenter.py" fi # A config shipped alongside (re-installing a known site) seeds a fresh install only. if [[ -f "$HERE/resi_pp_config.json" && ! -f "$DEST/resi_pp_config.json" ]]; then cp "$HERE/resi_pp_config.json" "$DEST/resi_pp_config.json" fi complete=$(/usr/bin/python3 - "$DEST/resi_pp_config.json" <<'EOF' import json, subprocess, sys try: c = json.load(open(sys.argv[1])) except Exception: print("no"); sys.exit() ok = c.get("resi_user") and c.get("player") and c.get("propresenter") and c.get("timer_name") ok = ok and subprocess.run(["security", "find-generic-password", "-a", c["resi_user"], "-s", "resi-propresenter"], capture_output=True).returncode == 0 print("yes" if ok else "no") EOF ) if [[ "$complete" != "yes" || "$1" == "--setup" ]]; then # Over SSH the login keychain is locked, and saving the Resi password into it fails # with "User interaction is not allowed" until it is unlocked here. if [[ -n "$SSH_CONNECTION" ]]; then echo "Unlock $(scutil --get ComputerName)'s Keychain: enter the Mac login password for $(whoami)." security unlock-keychain "$HOME/Library/Keychains/login.keychain-db" fi /usr/bin/python3 "$DEST/resi_to_propresenter.py" --setup fi cat > "$PLIST" < Label $LABEL ProgramArguments /usr/bin/python3 $DEST/resi_to_propresenter.py RunAtLoad KeepAlive ThrottleInterval 30 StandardOutPath $LOG StandardErrorPath $LOG EOF launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true launchctl bootstrap "gui/$(id -u)" "$PLIST" echo "Installed. Waiting for the first lines of the log..." sleep 10 tail -n 8 "$LOG" echo echo "Running on $(scutil --get ComputerName). Log: $LOG" # After a reboot the service only starts once someone is logged in. autologin=$(defaults read /Library/Preferences/com.apple.loginwindow autoLoginUser 2>/dev/null || true) if [[ "$autologin" != "$(whoami)" ]]; then echo echo "NOTE: this Mac doesn't log in to '$(whoami)' automatically, so after a reboot the" echo "countdown won't start until someone logs in. To make it fully hands-off, turn on" echo "System Settings -> Users & Groups -> Automatically log in as $(whoami) (needs FileVault off)." fi