A local web app for controlling Sonos speakers with saved audio profiles, scheduling, and automatic session detection. Built with React + Vite, talks to your Sonos over node-sonos-http-api.
- See what's currently playing — track title, artist, album, and album art update in real time
- Control playback (play, pause, skip, previous) without opening the Sonos app
- View the full queue so you know what's coming up next
- Album art always shows the correct artwork for the track that's actually playing
- Combine multiple speakers into one group with a single tap, or split them back apart
- Volume controls automatically adjust to affect the whole group — no more one speaker being louder than another
- The header updates to show which speakers are currently playing together
- Save your favourite sound settings as named profiles — for example "Movie Night", "Music", or "Late Night"
- Each profile remembers volume, bass, treble, night mode, speech enhancement, and subwoofer settings
- Apply any profile in one tap, or snapshot your current Sonos settings directly into a new profile
- Set profiles to apply automatically at specific times on specific days — useful for a consistent start-of-evening sound level without touching anything
- The activity log keeps a record of every change so you always know what was applied and when
- Fine-tune volume, bass, treble, and subwoofer in real time using sliders — without touching your saved profiles
- Toggle the subwoofer on or off on the fly
- Can run as a background service on macOS so the controller is always available — even when the Mac is asleep or locked
- Settings are saved locally and survive restarts, browser clears, and port changes
- macOS or Windows
- A Sonos speaker reachable on your local network
- That's it — Node.js, node-sonos-http-api, and all other dependencies are installed automatically by the install script
Clone the repo, then run the install script:
git clone https://github.com/tonypest0/sonos-controller.git
cd sonos-controller
bash install.shThe script will:
- Install Node.js via Homebrew if it isn't already present
- Run
npm install— this pulls in all dependencies including node-sonos-http-api - Build the app
- Ask for your Sonos room name and preferred ports
- Set up two background services (launchd daemons) that start automatically at boot — even when the Mac is locked
Once complete, open http://localhost:3000 in your browser.
Open PowerShell as Administrator, then run:
git clone https://github.com/tonypest0/sonos-controller.git
cd sonos-controller
Set-ExecutionPolicy Bypass -Scope Process -Force
.\install.ps1The script will:
- Install Node.js via winget if it isn't already present
- Run
npm install— this pulls in all dependencies including node-sonos-http-api - Build the app
- Ask for your Sonos room name and preferred ports
- Register two Windows scheduled tasks under
\Sonos\that start at boot and restart automatically on failure
Once complete, open http://localhost:3000 in your browser.
Finding your room name: Open the Sonos app, tap your speaker — the name shown there (case-sensitive) is what to enter during setup.
macOS:
git pull
npm run build
sudo launchctl kickstart -k system/com.sonos.controllerWindows (run as Administrator):
git pull
npm run build
Restart-ScheduledTask -TaskPath "\Sonos" -TaskName "SonosController"The Sonos API does not send CORS headers, so browsers block direct requests to it. Both the development server (vite.config.js) and the production server (server.js) include a lightweight proxy that forwards all API calls from the browser through Node.js — no browser CORS issues, no extra configuration needed.
node-sonos-http-api is included as an npm dependency and installed automatically with npm install. It runs as a separate process alongside the controller server.
To run a local dev server with hot reload:
npm install
npm run devOpen http://localhost:5173. The Vite dev server includes the same proxy and store middleware as the production server, so everything works identically.
src/
components/
ConnectionConfig.jsx # Host/port/room settings UI
NowPlaying.jsx # Now Playing card with transport controls
ProfileCard.jsx # Single profile display + apply button
ProfileEditor.jsx # Create / edit profile form
Queue.jsx # Current queue panel with thumbnails
QuickControls.jsx # Live sliders + speaker group toggle
Scheduler.jsx # Schedule list + add form
ActivityLog.jsx # Event history log
hooks/
useNowPlaying.js # Polls /zones for playback state + group info
useProfiles.js # Profile storage and management
useSonosApi.js # All Sonos API calls
useScheduler.js # Schedule evaluation and triggering
useSessionWatcher.js # Playback state polling for session start
useActivityLog.js # Log state management
lib/
fileStore.js # localStorage + server-side file persistence
sonosArt.js # Album art URL resolution helper
App.jsx
main.jsx
server.js # Production server (static files + proxy + store)
install.sh # One-command installer for macOS
vite.config.js # Dev server with proxy + store middleware
| Issue | Fix |
|---|---|
| Volume slider showed wrong value when speakers were grouped | Switched to reading groupState.volume from /zones instead of individual room volume from /state |
| YouTube Music liked-songs showed playlist thumbnail instead of track art | Now fetches art via the Sonos device's /getaa endpoint (albumArtUri) rather than absoluteAlbumArtUri |
| Port conflict when running alongside Claude Code preview server | Daemon now uses PORT env var; default bumped to avoid collision |
| Live Controls panel overlapped content when scrolling | Panel is now collapsible and collapses automatically after a profile is applied |
| Volume write applied to individual room only when grouped | Write now uses groupvolume command when Kitchen is in the group |
CC BY-NC 4.0 — free for personal and non-commercial use. Commercial use requires explicit written permission.
