I, Noisebot
Or: How I made playing records through my home Sonos set up nice with some DIY dedicated hardware. You can find the project code on my github.
This write-up was started a few months after finishing the project, and so there is a chance I have misremembered some detail, some side learning, some bit of context. Do leave a comment if you catch a mistake!
The Problem
I bought a couple of Sonos speakers some years back. It has been great for streaming digital audio, but some of the shine wore off when I added my phonograph to the system1. I was using a setup something like this:
I didn't have speakers wired to the Line In box, which meant actually playing a record required the following steps:
- take out my smartphone and open the sonos app
- go to the "rooms" tab
- select the "Line In" device
- hit the "Group" button and connect another speaker—"Living Room", say
- go to the "browse" tab
- select the "Line in" input (provided by the Line In "room", i.e. device)
- put the record on the phonograph
- start the phonograph spinning
- put the needle down
Changing the volume was another whole round of taps:
- take out phone, open sonos app
- back to the "rooms" tab
- select the line in group
- pull up selected group's playback controls to get volume
It was all perfectly doable—and credit where due, the volume slider interface is very nice when you get there—but tapping through all those screens added friction, made it feel like a chore, and we didn't listen to records at all for a long time. There had to be a better way.
A Better Way
Here's the process I wanted:
- put the record on the phonograph
- start the phonograph spinning
- press some easy to reach button next to the phonograph that does all the work to connect speaker(s) and route audio
- put the needle down
And for changing the volume:
- twiddle a volume knob
I had a UI in mind, even: as a programmer, I do enough typing to have researched keyboard enthusiast spaces online, and there I had seen several examples of macro pads2 equipped with a handful of remappable keys and a rotary knob. Here's the layout I ended up going with, but I could have gone smaller: the whole top row was optional.
- top row: playback controls
- left:
b
ackwards track - middle: ensure
p
honograph is audio source - right:
f
orwards track
- left:
- bottom: device toggles
l
iving room speakert
v soundbark
itchen speaker
- knob:
- clockwise:
u
p volume - counterclockwise:
d
own volume - press: toggle
m
ute
- clockwise:
The breakthrough: finding a library to control the speakers
I knew Sonos speakers coordinate with network requests over wifi3; if I had a programatic way to send the same kind of network requests, I could use trigger it with that macro pad and automate away all the bother with telephones. If you press the "living room" button, the speaker there either starts or stops playing the record; turn the volume knob, the volume changes. Creating a wrapper library was likely a matter of observing and reverse-engineering some private HTTP api, and people do that every day. To a search engine!
There were indeed some open-source libraries for interacting with sonos, and they were all on npm; I've written a lot of javascript in my career, so a node script was old hat. Marvelous. I did some quick experiments; the one that made it easiest to find my speakers and order them around goes by sonos
on npm and node-sonos
on github.
For hardware, I had an unused Raspberry Pi Zero4 around: that gave me a computer which was tiny enough to stash in a small device, while still having a wifi antenna to communicate with the speakers and enough CPU to run a node script in an ordinary linux terminal. The programming model looked straightforward, too: the macropad was just a funny USB keyboard, so I could directly connect it to the pi while running the script, which just has to listen for characters coming in on stdin
and get ready to trigger callbacks when it reads the right ones. My plan was coming together nicely:
This looked close to ideal, and the only thing I didn't already own was a little macro pad; so I went ahead and ordered the cheapest one I could find on aliexpress:

With my hardware assembled, I still had to complete three tasks, which could all be—at least partially—developed in isolation:
- write a headless but interactive sonos TUI as a node.js script: I could write and run the code on a normal laptop if the Raspberry Pi weren't ready, using the laptop keyboard to trigger actions if the mini one weren't ready
- set up a raspberry pi zero to run that script, using a laptop keyboard over
ssh
to test the script if the mini keyboard weren't ready - find out what codes my mini keyboard sends for each key and the knob, remapping them as needed; this has no dependency on the Raspberry Pi at all.
Each problem has a dedicated page with a more detailed explanation. Let's dig in!
Footnotes
-
Part of this was just collateral damage from a classically graceless corporate upgrade: Sonos dropped support for the
Connect:Amp
device (the one with a line in for the phonograph) more or less while I was unwrapping it, and immediately commenced a nagging campaign to get us to upgrade to a shiny and incompatible v2 of their smartphone app. ↩ -
"Macro pads" are small mechanical keyboards with just a handful of keys, each of which is then programmed to send some useful, but hard to type, keyboard shortcut ↩
-
Presumably with audio streamed over UDP and ordinary http requests for commands and events ↩
-
specifically, a Raspberry Pi Zero W v1.1 ↩