diff --git a/docs/guides/ON-DEVICE-INSTALL-WALKTHROUGH.md b/docs/guides/ON-DEVICE-INSTALL-WALKTHROUGH.md new file mode 100644 index 0000000..8c5ab15 --- /dev/null +++ b/docs/guides/ON-DEVICE-INSTALL-WALKTHROUGH.md @@ -0,0 +1,296 @@ +# On-Device Install Walkthrough + +A complete end-to-end runbook for installing AfterTouch directly on a +Bose SoundTouch speaker — from first SSH connection through verified +radio preset playback. + +**Credit:** This guide is based on a step-by-step walkthrough contributed +by [weissigera](https://github.com/weissigera) in +[issue #329](https://github.com/gesellix/Bose-SoundTouch/issues/329#issuecomment-4521280831), +documenting a successful fresh installation on a SoundTouch 20 Series I. + +For the installer reference and troubleshooting tips see +[scripts/on-device-install/README.md](../../scripts/on-device-install/README.md). + +--- + +## Prerequisites + +- SSH enabled on the speaker (the usual "Stick with remote_services" procedure). +- Your machine can reach the speaker on the LAN. +- The speaker's LAN IP address — replace `192.0.2.1` throughout with the + actual address shown in your router or `arp -a`. + +> **Note on SSH host-key negotiation:** SoundTouch speakers only advertise +> legacy host-key algorithms (`ssh-rsa`, `ssh-dss`). Modern OpenSSH clients +> reject these by default. The `-oHostKeyAlgorithms=+ssh-rsa` flag below +> opts them back in. Without it you'll see +> `no matching host key type found`. + +--- + +## Step 1 — Connect to the speaker via SSH + +```bash +ssh -oHostKeyAlgorithms=+ssh-rsa root@192.0.2.1 +``` + +You should see a prompt such as `root@soundtouch-device:~#`. + +--- + +## Step 2 — Check free space (and clean up if needed) + +The persistent `/mnt/nv` partition typically has 20–40 MB free — enough for +the AfterTouch binary (~12 MB) plus one backup. Check first: + +```bash +rw # remount rootfs read-write +df -h /mnt/nv +``` + +If you have an older installation with multiple backup or artefact files left +behind by earlier upgrades, remove them: + +```bash +# List what's there +ls -lh /mnt/nv/aftertouch/ + +# Remove specific stale files (adjust version numbers to what you see) +rm -f /mnt/nv/aftertouch/aftertouch-service.v0.80.1.backup +rm -f /mnt/nv/aftertouch/aftertouch-service.v0.86.0.backup +rm -f /mnt/nv/aftertouch/aftertouch-service.v0.86.0.old +rm -f /mnt/nv/aftertouch/aftertouch-service.new +rm -f /mnt/nv/soundtouch-cli # cli binary if left there by hand +rm -f /mnt/nv/aftertouch/soundtouch-cli + +df -h /mnt/nv # confirm space recovered +``` + +> **From v0.89.0 onwards the installer prunes stale artefacts automatically** +> during every upgrade — manual cleanup should no longer be necessary on +> fresh installs. + +--- + +## Step 3 — Install (or upgrade) AfterTouch + +Run the canonical one-liner. It downloads the binary and init script, +creates `/mnt/nv/aftertouch/`, symlinks `/opt/aftertouch`, backs up the +currently running binary, and starts the service: + +```bash +rw && curl -sSL https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh | sh +``` + +To target a specific version instead of the default: + +```bash +# Via environment variable (works with pipe-to-sh) +VERSION=0.92.0 rw && curl -sSL https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh | sh + +# Via command-line flag (pass args after sh -s --) +curl -sSL https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh | sh -s -- --version 0.92.0 +``` + +Verify the installed version: + +```bash +wget -qO- http://localhost:8000/health +``` + +The JSON response should include `"version":"v0.92.0"` (or whichever +version you installed). + +--- + +## Step 4 — Reboot the speaker + +```bash +sync +reboot +``` + +Wait 2–3 minutes for the speaker to come back up, then reconnect: + +```bash +ssh -oHostKeyAlgorithms=+ssh-rsa root@192.0.2.1 +``` + +--- + +## Step 5 — Open an SSH tunnel and access the Admin UI + +**Open a new terminal on your machine** (not inside the speaker's SSH +session — see [issue #250](https://github.com/gesellix/Bose-SoundTouch/issues/250) +for the port-forward-from-inside trap) and run: + +```bash +ssh -oHostKeyAlgorithms=+ssh-rsa -L 8000:localhost:8000 root@192.0.2.1 +``` + +Keep this terminal open. Navigate to **http://localhost:8000** in your +browser. + +> Skip this step if your speaker's firmware exposes port 8000 on the LAN +> directly — you can reach `http://192.0.2.1:8000` without a tunnel in that +> case. + +--- + +## Step 6 — Run the Health QuickFix for empty `margeAccountUUID` + +In the AfterTouch UI: + +1. Open the **Health** tab. +2. Run or refresh the health checks. +3. Look for the warning: + > *Speaker reports an empty ``* +4. Click the **QuickFix** button (labelled "Fix", "Pair account", or + "Apply QuickFix" depending on the version) and confirm. + +Then reboot again to let the pairing take effect: + +```bash +sync +reboot +``` + +--- + +## Step 7 — Verify pairing and sources + +After the reboot reconnect via SSH and check: + +```bash +ssh -oHostKeyAlgorithms=+ssh-rsa root@192.0.2.1 + +# margeAccountUUID must NOT be empty after the QuickFix +wget -qO- http://localhost:8090/info | grep margeAccountUUID + +# Sources must include LOCAL_INTERNET_RADIO, TUNEIN, and RADIO_BROWSER +wget -qO- http://localhost:8090/sources +``` + +If `margeAccountUUID` is still empty, re-run the Health QuickFix (Step 6) +and reboot again. + +--- + +## Step 8 — Download soundtouch-cli (optional, for preset setup) + +If you want to program preset buttons from the command line, download the +CLI binary to the speaker's `/tmp` (tmpfs, so it survives only until the +next reboot — which is fine for a one-time setup run): + +```bash +cd /tmp + +curl -L --fail -o soundtouch-cli \ + https://github.com/gesellix/Bose-SoundTouch/releases/download/v0.92.0/soundtouch-cli-v0.92.0-linux-armv7 +chmod +x soundtouch-cli + +/tmp/soundtouch-cli --version +``` + +Replace `v0.92.0` with the version you installed. + +--- + +## Step 9 — Store custom radio streams to preset buttons + +Each station must be playing before it can be saved. The `sleep 5` gives +the speaker time to buffer and confirm the stream before storing. + +> **Press preset buttons briefly.** A long press on the physical hardware +> overwrites the stored preset. + +```bash +# Preset 1 — Hitradio OE3 +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "http://orf-live.ors-shoutcast.at/oe3-q2a" \ + --name "Hitradio OE3" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 1 + +# Preset 2 — Lounge FM +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "http://188.138.9.183/digital.mp3" \ + --name "Lounge FM" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 2 + +# Preset 3 — Country Nonstop +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "https://stream.laut.fm/country-nonstop" \ + --name "Country Nonstop" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 3 + +# Preset 4 — Radio Piterpan +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "https://klasse1.fluidstream.eu/piterpan.mp3?FLID=8" \ + --name "Radio Piterpan" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 4 + +# Preset 5 — kronehit +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "https://secureonair.krone.at/kronehit-hp.mp3" \ + --name "kronehit" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 5 + +# Preset 6 — Radio Niederösterreich +/tmp/soundtouch-cli --host 127.0.0.1 source custom-radio \ + --url "http://orf-live.ors-shoutcast.at/noe-q2a" \ + --name "Radio Niederoesterreich" \ + --service-url "http://localhost:8000" +sleep 5 +/tmp/soundtouch-cli --host 127.0.0.1 preset store-current --slot 6 +``` + +These are the stations from weissigera's setup (Austrian public and +internet radio). Replace any or all of them with your own streams — the +pattern is the same regardless of station. + +--- + +## Step 10 — Verify presets and final reboot + +```bash +wget -qO- http://localhost:8090/presets +``` + +You should see all six preset slots populated. Then do a final reboot and +test the physical buttons: + +```bash +sync +reboot +``` + +After the speaker comes back up, press preset buttons 1–6 briefly — each +should start playing the corresponding stream. + +--- + +## Troubleshooting + +| Symptom | First check | +|---------|------------| +| SSH "no matching host key type" | Add `-oHostKeyAlgorithms=+ssh-rsa` | +| Port 8000 not reachable from LAN | Use the SSH tunnel (Step 5) | +| `margeAccountUUID` still empty after reboot | Re-run Health QuickFix, reboot again | +| Radio source error 1005 | `margeAccountUUID` is empty — complete Step 6 first | +| `http://localhost:8000` not responding after install | `logread \| grep aftertouch \| tail -20` | +| No space left on device during install | Run the cleanup in Step 2; check `df -h /mnt/nv` | + +For more detail on any of these, see +[TROUBLESHOOTING.md](./TROUBLESHOOTING.md) and the +[on-device installer README](../../scripts/on-device-install/README.md). diff --git a/scripts/on-device-install/README.md b/scripts/on-device-install/README.md index 6a59e31..f8c2b53 100644 --- a/scripts/on-device-install/README.md +++ b/scripts/on-device-install/README.md @@ -2,6 +2,9 @@ Allows to run AfterTouch on SoundTouch devices directly, eliminating the need to run and maintain a separate server on the local network. +For a complete step-by-step walkthrough — from first SSH connection through verified radio preset playback — see +[docs/guides/ON-DEVICE-INSTALL-WALKTHROUGH.md](../../docs/guides/ON-DEVICE-INSTALL-WALKTHROUGH.md). + ## Disclaimer ### Invasiveness @@ -82,7 +85,31 @@ The init script's `status` now distinguishes "running with listener up" from "PI ## Updating AfterTouch -To update AfterTouch, simply run the installation command again. The installer will check if there's a new version available and update it if necessary. +Run the installer again with the version you want to install. The script backs up the currently-running binary (named after its version), installs the new one, and prunes older leftover artefacts to keep `/mnt/nv` free. + +**Install (or upgrade to) a specific version** — three equivalent ways: + +```bash +# 1. Environment variable (works when piping into sh) +VERSION=0.92.0 rw && curl -sSL https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh | sh + +# 2. Command-line flag (pass args after `sh -s --`) +rw && curl -sSL https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh | sh -s -- --version 0.92.0 + +# 3. Download first, then run with a flag +curl -sSLo install.sh https://raw.githubusercontent.com/gesellix/Bose-SoundTouch/main/scripts/on-device-install/install.sh +sh install.sh --version 0.92.0 +``` + +Running **without** a version override installs the version hard-coded in the script (the latest release at the time the script was published). That default is updated with each release; if you're running from `main`, it reflects the most recent tagged version. + +> **Tip — rollback:** if the new binary misbehaves, the installer left a `.backup` file alongside it: +> ```bash +> ls /mnt/nv/aftertouch/aftertouch-service*.backup +> cp /mnt/nv/aftertouch/aftertouch-service..backup \ +> /mnt/nv/aftertouch/aftertouch-service +> /etc/init.d/aftertouch restart +> ``` ## Uninstallation diff --git a/scripts/on-device-install/aftertouch b/scripts/on-device-install/aftertouch index 42a72ed..ccb04b3 100644 --- a/scripts/on-device-install/aftertouch +++ b/scripts/on-device-install/aftertouch @@ -81,10 +81,24 @@ case "$1" in stop) echo "Stopping $DESC..." if [ -f "$PIDFILE" ]; then + PID=$(cat "$PIDFILE") start-stop-daemon --stop \ --quiet \ --oknodo \ --pidfile "$PIDFILE" + # Wait up to 15 s for SIGTERM to take effect before escalating. + # The Go HTTP server exits promptly on SIGTERM in normal conditions; + # the loop handles the rare case where it is stuck in a blocking syscall. + tries=0 + while [ $tries -lt 15 ] && kill -0 "$PID" 2>/dev/null; do + sleep 1 + tries=$((tries + 1)) + done + if kill -0 "$PID" 2>/dev/null; then + echo "Warning: $NAME (PID $PID) still alive after ${tries}s; sending SIGKILL..." >&2 + kill -9 "$PID" 2>/dev/null || true + sleep 1 + fi rm -f "$PIDFILE" else echo "No $NAME running (no PID file)." >&2 diff --git a/scripts/on-device-install/install.sh b/scripts/on-device-install/install.sh index f94ecac..1adb4e3 100644 --- a/scripts/on-device-install/install.sh +++ b/scripts/on-device-install/install.sh @@ -1,7 +1,31 @@ #!/bin/bash set -eo pipefail +# Default version installed when no override is provided. Update this value +# each time a new release is cut so that running the canonical one-liner +# curl -sSL .../install.sh | sh +# picks up the latest binary without extra arguments. +# +# Override via environment variable or the --version/-v flag: +# VERSION=0.92.0 curl -sSL .../install.sh | sh +# curl -sSL .../install.sh | sh -s -- --version 0.92.0 VERSION=${VERSION:-0.91.0} + +# Parse optional command-line arguments so the script can be invoked as: +# install.sh --version 0.92.0 +# install.sh -v 0.92.0 +while [ $# -gt 0 ]; do + case "$1" in + --version|-v) + if [ -z "$2" ]; then + echo "ERROR: --version requires an argument." >&2; exit 1 + fi + VERSION="$2"; shift 2;; + --) shift; break;; + *) echo "Unknown argument: $1" >&2; exit 1;; + esac +done + GH_REPO=${GH_REPO:-gesellix/Bose-SoundTouch} BINARY_URL=${BINARY_URL:-https://github.com/$GH_REPO/releases/download/v$VERSION/soundtouch-service-v$VERSION-linux-armv7} INIT_SCRIPT_URL=${INIT_SCRIPT_URL:-https://raw.githubusercontent.com/$GH_REPO/v$VERSION/scripts/on-device-install/aftertouch}