SonosStreamDeck is a Stream Deck plugin for controlling Sonos playback and showing live now-playing information on Stream Deck hardware.
The product is the plugin itself.
- Users install it into the Stream Deck application.
- Users drag actions onto Stream Deck keys or Stream Deck Plus encoder slots.
- Users configure each action through the Stream Deck property inspector.
- The hardware surface becomes a focused Sonos control panel.
The current repository already supports local end-to-end testing against an internal Sonos broker stub, including auth kickoff, group discovery, command dispatch, state bootstrap, and live playback updates.
These are the current bundled plugin and action visuals. In the running plugin, titles, state, encoder feedback, progress, and album art can update live from Sonos state.
| Plugin | Play / Pause | Album Art |
|---|---|---|
![]() |
![]() |
![]() |
| Mute / Unmute | Next Track | Now Playing |
|---|---|---|
![]() |
![]() |
![]() |
The album-art key and now-playing encoder are more dynamic at runtime than these static assets suggest:
- album art can be replaced with broker-provided artwork or plugin-generated SVG fallback art
- the encoder surface can show current track, elapsed / duration, and progress indicator feedback
This curated preview uses the real bundled action art to show a more intentional Stream Deck Plus-style layout for the plugin.
- Play / Pause
- Mute / Unmute
- Next Track
- Previous Track
- Play Mode
- Album Art
- Now Playing encoder surface for Stream Deck Plus
- broker base URL configured in the property inspector
- Sonos auth started from the property inspector
- per-action Sonos group targeting
- broker-backed playback commands
- broker-backed state bootstrap with
fetchState(...) - live updates over SSE subscriptions
- shared target-aware state cache across visible actions
- local progress estimation between broker updates
- dynamic album-art rendering with SVG fallback visuals
The current implementation is intentionally tested against a local broker stub first. The main items still planned after this milestone are:
- real production broker-backed Sonos auth and group discovery
- real fetched or proxied album art instead of stub data URIs
- capability-aware visuals such as dimming or altering controls when skip or pause is unavailable
- tighter property inspector UX for stale or missing Sonos targets
For the current status snapshot, see docs/implementation-status.md.
The project uses a thin Stream Deck plugin plus Sonos broker pattern.
flowchart LR
User[User on Stream Deck hardware] --> SD[Stream Deck software]
SD --> Plugin[SonosStreamDeck plugin]
Plugin --> PI[Property inspector]
Plugin --> Core[PluginCore and SonosStateStore]
Core --> Client[HttpSonosClient]
Client --> Broker[Sonos broker service]
Broker --> Sonos[Sonos APIs and event sources]
Why this split exists:
- the plugin owns user interaction, rendering, and Stream Deck integration
- the broker owns Sonos-facing auth, discovery, normalized state, and event delivery
- the plugin stores only non-secret connection metadata such as the broker URL and broker session reference
sequenceDiagram
participant User
participant PI as Property Inspector
participant Plugin as PluginCore
participant Broker
participant Deck as Stream Deck Action
User->>PI: Set broker URL and start auth
PI->>SD: setGlobalSettings (connection, defaultTarget, actionTargets)
SD->>Plugin: didReceiveGlobalSettings
Plugin->>Broker: commands / state / SSE
Plugin->>Broker: POST /v1/sonos/auth/start
Broker-->>Plugin: authorizeUrl, sessionRef
Plugin-->>PI: open-auth-url
Plugin->>Broker: poll /v1/sonos/connection
Plugin->>Broker: GET /v1/sonos/groups
User->>PI: Select household/group for action
PI->>Deck: persist per-action settings
Deck->>Plugin: action appears with target settings
Plugin->>Broker: GET /v1/sonos/state
Plugin->>Broker: GET /v1/sonos/events (SSE)
Broker-->>Plugin: full state snapshots
Plugin-->>Deck: update titles, states, images, encoder feedback
User->>Deck: press hardware control
Deck->>Plugin: keyDown / dialDown / touchTap
Plugin->>Broker: POST /v1/sonos/commands
flowchart TD
Entry[src/plugin.ts] --> Actions[Action classes]
Entry --> Core[PluginCore]
Core --> Store[SonosStateStore]
Core --> Client[HttpSonosClient]
Actions --> Core
PI[settings.html and settings.js] --> Core
Core --> Runtimes[Shared per-target runtimes]
Runtimes --> Store
Client --> BrokerStub[scripts/sonos-broker-stub.mjs]
More detail lives in docs/architecture.md and docs/sonos-service-contract.md.
src/plugin.ts- plugin entry point and action registrationsrc/actions/- Stream Deck action classessrc/core/- shared plugin runtime, settings parsing, and state storesrc/sonos/client.ts- typed Sonos broker clientcom.sonosstreamdeck.plugin.sdPlugin/- Stream Deck plugin bundle, manifest, assets, and property inspectorscripts/sonos-broker-stub.mjs- local broker stub for end-to-end testingdocs/- project brain: architecture, status, ADRs, worklogs, troubleshooting
- Node.js 24 or higher
- Stream Deck software 7.1 or higher
- Stream Deck hardware, or Stream Deck Mobile for basic keypad testing
- Stream Deck Plus for encoder and touch-strip validation
- npm
These requirements align with the current Stream Deck SDK guidance and the plugin manifest.
Install dependencies:
npm installBuild the plugin bundle:
npm run buildValidate the plugin bundle:
npm run validateUseful development commands:
npm run watch
npm run broker:stub
npm run broker:stub:watchThe easiest local development flow uses the Stream Deck CLI and developer mode.
npx streamdeck devDeveloper mode enables local plugin development and makes it easier to debug both the plugin runtime and the property inspector.
npx streamdeck link com.sonosstreamdeck.plugin.sdPluginThis links the local plugin folder into the Stream Deck app so local builds are picked up directly.
In a separate terminal:
npm run broker:stubThe stub defaults to:
- host:
127.0.0.1 - port:
47831 - base URL:
http://127.0.0.1:47831
For one-off builds:
npm run buildFor iterative development:
npm run watchThe watch script rebuilds the plugin and restarts com.sonosstreamdeck.plugin after each change.
Inside the Stream Deck software:
- Find the
SonosStreamDeckcategory. - Drag one or more actions onto keys or an encoder slot.
- Open the property inspector.
- Enter
http://127.0.0.1:47831as the service base URL. - Click
Connect Sonos. - Complete the stub auth page opened in the browser.
- Refresh groups if needed.
- Choose a Sonos group for each action.
At that point you can press hardware buttons and see live state updates from the stub.
Create a packaged .streamDeckPlugin file:
npx streamdeck pack com.sonosstreamdeck.plugin.sdPlugin -o distThis produces a distributable plugin package under dist/.
For a release-style install workflow:
- Build and validate the plugin.
- Pack it with the command above.
- Open the generated
.streamDeckPluginfile on a machine with Stream Deck installed.
Use these checklists for the first local hardware test.
- Start
npm run broker:stub. - Run
npm run buildornpm run watch. - Confirm the plugin appears in Stream Deck.
- Add
Play / Pause,Next Track, andAlbum Artactions. - Configure the broker URL and connect through the property inspector.
- Assign a discovered Sonos group.
- Press play / pause and verify the title and state change.
- Press next / previous and verify metadata updates.
- Verify album art and progress refresh over time.
- Add the
Now Playingencoder action to a Stream Deck Plus encoder slot. - Confirm the action shows the current track title after state sync.
- Verify elapsed and duration feedback updates while playback is active.
- Verify the encoder progress indicator advances over time.
- Press or tap the encoder surface and confirm play / pause toggles correctly.
Automated smoke checks (build, validate, typecheck, broker stub lifecycle):
npm run smokenpm test is an alias for the same command. To run only broker HTTP checks against an already-running stub:
npm run broker:testHardware verification still requires manual Stream Deck testing against npm run broker:stub.
GitHub Actions runs npm ci and npm run smoke on push and pull request.
Today, the repository is ready for:
- local build and validation
- local install into Stream Deck software
- hardware testing against the internal broker stub
- property inspector testing for connection and group assignment
- shared live-state testing across multiple visible actions targeting the same group
The repository is not yet ready for true production Sonos end-to-end testing without replacing the stubbed broker behavior.
If the plugin does not appear in Stream Deck after linking or building:
- restart the Stream Deck app
- rerun
npx streamdeck link com.sonosstreamdeck.plugin.sdPlugin - rerun
npm run validate - confirm Stream Deck developer mode is enabled
If auth or state updates do not work:
- confirm the broker stub is running
- confirm the property inspector base URL is
http://127.0.0.1:47831 - confirm the action has a selected Sonos group
- inspect docs/troubleshooting.md
The repo includes a lightweight project brain in docs/.
- docs/00_HOME.md
- docs/architecture.md
- docs/implementation-status.md
- docs/sonos-service-contract.md
- docs/troubleshooting.md
If you want to contribute, start with CONTRIBUTING.md.
- build verified locally
- Stream Deck validation verified locally
- stub-backed auth, discovery, commands, state, and SSE are wired end to end
- the next major milestone is replacing stub-backed broker behavior with the real Sonos production flow






