This release follows the Sendspin 1.0.0-rc1 spec, so it has a lot of breaking changes, both in the Python API and on the wire for the client SDK.
Compatibility
The server still accepts clients that speak the older wire, like aiosendspin 6.x to 9.x, older sendspin-js releases, ESPHome (sendspin-cpp), sendspin-rs and SendSpinDroid. These clients get flagged as noncompliant, and are only rejected with allow_noncompliant_clients=False.
The client SDK only supports the current spec:
- A client built on this release can't connect to servers running aiosendspin 9.1.1 or older.
- A client pairing store saved by this release can't be loaded by aiosendspin 9.x anymore.
Upgrading
Renamed to follow the spec:
- PIN is now pairing code everywhere, for example
PairMethod.STATIC_PAIRING_CODEandClientPairingStore.static_pairing_code(#342). static_delay_msis nowoutput_delay_ms, andStaticDelayChangedEventis nowOutputDelayChangedEvent(#351).trust_levelis replaced bypsk_category.TrustLevelandConnectionSecurity.trust_levelare removed, andPskResolvertakes thePskCategoryas a second argument (#355, #473).supported_pair_methodsis keyed by pairing method instead of a list, andPairMethodDescriptorlost itsmethodandmin_pin_lengthfields (#354).- Artwork
media_widthandmedia_heightare nowwidthandheight, and BMP is no longer supported (#343).
Changed behavior:
commit_audionow resamples and encodes on a worker thread, so customAudioTransformerimplementations need to be thread-safe (#431).server/statealways carries the full role state. TheSessionUpdateMetadataandSessionUpdateColorfields are plain optional fields now, and the client listeners for metadata, color and controller can receiveNone(#408, #434).Metadata.repeatandMetadata.shuffleare ignored. UseControllerGroupRole.set_repeatandset_shuffleinstead (#408).- The server keeps reconnecting to clients forever, so
retry_indefinitelyis ignored (#426). - The audio chunk callback receives
send_aheadas an extra argument (#403). - A pairing code attempt on a paired client always re-pairs it, since the spec no longer has a re-verification flow (#466).
Removed:
PairingAttempt.verify(#466).- Management and record mode in the client SDK, and
ClientPairingConfig.pairing_psk_enabled(#470, #471). The server still supports management for older clients, but it's deprecated.
What’s Changed
⚠ Breaking Changes
- #473 - Remove
TrustLevelandConnectionSecurity.trust_level(@maximmaxim345) - #471 - Remove
ClientPairingConfig.pairing_psk_enabled(@maximmaxim345) - #470 - Remove management and record mode from the client SDK (@maximmaxim345)
- #466 - Remove pairing code verification of paired clients (@maximmaxim345)
- #431 - Run commit_audio resampling and encoding on a worker thread (@balloobbot)
- #333 - Add scheduled metadata, color, and artwork updates (@maximmaxim345)
- #434 - Drop null role objects from server/state (@chrisuthe)
- #435 - Send server/activate directly after a re-handshake (@chrisuthe)
- #428 - Require a server start before opening a source stream (@chrisuthe)
- #427 - Gate source start on client state and accept any source format (@chrisuthe)
- #426 - Keep reconnecting, carry application-specific role data and tolerate unknown protocol values (@chrisuthe)
- #424 - Add client pairing cancel, unpaired-access toggle, pair-pending message and grouped pairing codes (@chrisuthe)
- #420 - Send metadata on every seek, clamp relative seeks and validate client group commands (@chrisuthe)
- #421 - Bind pairing PSK tokens to their client and keep the pairing window open across attempts (@chrisuthe)
- #416 - Send artwork as announce/part/cancel transfers (@chrisuthe)
- #418 - Run pairing alongside playback and admit mixed activity sets (@chrisuthe)
- #414 - Move visualizer stream configuration into client/state (@chrisuthe)
- #415 - Run dynamic pairing code in rounds bound into the CPace sid (@chrisuthe)
- #413 - Configure artwork channels from the client/state artwork object (@chrisuthe)
- #354 - SPEC 179/189 - key supported_pair_methods by pairing method identifier (@chrisuthe)
- #408 - Emit full-state metadata and color role objects in server/state (@chrisuthe)
- #405 - Fragment Noise messages with binary ID 1 and a flags byte (@chrisuthe)
- #402 - Require client/pair-init before Pairing PSK finalize (@chrisuthe)
- #403 - Add send_ahead to the player audio chunk header (@chrisuthe)
- #406 - Move operator languages hint to server/hello (spec #241) (@chrisuthe)
- #400 - Move player supported_commands from client/hello to client/state (@chrisuthe)
- #351 - Implement spec PR #164: rename static_delay_ms to output_delay_ms (@OnFreund)
- #355 - SPEC 158 - psk category replaces trust level (@chrisuthe)
- #343 - Implement spec PR #168: letterbox art, rename media_width/media_height, drop BMP (@OnFreund)
- #344 - Wrap nonce_B (@arturpragacz)
- #342 - Convert pairing to the spec's pairing-code wire (@teancom)
🐛 Bugfixes
- #479 - Fix paired clients losing playback after a cancelled re-pair (@maximmaxim345)
- #480 - Fix older clients reconnecting when a pairing attempt fails (@maximmaxim345)
- #478 - Fix long silence after a player joins a playing group (@maximmaxim345)
- #477 - Fix early metadata, color and artwork on older clients (@maximmaxim345)
- #476 - Fix reclaimed clients connecting without playback after mDNS re-announce (@maximmaxim345)
- #474 - Fix missing repeat and shuffle on older clients (@maximmaxim345)
- #468 - End code pairing as soon as the client cancels (@maximmaxim345)
- #467 - Clamp out-of-range
output_delay_msin server commands (@maximmaxim345) - #465 - Fix metadata and color clears on ESPHome devices (@maximmaxim345)
- #464 - Fix PIN pairing on aiosendspin 7.x/8.x and sendspin-js 4.0.0 (@maximmaxim345)
- #463 - Fix code re-pairing of paired clients and other pairing spec gaps (@maximmaxim345)
- #462 - Fix PIN pairing on older aiosendspin and sendspin-js clients (@maximmaxim345)
- #461 - Fix Pairing PSK on older aiosendspin and sendspin-js clients (@maximmaxim345)
- #457 - Fix missing artwork after reconnect and stray stream messages (@maximmaxim345)
- #460 - Fix disconnect when pairing restarts right after a cancel (@maximmaxim345)
- #458 - Warn on refused unencrypted clients and drop false error logs (@maximmaxim345)
- #459 - Fix playback stopping right after a player pairs (@maximmaxim345)
- #456 - Fix resent audio at mid-stream player format changes (@maximmaxim345)
- #454 - Fix pairing races that drop the connection or block playback (@maximmaxim345)
- #455 - Fix stale artwork, track position and reconnect edge cases (@maximmaxim345)
- #453 - Clear unset metadata and color fields on older clients (@maximmaxim345)
- #451 - Fix artwork on older clients with a disabled channel (@maximmaxim345)
- #449 - Fix older clients without a player role getting the new wire (@maximmaxim345)
- #448 - Stop flagging objects for inactive roles as noncompliant (@maximmaxim345)
- #440 - Keep a reconnecting player in its group while its clock re-syncs (@chrisuthe)
- #441 - Admit source@v1 on unpaired sessions with unpaired access enabled (@chrisuthe)
- #442 - Reset the reported position to zero when a group stops (@chrisuthe)
- #437 - Wake the artwork transfer loop when a held image is dropped (@chrisuthe)
- #436 - Ignore reserved binary IDs and end removed stream roles on server/activate (@chrisuthe)
- #433 - Close the handshake on an unknown psk_category (@chrisuthe)
- #430 - Tolerate in-flight source stream responses and reject source audio without an open stream (@chrisuthe)
- #429 - Anchor the source bridge timeline and stop dropping audio on timestamp jitter (@chrisuthe)
- #425 - Read pairing records saved with the pre-rename pair method names (@chrisuthe)
- #417 - End removed roles before server/activate and gate re-added roles on client/state (@chrisuthe)
- #411 - Discard old-key client messages during a server re-handshake (@chrisuthe)
- #412 - Handle client/leave and ignore unknown client message types (@chrisuthe)
- #409 - Hold player availability until the clock converges (@chrisuthe)
- #410 - Derive controller group volume and mute from supporting players (@chrisuthe)
- #360 - Keep the server running when mDNS advertising fails (@marcelveldt)
🚀 Features
- #469 - Add
timestamp_uskeyword toset_metadatafor scheduled updates (@maximmaxim345) - #444 - Drop description parts that are only control characters (@chrisuthe)
- #443 - Name the device and its software version in noncompliance warnings (@chrisuthe)
- #423 - Align the server player role with the spec send constraints and buffer accounting (@chrisuthe)
- #422 - Discard audio while unavailable and add an output delay listener to the client SDK (@chrisuthe)
- #419 - Drop stale and unavailable visualizer frames and keep the wire cursor monotonic (@chrisuthe)
- #362 - Make Opus optional and announce the codecs a source may use (@marcelveldt)
- #407 - Send server/error on client/init failure (@chrisuthe)
- #404 - Carry the player format preference in client/state (@chrisuthe)
- #356 - fall back to the Sentinel PSK when a credential no longer matches (@chrisuthe)
- #359 - group name on group update (@chrisuthe)
🧰 Maintenance
- #475 - Document negotiated codec payloads in audio callbacks (@unlockdigital)
- #452 - Review only against open spec PRs (@maximmaxim345)
- #450 - Point AI reviews at the spec and auto-label PRs (@maximmaxim345)
- #439 - Deprecate the management activity and evict client pairing records at capacity (@chrisuthe)
- #438 - Deprecate visualizer pitch and the draft_r1 visualizer role (@chrisuthe)
- #403 - Add send_ahead to the player audio chunk header (@chrisuthe)
- #401 - Drop server_transmitted from stream/end (@chrisuthe)
- #358 - SPEC 163 - client stream rename (@chrisuthe)
⬆️ Dependencies
4 changes
- #446 - Bump ruff from 0.16.7 to 0.16.9 (@dependabot[bot])
- #361 - Bump ruff from 0.16.6 to 0.16.7 (@dependabot[bot])
- #350 - Drop unused
isortandsyrupydev dependencies (@maximmaxim345) - #349 - Bump ruff from 0.16.4 to 0.16.6 (@dependabot[bot])