mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
Completes the docs-tier RFC-5737 rollout by sweeping the remaining
192.168.1.x references that lived outside .md / .txt / test files:
- .env.example — active PREFERRED_DEVICES default + examples
- .github/ISSUE_TEMPLATE/*.yml + workflows — issue template + CI examples
- cmd/websocket-demo/main.go, doc.go — top-level docs
- examples/*/main.go (7 files) — example program comments
- pkg/client/client.go — godoc examples
- pkg/models/doc.go — package godoc
- pkg/service/{amazon,spotify,zeroconf}/zeroconf.go — godoc comments
- pkg/service/handlers/web/index.html — placeholder text in the UI
- scripts/prepare-release.sh — example invocations
- scripts/spotify/spotify-prime-speaker.sh — usage comment
- tests/integration/http-client/http-client.env.json — fixture IPs
Same mapping as the docs commit (136d24a): 192.168.1.X → 192.0.2.X
preserving the last octet.
One semantic carve-out: the three zeroconf `zcBaseURL` godoc comments
in pkg/service/{amazon,spotify,zeroconf}/zeroconf.go switched to
192.168.10.10 instead of the doc range, because validateZcBaseURL
only accepts RFC-1918 / loopback / link-local. The comment must show
a value the validator actually accepts — see the matching test fix
in 92f66a2 for the same reason.
go build ./... clean. go test ./... clean except the pre-existing
TestDocsConsistency (untracked DEVICE-LOCAL-INSTALL.md, unrelated).
golangci-lint run ./... — 0 issues after a gofmt fix on
examples/zone-slave-operations/main.go.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
142 lines
4.4 KiB
Bash
142 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# spotify-prime-speaker — Prime a Bose SoundTouch speaker for Spotify playback
|
|
#
|
|
# Activates Spotify on a SoundTouch speaker by sending an access token
|
|
# via the Spotify Connect ZeroConf endpoint (port 8200). This is the
|
|
# same mechanism the Spotify desktop app uses internally.
|
|
#
|
|
# Works standalone — no soundtouch-service, ueberboese, or other server required.
|
|
#
|
|
# Requirements: curl, jq
|
|
#
|
|
# Usage:
|
|
# ./spotify-prime-speaker SPEAKER_IP ACCESS_TOKEN
|
|
#
|
|
# Example:
|
|
# ./spotify-prime-speaker 192.0.2.143 BQDj...your_token...
|
|
#
|
|
# How to get an access token:
|
|
# - Spotify Developer Console: https://developer.spotify.com
|
|
# (create an app, use the "Get Token" button)
|
|
# - Via soundtouch-service management API: POST /mgmt/spotify/auth/init
|
|
# - Via ueberboese management API: POST /mgmt/spotify/init
|
|
# - Any Spotify OAuth Authorization Code flow with user-read-email scope
|
|
#
|
|
# Notes:
|
|
# - Access tokens expire after 1 hour (3600 seconds)
|
|
# - The speaker must be on the same network and reachable on port 8200
|
|
# - After priming, Spotify presets on the speaker should work immediately
|
|
# - Re-run after each speaker reboot (or use a server like soundtouch-service
|
|
# to automate this)
|
|
#
|
|
# How it works:
|
|
# The Bose SoundTouch speaker exposes a Spotify Connect ZeroConf API
|
|
# on port 8200. By sending an addUser request with a valid Spotify
|
|
# access token, the speaker activates its built-in Spotify Connect
|
|
# client. No encryption is needed — the token is sent as plain text,
|
|
# exactly like the Spotify desktop app does it.
|
|
#
|
|
# Related:
|
|
# - https://github.com/gesellix/Bose-SoundTouch (comprehensive toolkit)
|
|
set -euo pipefail
|
|
|
|
# --- Argument parsing ---
|
|
if [ $# -lt 2 ]; then
|
|
echo "Usage: $0 SPEAKER_IP ACCESS_TOKEN"
|
|
echo ""
|
|
echo "Prime a Bose SoundTouch speaker for Spotify playback."
|
|
echo ""
|
|
echo "Arguments:"
|
|
echo " SPEAKER_IP IP address of the SoundTouch speaker"
|
|
echo " ACCESS_TOKEN Spotify access token (starts with BQ...)"
|
|
echo ""
|
|
echo "Get a token at https://developer.spotify.com or via a server's OAuth flow."
|
|
exit 1
|
|
fi
|
|
|
|
SPEAKER_IP="$1"
|
|
TOKEN="$2"
|
|
ZC_URL="http://${SPEAKER_IP}:8200/zc"
|
|
|
|
# --- Dependency check ---
|
|
for cmd in curl jq; do
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
echo "Error: $cmd is required but not installed." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# --- Step 1: Discover Spotify username from token ---
|
|
echo "Discovering Spotify user from token..."
|
|
ME_RESPONSE=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
|
|
https://api.spotify.com/v1/me 2>&1) || {
|
|
echo "Error: Failed to call Spotify /me API. Is the token valid?" >&2
|
|
echo " (tokens expire after 1 hour)" >&2
|
|
exit 1
|
|
}
|
|
|
|
USER=$(echo "$ME_RESPONSE" | jq -r '.id // empty')
|
|
if [ -z "$USER" ]; then
|
|
echo "Error: Could not extract user ID from Spotify response." >&2
|
|
echo "$ME_RESPONSE" >&2
|
|
exit 1
|
|
fi
|
|
echo " Spotify user: $USER"
|
|
|
|
# --- Step 2: Check current speaker status ---
|
|
echo "Checking speaker at ${SPEAKER_IP}:8200..."
|
|
INFO=$(curl -sf "${ZC_URL}?action=getInfo" 2>&1) || {
|
|
echo "Error: Could not reach speaker at ${SPEAKER_IP}:8200." >&2
|
|
echo " Is the speaker on and on the same network?" >&2
|
|
exit 1
|
|
}
|
|
|
|
ACTIVE=$(echo "$INFO" | jq -r '.activeUser // empty')
|
|
DEVICE_NAME=$(echo "$INFO" | jq -r '.remoteName // empty')
|
|
|
|
if [ -n "$DEVICE_NAME" ]; then
|
|
echo " Speaker: $DEVICE_NAME"
|
|
fi
|
|
|
|
if [ -n "$ACTIVE" ]; then
|
|
echo " Already primed (activeUser=$ACTIVE)"
|
|
echo "Done — speaker is ready for Spotify playback."
|
|
exit 0
|
|
fi
|
|
|
|
echo " No active Spotify user — priming now..."
|
|
|
|
# --- Step 3: Send addUser ---
|
|
RESULT=$(curl -sf -X POST "${ZC_URL}" \
|
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
-d "action=addUser&userName=${USER}&blob=${TOKEN}&clientKey=&tokenType=accesstoken" \
|
|
2>&1) || {
|
|
echo "Error: addUser request failed." >&2
|
|
exit 1
|
|
}
|
|
|
|
STATUS=$(echo "$RESULT" | jq -r '.status // -1')
|
|
STATUS_STR=$(echo "$RESULT" | jq -r '.statusString // empty')
|
|
|
|
if [ "$STATUS" != "101" ]; then
|
|
echo "Error: Speaker returned status $STATUS ($STATUS_STR)" >&2
|
|
echo "$RESULT" | jq . >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo " Speaker accepted the token (status 101)."
|
|
|
|
# --- Step 4: Verify ---
|
|
echo " Verifying (waiting 2 seconds)..."
|
|
sleep 2
|
|
ACTIVE=$(curl -sf "${ZC_URL}?action=getInfo" | jq -r '.activeUser // empty')
|
|
|
|
if [ -n "$ACTIVE" ]; then
|
|
echo "Done — speaker primed for Spotify (activeUser=$ACTIVE)"
|
|
else
|
|
echo "Warning: Speaker returned 101 but activeUser is still empty."
|
|
echo " The speaker may need more time. Try pressing a Spotify preset."
|
|
exit 1
|
|
fi
|