Skip to content

Add Sleep Timer support (set_sleep_timer endpoint)#1718

Open
spacenono wants to merge 3 commits into
librespot-org:devfrom
spacenono:feat/sleep-timer
Open

Add Sleep Timer support (set_sleep_timer endpoint)#1718
spacenono wants to merge 3 commits into
librespot-org:devfrom
spacenono:feat/sleep-timer

Conversation

@spacenono

Copy link
Copy Markdown

Implements #1659.

The Spotify mobile app's Sleep timer (Now Playing → ⋯ → Sleep timer) sends a
set_sleep_timer command that librespot currently rejects with
"Not implemented { unknown endpoint: set_sleep_timer }". This PR handles it.

Behaviour

  • A duration timer arms a one-shot countdown; playback is paused when it
    expires, and the paused state is propagated back to the controlling client.
  • A zero/missing duration disarms the timer; re-sending a timer re-arms it.
  • A manual resume does not cancel the timer (it still fires at its deadline),
    matching the official client.

Design

  • A single absolute deadline (Option<tokio::time::Instant>) is stored on the
    task and awaited with sleep_until inside the existing select! loop, so
    recreating the future every iteration never drifts the deadline (same spirit
    as the keep-alive timer in Rework session keep-alive logic v2 #1359, without pinning a Sleep).
  • The timer_type discriminator is kept as a raw string so unsupported or
    future types are logged by name.

Scope / future work

  • duration is fully implemented and tested end-to-end on an iPhone against a
    macOS build (timer armed, fired, paused, state propagated).
  • timestamp is parsed defensively (no real-world sample captured yet).
  • The end of track option sends type: "end_of_track"; it is a different,
    event-driven mechanism and is logged + ignored for now.

Known limitation — open question for maintainers

With this change the device honours the command and pauses correctly, but the
mobile app still briefly shows a "you can't use the sleep timer on this device"
popup. This appears to be client-side capability gating for the (recently
added, Aug 2025) cross-device sleep timer: the device never advertises the
corresponding capability. The most likely place is the empty
Capabilities.connect_capabilities (tag 27) string field, but I couldn't find
the expected token/format in any public source. Do you know how this
capability should be advertised?
Happy to add it if so.

Breaking change

Adds a SetSleepTimer variant to the public, non-exhaustive
core::dealer::protocol::Command enum — downstream exhaustive matches must be
updated.

spacenono and others added 3 commits June 3, 2026 10:22
Add a `SetSleepTimer` variant to the dealer `Command` enum together with the `SetSleepTimerCommand` and `SleepTimer` types, so that the `set_sleep_timer` endpoint sent by Spotify clients is deserialized instead of falling through to the `Unknown` catch-all.

The inner `timer_type` object keeps its `type` discriminator as a raw string so that unsupported or future timer types (e.g. `end_of_track`) are reported by name rather than silently dropped. Only `duration` is fully modelled; `timestamp` is parsed defensively as its exact shape has not been confirmed against a real client yet.

BREAKING CHANGE: a new variant is added to the public, non-exhaustive `Command` enum; downstream exhaustive matches on it must be updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Arm a one-shot timer from the `set_sleep_timer` command and pause playback when it expires. The deadline is stored as a single absolute `Instant` and awaited with `sleep_until` inside the main `select!` loop, so re-arming or recreating the future never drifts the deadline. A zero duration disarms the timer; unsupported types (such as `end_of_track`) are logged and ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
clippy 1.96 introduced the `unnecessary_sort_by` lint, which fails the `-D warnings` CI on the pre-existing `get_covers` sort. Apply the suggested `sort_by_key` + `Reverse` rewrite. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@spacenono

Copy link
Copy Markdown
Author

Note on the fix(metadata) commit in this PR

Heads-up so this doesn't look out of scope: CI runs clippy with -D warnings, and clippy 1.96 (released 2026-05-25) introduced a new lint, clippy::unnecessary_sort_by, which flags pre-existing code in metadata/src/audio/item.rs:197 — unrelated to the sleep timer. This PR just happened to be the first to run CI against the newer clippy, so it surfaced the warning here.

I added a separate, clearly-scoped commit (fix(metadata): satisfy clippy::unnecessary_sort_by) that applies clippy's own suggestion (sort_by_key + Reverse, no behaviour change) so the pipeline goes green.

Happy to drop that commit or move it to its own PR if you'd rather handle the cleanup separately — just let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant