Sendspin
The sendspin component adds support for Sendspin,
a multi-room synchronized audio protocol. The hub manages the connection to a Sendspin server and distributes state updates to any Sendspin child components.
The device uses mDNS to discover Sendspin servers on the network, so mDNS traffic must not be blocked on your network. TCP port 8928 must also be reachable between the device and the server.
On its own, the hub does not expose any user-facing entities. Once a Sendspin server is present on the network, it will connect to the device and begin sending group state updates, but audio playback, control, and metadata require the corresponding child components to be configured as well. The hub can also stream a microphone to the server; see Source. The Sendspin Switch can stop the client so no server connects.
Every connection to a Sendspin server is encrypted. By default any server on the network may play on the device. Turn off unpaired access to allow only servers that have paired with it.
This platform only works on ESP32-based chips, with the ESP-IDF framework.
IMPORTANT
The device needs a Sendspin 1.0.0-rc1 server, such as Music Assistant 2.11.0b4 or newer. Servers using an earlier protocol version cannot connect.
WARNING
The Sendspin protocol is not yet finalized and this component is considered experimental. Breaking changes may occur in future ESPHome releases as the protocol and component evolve.
# Example configuration entrysendspin:Configuration variables
Section titled “Configuration variables”-
id (Optional, ID): Manually specify the ID for this hub. Required if you want to reference this hub explicitly from child components.
-
task_stack_in_psram (Optional, boolean): Place the internal HTTP server and protocol task stacks in PSRAM to reduce internal RAM usage. Requires the Psram component to be configured. Defaults to
false. -
manufacturer (Optional, string): The manufacturer the device reports to the Sendspin server. Defaults to the author part of the project name when one is set, otherwise
ESPHome. -
model (Optional, string): The model the device reports to the Sendspin server. Defaults to the project part of the project name when one is set, otherwise the device’s name.
-
firmware_version (Optional, string): The firmware version the device reports to the Sendspin server. Defaults to the project version when one is set, otherwise the ESPHome version.
-
static_pairing_code (Optional, string): A fixed pairing code of exactly 8 decimal digits. Always quote the value, including in
secrets.yamlwhen using!secret, for example"01234567", since an unquoted number is rejected. Cannot be combined with a dynamic pairing code. See Static Pairing Code. -
unpaired_access (Optional, boolean): Whether servers that have not paired with the device may play on it. Set to
falseonce a server is paired so only paired servers can play. Defaults totrue. To change it at runtime, use a Sendspin Switch of typeunpaired_accessinstead; the two cannot be combined. -
source (Optional, mapping): Streams a microphone to the Sendspin server. See Source.
- microphone (Required, Microphone Source Configuration):
The microphone to stream. The source’s
bits_per_sample(default16) must be16,24or32, and at most two channels can be selected. The microphone must have a fixed sample rate, which the stream uses as is. Audio is sent as uncompressed PCM. - task_stack_in_psram (Optional, boolean): Place the source task’s stack in PSRAM to reduce internal RAM
usage. Only use this if the device runs out of internal memory. Requires the PSRAM
component. Defaults to
false. - on_streaming_started (Optional, Automation): An automation to perform when the server starts the stream.
- on_streaming_stopped (Optional, Automation): An automation to perform when the stream stops.
- microphone (Required, Microphone Source Configuration):
The microphone to stream. The source’s
Pairing
Section titled “Pairing”A server pairs with the device once, and the device stores the result. A paired server can always play, even when unpaired access is off. The device can offer these ways to pair:
Static Pairing Code
Section titled “Static Pairing Code”Set static_pairing_code to a fixed 8-digit code, then enter that code on the server. Pick a random code that is
unique to the device. An attempt must also be confirmed on the device with the
sendspin.confirm_pairing_window action, for example from a button press.
on_open_pairing_window runs when an attempt is waiting for that confirmation.
Confirming opens a pairing window of 5 minutes. It closes early when pairing succeeds, after 5 wrong codes,
when the server disconnects, or when sendspin.cancel_pairing_window runs.
After it closes, the next attempt has to be confirmed again.
Dynamic Pairing Code
Section titled “Dynamic Pairing Code”The device shows a new 6-digit code for each attempt, which you enter on the server. Configure
on_display_pairing_code to show the code, or publish it with a
Sendspin Text Sensor of type pairing_code. Attempts do not need to be
confirmed on the device, and there is no pairing window. After 20 wrong codes, though, further attempts wait until
sendspin.confirm_pairing_window runs, which resets the count. Give the
device a way to run it, such as a button. on_open_pairing_window runs when an attempt is waiting.
Only one pairing code method can be offered. The device offers a dynamic pairing code when
on_display_pairing_code or a pairing_code text sensor is configured, so neither can be combined with
static_pairing_code.
Unpaired Access
Section titled “Unpaired Access”While unpaired_access is true, any server on the network may play on the device without pairing. When it is
false, servers that have not paired can still connect to pair, but cannot play. A
Sendspin Switch of type unpaired_access can turn it on and off at runtime instead.
WARNING
With unpaired_access: false, make sure the device offers a static or
dynamic pairing code. Otherwise no new server can pair, so only servers that already
paired can play.
Source
Section titled “Source”With source configured, the device can stream a microphone to the Sendspin server, for example to share a line
input or a room microphone. The server decides when the stream runs. The device starts the microphone when the
server opens the stream and stops it when the stream closes. on_streaming_started and on_streaming_stopped run at
those moments.
Each chunk carries the time its audio was captured, so the server can line it up with other audio.
The audio is sent as uncompressed PCM, which needs a stable network connection: 256 kbit/s for 16 kHz mono 16-bit audio, and about 1.5 Mbit/s for 48 kHz stereo 16-bit audio. 24 and 32 bits per sample need 1.5 and 2 times as much. While sending stalls, the device keeps about half a second of audio in a buffer, placed in PSRAM when available. If a stall lasts longer, the delayed audio is dropped and the stream continues from live audio.
To tell the server whether the device has audio to offer, use the
sendspin.source.set_signal action. The server can start the stream when
this changes to present and stop it when it changes to absent. Set it from a button or a switch, or for a line input,
from whether the connected device is playing. A microphone should use a button or a switch rather than listening for
sound.
Automations
Section titled “Automations”- on_pairing_succeeded (Optional, Automation): An automation to perform when a server
finishes pairing with the device. The variable
server_id(std::string) holds the server’s ID. - on_pairing_failed (Optional, Automation): An automation to perform when a pairing attempt
fails. The variables
server_id(std::string) andreason(StringRef) are available.reasonis one ofattempt_timeout,concurrent_attempt,method_not_supported,pairing_code_mismatch,user_cancelledorunknown. Compare it directly, for examplereason == "user_cancelled", and usereason.c_str()to log it. - on_open_pairing_window (Optional, Automation): An automation to perform when a pairing
attempt is waiting to be confirmed on the device: a static pairing code attempt while no
pairing window is open, or a dynamic pairing code attempt after 20 wrong codes. Run
sendspin.confirm_pairing_windowto allow it. - on_close_pairing_window (Optional, Automation): An automation to perform when the
attempt that ran
on_open_pairing_windowends, whatever the outcome. Use it to dismiss a prompt. - on_display_pairing_code (Optional, Automation): An automation to perform when a server
asks the device to show a dynamic pairing code. The variable
code(std::string) holds the 6-digit code. - on_clear_pairing_code (Optional, Automation): An automation to perform when the dynamic pairing code is no longer needed.
Actions
Section titled “Actions”sendspin.switch Action
Section titled “sendspin.switch Action”This action switches the Sendspin client to the next active group.
on_...: - sendspin.switch:sendspin.confirm_pairing_window Action
Section titled “sendspin.confirm_pairing_window Action”This action confirms a pairing attempt on the device. Run it in response to a user action such as a button press.
- With a static pairing code, it opens the pairing window. If no attempt is waiting yet, the window still opens, so you can press the button before starting pairing on the server.
- With a dynamic pairing code, it resets the count of wrong codes, so a waiting attempt can continue.
on_...: - sendspin.confirm_pairing_window:sendspin.cancel_pairing_window Action
Section titled “sendspin.cancel_pairing_window Action”This action closes the pairing window, so a waiting pairing attempt is not confirmed.
on_...: - sendspin.cancel_pairing_window:sendspin.source.set_signal Action
Section titled “sendspin.source.set_signal Action”This action tells the server whether the source has audio to
offer. The device reports that it has none until the action first runs. Requires a source block.
on_...: - sendspin.source.set_signal: present: true- present (Required, boolean, templatable):
truewhen the source has audio to offer,falsewhen it does not.
Conditions
Section titled “Conditions”sendspin.source.is_streaming Condition
Section titled “sendspin.source.is_streaming Condition”This condition checks whether the source is streaming to the
server. Requires a source block.
on_...: - if: condition: sendspin.source.is_streaming: then: - logger.log: "Streaming to the server"Examples
Section titled “Examples”Pair with a static code that is confirmed with a button press:
sendspin: static_pairing_code: !secret sendspin_pairing_code on_open_pairing_window: - logger.log: "Press the button to pair"
binary_sensor: - platform: gpio pin: GPIOXX name: "Pair Button" on_press: - sendspin.confirm_pairing_window:Stream a stereo line input to the server, with a switch that offers it to the server. line_in is a
microphone with a fixed sample rate:
sendspin: source: microphone: microphone: line_in channels: [0, 1]
switch: - platform: template name: "Share Line In" optimistic: true turn_on_action: - sendspin.source.set_signal: present: true turn_off_action: - sendspin.source.set_signal: present: falseReport the line input as present while something plays on it, using a
sound level sensor. This keeps the input capturing all the time, so use it for a
line input, not a microphone. Set the threshold between the input’s level when nothing plays and its level for quiet
music. delayed_off keeps short pauses, such as between tracks, from reporting the input as absent:
sendspin: source: microphone: microphone: line_in channels: [0, 1]
sensor: - platform: sound_level microphone: microphone: line_in channels: [0, 1] passive: false measurement_duration: 500ms rms: id: line_in_level internal: true
binary_sensor: - platform: template name: "Line In Playing" lambda: return id(line_in_level).state > -60; filters: - delayed_off: 10s on_press: - sendspin.source.set_signal: present: true on_release: - sendspin.source.set_signal: present: falsePair with a dynamic code that is shown as a text sensor, with a button for when attempts need confirming:
sendspin:
text_sensor: - platform: sendspin name: "Pairing Code" type: pairing_code
binary_sensor: - platform: gpio pin: GPIOXX name: "Pair Button" on_press: - sendspin.confirm_pairing_window:Child components
Section titled “Child components”- Sendspin Image: Displays album and artist artwork streamed from the group on a connected display.
- Sendspin Group Media Player: Exposes group-wide playback and volume controls as a Home Assistant media player entity.
- Sendspin Media Source: Plays synchronized audio from a Sendspin group through a Speaker Source media player.
- Sendspin Sensor: Exposes numeric metadata about the currently playing audio (track progress, track duration, etc.).
- Sendspin Switch: Enables and disables the Sendspin client, and turns unpaired access on and off.
- Sendspin Text Sensor: Exposes metadata about the currently playing audio (title, artist, album, etc.), and the pairing code.