Offbeat Iot
Get started
Using the Spotify integration

Using the Spotify integration

Send Spotify commands from your device. Command keys use the spotify.context.action form.

Canonical WebSocket beta

Canonical WebSocket devices use provider-neutral commands. Connect Spotify in the Offbeat IoT UI, map it to your device, and make sure a Spotify Connect device is active before sending a media control command. Each command accepts an optional target; omit it when Spotify is the only mapped media integration for the device.

Use your normal device WebSocket URL and credentials, with these additional handshake headers:

X-Offbeat-WebSocket-Protocol: canonical-v1
Accept: application/json
Resume playback (media.play)
Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.play",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}

Omit contentId to resume. To play a search result, pass its beta Spotify URI as contentId; this Spotify-specific identifier is a documented beta exception to the otherwise provider-neutral protocol.

Play beta contentId request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.play",
  "params" : {
    "target" : "Spotify",
    "contentId" : "spotify:track:track-1"
  }
}
JSON example
void sendSpotifyPlayJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlayJsonRequest(body)) {
    Serial.println("Unable to build spotify.play request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPlayJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlayJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyPlayCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildPlayCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPlayCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlayCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
List recently played media (media.history.list)

Returns recently played tracks with provider-neutral display metadata. limit is optional, defaults to 20, and is bounded from 1 to 50. Each contentId is the existing beta Spotify URI.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.history.list",
  "params" : {
    "target" : "Spotify",
    "limit" : 5
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "items" : [ {
      "contentId" : "spotify:track:recent-1",
      "title" : "Recent",
      "contentType" : "track"
    } ]
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
void sendSpotifyCurrentlyPlayingJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildCurrentlyPlayingJsonRequest(body)) {
    Serial.println("Unable to build spotify.currently-playing request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.currently-playing");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
}

void handleSpotifyCurrentlyPlayingJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseCurrentlyPlayingJsonResponse(payload, length, response, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify currently-playing response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.currently-playing.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyCurrentlyPlayingCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildCurrentlyPlayingCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.currently-playing request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.currently-playing");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
}

void handleSpotifyCurrentlyPlayingCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(2048);
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseCurrentlyPlayingCborResponse(payload, length, buffer, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify currently-playing response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.currently-playing.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
List saved media tracks (media.library.tracks.list)

Returns saved tracks with provider-neutral display metadata. limit is optional, defaults to 20, and is bounded from 1 to 50. Each contentId is the existing beta Spotify URI.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.library.tracks.list",
  "params" : {
    "target" : "Spotify",
    "limit" : 5
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "items" : [ {
      "contentId" : "spotify:track:saved-1",
      "title" : "Saved",
      "contentType" : "track"
    } ]
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Get playback queue (media.queue.get)

Returns the current item, when Spotify reports one, and the ordered queued items. Each item includes display metadata and a beta Spotify URI contentId.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.queue.get",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "current" : {
      "contentId" : "spotify:track:current-1",
      "title" : "Current",
      "contentType" : "track"
    },
    "queued" : [ {
      "contentId" : "spotify:episode:queued-1",
      "title" : "Queued",
      "contentType" : "episode"
    } ]
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceJson(WebSocketsClient& socket) {
  StaticJsonDocument<256> payload;
  payload["spotify.play.items"] = kTrackUri;
  payload["spotify.play.on"] = kDeviceId;

  String body;
  if (serializeJson(payload, body) == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceCbor(WebSocketsClient& socket) {
  CborBuffer buffer(192);
  CborObject payload(buffer);
  payload.set("spotify.play.items", kTrackUri);
  payload.set("spotify.play.on", kDeviceId);

  uint8_t encoded[192];
  size_t encodedLength = payload.encode(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
Add a queue item (media.queue.add)

Pass a beta Spotify track or episode URI as contentId, such as one returned by media.search. Albums, playlists, and other Spotify URI types are rejected.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.queue.add",
  "params" : {
    "target" : "Spotify",
    "contentId" : "spotify:track:track-1"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceJson(WebSocketsClient& socket) {
  StaticJsonDocument<256> payload;
  payload["spotify.play.items"] = kTrackUri;
  payload["spotify.play.on"] = kDeviceId;

  String body;
  if (serializeJson(payload, body) == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceCbor(WebSocketsClient& socket) {
  CborBuffer buffer(192);
  CborObject payload(buffer);
  payload.set("spotify.play.items", kTrackUri);
  payload.set("spotify.play.on", kDeviceId);

  uint8_t encoded[192];
  size_t encodedLength = payload.encode(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
List playlists (media.playlist.list)

Returns the playlists of the mapped Spotify integration, each carrying the existing beta Spotify playlist URI as playlistId. limit is optional, defaults to 20, and is bounded from 1 to 50.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.playlist.list",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "playlists" : [ {
      "playlistId" : "spotify:playlist:playlist-1",
      "name" : "Road Trip"
    } ]
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Create a playlist (media.playlist.create)

Creates a new playlist with the given name in the mapped Spotify integration. description and public are optional. The response returns the existing beta Spotify playlist URI as playlistId, ready to pass to media.playlist.items.add.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.playlist.create",
  "params" : {
    "target" : "Spotify",
    "name" : "New Playlist"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "playlistId" : "spotify:playlist:playlist-1"
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Add an item to a playlist (media.playlist.items.add)

Adds a beta Spotify track or episode URI contentId to the playlist identified by playlistId (a beta Spotify playlist URI from media.playlist.create or media.playlist.list). Albums, playlists, and other Spotify URI types are rejected as contentId.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.playlist.items.add",
  "params" : {
    "target" : "Spotify",
    "playlistId" : "spotify:playlist:playlist-1",
    "contentId" : "spotify:track:track-1"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceJson(WebSocketsClient& socket) {
  StaticJsonDocument<256> payload;
  payload["spotify.play.items"] = kTrackUri;
  payload["spotify.play.on"] = kDeviceId;

  String body;
  if (serializeJson(payload, body) == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceCbor(WebSocketsClient& socket) {
  CborBuffer buffer(192);
  CborObject payload(buffer);
  payload.set("spotify.play.items", kTrackUri);
  payload.set("spotify.play.on", kDeviceId);

  uint8_t encoded[192];
  size_t encodedLength = payload.encode(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
Pause playback (media.pause)
JSON example
CBOR example
Stop playback (media.stop)

Spotify has pause rather than a separate stop operation. This command pauses the active player; a later media.play resumes playback from the paused position.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.stop",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Set current item favorite (media.favorite.current.set)

Set required boolean favorite to true to save the current item, or false to remove it. It returns INTEGRATION_UNAVAILABLE when no current item is available.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.favorite.current.set",
  "params" : {
    "target" : "Spotify",
    "favorite" : true
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable current item error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.pause",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.next",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example
Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.previous",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example

Returns provider-neutral active, playing, and optional positionMs, title, artist, album, durationMs, contentType, shuffleEnabled, and repeatMode (off, context, or track) fields. With no active player, it succeeds with active: false and playing: false.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.state.get",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "active" : true,
    "playing" : true,
    "positionMs" : 42000,
    "shuffleEnabled" : true,
    "repeatMode" : "track"
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
void sendSpotifyGetStateJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildGetStateJsonRequest(body)) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateJsonResponse(payload, length, response, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyGetStateCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildGetStateCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(2048);
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateCborResponse(payload, length, buffer, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}

Set the active player’s volume with an integer volumePercent from 0 to 100.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.volume.set",
  "params" : {
    "target" : "Spotify",
    "volumePercent" : 42
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
void sendSpotifySetVolumeJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildSetVolumeJsonRequest(kSpotifyVolumePercent, body)) {
    Serial.println("Unable to build spotify.volume request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.volume");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifySetVolumeJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseSetVolumeJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify volume response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.volume.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.volume.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifySetVolumeCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildSetVolumeCborRequest(kSpotifyVolumePercent, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.volume request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.volume");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifySetVolumeCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseSetVolumeCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify volume response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.volume.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.volume.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

Returns provider-neutral active and optional volumePercent fields. It does not expose the Spotify device identity.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.volume.get",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "active" : true,
    "volumePercent" : 42
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
void sendSpotifyGetStateJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildGetStateJsonRequest(body)) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateJsonResponse(payload, length, response, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyGetStateCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildGetStateCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(2048);
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateCborResponse(payload, length, buffer, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}

Returns the players available to the mapped Spotify integration. For this beta, retain a returned player id and provide it as playerId when calling media.play to start playback on that player.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.players.list",
  "params" : {
    "target" : "Spotify"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : {
    "players" : [ {
      "name" : "Kitchen",
      "active" : true,
      "id" : "player-1"
    } ]
  }
}
Unavailable integration error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
void sendSpotifyGetDevicesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildGetDevicesJsonRequest(body)) {
    Serial.println("Unable to build spotify.devices request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.devices");
}

void printSpotifyDevice(const offbeat::spotify::SpotifyDevice& device) {
  Serial.print("Device id: ");
  Serial.println(device.id);

  Serial.print("Device name: ");
  Serial.println(device.name);

  Serial.print("Volume: ");
  Serial.println(device.volume);

  Serial.print("DEVICE_ID=");
  Serial.println(device.id);
  Serial.print("DEVICE_NAME=");
  Serial.println(device.name);
  Serial.print("VOLUME=");
  Serial.println(device.volume);
}

void handleSpotifyGetDevicesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyGetDevicesParseStatus status =
      offbeat::spotify::visitGetDevicesJsonResponse(payload, length, response, printSpotifyDevice);

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify response");
    return;
  }

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kMissingResponse) {
    Serial.println("No spotify.devices.response payload");
    return;
  }

  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyGetDevicesCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildGetDevicesCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.devices request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.devices");
}

void printSpotifyDevice(const offbeat::spotify::SpotifyDevice& device) {
  Serial.print("Device id: ");
  Serial.println(device.id);

  Serial.print("Device name: ");
  Serial.println(device.name);

  Serial.print("Volume: ");
  Serial.println(device.volume);

  Serial.print("DEVICE_ID=");
  Serial.println(device.id);
  Serial.print("DEVICE_NAME=");
  Serial.println(device.name);
  Serial.print("VOLUME=");
  Serial.println(device.volume);
}

void handleSpotifyGetDevicesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(1024);
  offbeat::spotify::SpotifyGetDevicesParseStatus status =
      offbeat::spotify::visitGetDevicesCborResponse(payload, length, buffer, printSpotifyDevice);

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify CBOR response");
    return;
  }

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kMissingResponse) {
    Serial.println("No spotify.devices.response payload");
    return;
  }

  Serial.println("TEST:PASS");
}

Set enabled to true to shuffle the active player’s playback, or false to disable shuffle.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.shuffle.set",
  "params" : {
    "target" : "Spotify",
    "enabled" : true
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDesiredShuffleState[] = "on";

void sendSpotifyShuffleOnJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildShuffleJsonRequest(kDesiredShuffleState, body)) {
    Serial.println("Unable to build spotify.shuffle request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shuffle");
}

void handleSpotifyShuffleOnJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseShuffleJsonResponse(payload, length, response, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify shuffle response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.shuffle.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.shuffle.response payload");
    return;
  }

  Serial.print("Desired shuffle state: ");
  Serial.println(kDesiredShuffleState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("SHUFFLE_STATE=");
  Serial.println(kDesiredShuffleState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDesiredShuffleState[] = "on";

void sendSpotifyShuffleOnCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildShuffleCborRequest(
      kDesiredShuffleState, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shuffle request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shuffle");
}

void handleSpotifyShuffleOnCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseShuffleCborResponse(payload, length, buffer, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify shuffle response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.shuffle.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.shuffle.response payload");
    return;
  }

  Serial.print("Desired shuffle state: ");
  Serial.println(kDesiredShuffleState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("SHUFFLE_STATE=");
  Serial.println(kDesiredShuffleState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}

Set mode to off, context, or track for the active player.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.repeat.set",
  "params" : {
    "target" : "Spotify",
    "mode" : "track"
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDesiredRepeatState[] = "context";

void sendSpotifyRepeatContextJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildRepeatJsonRequest(kDesiredRepeatState, body)) {
    Serial.println("Unable to build spotify.repeat request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.repeat");
}

void handleSpotifyRepeatContextJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseRepeatJsonResponse(payload, length, response, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify repeat response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.repeat.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.repeat.response payload");
    return;
  }

  Serial.print("Desired repeat state: ");
  Serial.println(kDesiredRepeatState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("REPEAT_STATE=");
  Serial.println(kDesiredRepeatState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDesiredRepeatState[] = "context";

void sendSpotifyRepeatContextCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildRepeatCborRequest(
      kDesiredRepeatState, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.repeat request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.repeat");
}

void handleSpotifyRepeatContextCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseRepeatCborResponse(payload, length, buffer, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify repeat response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.repeat.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.repeat.response payload");
    return;
  }

  Serial.print("Desired repeat state: ");
  Serial.println(kDesiredRepeatState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("REPEAT_STATE=");
  Serial.println(kDesiredRepeatState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}

Set the active player’s playback position with non-negative positionMs.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.seek",
  "params" : {
    "target" : "Spotify",
    "positionMs" : 42000
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
CBOR example

Transfer playback to a playerId returned by media.players.list. This uses the same Spotify Connect identifier beta exception as media.play. Omit play (or set it to true) to start playback after transfer; set it to false to transfer without starting playback.

Request
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "method" : "media.transfer",
  "params" : {
    "target" : "Spotify",
    "playerId" : "player-1",
    "play" : true
  }
}
Success response
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "result" : { }
}
Unavailable player error
{
  "jsonrpc" : "2.0",
  "id" : "media-1",
  "error" : {
    "code" : -32014,
    "message" : "The requested integration is unavailable",
    "data" : {
      "reason" : "INTEGRATION_UNAVAILABLE",
      "retryable" : false,
      "retryAfterMs" : null
    }
  }
}
JSON example
constexpr char kDeviceId[] = "the id";

void sendSpotifyPlayOnDeviceJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlaybackCommandJsonRequest("spotify.play.on", kDeviceId, body)) {
    Serial.println("Unable to build spotify.play.on request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";

void sendSpotifyPlayOnDeviceCbor(WebSocketsClient& socket) {
  uint8_t encoded[160];
  size_t encodedLength = offbeat::spotify::buildPlaybackCommandCborRequest(
      "spotify.play.on", kDeviceId, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.on request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

If Spotify cannot be reached, canonical media commands return the provider-neutral INTEGRATION_UNAVAILABLE error.

The canonical capability currently includes discovery search, recently played and saved-track lists, queue inspection and add, resume, playback by beta contentId, pause, stop, current-item favorites, next, previous, state lookup, volume control, player selection, shuffle, repeat, seek, transfer, playlist listing and creation, and adding items to a playlist.

=== Binary CBOR frames

To exchange binary WebSocket frames instead of JSON text frames, use Accept: application/cbor during the canonical handshake. The examples below use CBOR diagnostic notation: map keys are integers on the wire, not quoted JSON property names.

media.play CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 10, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.play with beta contentId CBOR request
{0: 1, 1: 0, 2: "media-1", 3: 10, 4: "Spotify", 5: {{68: "spotify:track:track-1"}}}
media.search CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 63, 4: "Spotify", 5: {{50: "hello"}}}
{0: 1, 1: 1, 2: "media-1", 5: {67: [{68: "spotify:track:track-1", 62: "Hello World", 66: "track"}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.queue.get CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 64, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {69: {68: "spotify:track:current-1", 62: "Current", 66: "track"}, 70: [{68: "spotify:episode:queued-1", 62: "Queued", 66: "episode"}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.queue.add CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 65, 4: "Spotify", 5: {{68: "spotify:track:track-1"}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.playlist.list CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 66, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {86: [{77: "spotify:playlist:playlist-1", 78: "Road Trip"}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.playlist.create CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 67, 4: "Spotify", 5: {{78: "New Playlist"}}}
{0: 1, 1: 1, 2: "media-1", 5: {77: "spotify:playlist:playlist-1"}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.playlist.items.add CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 68, 4: "Spotify", 5: {{77: "spotify:playlist:playlist-1", 68: "spotify:track:track-1"}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.pause CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 11, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.stop CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 71, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.favorite.current.set CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 72, 4: "Spotify", 5: {{71: true}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.next CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 12, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.previous CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 13, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.state.get CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 14, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {51: true, 52: true, 53: 42000, 59: true, 60: "track"}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.volume.set CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 15, 4: "Spotify", 5: {{54: 42}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.volume.get CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 16, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {51: true, 54: 42}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.players.list CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 17, 4: "Spotify", 5: {}}
{0: 1, 1: 1, 2: "media-1", 5: {55: [{56: "player-1", 57: "Kitchen", 58: true}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.shuffle.set CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 18, 4: "Spotify", 5: {{59: true}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.repeat.set CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 19, 4: "Spotify", 5: {{60: "track"}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.seek CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 61, 4: "Spotify", 5: {{53: 42000}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.transfer CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 62, 4: "Spotify", 5: {{56: "player-1", 61: true}}}
{0: 1, 1: 1, 2: "media-1", 5: {}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.history.list CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 73, 4: "Spotify", 5: {{73: 5}}}
{0: 1, 1: 1, 2: "media-1", 5: {67: [{68: "spotify:track:recent-1", 62: "Recent", 66: "track"}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}
media.library.tracks.list CBOR request, success, and error
{0: 1, 1: 0, 2: "media-1", 3: 74, 4: "Spotify", 5: {{73: 5}}}
{0: 1, 1: 1, 2: "media-1", 5: {67: [{68: "spotify:track:saved-1", 62: "Saved", 66: "track"}]}}
{0: 1, 1: 3, 2: "media-1", 6: {0: -32014, 1: "The requested integration is unavailable", 2: {0: "INTEGRATION_UNAVAILABLE", 1: false}}}

In CBOR, 0 is the protocol version, 1 is the frame type (0 request, 1 response, 3 error), 2 is the correlation id, 3 is the method id (10 play, 11 pause, 12 next, 13 previous, 14 state, 15 volume set, 16 volume get, 17 player list, 18 shuffle set, 19 repeat set, 61 seek, 62 transfer, 63 search, 64 queue get, 65 queue add, 71 stop, 72 current favorite set, 73 history list, and 74 library tracks list), 4 is the integration target, 5 is parameters or result, and 6 is the error. media.state.get uses result keys 51 active, 52 playing, optional 53 position in milliseconds, 59 shuffle enabled, 60 repeat mode, and 54 volume percent. Current-media detail keys are 62 title, 63 artist, 64 album, 65 duration in milliseconds, and 66 content type. media.transfer uses 56 player id and optional 61 play. media.search query is key 50 and optional contentTypes is key 72; list-method limit is key 73. Result items are key 67, and beta Spotify URI contentId is key 68 (also used by media.play and media.queue.add). media.queue.get uses 69 for the current item and 70 for queued items. media.favorite.current.set uses 71 for required boolean favorite.

== Contexts

== Player

Returns Spotify Connect devices available to the linked account.

spotify.player.list-devices

Request
{
  "spotify.player.list-devices" : ""
}
Example response
{
  "spotify.player.list-devices.response" : {
    "the id" : {
      "volume" : 59,
      "name" : "Kitchen speaker"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyGetDevicesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildGetDevicesJsonRequest(body)) {
    Serial.println("Unable to build spotify.devices request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.devices");
}

void printSpotifyDevice(const offbeat::spotify::SpotifyDevice& device) {
  Serial.print("Device id: ");
  Serial.println(device.id);

  Serial.print("Device name: ");
  Serial.println(device.name);

  Serial.print("Volume: ");
  Serial.println(device.volume);

  Serial.print("DEVICE_ID=");
  Serial.println(device.id);
  Serial.print("DEVICE_NAME=");
  Serial.println(device.name);
  Serial.print("VOLUME=");
  Serial.println(device.volume);
}

void handleSpotifyGetDevicesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyGetDevicesParseStatus status =
      offbeat::spotify::visitGetDevicesJsonResponse(payload, length, response, printSpotifyDevice);

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify response");
    return;
  }

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kMissingResponse) {
    Serial.println("No spotify.devices.response payload");
    return;
  }

  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyGetDevicesCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildGetDevicesCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.devices request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.devices");
}

void printSpotifyDevice(const offbeat::spotify::SpotifyDevice& device) {
  Serial.print("Device id: ");
  Serial.println(device.id);

  Serial.print("Device name: ");
  Serial.println(device.name);

  Serial.print("Volume: ");
  Serial.println(device.volume);

  Serial.print("DEVICE_ID=");
  Serial.println(device.id);
  Serial.print("DEVICE_NAME=");
  Serial.println(device.name);
  Serial.print("VOLUME=");
  Serial.println(device.volume);
}

void handleSpotifyGetDevicesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(1024);
  offbeat::spotify::SpotifyGetDevicesParseStatus status =
      offbeat::spotify::visitGetDevicesCborResponse(payload, length, buffer, printSpotifyDevice);

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify CBOR response");
    return;
  }

  if (status == offbeat::spotify::SpotifyGetDevicesParseStatus::kMissingResponse) {
    Serial.println("No spotify.devices.response payload");
    return;
  }

  Serial.println("TEST:PASS");
}

Returns the current playback state.

spotify.player.get-state

Request
{
  "spotify.player.get-state" : ""
}
Example response
{
  "spotify.player.get-state.response" : {
    "isPlaying" : true,
    "item" : {
      "artist" : "Sample Artist",
      "album" : "Sample Album",
      "name" : "Sample Track"
    },
    "shuffling" : false,
    "device" : {
      "volumepercent" : 59,
      "name" : "Kitchen speaker",
      "id" : "string"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyGetStateJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildGetStateJsonRequest(body)) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateJsonResponse(payload, length, response, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyGetStateCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildGetStateCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.state request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.state");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  if (playbackState.hasShuffling) {
    Serial.print("Shuffling: ");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
  if (playbackState.hasShuffling) {
    Serial.print("SHUFFLING=");
    Serial.println(playbackState.shuffling ? "true" : "false");
  }
}

void handleSpotifyGetStateCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(2048);
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseGetStateCborResponse(payload, length, buffer, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify state response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.state.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}

Starts playback on the active device.

spotify.player.play

Request
{
  "spotify.player.play" : ""
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.play.response" : {
    "result" : "OK"
  }
}
JSON example
void sendSpotifyPlayJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlayJsonRequest(body)) {
    Serial.println("Unable to build spotify.play request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPlayJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlayJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyPlayCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildPlayCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPlayCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlayCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

spotify.player.play-items starts the supplied album, artist, playlist, or track selection.

Request
{
  "spotify.player.play-items" : "spotify:track:a single track"
}
Example response
{
  "spotify.player.play-items.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceJson(WebSocketsClient& socket) {
  StaticJsonDocument<256> payload;
  payload["spotify.play.items"] = kTrackUri;
  payload["spotify.play.on"] = kDeviceId;

  String body;
  if (serializeJson(payload, body) == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";
constexpr char kTrackUri[] = "spotify:track:playing on device";

void sendSpotifyPlayItemsOnDeviceCbor(WebSocketsClient& socket) {
  CborBuffer buffer(192);
  CborObject payload(buffer);
  payload.set("spotify.play.items", kTrackUri);
  payload.set("spotify.play.on", kDeviceId);

  uint8_t encoded[192];
  size_t encodedLength = payload.encode(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.items request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.items");
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayItemsOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Track URI: ");
  Serial.println(kTrackUri);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("TRACK_URI=");
  Serial.println(kTrackUri);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

spotify.player.get-currently-playing returns the currently playing content.

Request
{
  "spotify.player.get-currently-playing" : ""
}
Example response
{
  "spotify.player.get-currently-playing.response" : {
    "isPlaying" : false,
    "item" : {
      "artist" : "Sample Artist",
      "album" : "Sample Album",
      "name" : "Sample Track"
    },
    "device" : {
      "volumepercent" : 59,
      "name" : "Kitchen speaker",
      "id" : "string"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyCurrentlyPlayingJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildCurrentlyPlayingJsonRequest(body)) {
    Serial.println("Unable to build spotify.currently-playing request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.currently-playing");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
}

void handleSpotifyCurrentlyPlayingJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<2048> response;
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseCurrentlyPlayingJsonResponse(payload, length, response, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify currently-playing response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.currently-playing.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyCurrentlyPlayingCbor(WebSocketsClient& socket) {
  uint8_t encoded[64];
  size_t encodedLength = offbeat::spotify::buildCurrentlyPlayingCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.currently-playing request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.currently-playing");
}

void printSpotifyPlaybackState(const offbeat::spotify::SpotifyPlaybackState& playbackState) {
  Serial.print("Playing: ");
  Serial.println(playbackState.isPlaying ? "true" : "false");

  Serial.print("Track name: ");
  Serial.println(playbackState.item.name);

  Serial.print("Artist: ");
  Serial.println(playbackState.item.artist);

  Serial.print("Album: ");
  Serial.println(playbackState.item.album);

  Serial.print("Device name: ");
  Serial.println(playbackState.device.name);

  Serial.print("Volume percent: ");
  Serial.println(playbackState.device.volumePercent);

  Serial.print("PLAYING=");
  Serial.println(playbackState.isPlaying ? "true" : "false");
  Serial.print("TRACK_NAME=");
  Serial.println(playbackState.item.name);
  Serial.print("TRACK_ARTIST=");
  Serial.println(playbackState.item.artist);
  Serial.print("TRACK_ALBUM=");
  Serial.println(playbackState.item.album);
  Serial.print("DEVICE_NAME=");
  Serial.println(playbackState.device.name);
  Serial.print("VOLUME_PERCENT=");
  Serial.println(playbackState.device.volumePercent);
}

void handleSpotifyCurrentlyPlayingCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(2048);
  offbeat::spotify::SpotifyPlaybackState playbackState;
  offbeat::spotify::SpotifyPlaybackStateParseStatus status =
      offbeat::spotify::parseCurrentlyPlayingCborResponse(payload, length, buffer, playbackState);

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify currently-playing response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackStateParseStatus::kMissingResponse) {
    Serial.println("No spotify.currently-playing.response payload");
    return;
  }

  printSpotifyPlaybackState(playbackState);
  Serial.println("TEST:PASS");
}

spotify.player.play-on-device starts playback on a selected device.

Request
{
  "spotify.player.play-on-device" : "the id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.play-on-device.response" : {
    "result" : "OK"
  }
}
JSON example
constexpr char kDeviceId[] = "the id";

void sendSpotifyPlayOnDeviceJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlaybackCommandJsonRequest("spotify.play.on", kDeviceId, body)) {
    Serial.println("Unable to build spotify.play.on request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayOnDeviceJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDeviceId[] = "the id";

void sendSpotifyPlayOnDeviceCbor(WebSocketsClient& socket) {
  uint8_t encoded[160];
  size_t encodedLength = offbeat::spotify::buildPlaybackCommandCborRequest(
      "spotify.play.on", kDeviceId, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.play.on request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.play.on");
}

void handleSpotifyPlayOnDeviceCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.play.on.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify play.on response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.play.on.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.play.on.response payload");
    return;
  }

  Serial.print("Device ID: ");
  Serial.println(kDeviceId);
  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("DEVICE_ID=");
  Serial.println(kDeviceId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

spotify.player.next skips to the next track.

Request
{
  "spotify.player.next" : ""
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.next.response" : {
    "result" : "OK"
  }
}
JSON example
void sendSpotifyNextJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildNextJsonRequest(body)) {
    Serial.println("Unable to build spotify.next request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.next");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyNextJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseNextJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify next response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.next.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.next.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyNextCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildNextCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.next request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.next");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyNextCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseNextCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify next response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.next.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.next.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

spotify.player.previous skips to the previous track.

Request
{
  "spotify.player.previous" : ""
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.previous.response" : {
    "result" : "OK"
  }
}
JSON example
void sendSpotifyPreviousJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPreviousJsonRequest(body)) {
    Serial.println("Unable to build spotify.previous request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.previous");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPreviousJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePreviousJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify previous response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.previous.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.previous.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyPreviousCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildPreviousCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.previous request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.previous");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPreviousCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePreviousCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify previous response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.previous.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.previous.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

spotify.player.pause pauses playback.

Request
{
  "spotify.player.pause" : ""
}
Example response
{
  "spotify.player.pause.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyPauseJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPauseJsonRequest(body)) {
    Serial.println("Unable to build spotify.pause request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.pause");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPauseJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePauseJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify pause response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.pause.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.pause.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyPauseCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildPauseCborRequest(encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.pause request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.pause");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifyPauseCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePauseCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify pause response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.pause.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.pause.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

spotify.player.stop stops playback.

Request
{
  "spotify.player.stop" : ""
}
Example response
{
  "spotify.player.stop.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyStopPlaybackJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlaybackCommandJsonRequest("spotify.stop", "", body)) {
    Serial.println("Unable to build spotify.stop request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.stop");
}

void handleSpotifyStopPlaybackJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.stop.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify stop response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.stop.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.stop.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyStopPlaybackCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength =
      offbeat::spotify::buildPlaybackCommandCborRequest("spotify.stop", "", encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.stop request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.stop");
}

void handleSpotifyStopPlaybackCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.stop.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify stop response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.stop.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.stop.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

spotify.player.set-volume sets the playback volume.

Request
{
  "spotify.player.set-volume" : "12"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.set-volume.response" : {
    "result" : "OK"
  }
}
JSON example
void sendSpotifySetVolumeJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildSetVolumeJsonRequest(kSpotifyVolumePercent, body)) {
    Serial.println("Unable to build spotify.volume request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.volume");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifySetVolumeJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseSetVolumeJsonResponse(payload, length, response, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify volume response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.volume.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.volume.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifySetVolumeCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildSetVolumeCborRequest(kSpotifyVolumePercent, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.volume request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.volume");
}

void printSpotifyPlaybackCommandResult(const offbeat::spotify::SpotifyPlaybackCommandResult& result) {
  Serial.print("Result: ");
  Serial.println(result.result);

  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);

  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
}

void handleSpotifySetVolumeCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(512);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseSetVolumeCborResponse(payload, length, buffer, result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify volume response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.volume.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.volume.response payload");
    return;
  }

  printSpotifyPlaybackCommandResult(result);
  Serial.println("TEST:PASS");
}

spotify.player.set-shuffle sets shuffle playback.

Request
{
  "spotify.player.set-shuffle" : "on"
}
Example response
{
  "spotify.player.set-shuffle.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kDesiredShuffleState[] = "on";

void sendSpotifyShuffleOnJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildShuffleJsonRequest(kDesiredShuffleState, body)) {
    Serial.println("Unable to build spotify.shuffle request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shuffle");
}

void handleSpotifyShuffleOnJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseShuffleJsonResponse(payload, length, response, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify shuffle response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.shuffle.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.shuffle.response payload");
    return;
  }

  Serial.print("Desired shuffle state: ");
  Serial.println(kDesiredShuffleState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("SHUFFLE_STATE=");
  Serial.println(kDesiredShuffleState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDesiredShuffleState[] = "on";

void sendSpotifyShuffleOnCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildShuffleCborRequest(
      kDesiredShuffleState, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shuffle request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shuffle");
}

void handleSpotifyShuffleOnCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseShuffleCborResponse(payload, length, buffer, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify shuffle response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.shuffle.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.shuffle.response payload");
    return;
  }

  Serial.print("Desired shuffle state: ");
  Serial.println(kDesiredShuffleState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("SHUFFLE_STATE=");
  Serial.println(kDesiredShuffleState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}

spotify.player.set-repeat sets repeat playback.

Request
{
  "spotify.player.set-repeat" : "track"
}
Example response
{
  "spotify.player.set-repeat.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kDesiredRepeatState[] = "context";

void sendSpotifyRepeatContextJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildRepeatJsonRequest(kDesiredRepeatState, body)) {
    Serial.println("Unable to build spotify.repeat request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.repeat");
}

void handleSpotifyRepeatContextJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseRepeatJsonResponse(payload, length, response, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify repeat response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.repeat.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.repeat.response payload");
    return;
  }

  Serial.print("Desired repeat state: ");
  Serial.println(kDesiredRepeatState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("REPEAT_STATE=");
  Serial.println(kDesiredRepeatState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kDesiredRepeatState[] = "context";

void sendSpotifyRepeatContextCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength = offbeat::spotify::buildRepeatCborRequest(
      kDesiredRepeatState, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.repeat request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.repeat");
}

void handleSpotifyRepeatContextCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult actionResult;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parseRepeatCborResponse(payload, length, buffer, actionResult);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify repeat response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.repeat.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.repeat.response payload");
    return;
  }

  Serial.print("Desired repeat state: ");
  Serial.println(kDesiredRepeatState);
  Serial.print("Result: ");
  Serial.println(actionResult.result);
  Serial.print("Endpoint ID: ");
  Serial.println(actionResult.endpointId);
  Serial.print("REPEAT_STATE=");
  Serial.println(kDesiredRepeatState);
  Serial.print("RESULT=");
  Serial.println(actionResult.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(actionResult.endpointId);
  Serial.println("TEST:PASS");
}

spotify.player.transfer transfers playback between devices.

Request
{
  "spotify.player.transfer" : "{\"device_ids\":[\"device-id\"],\"play\":true}"
}
Example response
{
  "spotify.player.transfer.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kTransferPlaybackJsonValue[] = "{\"device_ids\":[\"device-id\"],\"play\":true}";

void sendSpotifyTransferPlaybackJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.player.transfer", kTransferPlaybackJsonValue, body)) {
    Serial.println("Unable to build spotify.player.transfer request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.player.transfer");
}

void handleSpotifyTransferPlaybackJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.player.transfer.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.transfer response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.transfer.response payload");
    return;
  }

  if (kTransferPlaybackJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kTransferPlaybackJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kTransferPlaybackCborValue[] = "{\"device_ids\":[\"device-id\"],\"play\":true}";

void sendSpotifyTransferPlaybackCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.player.transfer", kTransferPlaybackCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.player.transfer request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.player.transfer");
}

void handleSpotifyTransferPlaybackCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.player.transfer.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.transfer response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.transfer.response payload");
    return;
  }

  if (kTransferPlaybackCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kTransferPlaybackCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.player.seek seeks to a playback position.

Request
{
  "spotify.player.seek" : "position_ms=120000"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.seek.response" : {
    "result" : "OK"
  }
}
JSON example
constexpr char kSeekToPositionJsonValue[] = "position_ms=120000";

void sendSpotifySeekToPositionJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.player.seek", kSeekToPositionJsonValue, body)) {
    Serial.println("Unable to build spotify.player.seek request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.player.seek");
}

void handleSpotifySeekToPositionJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.player.seek.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.seek response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.seek.response payload");
    return;
  }

  if (kSeekToPositionJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSeekToPositionJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSeekToPositionCborValue[] = "position_ms=120000";

void sendSpotifySeekToPositionCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.player.seek", kSeekToPositionCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.player.seek request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.player.seek");
}

void handleSpotifySeekToPositionCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.player.seek.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.seek response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.seek.response payload");
    return;
  }

  if (kSeekToPositionCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSeekToPositionCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.player.list-recently-played returns recently played tracks.

Request
{
  "spotify.player.list-recently-played" : "limit=5"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.player.list-recently-played.response" : {
    "result" : {
      "items" : [ {
        "track" : {
          "album" : {
            "album_type" : null,
            "total_tracks" : null,
            "available_markets" : [ ],
            "external_urls" : null,
            "href" : null,
            "id" : "album-id",
            "images" : [ ],
            "name" : "Sample Album",
            "release_date" : null,
            "release_date_precision" : null,
            "type" : null,
            "uri" : null,
            "artists" : [ ]
          },
          "artists" : [ {
            "id" : "artist-id",
            "name" : "Sample Artist"
          } ],
          "available_markets" : [ ],
          "id" : "track-id",
          "name" : "Sample Track"
        }
      } ]
    }
  }
}
JSON example
constexpr char kGetRecentlyPlayedTracksJsonValue[] = "limit=5";

void sendSpotifyGetRecentlyPlayedTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.player.recently-played", kGetRecentlyPlayedTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.player.recently-played request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.player.recently-played");
}

void handleSpotifyGetRecentlyPlayedTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.player.recently-played.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.recently-played response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.recently-played.response payload");
    return;
  }

  if (kGetRecentlyPlayedTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetRecentlyPlayedTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetRecentlyPlayedTracksCborValue[] = "limit=5";

void sendSpotifyGetRecentlyPlayedTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.player.recently-played", kGetRecentlyPlayedTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.player.recently-played request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.player.recently-played");
}

void handleSpotifyGetRecentlyPlayedTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.player.recently-played.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.recently-played response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.recently-played.response payload");
    return;
  }

  if (kGetRecentlyPlayedTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetRecentlyPlayedTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.queue.get returns the current playback queue.

Request
{
  "spotify.queue.get" : ""
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.queue.get.response" : {
    "result" : {
      "currently_playing" : {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-id",
        "name" : "Sample Track"
      },
      "queue" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-id",
        "name" : "Sample Track"
      } ]
    }
  }
}
JSON example
constexpr char kGetTheUsersQueueJsonValue[] = "";

void sendSpotifyGetTheUsersQueueJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.player.queue.get", kGetTheUsersQueueJsonValue, body)) {
    Serial.println("Unable to build spotify.player.queue.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.player.queue.get");
}

void handleSpotifyGetTheUsersQueueJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.player.queue.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.queue.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.queue.get.response payload");
    return;
  }

  if (kGetTheUsersQueueJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTheUsersQueueJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetTheUsersQueueCborValue[] = "";

void sendSpotifyGetTheUsersQueueCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.player.queue.get", kGetTheUsersQueueCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.player.queue.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.player.queue.get");
}

void handleSpotifyGetTheUsersQueueCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.player.queue.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.queue.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.queue.get.response payload");
    return;
  }

  if (kGetTheUsersQueueCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTheUsersQueueCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.queue.add adds an item to the playback queue.

Request
{
  "spotify.queue.add" : "uri=spotify:track:track-id"
}
Example response
{
  "spotify.queue.add.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kAddItemToPlaybackQueueJsonValue[] = "uri=spotify:track:track-id";

void sendSpotifyAddItemToPlaybackQueueJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.player.queue.add", kAddItemToPlaybackQueueJsonValue, body)) {
    Serial.println("Unable to build spotify.player.queue.add request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.player.queue.add");
}

void handleSpotifyAddItemToPlaybackQueueJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.player.queue.add.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.queue.add response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.queue.add.response payload");
    return;
  }

  if (kAddItemToPlaybackQueueJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddItemToPlaybackQueueJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kAddItemToPlaybackQueueCborValue[] = "uri=spotify:track:track-id";

void sendSpotifyAddItemToPlaybackQueueCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.player.queue.add", kAddItemToPlaybackQueueCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.player.queue.add request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.player.queue.add");
}

void handleSpotifyAddItemToPlaybackQueueCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.player.queue.add.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.player.queue.add response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.player.queue.add.response payload");
    return;
  }

  if (kAddItemToPlaybackQueueCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddItemToPlaybackQueueCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Albums

Returns an album identified by its Spotify ID.

spotify.albums.get

Request
{
  "spotify.albums.get" : "album-id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.albums.get.response" : {
    "result" : {
      "album_type" : null,
      "total_tracks" : null,
      "available_markets" : [ ],
      "external_urls" : null,
      "href" : null,
      "id" : "album-id",
      "images" : [ ],
      "name" : "Sample Album",
      "release_date" : null,
      "release_date_precision" : null,
      "type" : null,
      "uri" : null,
      "artists" : [ ],
      "tracks" : null,
      "copyrights" : [ ],
      "external_ids" : null,
      "genres" : [ ],
      "label" : null,
      "popularity" : null
    }
  }
}
JSON example
constexpr char kGetAlbumJsonValue[] = "album-id";

void sendSpotifyGetAlbumJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.get", kGetAlbumJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.get");
}

void handleSpotifyGetAlbumJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.get.response payload");
    return;
  }

  if (kGetAlbumJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAlbumJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAlbumCborValue[] = "album-id";

void sendSpotifyGetAlbumCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.get", kGetAlbumCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.get");
}

void handleSpotifyGetAlbumCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.get.response payload");
    return;
  }

  if (kGetAlbumCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAlbumCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

Returns albums for explicitly supplied Spotify IDs.

spotify.albums.get-many

Request
{
  "spotify.albums.get-many" : "ids=album-1,album-2"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.albums.get-many.response" : {
    "result" : {
      "albums" : [ {
        "album_type" : null,
        "total_tracks" : null,
        "available_markets" : [ ],
        "external_urls" : null,
        "href" : null,
        "id" : "album-1",
        "images" : [ ],
        "name" : "Morning Drive",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "artists" : [ ],
        "tracks" : null,
        "copyrights" : [ ],
        "external_ids" : null,
        "genres" : [ ],
        "label" : null,
        "popularity" : null
      }, {
        "album_type" : null,
        "total_tracks" : null,
        "available_markets" : [ ],
        "external_urls" : null,
        "href" : null,
        "id" : "album-2",
        "images" : [ ],
        "name" : "Evening Chill",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "artists" : [ ],
        "tracks" : null,
        "copyrights" : [ ],
        "external_ids" : null,
        "genres" : [ ],
        "label" : null,
        "popularity" : null
      } ]
    }
  }
}
JSON example
constexpr char kGetSeveralAlbumsJsonValue[] = "ids=album-1,album-2";

void sendSpotifyGetSeveralAlbumsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.get.several", kGetSeveralAlbumsJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.get.several");
}

void handleSpotifyGetSeveralAlbumsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.get.several.response payload");
    return;
  }

  if (kGetSeveralAlbumsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralAlbumsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralAlbumsCborValue[] = "ids=album-1,album-2";

void sendSpotifyGetSeveralAlbumsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.get.several", kGetSeveralAlbumsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.get.several");
}

void handleSpotifyGetSeveralAlbumsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.get.several.response payload");
    return;
  }

  if (kGetSeveralAlbumsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralAlbumsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

Returns tracks belonging to an album.

spotify.albums.list-tracks

Request
{
  "spotify.albums.list-tracks" : "album-id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.albums.list-tracks.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-id",
        "name" : "Sample Track"
      } ]
    }
  }
}
JSON example
constexpr char kGetAlbumTracksJsonValue[] = "album-id";

void sendSpotifyGetAlbumTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.tracks", kGetAlbumTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.tracks request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.tracks");
}

void handleSpotifyGetAlbumTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.tracks.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.tracks response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.tracks.response payload");
    return;
  }

  if (kGetAlbumTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAlbumTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAlbumTracksCborValue[] = "album-id";

void sendSpotifyGetAlbumTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.tracks", kGetAlbumTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.tracks request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.tracks");
}

void handleSpotifyGetAlbumTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.tracks.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.tracks response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.tracks.response payload");
    return;
  }

  if (kGetAlbumTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAlbumTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-albums.list returns the current user’s saved albums.

Request
{
  "spotify.saved-albums.list" : "limit=2"
}
Example response
{
  "spotify.saved-albums.list.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ],
          "tracks" : null,
          "copyrights" : [ ],
          "external_ids" : null,
          "genres" : [ ],
          "label" : null,
          "popularity" : null
        }
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersSavedAlbumsJsonValue[] = "limit=2";

void sendSpotifyGetUsersSavedAlbumsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.saved.get", kGetUsersSavedAlbumsJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.saved.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.saved.get");
}

void handleSpotifyGetUsersSavedAlbumsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedAlbumsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedAlbumsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersSavedAlbumsCborValue[] = "limit=2";

void sendSpotifyGetUsersSavedAlbumsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.saved.get", kGetUsersSavedAlbumsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.saved.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.saved.get");
}

void handleSpotifyGetUsersSavedAlbumsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedAlbumsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedAlbumsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-albums.save saves albums for the current user.

Request
{
  "spotify.saved-albums.save" : "[\"album-id\"]"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.saved-albums.save.response" : {
    "result" : "OK"
  }
}
JSON example
constexpr char kSaveAlbumsForCurrentUserJsonValue[] = "[\"album-id\"]";

void sendSpotifySaveAlbumsForCurrentUserJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.saved.save", kSaveAlbumsForCurrentUserJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.saved.save request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.saved.save");
}

void handleSpotifySaveAlbumsForCurrentUserJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.save.response payload");
    return;
  }

  if (kSaveAlbumsForCurrentUserJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveAlbumsForCurrentUserJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSaveAlbumsForCurrentUserCborValue[] = "[\"album-id\"]";

void sendSpotifySaveAlbumsForCurrentUserCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.saved.save", kSaveAlbumsForCurrentUserCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.saved.save request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.saved.save");
}

void handleSpotifySaveAlbumsForCurrentUserCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.save.response payload");
    return;
  }

  if (kSaveAlbumsForCurrentUserCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveAlbumsForCurrentUserCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-albums.remove removes saved albums.

Request
{
  "spotify.saved-albums.remove" : "[\"album-id\"]"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.saved-albums.remove.response" : {
    "result" : "OK"
  }
}
JSON example
constexpr char kRemoveUsersSavedAlbumsJsonValue[] = "[\"album-id\"]";

void sendSpotifyRemoveUsersSavedAlbumsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.saved.remove", kRemoveUsersSavedAlbumsJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.saved.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.saved.remove");
}

void handleSpotifyRemoveUsersSavedAlbumsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedAlbumsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedAlbumsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemoveUsersSavedAlbumsCborValue[] = "[\"album-id\"]";

void sendSpotifyRemoveUsersSavedAlbumsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.saved.remove", kRemoveUsersSavedAlbumsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.saved.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.saved.remove");
}

void handleSpotifyRemoveUsersSavedAlbumsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedAlbumsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedAlbumsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-albums.check checks whether albums are saved.

Request
{
  "spotify.saved-albums.check" : "ids=album-id"
}
Example response
{
  "spotify.saved-albums.check.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckUsersSavedAlbumsJsonValue[] = "ids=album-id";

void sendSpotifyCheckUsersSavedAlbumsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.saved.check", kCheckUsersSavedAlbumsJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.saved.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.saved.check");
}

void handleSpotifyCheckUsersSavedAlbumsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedAlbumsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedAlbumsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckUsersSavedAlbumsCborValue[] = "ids=album-id";

void sendSpotifyCheckUsersSavedAlbumsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.saved.check", kCheckUsersSavedAlbumsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.saved.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.saved.check");
}

void handleSpotifyCheckUsersSavedAlbumsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedAlbumsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedAlbumsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.albums.list-new-releases returns new album releases.

Request
{
  "spotify.albums.list-new-releases" : "country=US"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.albums.list-new-releases.response" : {
    "result" : {
      "albums" : {
        "href" : null,
        "limit" : null,
        "next" : null,
        "offset" : null,
        "previous" : null,
        "total" : null,
        "items" : [ {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        } ]
      }
    }
  }
}
JSON example
constexpr char kGetNewReleasesJsonValue[] = "country=US";

void sendSpotifyGetNewReleasesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.albums.new-releases", kGetNewReleasesJsonValue, body)) {
    Serial.println("Unable to build spotify.albums.new-releases request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.albums.new-releases");
}

void handleSpotifyGetNewReleasesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.albums.new-releases.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.new-releases response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.new-releases.response payload");
    return;
  }

  if (kGetNewReleasesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetNewReleasesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetNewReleasesCborValue[] = "country=US";

void sendSpotifyGetNewReleasesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.albums.new-releases", kGetNewReleasesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.albums.new-releases request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.albums.new-releases");
}

void handleSpotifyGetNewReleasesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.albums.new-releases.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.albums.new-releases response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.albums.new-releases.response payload");
    return;
  }

  if (kGetNewReleasesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetNewReleasesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Artists

spotify.artists.get returns an artist by Spotify ID.

Request
{
  "spotify.artists.get" : "artist-id"
}
Example response
{
  "spotify.artists.get.response" : {
    "result" : {
      "genres" : [ ],
      "id" : "artist-id",
      "images" : [ ],
      "name" : "Sample Artist"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetArtistJsonValue[] = "artist-id";

void sendSpotifyGetArtistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.artists.get", kGetArtistJsonValue, body)) {
    Serial.println("Unable to build spotify.artists.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.artists.get");
}

void handleSpotifyGetArtistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.artists.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.get.response payload");
    return;
  }

  if (kGetArtistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetArtistCborValue[] = "artist-id";

void sendSpotifyGetArtistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.artists.get", kGetArtistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.artists.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.artists.get");
}

void handleSpotifyGetArtistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.artists.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.get.response payload");
    return;
  }

  if (kGetArtistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.artists.get-many returns artists for supplied IDs.

Request
{
  "spotify.artists.get-many" : "ids=artist-1,artist-2"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.artists.get-many.response" : {
    "result" : {
      "artists" : [ {
        "genres" : [ ],
        "id" : "artist-1",
        "images" : [ ],
        "name" : "Headliner"
      }, {
        "genres" : [ ],
        "id" : "artist-2",
        "images" : [ ],
        "name" : "Support Act"
      } ]
    }
  }
}
JSON example
constexpr char kGetSeveralArtistsJsonValue[] = "ids=artist-1,artist-2";

void sendSpotifyGetSeveralArtistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.artists.get.several", kGetSeveralArtistsJsonValue, body)) {
    Serial.println("Unable to build spotify.artists.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.artists.get.several");
}

void handleSpotifyGetSeveralArtistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.artists.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.get.several.response payload");
    return;
  }

  if (kGetSeveralArtistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralArtistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralArtistsCborValue[] = "ids=artist-1,artist-2";

void sendSpotifyGetSeveralArtistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.artists.get.several", kGetSeveralArtistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.artists.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.artists.get.several");
}

void handleSpotifyGetSeveralArtistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.artists.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.get.several.response payload");
    return;
  }

  if (kGetSeveralArtistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralArtistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.artists.list-albums returns an artist’s albums.

Request
{
  "spotify.artists.list-albums" : "artist-id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.artists.list-albums.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "album_type" : null,
        "total_tracks" : null,
        "available_markets" : [ ],
        "external_urls" : null,
        "href" : null,
        "id" : "album-id",
        "images" : [ ],
        "name" : "Sample Album",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "artists" : [ ]
      } ]
    }
  }
}
JSON example
constexpr char kGetArtistsAlbumsJsonValue[] = "artist-id";

void sendSpotifyGetArtistsAlbumsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.artists.albums", kGetArtistsAlbumsJsonValue, body)) {
    Serial.println("Unable to build spotify.artists.albums request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.artists.albums");
}

void handleSpotifyGetArtistsAlbumsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.artists.albums.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.albums response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.albums.response payload");
    return;
  }

  if (kGetArtistsAlbumsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsAlbumsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetArtistsAlbumsCborValue[] = "artist-id";

void sendSpotifyGetArtistsAlbumsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.artists.albums", kGetArtistsAlbumsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.artists.albums request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.artists.albums");
}

void handleSpotifyGetArtistsAlbumsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.artists.albums.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.albums response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.albums.response payload");
    return;
  }

  if (kGetArtistsAlbumsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsAlbumsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.artists.list-top-tracks returns an artist’s top tracks.

Request
{
  "spotify.artists.list-top-tracks" : "artist-id/top-tracks?market=US"
}
Example response
{
  "spotify.artists.list-top-tracks.response" : {
    "result" : {
      "tracks" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-1",
        "name" : "Main Hit"
      }, {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-2",
        "name" : "Deep Cut"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetArtistsTopTracksJsonValue[] = "artist-id/top-tracks?market=US";

void sendSpotifyGetArtistsTopTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.artists.top-tracks", kGetArtistsTopTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.artists.top-tracks request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.artists.top-tracks");
}

void handleSpotifyGetArtistsTopTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.artists.top-tracks.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.top-tracks response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.top-tracks.response payload");
    return;
  }

  if (kGetArtistsTopTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsTopTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetArtistsTopTracksCborValue[] = "artist-id/top-tracks?market=US";

void sendSpotifyGetArtistsTopTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.artists.top-tracks", kGetArtistsTopTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.artists.top-tracks request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.artists.top-tracks");
}

void handleSpotifyGetArtistsTopTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.artists.top-tracks.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.top-tracks response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.top-tracks.response payload");
    return;
  }

  if (kGetArtistsTopTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsTopTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.artists.list-related returns artists related to an artist.

Request
{
  "spotify.artists.list-related" : "artist-id"
}
Example response
{
  "spotify.artists.list-related.response" : {
    "result" : {
      "artists" : [ {
        "genres" : [ ],
        "id" : "artist-3",
        "images" : [ ],
        "name" : "Neighbor Band"
      }, {
        "genres" : [ ],
        "id" : "artist-4",
        "images" : [ ],
        "name" : "Cousin Crew"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetArtistsRelatedArtistsJsonValue[] = "artist-id";

void sendSpotifyGetArtistsRelatedArtistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.artists.related", kGetArtistsRelatedArtistsJsonValue, body)) {
    Serial.println("Unable to build spotify.artists.related request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.artists.related");
}

void handleSpotifyGetArtistsRelatedArtistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.artists.related.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.related response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.related.response payload");
    return;
  }

  if (kGetArtistsRelatedArtistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsRelatedArtistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetArtistsRelatedArtistsCborValue[] = "artist-id";

void sendSpotifyGetArtistsRelatedArtistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.artists.related", kGetArtistsRelatedArtistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.artists.related request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.artists.related");
}

void handleSpotifyGetArtistsRelatedArtistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.artists.related.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.artists.related response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.artists.related.response payload");
    return;
  }

  if (kGetArtistsRelatedArtistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetArtistsRelatedArtistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.artists.list-followed returns artists followed by the current user.

Request
{
  "spotify.artists.list-followed" : "type=artist&limit=5"
}
Example response
{
  "spotify.artists.list-followed.response" : {
    "result" : {
      "artists" : {
        "items" : [ {
          "genres" : [ ],
          "id" : "artist-id",
          "images" : [ ],
          "name" : "Followed Artist"
        } ]
      }
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetFollowedArtistsJsonValue[] = "type=artist&limit=5";

void sendSpotifyGetFollowedArtistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.followed-artists", kGetFollowedArtistsJsonValue, body)) {
    Serial.println("Unable to build spotify.users.followed-artists request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.followed-artists");
}

void handleSpotifyGetFollowedArtistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.followed-artists.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.followed-artists response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.followed-artists.response payload");
    return;
  }

  if (kGetFollowedArtistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetFollowedArtistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetFollowedArtistsCborValue[] = "type=artist&limit=5";

void sendSpotifyGetFollowedArtistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.followed-artists", kGetFollowedArtistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.followed-artists request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.followed-artists");
}

void handleSpotifyGetFollowedArtistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.followed-artists.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.followed-artists response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.followed-artists.response payload");
    return;
  }

  if (kGetFollowedArtistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetFollowedArtistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Audiobooks

spotify.audiobooks.get returns an audiobook by Spotify ID.

Request
{
  "spotify.audiobooks.get" : "audiobook-id"
}
Example response
{
  "spotify.audiobooks.get.response" : {
    "result" : {
      "authors" : [ ],
      "available_markets" : [ ],
      "copyrights" : [ ],
      "description" : null,
      "html_description" : null,
      "explicit" : null,
      "external_urls" : null,
      "href" : null,
      "id" : "audiobook-id",
      "images" : [ ],
      "languages" : [ ],
      "media_type" : null,
      "name" : "Sample Audiobook",
      "narrators" : [ ],
      "publisher" : null,
      "type" : null,
      "uri" : null,
      "total_chapters" : null,
      "chapters" : null
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetAnAudiobookJsonValue[] = "audiobook-id";

void sendSpotifyGetAnAudiobookJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.get", kGetAnAudiobookJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.get");
}

void handleSpotifyGetAnAudiobookJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.get.response payload");
    return;
  }

  if (kGetAnAudiobookJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAnAudiobookJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAnAudiobookCborValue[] = "audiobook-id";

void sendSpotifyGetAnAudiobookCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.get", kGetAnAudiobookCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.get");
}

void handleSpotifyGetAnAudiobookCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.get.response payload");
    return;
  }

  if (kGetAnAudiobookCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAnAudiobookCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.audiobooks.get-many returns audiobooks for supplied IDs.

Request
{
  "spotify.audiobooks.get-many" : "ids=book-1,book-2"
}
Example response
{
  "spotify.audiobooks.get-many.response" : {
    "result" : {
      "audiobooks" : [ {
        "authors" : [ ],
        "available_markets" : [ ],
        "copyrights" : [ ],
        "description" : null,
        "html_description" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "book-1",
        "images" : [ ],
        "languages" : [ ],
        "media_type" : null,
        "name" : "Morning Stories",
        "narrators" : [ ],
        "publisher" : null,
        "type" : null,
        "uri" : null,
        "total_chapters" : null,
        "chapters" : null
      }, {
        "authors" : [ ],
        "available_markets" : [ ],
        "copyrights" : [ ],
        "description" : null,
        "html_description" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "book-2",
        "images" : [ ],
        "languages" : [ ],
        "media_type" : null,
        "name" : "Evening Stories",
        "narrators" : [ ],
        "publisher" : null,
        "type" : null,
        "uri" : null,
        "total_chapters" : null,
        "chapters" : null
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSeveralAudiobooksJsonValue[] = "ids=book-1,book-2";

void sendSpotifyGetSeveralAudiobooksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.get.several", kGetSeveralAudiobooksJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.get.several");
}

void handleSpotifyGetSeveralAudiobooksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.get.several.response payload");
    return;
  }

  if (kGetSeveralAudiobooksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralAudiobooksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralAudiobooksCborValue[] = "ids=book-1,book-2";

void sendSpotifyGetSeveralAudiobooksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.get.several", kGetSeveralAudiobooksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.get.several");
}

void handleSpotifyGetSeveralAudiobooksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.get.several.response payload");
    return;
  }

  if (kGetSeveralAudiobooksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralAudiobooksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.audiobooks.list-chapters returns an audiobook’s chapters.

Request
{
  "spotify.audiobooks.list-chapters" : "audiobook-id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.audiobooks.list-chapters.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "audio_preview_url" : null,
        "available_markets" : [ ],
        "chapter_number" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "chapter-id",
        "images" : [ ],
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Opening Chapter",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null
      } ]
    }
  }
}
JSON example
constexpr char kGetAudiobookChaptersJsonValue[] = "audiobook-id";

void sendSpotifyGetAudiobookChaptersJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.chapters", kGetAudiobookChaptersJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.chapters request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.chapters");
}

void handleSpotifyGetAudiobookChaptersJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.chapters.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.chapters response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.chapters.response payload");
    return;
  }

  if (kGetAudiobookChaptersJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAudiobookChaptersJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAudiobookChaptersCborValue[] = "audiobook-id";

void sendSpotifyGetAudiobookChaptersCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.chapters", kGetAudiobookChaptersCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.chapters request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.chapters");
}

void handleSpotifyGetAudiobookChaptersCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.chapters.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.chapters response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.chapters.response payload");
    return;
  }

  if (kGetAudiobookChaptersCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAudiobookChaptersCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-audiobooks.list returns the current user’s saved audiobooks.

Request
{
  "spotify.saved-audiobooks.list" : "limit=5"
}
Example response
{
  "spotify.saved-audiobooks.list.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "audiobook" : {
          "authors" : [ ],
          "available_markets" : [ ],
          "copyrights" : [ ],
          "description" : null,
          "html_description" : null,
          "explicit" : null,
          "external_urls" : null,
          "href" : null,
          "id" : "audiobook-id",
          "images" : [ ],
          "languages" : [ ],
          "media_type" : null,
          "name" : "Sample Audiobook",
          "narrators" : [ ],
          "publisher" : null,
          "type" : null,
          "uri" : null,
          "total_chapters" : null,
          "chapters" : null
        }
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersSavedAudiobooksJsonValue[] = "limit=5";

void sendSpotifyGetUsersSavedAudiobooksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.saved.get", kGetUsersSavedAudiobooksJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.saved.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.get");
}

void handleSpotifyGetUsersSavedAudiobooksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedAudiobooksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedAudiobooksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersSavedAudiobooksCborValue[] = "limit=5";

void sendSpotifyGetUsersSavedAudiobooksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.saved.get", kGetUsersSavedAudiobooksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.saved.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.get");
}

void handleSpotifyGetUsersSavedAudiobooksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedAudiobooksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedAudiobooksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-audiobooks.save saves audiobooks for the current user.

Request
{
  "spotify.saved-audiobooks.save" : "[\"audiobook-id\"]"
}
Example response
{
  "spotify.saved-audiobooks.save.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kSaveAudiobooksForCurrentUserJsonValue[] = "[\"audiobook-id\"]";

void sendSpotifySaveAudiobooksForCurrentUserJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.saved.save", kSaveAudiobooksForCurrentUserJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.saved.save request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.save");
}

void handleSpotifySaveAudiobooksForCurrentUserJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.save.response payload");
    return;
  }

  if (kSaveAudiobooksForCurrentUserJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveAudiobooksForCurrentUserJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSaveAudiobooksForCurrentUserCborValue[] = "[\"audiobook-id\"]";

void sendSpotifySaveAudiobooksForCurrentUserCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.saved.save", kSaveAudiobooksForCurrentUserCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.saved.save request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.save");
}

void handleSpotifySaveAudiobooksForCurrentUserCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.save.response payload");
    return;
  }

  if (kSaveAudiobooksForCurrentUserCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveAudiobooksForCurrentUserCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-audiobooks.remove removes audiobooks saved by the current user.

Request
{
  "spotify.saved-audiobooks.remove" : "[\"audiobook-id\"]"
}
Example response
{
  "spotify.saved-audiobooks.remove.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kRemoveUsersSavedAudiobooksJsonValue[] = "[\"audiobook-id\"]";

void sendSpotifyRemoveUsersSavedAudiobooksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.saved.remove", kRemoveUsersSavedAudiobooksJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.saved.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.remove");
}

void handleSpotifyRemoveUsersSavedAudiobooksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedAudiobooksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedAudiobooksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemoveUsersSavedAudiobooksCborValue[] = "[\"audiobook-id\"]";

void sendSpotifyRemoveUsersSavedAudiobooksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.saved.remove", kRemoveUsersSavedAudiobooksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.saved.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.remove");
}

void handleSpotifyRemoveUsersSavedAudiobooksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedAudiobooksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedAudiobooksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-audiobooks.check checks whether audiobooks are saved.

Request
{
  "spotify.saved-audiobooks.check" : "ids=audiobook-id"
}
Example response
{
  "spotify.saved-audiobooks.check.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckUsersSavedAudiobooksJsonValue[] = "ids=audiobook-id";

void sendSpotifyCheckUsersSavedAudiobooksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.audiobooks.saved.check", kCheckUsersSavedAudiobooksJsonValue, body)) {
    Serial.println("Unable to build spotify.audiobooks.saved.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.check");
}

void handleSpotifyCheckUsersSavedAudiobooksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.audiobooks.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedAudiobooksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedAudiobooksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckUsersSavedAudiobooksCborValue[] = "ids=audiobook-id";

void sendSpotifyCheckUsersSavedAudiobooksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.audiobooks.saved.check", kCheckUsersSavedAudiobooksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.audiobooks.saved.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.audiobooks.saved.check");
}

void handleSpotifyCheckUsersSavedAudiobooksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.audiobooks.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.audiobooks.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.audiobooks.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedAudiobooksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedAudiobooksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Categories

spotify.categories.list returns available browse categories.

Request

Unresolved directive in spotify-request-response.adoc - include::/home/jenkins/workspace/apps_soeren_soeren-ci_main/soren-app/target/generated-snippets/spotify/categories-list—​get-several-browse-categories/request-body.adoc[]

Example response

Unresolved directive in spotify-request-response.adoc - include::/home/jenkins/workspace/apps_soeren_soeren-ci_main/soren-app/target/generated-snippets/spotify/categories-list—​get-several-browse-categories/response-body.adoc[]

JSON example
constexpr char kGetSeveralBrowseCategoriesJsonValue[] = "country=US";

void sendSpotifyGetSeveralBrowseCategoriesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.categories.get", kGetSeveralBrowseCategoriesJsonValue, body)) {
    Serial.println("Unable to build spotify.categories.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.categories.get");
}

void handleSpotifyGetSeveralBrowseCategoriesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.categories.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.categories.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.categories.get.response payload");
    return;
  }

  if (kGetSeveralBrowseCategoriesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralBrowseCategoriesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralBrowseCategoriesCborValue[] = "country=US";

void sendSpotifyGetSeveralBrowseCategoriesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.categories.get", kGetSeveralBrowseCategoriesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.categories.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.categories.get");
}

void handleSpotifyGetSeveralBrowseCategoriesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.categories.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.categories.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.categories.get.response payload");
    return;
  }

  if (kGetSeveralBrowseCategoriesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralBrowseCategoriesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.categories.get returns a browse category by ID.

Request
{
  "spotify.categories.get" : "category-id"
}
Example response
{
  "spotify.categories.get.response" : {
    "result" : {
      "href" : null,
      "icons" : [ ],
      "id" : "category-id",
      "name" : "Party Time"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSingleBrowseCategoryJsonValue[] = "category-id";

void sendSpotifyGetSingleBrowseCategoryJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.categories.get.single", kGetSingleBrowseCategoryJsonValue, body)) {
    Serial.println("Unable to build spotify.categories.get.single request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.categories.get.single");
}

void handleSpotifyGetSingleBrowseCategoryJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.categories.get.single.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.categories.get.single response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.categories.get.single.response payload");
    return;
  }

  if (kGetSingleBrowseCategoryJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSingleBrowseCategoryJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSingleBrowseCategoryCborValue[] = "category-id";

void sendSpotifyGetSingleBrowseCategoryCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.categories.get.single", kGetSingleBrowseCategoryCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.categories.get.single request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.categories.get.single");
}

void handleSpotifyGetSingleBrowseCategoryCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.categories.get.single.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.categories.get.single response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.categories.get.single.response payload");
    return;
  }

  if (kGetSingleBrowseCategoryCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSingleBrowseCategoryCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Chapters

spotify.chapters.get returns a chapter by Spotify ID.

Request
{
  "spotify.chapters.get" : "chapter-id"
}
Example response
{
  "spotify.chapters.get.response" : {
    "result" : {
      "audio_preview_url" : null,
      "available_markets" : [ ],
      "chapter_number" : null,
      "description" : null,
      "html_description" : null,
      "duration_ms" : null,
      "explicit" : null,
      "external_urls" : null,
      "href" : null,
      "id" : "chapter-id",
      "images" : [ ],
      "is_playable" : null,
      "languages" : [ ],
      "name" : "Opening Chapter",
      "release_date" : null,
      "release_date_precision" : null,
      "type" : null,
      "uri" : null,
      "audiobook" : null
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetAChapterJsonValue[] = "chapter-id";

void sendSpotifyGetAChapterJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.chapters.get", kGetAChapterJsonValue, body)) {
    Serial.println("Unable to build spotify.chapters.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.chapters.get");
}

void handleSpotifyGetAChapterJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.chapters.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.chapters.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.chapters.get.response payload");
    return;
  }

  if (kGetAChapterJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAChapterJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAChapterCborValue[] = "chapter-id";

void sendSpotifyGetAChapterCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.chapters.get", kGetAChapterCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.chapters.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.chapters.get");
}

void handleSpotifyGetAChapterCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.chapters.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.chapters.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.chapters.get.response payload");
    return;
  }

  if (kGetAChapterCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAChapterCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.chapters.get-many returns chapters for supplied IDs.

Request
{
  "spotify.chapters.get-many" : "ids=chapter-1,chapter-2"
}
Example response
{
  "spotify.chapters.get-many.response" : {
    "result" : {
      "chapters" : [ {
        "audio_preview_url" : null,
        "available_markets" : [ ],
        "chapter_number" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "chapter-1",
        "images" : [ ],
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Introduction",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "audiobook" : null
      }, {
        "audio_preview_url" : null,
        "available_markets" : [ ],
        "chapter_number" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "chapter-2",
        "images" : [ ],
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Deep Dive",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "audiobook" : null
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSeveralChaptersJsonValue[] = "ids=chapter-1,chapter-2";

void sendSpotifyGetSeveralChaptersJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.chapters.get.several", kGetSeveralChaptersJsonValue, body)) {
    Serial.println("Unable to build spotify.chapters.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.chapters.get.several");
}

void handleSpotifyGetSeveralChaptersJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.chapters.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.chapters.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.chapters.get.several.response payload");
    return;
  }

  if (kGetSeveralChaptersJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralChaptersJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralChaptersCborValue[] = "ids=chapter-1,chapter-2";

void sendSpotifyGetSeveralChaptersCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.chapters.get.several", kGetSeveralChaptersCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.chapters.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.chapters.get.several");
}

void handleSpotifyGetSeveralChaptersCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.chapters.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.chapters.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.chapters.get.several.response payload");
    return;
  }

  if (kGetSeveralChaptersCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralChaptersCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Episodes

spotify.episodes.get returns an episode by Spotify ID.

Request
{
  "spotify.episodes.get" : "episode-id"
}
Example response
{
  "spotify.episodes.get.response" : {
    "result" : {
      "audio_preview_url" : null,
      "description" : null,
      "html_description" : null,
      "duration_ms" : null,
      "explicit" : null,
      "external_urls" : null,
      "href" : null,
      "id" : "episode-id",
      "images" : [ ],
      "is_externally_hosted" : null,
      "is_playable" : null,
      "languages" : [ ],
      "name" : "Sample Episode",
      "release_date" : null,
      "release_date_precision" : null,
      "type" : null,
      "uri" : null,
      "show" : null
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetEpisodeJsonValue[] = "episode-id";

void sendSpotifyGetEpisodeJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.get", kGetEpisodeJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.get");
}

void handleSpotifyGetEpisodeJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.get.response payload");
    return;
  }

  if (kGetEpisodeJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetEpisodeJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetEpisodeCborValue[] = "episode-id";

void sendSpotifyGetEpisodeCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.get", kGetEpisodeCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.get");
}

void handleSpotifyGetEpisodeCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.get.response payload");
    return;
  }

  if (kGetEpisodeCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetEpisodeCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.episodes.get-many returns episodes for supplied IDs.

Request
{
  "spotify.episodes.get-many" : "ids=episode-1,episode-2"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.episodes.get-many.response" : {
    "result" : {
      "episodes" : [ {
        "audio_preview_url" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "episode-1",
        "images" : [ ],
        "is_externally_hosted" : null,
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Chapter One",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "show" : null
      }, {
        "audio_preview_url" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "episode-2",
        "images" : [ ],
        "is_externally_hosted" : null,
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Chapter Two",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "show" : null
      } ]
    }
  }
}
JSON example
constexpr char kGetSeveralEpisodesJsonValue[] = "ids=episode-1,episode-2";

void sendSpotifyGetSeveralEpisodesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.get.several", kGetSeveralEpisodesJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.get.several");
}

void handleSpotifyGetSeveralEpisodesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.get.several.response payload");
    return;
  }

  if (kGetSeveralEpisodesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralEpisodesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralEpisodesCborValue[] = "ids=episode-1,episode-2";

void sendSpotifyGetSeveralEpisodesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.get.several", kGetSeveralEpisodesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.get.several");
}

void handleSpotifyGetSeveralEpisodesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.get.several.response payload");
    return;
  }

  if (kGetSeveralEpisodesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralEpisodesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-episodes.list returns the current user’s saved episodes.

Request
{
  "spotify.saved-episodes.list" : "limit=1"
}
Example response
{
  "spotify.saved-episodes.list.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "episode" : {
          "audio_preview_url" : null,
          "description" : null,
          "html_description" : null,
          "duration_ms" : null,
          "explicit" : null,
          "external_urls" : null,
          "href" : null,
          "id" : "episode-id",
          "images" : [ ],
          "is_externally_hosted" : null,
          "is_playable" : null,
          "languages" : [ ],
          "name" : "Sample Episode",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "show" : null
        }
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersSavedEpisodesJsonValue[] = "limit=1";

void sendSpotifyGetUsersSavedEpisodesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.saved.get", kGetUsersSavedEpisodesJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.saved.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.get");
}

void handleSpotifyGetUsersSavedEpisodesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedEpisodesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedEpisodesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersSavedEpisodesCborValue[] = "limit=1";

void sendSpotifyGetUsersSavedEpisodesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.saved.get", kGetUsersSavedEpisodesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.saved.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.get");
}

void handleSpotifyGetUsersSavedEpisodesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedEpisodesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedEpisodesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-episodes.save saves episodes for the current user.

Request
{
  "spotify.saved-episodes.save" : "[\"episode-id\"]"
}
Example response
{
  "spotify.saved-episodes.save.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kSaveEpisodesForCurrentUserJsonValue[] = "[\"episode-id\"]";

void sendSpotifySaveEpisodesForCurrentUserJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.saved.save", kSaveEpisodesForCurrentUserJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.saved.save request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.save");
}

void handleSpotifySaveEpisodesForCurrentUserJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.save.response payload");
    return;
  }

  if (kSaveEpisodesForCurrentUserJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveEpisodesForCurrentUserJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSaveEpisodesForCurrentUserCborValue[] = "[\"episode-id\"]";

void sendSpotifySaveEpisodesForCurrentUserCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.saved.save", kSaveEpisodesForCurrentUserCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.saved.save request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.save");
}

void handleSpotifySaveEpisodesForCurrentUserCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.save.response payload");
    return;
  }

  if (kSaveEpisodesForCurrentUserCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveEpisodesForCurrentUserCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-episodes.remove removes episodes saved by the current user.

Request
{
  "spotify.saved-episodes.remove" : "[\"episode-id\"]"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.saved-episodes.remove.response" : {
    "result" : "OK"
  }
}
JSON example
constexpr char kRemoveUsersSavedEpisodesJsonValue[] = "[\"episode-id\"]";

void sendSpotifyRemoveUsersSavedEpisodesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.saved.remove", kRemoveUsersSavedEpisodesJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.saved.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.remove");
}

void handleSpotifyRemoveUsersSavedEpisodesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedEpisodesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedEpisodesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemoveUsersSavedEpisodesCborValue[] = "[\"episode-id\"]";

void sendSpotifyRemoveUsersSavedEpisodesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.saved.remove", kRemoveUsersSavedEpisodesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.saved.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.remove");
}

void handleSpotifyRemoveUsersSavedEpisodesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedEpisodesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedEpisodesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-episodes.check checks whether episodes are saved.

Request
{
  "spotify.saved-episodes.check" : "ids=episode-id"
}
Example response
{
  "spotify.saved-episodes.check.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckUsersSavedEpisodesJsonValue[] = "ids=episode-id";

void sendSpotifyCheckUsersSavedEpisodesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.episodes.saved.check", kCheckUsersSavedEpisodesJsonValue, body)) {
    Serial.println("Unable to build spotify.episodes.saved.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.check");
}

void handleSpotifyCheckUsersSavedEpisodesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.episodes.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedEpisodesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedEpisodesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckUsersSavedEpisodesCborValue[] = "ids=episode-id";

void sendSpotifyCheckUsersSavedEpisodesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.episodes.saved.check", kCheckUsersSavedEpisodesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.episodes.saved.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.episodes.saved.check");
}

void handleSpotifyCheckUsersSavedEpisodesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.episodes.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.episodes.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.episodes.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedEpisodesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedEpisodesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Genres

spotify.genres.list-seeds returns available recommendation genre seeds.

Request
{
  "spotify.genres.list-seeds" : ""
}
Example response
{
  "spotify.genres.list-seeds.response" : {
    "result" : {
      "genres" : [ "alternative", "samba" ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetAvailableGenreSeedsJsonValue[] = "";

void sendSpotifyGetAvailableGenreSeedsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.genres.seeds.get", kGetAvailableGenreSeedsJsonValue, body)) {
    Serial.println("Unable to build spotify.genres.seeds.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.genres.seeds.get");
}

void handleSpotifyGetAvailableGenreSeedsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.genres.seeds.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.genres.seeds.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.genres.seeds.get.response payload");
    return;
  }

  if (kGetAvailableGenreSeedsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAvailableGenreSeedsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetAvailableGenreSeedsCborValue[] = "";

void sendSpotifyGetAvailableGenreSeedsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.genres.seeds.get", kGetAvailableGenreSeedsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.genres.seeds.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.genres.seeds.get");
}

void handleSpotifyGetAvailableGenreSeedsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.genres.seeds.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.genres.seeds.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.genres.seeds.get.response payload");
    return;
  }

  if (kGetAvailableGenreSeedsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetAvailableGenreSeedsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Playlists

spotify.playlists.get

Request
{
  "spotify.playlists.get" : "playlist-id"
}
Example response
{
  "spotify.playlists.get.response" : {
    "result" : {
      "id" : "playlist-id",
      "images" : [ ],
      "name" : "Sample Playlist"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetPlaylistJsonValue[] = "playlist-id";

void sendSpotifyGetPlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.get", kGetPlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.get");
}

void handleSpotifyGetPlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.get.response payload");
    return;
  }

  if (kGetPlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetPlaylistCborValue[] = "playlist-id";

void sendSpotifyGetPlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.get", kGetPlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.get");
}

void handleSpotifyGetPlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.get.response payload");
    return;
  }

  if (kGetPlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.update-details

Request
{
  "spotify.playlists.update-details" : "playlist-id||{\"name\":\"Road Trip\"}"
}
Example response
{
  "spotify.playlists.update-details.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kChangePlaylistDetailsJsonValue[] = "playlist-id||{\"name\":\"Road Trip\"}";

void sendSpotifyChangePlaylistDetailsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.change-details", kChangePlaylistDetailsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.change-details request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.change-details");
}

void handleSpotifyChangePlaylistDetailsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.change-details.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.change-details response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.change-details.response payload");
    return;
  }

  if (kChangePlaylistDetailsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kChangePlaylistDetailsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kChangePlaylistDetailsCborValue[] = "playlist-id||{\"name\":\"Road Trip\"}";

void sendSpotifyChangePlaylistDetailsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.change-details", kChangePlaylistDetailsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.change-details request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.change-details");
}

void handleSpotifyChangePlaylistDetailsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.change-details.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.change-details response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.change-details.response payload");
    return;
  }

  if (kChangePlaylistDetailsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kChangePlaylistDetailsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.list-items

Request
{
  "spotify.playlists.list-items" : "playlist-id/tracks?limit=10"
}
Example response
{
  "spotify.playlists.list-items.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "track" : {
          "album" : {
            "album_type" : null,
            "total_tracks" : null,
            "available_markets" : [ ],
            "external_urls" : null,
            "href" : null,
            "id" : "album-id",
            "images" : [ ],
            "name" : "Sample Album",
            "release_date" : null,
            "release_date_precision" : null,
            "type" : null,
            "uri" : null,
            "artists" : [ ]
          },
          "artists" : [ {
            "id" : "artist-id",
            "name" : "Sample Artist"
          } ],
          "available_markets" : [ ],
          "id" : "track-1",
          "name" : "Sample Track"
        }
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetPlaylistItemsJsonValue[] = "playlist-id/tracks?limit=10";

void sendSpotifyGetPlaylistItemsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.items.get", kGetPlaylistItemsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.items.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.items.get");
}

void handleSpotifyGetPlaylistItemsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.items.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.get.response payload");
    return;
  }

  if (kGetPlaylistItemsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistItemsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetPlaylistItemsCborValue[] = "playlist-id/tracks?limit=10";

void sendSpotifyGetPlaylistItemsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.items.get", kGetPlaylistItemsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.items.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.items.get");
}

void handleSpotifyGetPlaylistItemsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.items.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.get.response payload");
    return;
  }

  if (kGetPlaylistItemsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistItemsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.update-items

Request
{
  "spotify.playlists.update-items" : "playlist-id||{\"uris\":[\"spotify:track:1\"]}"
}
Example response
{
  "spotify.playlists.update-items.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kUpdatePlaylistItemsJsonValue[] = "playlist-id||{\"uris\":[\"spotify:track:1\"]}";

void sendSpotifyUpdatePlaylistItemsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.items.update", kUpdatePlaylistItemsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.items.update request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.items.update");
}

void handleSpotifyUpdatePlaylistItemsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.items.update.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.update response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.update.response payload");
    return;
  }

  if (kUpdatePlaylistItemsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUpdatePlaylistItemsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kUpdatePlaylistItemsCborValue[] = "playlist-id||{\"uris\":[\"spotify:track:1\"]}";

void sendSpotifyUpdatePlaylistItemsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.items.update", kUpdatePlaylistItemsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.items.update request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.items.update");
}

void handleSpotifyUpdatePlaylistItemsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.items.update.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.update response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.update.response payload");
    return;
  }

  if (kUpdatePlaylistItemsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUpdatePlaylistItemsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.add-items

Request
{
  "spotify.playlists.add-items" : "playlist-id||{\"uris\":[\"spotify:track:1\"]}"
}
Example response
{
  "spotify.playlists.add-items.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kAddItemsToPlaylistJsonValue[] = "playlist-id||{\"uris\":[\"spotify:track:1\"]}";

void sendSpotifyAddItemsToPlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.items.add", kAddItemsToPlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.items.add request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.items.add");
}

void handleSpotifyAddItemsToPlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.items.add.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.add response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.add.response payload");
    return;
  }

  if (kAddItemsToPlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddItemsToPlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kAddItemsToPlaylistCborValue[] = "playlist-id||{\"uris\":[\"spotify:track:1\"]}";

void sendSpotifyAddItemsToPlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.items.add", kAddItemsToPlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.items.add request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.items.add");
}

void handleSpotifyAddItemsToPlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.items.add.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.add response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.add.response payload");
    return;
  }

  if (kAddItemsToPlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddItemsToPlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.remove-items

Request
{
  "spotify.playlists.remove-items" : "playlist-id||{\"tracks\":[{\"uri\":\"spotify:track:1\"}]}"
}
Example response
{
  "spotify.playlists.remove-items.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kRemovePlaylistItemsJsonValue[] = "playlist-id||{\"tracks\":[{\"uri\":\"spotify:track:1\"}]}";

void sendSpotifyRemovePlaylistItemsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.items.remove", kRemovePlaylistItemsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.items.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.items.remove");
}

void handleSpotifyRemovePlaylistItemsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.items.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.remove.response payload");
    return;
  }

  if (kRemovePlaylistItemsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemovePlaylistItemsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemovePlaylistItemsCborValue[] = "playlist-id||{\"tracks\":[{\"uri\":\"spotify:track:1\"}]}";

void sendSpotifyRemovePlaylistItemsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.items.remove", kRemovePlaylistItemsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.items.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.items.remove");
}

void handleSpotifyRemovePlaylistItemsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.items.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.items.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.items.remove.response payload");
    return;
  }

  if (kRemovePlaylistItemsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemovePlaylistItemsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.list-current-user

Request
{
  "spotify.playlists.list-current-user" : "limit=5"
}
Example response
{
  "spotify.playlists.list-current-user.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "id" : "playlist-1",
        "images" : [ ],
        "name" : "Daily Mix"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetCurrentUsersPlaylistsJsonValue[] = "limit=5";

void sendSpotifyGetCurrentUsersPlaylistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.current-user.get", kGetCurrentUsersPlaylistsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.current-user.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.current-user.get");
}

void handleSpotifyGetCurrentUsersPlaylistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.current-user.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.current-user.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.current-user.get.response payload");
    return;
  }

  if (kGetCurrentUsersPlaylistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCurrentUsersPlaylistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetCurrentUsersPlaylistsCborValue[] = "limit=5";

void sendSpotifyGetCurrentUsersPlaylistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.current-user.get", kGetCurrentUsersPlaylistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.current-user.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.current-user.get");
}

void handleSpotifyGetCurrentUsersPlaylistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.current-user.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.current-user.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.current-user.get.response payload");
    return;
  }

  if (kGetCurrentUsersPlaylistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCurrentUsersPlaylistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.list-user

Request
{
  "spotify.playlists.list-user" : "user-id/playlists?limit=5"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.playlists.list-user.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "id" : "playlist-2",
        "images" : [ ],
        "name" : "Road Trip"
      } ]
    }
  }
}
JSON example
constexpr char kGetUsersPlaylistsJsonValue[] = "user-id/playlists?limit=5";

void sendSpotifyGetUsersPlaylistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.user.get", kGetUsersPlaylistsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.user.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.user.get");
}

void handleSpotifyGetUsersPlaylistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.user.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.user.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.user.get.response payload");
    return;
  }

  if (kGetUsersPlaylistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersPlaylistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersPlaylistsCborValue[] = "user-id/playlists?limit=5";

void sendSpotifyGetUsersPlaylistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.user.get", kGetUsersPlaylistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.user.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.user.get");
}

void handleSpotifyGetUsersPlaylistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.user.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.user.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.user.get.response payload");
    return;
  }

  if (kGetUsersPlaylistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersPlaylistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.create

Request
{
  "spotify.playlists.create" : "user-id||{\"name\":\"Chill\"}"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.playlists.create.response" : {
    "result" : {
      "id" : "playlist-id",
      "images" : [ ],
      "name" : "Chill"
    }
  }
}
JSON example
constexpr char kCreatePlaylistJsonValue[] = "user-id||{\"name\":\"Chill\"}";

void sendSpotifyCreatePlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.create", kCreatePlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.create request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.create");
}

void handleSpotifyCreatePlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.create.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.create response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.create.response payload");
    return;
  }

  if (kCreatePlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCreatePlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCreatePlaylistCborValue[] = "user-id||{\"name\":\"Chill\"}";

void sendSpotifyCreatePlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.create", kCreatePlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.create request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.create");
}

void handleSpotifyCreatePlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.create.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.create response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.create.response payload");
    return;
  }

  if (kCreatePlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCreatePlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.list-featured

Request
{
  "spotify.playlists.list-featured" : "country=US"
}
Example response
{
  "spotify.playlists.list-featured.response" : {
    "result" : {
      "playlists" : {
        "href" : null,
        "limit" : null,
        "next" : null,
        "offset" : null,
        "previous" : null,
        "total" : null,
        "items" : [ {
          "id" : "playlist-id",
          "images" : [ ],
          "name" : "Featured Picks"
        } ]
      }
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetFeaturedPlaylistsJsonValue[] = "country=US";

void sendSpotifyGetFeaturedPlaylistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.featured.get", kGetFeaturedPlaylistsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.featured.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.featured.get");
}

void handleSpotifyGetFeaturedPlaylistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.featured.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.featured.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.featured.get.response payload");
    return;
  }

  if (kGetFeaturedPlaylistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetFeaturedPlaylistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetFeaturedPlaylistsCborValue[] = "country=US";

void sendSpotifyGetFeaturedPlaylistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.featured.get", kGetFeaturedPlaylistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.featured.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.featured.get");
}

void handleSpotifyGetFeaturedPlaylistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.featured.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.featured.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.featured.get.response payload");
    return;
  }

  if (kGetFeaturedPlaylistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetFeaturedPlaylistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.list-category

Request
{
  "spotify.playlists.list-category" : "category-id/playlists?limit=10"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.playlists.list-category.response" : {
    "result" : {
      "playlists" : {
        "href" : null,
        "limit" : null,
        "next" : null,
        "offset" : null,
        "previous" : null,
        "total" : null,
        "items" : [ {
          "id" : "playlist-id",
          "images" : [ ],
          "name" : "Category Mix"
        } ]
      }
    }
  }
}
JSON example
constexpr char kGetCategoriesPlaylistsJsonValue[] = "category-id/playlists?limit=10";

void sendSpotifyGetCategoriesPlaylistsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.category.get", kGetCategoriesPlaylistsJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.category.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.category.get");
}

void handleSpotifyGetCategoriesPlaylistsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.category.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.category.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.category.get.response payload");
    return;
  }

  if (kGetCategoriesPlaylistsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCategoriesPlaylistsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetCategoriesPlaylistsCborValue[] = "category-id/playlists?limit=10";

void sendSpotifyGetCategoriesPlaylistsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.category.get", kGetCategoriesPlaylistsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.category.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.category.get");
}

void handleSpotifyGetCategoriesPlaylistsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.category.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.category.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.category.get.response payload");
    return;
  }

  if (kGetCategoriesPlaylistsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCategoriesPlaylistsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.follow

Request
{
  "spotify.playlists.follow" : "playlist-id||{\"public\":false}"
}
Example response
{
  "spotify.playlists.follow.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kFollowPlaylistJsonValue[] = "playlist-id||{\"public\":false}";

void sendSpotifyFollowPlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.playlists.follow", kFollowPlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.users.playlists.follow request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.playlists.follow");
}

void handleSpotifyFollowPlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.playlists.follow.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.follow response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.follow.response payload");
    return;
  }

  if (kFollowPlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kFollowPlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kFollowPlaylistCborValue[] = "playlist-id||{\"public\":false}";

void sendSpotifyFollowPlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.playlists.follow", kFollowPlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.playlists.follow request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.playlists.follow");
}

void handleSpotifyFollowPlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.playlists.follow.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.follow response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.follow.response payload");
    return;
  }

  if (kFollowPlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kFollowPlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.unfollow

Request
{
  "spotify.playlists.unfollow" : "playlist-id"
}
Example response
{
  "spotify.playlists.unfollow.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kUnfollowPlaylistJsonValue[] = "playlist-id";

void sendSpotifyUnfollowPlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.playlists.unfollow", kUnfollowPlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.users.playlists.unfollow request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.playlists.unfollow");
}

void handleSpotifyUnfollowPlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.playlists.unfollow.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.unfollow response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.unfollow.response payload");
    return;
  }

  if (kUnfollowPlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUnfollowPlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kUnfollowPlaylistCborValue[] = "playlist-id";

void sendSpotifyUnfollowPlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.playlists.unfollow", kUnfollowPlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.playlists.unfollow request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.playlists.unfollow");
}

void handleSpotifyUnfollowPlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.playlists.unfollow.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.unfollow response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.unfollow.response payload");
    return;
  }

  if (kUnfollowPlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUnfollowPlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlists.check-following

Request
{
  "spotify.playlists.check-following" : "playlist-id/followers/contains?ids=user-id"
}
Example response
{
  "spotify.playlists.check-following.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckIfCurrentUserFollowsPlaylistJsonValue[] = "playlist-id/followers/contains?ids=user-id";

void sendSpotifyCheckIfCurrentUserFollowsPlaylistJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.playlists.follow.check", kCheckIfCurrentUserFollowsPlaylistJsonValue, body)) {
    Serial.println("Unable to build spotify.users.playlists.follow.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.playlists.follow.check");
}

void handleSpotifyCheckIfCurrentUserFollowsPlaylistJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.playlists.follow.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.follow.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.follow.check.response payload");
    return;
  }

  if (kCheckIfCurrentUserFollowsPlaylistJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckIfCurrentUserFollowsPlaylistJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckIfCurrentUserFollowsPlaylistCborValue[] = "playlist-id/followers/contains?ids=user-id";

void sendSpotifyCheckIfCurrentUserFollowsPlaylistCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.playlists.follow.check", kCheckIfCurrentUserFollowsPlaylistCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.playlists.follow.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.playlists.follow.check");
}

void handleSpotifyCheckIfCurrentUserFollowsPlaylistCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.playlists.follow.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.playlists.follow.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.playlists.follow.check.response payload");
    return;
  }

  if (kCheckIfCurrentUserFollowsPlaylistCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckIfCurrentUserFollowsPlaylistCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlist-covers.get

Request
{
  "spotify.playlist-covers.get" : "playlist-id"
}
Example response
{
  "spotify.playlist-covers.get.response" : {
    "result" : [ {
      "url" : "https://image",
      "height" : null,
      "width" : null
    } ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetPlaylistCoverImageJsonValue[] = "playlist-id";

void sendSpotifyGetPlaylistCoverImageJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.cover.get", kGetPlaylistCoverImageJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.cover.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.cover.get");
}

void handleSpotifyGetPlaylistCoverImageJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.cover.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.cover.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.cover.get.response payload");
    return;
  }

  if (kGetPlaylistCoverImageJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistCoverImageJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetPlaylistCoverImageCborValue[] = "playlist-id";

void sendSpotifyGetPlaylistCoverImageCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.cover.get", kGetPlaylistCoverImageCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.cover.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.cover.get");
}

void handleSpotifyGetPlaylistCoverImageCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.cover.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.cover.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.cover.get.response payload");
    return;
  }

  if (kGetPlaylistCoverImageCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetPlaylistCoverImageCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.playlist-covers.add

Request
{
  "spotify.playlist-covers.add" : "playlist-id||BASE64DATA"
}
Example response
{
  "spotify.playlist-covers.add.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kAddCustomPlaylistCoverImageJsonValue[] = "playlist-id||BASE64DATA";

void sendSpotifyAddCustomPlaylistCoverImageJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.playlists.cover.add", kAddCustomPlaylistCoverImageJsonValue, body)) {
    Serial.println("Unable to build spotify.playlists.cover.add request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.playlists.cover.add");
}

void handleSpotifyAddCustomPlaylistCoverImageJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<8192> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.playlists.cover.add.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.cover.add response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.cover.add.response payload");
    return;
  }

  if (kAddCustomPlaylistCoverImageJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddCustomPlaylistCoverImageJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kAddCustomPlaylistCoverImageCborValue[] = "playlist-id||BASE64DATA";

void sendSpotifyAddCustomPlaylistCoverImageCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.playlists.cover.add", kAddCustomPlaylistCoverImageCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.playlists.cover.add request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.playlists.cover.add");
}

void handleSpotifyAddCustomPlaylistCoverImageCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(8192);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.playlists.cover.add.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.playlists.cover.add response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.playlists.cover.add.response payload");
    return;
  }

  if (kAddCustomPlaylistCoverImageCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kAddCustomPlaylistCoverImageCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.search.search searches Spotify content.

Request
{
  "spotify.search.search" : "type=track&q=hello"
}
Example response
{
  "spotify.search.search.response" : {
    "result" : {
      "tracks" : {
        "href" : null,
        "limit" : null,
        "next" : null,
        "offset" : null,
        "previous" : null,
        "total" : null,
        "items" : [ {
          "album" : {
            "album_type" : null,
            "total_tracks" : null,
            "available_markets" : [ ],
            "external_urls" : null,
            "href" : null,
            "id" : "album-id",
            "images" : [ ],
            "name" : "Sample Album",
            "release_date" : null,
            "release_date_precision" : null,
            "type" : null,
            "uri" : null,
            "artists" : [ ]
          },
          "artists" : [ {
            "id" : "artist-id",
            "name" : "Sample Artist"
          } ],
          "available_markets" : [ ],
          "id" : "track-id",
          "name" : "Hello World"
        } ]
      }
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kSearchForItemJsonValue[] = "type=track&q=hello";

void sendSpotifySearchForItemJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.search", kSearchForItemJsonValue, body)) {
    Serial.println("Unable to build spotify.search request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.search");
}

void handleSpotifySearchForItemJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.search.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.search response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.search.response payload");
    return;
  }

  if (kSearchForItemJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSearchForItemJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSearchForItemCborValue[] = "type=track&q=hello";

void sendSpotifySearchForItemCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.search", kSearchForItemCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.search request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.search");
}

void handleSpotifySearchForItemCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.search.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.search response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.search.response payload");
    return;
  }

  if (kSearchForItemCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSearchForItemCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Shows

spotify.shows.get returns a show by Spotify ID.

Request
{
  "spotify.shows.get" : "show-id"
}
Example response
{
  "spotify.shows.get.response" : {
    "result" : {
      "available_markets" : [ ],
      "copyrights" : [ ],
      "description" : null,
      "html_description" : null,
      "explicit" : null,
      "external_urls" : null,
      "href" : null,
      "id" : "show-id",
      "images" : [ ],
      "is_externally_hosted" : null,
      "languages" : [ ],
      "media_type" : null,
      "name" : "Sample Show",
      "publisher" : null,
      "type" : null,
      "uri" : null,
      "total_episodes" : null,
      "episodes" : null
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetShowJsonValue[] = "show-id";

void sendSpotifyGetShowJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.get", kGetShowJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.get");
}

void handleSpotifyGetShowJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.get.response payload");
    return;
  }

  if (kGetShowJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetShowJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetShowCborValue[] = "show-id";

void sendSpotifyGetShowCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.get", kGetShowCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.get");
}

void handleSpotifyGetShowCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.get.response payload");
    return;
  }

  if (kGetShowCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetShowCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.shows.get-many returns shows for supplied IDs.

Request
{
  "spotify.shows.get-many" : "ids=show-1,show-2"
}
Example response
{
  "spotify.shows.get-many.response" : {
    "result" : {
      "shows" : [ {
        "available_markets" : [ ],
        "copyrights" : [ ],
        "description" : null,
        "html_description" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "show-1",
        "images" : [ ],
        "is_externally_hosted" : null,
        "languages" : [ ],
        "media_type" : null,
        "name" : "Morning Show",
        "publisher" : null,
        "type" : null,
        "uri" : null,
        "total_episodes" : null,
        "episodes" : null
      }, {
        "available_markets" : [ ],
        "copyrights" : [ ],
        "description" : null,
        "html_description" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "show-2",
        "images" : [ ],
        "is_externally_hosted" : null,
        "languages" : [ ],
        "media_type" : null,
        "name" : "Night Show",
        "publisher" : null,
        "type" : null,
        "uri" : null,
        "total_episodes" : null,
        "episodes" : null
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSeveralShowsJsonValue[] = "ids=show-1,show-2";

void sendSpotifyGetSeveralShowsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.get.several", kGetSeveralShowsJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.get.several");
}

void handleSpotifyGetSeveralShowsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.get.several.response payload");
    return;
  }

  if (kGetSeveralShowsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralShowsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralShowsCborValue[] = "ids=show-1,show-2";

void sendSpotifyGetSeveralShowsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.get.several", kGetSeveralShowsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.get.several");
}

void handleSpotifyGetSeveralShowsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.get.several.response payload");
    return;
  }

  if (kGetSeveralShowsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralShowsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.shows.list-episodes returns episodes for a show.

Request
{
  "spotify.shows.list-episodes" : "show-id/episodes?market=US"
}
Example response
{
  "spotify.shows.list-episodes.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "audio_preview_url" : null,
        "description" : null,
        "html_description" : null,
        "duration_ms" : null,
        "explicit" : null,
        "external_urls" : null,
        "href" : null,
        "id" : "episode-id",
        "images" : [ ],
        "is_externally_hosted" : null,
        "is_playable" : null,
        "languages" : [ ],
        "name" : "Pilot Episode",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetShowEpisodesJsonValue[] = "show-id/episodes?market=US";

void sendSpotifyGetShowEpisodesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.episodes", kGetShowEpisodesJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.episodes request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.episodes");
}

void handleSpotifyGetShowEpisodesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.episodes.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.episodes response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.episodes.response payload");
    return;
  }

  if (kGetShowEpisodesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetShowEpisodesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetShowEpisodesCborValue[] = "show-id/episodes?market=US";

void sendSpotifyGetShowEpisodesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.episodes", kGetShowEpisodesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.episodes request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.episodes");
}

void handleSpotifyGetShowEpisodesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.episodes.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.episodes response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.episodes.response payload");
    return;
  }

  if (kGetShowEpisodesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetShowEpisodesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-shows.list returns the current user’s saved shows.

Request
{
  "spotify.saved-shows.list" : "limit=3"
}
Example response
{
  "spotify.saved-shows.list.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "show" : {
          "available_markets" : [ ],
          "copyrights" : [ ],
          "description" : null,
          "html_description" : null,
          "explicit" : null,
          "external_urls" : null,
          "href" : null,
          "id" : "show-id",
          "images" : [ ],
          "is_externally_hosted" : null,
          "languages" : [ ],
          "media_type" : null,
          "name" : "Sample Show",
          "publisher" : null,
          "type" : null,
          "uri" : null,
          "total_episodes" : null
        }
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersSavedShowsJsonValue[] = "limit=3";

void sendSpotifyGetUsersSavedShowsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.saved.get", kGetUsersSavedShowsJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.saved.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.saved.get");
}

void handleSpotifyGetUsersSavedShowsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedShowsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedShowsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersSavedShowsCborValue[] = "limit=3";

void sendSpotifyGetUsersSavedShowsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.saved.get", kGetUsersSavedShowsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.saved.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.saved.get");
}

void handleSpotifyGetUsersSavedShowsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedShowsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedShowsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-shows.save saves shows for the current user.

Request
{
  "spotify.saved-shows.save" : "[\"show-id\"]"
}
Example response
{
  "spotify.saved-shows.save.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kSaveShowsForCurrentUserJsonValue[] = "[\"show-id\"]";

void sendSpotifySaveShowsForCurrentUserJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.saved.save", kSaveShowsForCurrentUserJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.saved.save request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.saved.save");
}

void handleSpotifySaveShowsForCurrentUserJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.save.response payload");
    return;
  }

  if (kSaveShowsForCurrentUserJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveShowsForCurrentUserJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSaveShowsForCurrentUserCborValue[] = "[\"show-id\"]";

void sendSpotifySaveShowsForCurrentUserCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.saved.save", kSaveShowsForCurrentUserCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.saved.save request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.saved.save");
}

void handleSpotifySaveShowsForCurrentUserCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.save.response payload");
    return;
  }

  if (kSaveShowsForCurrentUserCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveShowsForCurrentUserCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-shows.remove removes shows saved by the current user.

Request
{
  "spotify.saved-shows.remove" : "[\"show-id\"]"
}
Example response
{
  "spotify.saved-shows.remove.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kRemoveUsersSavedShowsJsonValue[] = "[\"show-id\"]";

void sendSpotifyRemoveUsersSavedShowsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.saved.remove", kRemoveUsersSavedShowsJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.saved.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.saved.remove");
}

void handleSpotifyRemoveUsersSavedShowsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedShowsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedShowsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemoveUsersSavedShowsCborValue[] = "[\"show-id\"]";

void sendSpotifyRemoveUsersSavedShowsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.saved.remove", kRemoveUsersSavedShowsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.saved.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.saved.remove");
}

void handleSpotifyRemoveUsersSavedShowsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedShowsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedShowsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-shows.check checks whether shows are saved.

Request
{
  "spotify.saved-shows.check" : "ids=show-id"
}
Example response
{
  "spotify.saved-shows.check.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckUsersSavedShowsJsonValue[] = "ids=show-id";

void sendSpotifyCheckUsersSavedShowsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.shows.saved.check", kCheckUsersSavedShowsJsonValue, body)) {
    Serial.println("Unable to build spotify.shows.saved.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.shows.saved.check");
}

void handleSpotifyCheckUsersSavedShowsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.shows.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedShowsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedShowsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckUsersSavedShowsCborValue[] = "ids=show-id";

void sendSpotifyCheckUsersSavedShowsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.shows.saved.check", kCheckUsersSavedShowsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.shows.saved.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.shows.saved.check");
}

void handleSpotifyCheckUsersSavedShowsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.shows.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.shows.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.shows.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedShowsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedShowsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Tracks

spotify.tracks.get returns a track by Spotify ID.

Request
{
  "spotify.tracks.get" : "track-id"
}
Example response
{
  "spotify.tracks.get.response" : {
    "result" : {
      "album" : {
        "album_type" : null,
        "total_tracks" : null,
        "available_markets" : [ ],
        "external_urls" : null,
        "href" : null,
        "id" : "album-id",
        "images" : [ ],
        "name" : "Sample Album",
        "release_date" : null,
        "release_date_precision" : null,
        "type" : null,
        "uri" : null,
        "artists" : [ ]
      },
      "artists" : [ {
        "id" : "artist-id",
        "name" : "Sample Artist"
      } ],
      "available_markets" : [ ],
      "id" : "track-id",
      "name" : "Sample Track"
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetTrackJsonValue[] = "track-id";

void sendSpotifyGetTrackJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.get", kGetTrackJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.get");
}

void handleSpotifyGetTrackJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.get.response payload");
    return;
  }

  if (kGetTrackJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTrackJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetTrackCborValue[] = "track-id";

void sendSpotifyGetTrackCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.get", kGetTrackCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.get");
}

void handleSpotifyGetTrackCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.get.response payload");
    return;
  }

  if (kGetTrackCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTrackCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.tracks.get-many returns tracks for supplied IDs.

Request
{
  "spotify.tracks.get-many" : "ids=track-1,track-2"
}
Example response
{
  "spotify.tracks.get-many.response" : {
    "result" : {
      "tracks" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-1",
        "name" : "First Track"
      }, {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-2",
        "name" : "Second Track"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSeveralTracksJsonValue[] = "ids=track-1,track-2";

void sendSpotifyGetSeveralTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.get.several", kGetSeveralTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.get.several");
}

void handleSpotifyGetSeveralTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.get.several.response payload");
    return;
  }

  if (kGetSeveralTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralTracksCborValue[] = "ids=track-1,track-2";

void sendSpotifyGetSeveralTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.get.several", kGetSeveralTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.get.several");
}

void handleSpotifyGetSeveralTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.get.several.response payload");
    return;
  }

  if (kGetSeveralTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.tracks.like-current saves the current track.

Request
{
  "spotify.tracks.like-current" : ""
}
Example response
{
  "spotify.tracks.like-current.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyLikeCurrentTrackJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlaybackCommandJsonRequest("spotify.like", "", body)) {
    Serial.println("Unable to build spotify.like request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.like");
}

void handleSpotifyLikeCurrentTrackJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.like.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify like response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.like.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.like.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyLikeCurrentTrackCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength =
      offbeat::spotify::buildPlaybackCommandCborRequest("spotify.like", "", encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.like request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.like");
}

void handleSpotifyLikeCurrentTrackCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.like.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify like response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.like.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.like.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

spotify.tracks.unlike-current removes the current track from saved tracks.

Request
{
  "spotify.tracks.unlike-current" : ""
}
Example response
{
  "spotify.tracks.unlike-current.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
void sendSpotifyUnlikeCurrentTrackJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::buildPlaybackCommandJsonRequest("spotify.unlike", "", body)) {
    Serial.println("Unable to build spotify.unlike request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.unlike");
}

void handleSpotifyUnlikeCurrentTrackJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<512> response;
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandJsonResponse(
          payload, length, response, "spotify.unlike.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify unlike response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.unlike.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.unlike.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}
CBOR example
void sendSpotifyUnlikeCurrentTrackCbor(WebSocketsClient& socket) {
  uint8_t encoded[96];
  size_t encodedLength =
      offbeat::spotify::buildPlaybackCommandCborRequest("spotify.unlike", "", encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.unlike request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.unlike");
}

void handleSpotifyUnlikeCurrentTrackCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(256);
  offbeat::spotify::SpotifyPlaybackCommandResult result;
  offbeat::spotify::SpotifyPlaybackCommandParseStatus status =
      offbeat::spotify::parsePlaybackCommandCborResponse(
          payload, length, buffer, "spotify.unlike.response", result);

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify unlike response");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResponse) {
    Serial.println("No spotify.unlike.response payload");
    return;
  }

  if (status == offbeat::spotify::SpotifyPlaybackCommandParseStatus::kMissingResult) {
    Serial.println("No result in spotify.unlike.response payload");
    return;
  }

  Serial.print("Result: ");
  Serial.println(result.result);
  Serial.print("Endpoint ID: ");
  Serial.println(result.endpointId);
  Serial.print("RESULT=");
  Serial.println(result.result);
  Serial.print("ENDPOINT_ID=");
  Serial.println(result.endpointId);
  Serial.println("TEST:PASS");
}

spotify.saved-tracks.list returns the current user’s saved tracks.

Request
{
  "spotify.saved-tracks.list" : "limit=1"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.saved-tracks.list.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "track" : {
          "album" : {
            "album_type" : null,
            "total_tracks" : null,
            "available_markets" : [ ],
            "external_urls" : null,
            "href" : null,
            "id" : "album-id",
            "images" : [ ],
            "name" : "Sample Album",
            "release_date" : null,
            "release_date_precision" : null,
            "type" : null,
            "uri" : null,
            "artists" : [ ]
          },
          "artists" : [ {
            "id" : "artist-id",
            "name" : "Sample Artist"
          } ],
          "available_markets" : [ ],
          "id" : "track-id",
          "name" : "Sample Track"
        }
      } ]
    }
  }
}
JSON example
constexpr char kGetUsersSavedTracksJsonValue[] = "limit=1";

void sendSpotifyGetUsersSavedTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.saved.get", kGetUsersSavedTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.saved.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.get");
}

void handleSpotifyGetUsersSavedTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersSavedTracksCborValue[] = "limit=1";

void sendSpotifyGetUsersSavedTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.saved.get", kGetUsersSavedTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.saved.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.get");
}

void handleSpotifyGetUsersSavedTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.saved.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.get.response payload");
    return;
  }

  if (kGetUsersSavedTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersSavedTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-tracks.save saves tracks for the current user.

Request
{
  "spotify.saved-tracks.save" : "[\"track-id\"]"
}
Example response
{
  "spotify.saved-tracks.save.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kSaveTracksForCurrentUserJsonValue[] = "[\"track-id\"]";

void sendSpotifySaveTracksForCurrentUserJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.saved.save", kSaveTracksForCurrentUserJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.saved.save request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.save");
}

void handleSpotifySaveTracksForCurrentUserJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.save.response payload");
    return;
  }

  if (kSaveTracksForCurrentUserJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveTracksForCurrentUserJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kSaveTracksForCurrentUserCborValue[] = "[\"track-id\"]";

void sendSpotifySaveTracksForCurrentUserCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.saved.save", kSaveTracksForCurrentUserCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.saved.save request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.save");
}

void handleSpotifySaveTracksForCurrentUserCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.saved.save.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.save response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.save.response payload");
    return;
  }

  if (kSaveTracksForCurrentUserCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kSaveTracksForCurrentUserCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-tracks.remove removes saved tracks.

Request
{
  "spotify.saved-tracks.remove" : "[\"track-id\"]"
}
Example response
{
  "spotify.saved-tracks.remove.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kRemoveUsersSavedTracksJsonValue[] = "[\"track-id\"]";

void sendSpotifyRemoveUsersSavedTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.saved.remove", kRemoveUsersSavedTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.saved.remove request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.remove");
}

void handleSpotifyRemoveUsersSavedTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kRemoveUsersSavedTracksCborValue[] = "[\"track-id\"]";

void sendSpotifyRemoveUsersSavedTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.saved.remove", kRemoveUsersSavedTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.saved.remove request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.remove");
}

void handleSpotifyRemoveUsersSavedTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.saved.remove.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.remove response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.remove.response payload");
    return;
  }

  if (kRemoveUsersSavedTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kRemoveUsersSavedTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.saved-tracks.check checks whether tracks are saved.

Request
{
  "spotify.saved-tracks.check" : "ids=track-id"
}
Example response
{
  "spotify.saved-tracks.check.response" : {
    "result" : [ true ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckUsersSavedTracksJsonValue[] = "ids=track-id";

void sendSpotifyCheckUsersSavedTracksJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.saved.check", kCheckUsersSavedTracksJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.saved.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.check");
}

void handleSpotifyCheckUsersSavedTracksJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedTracksJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedTracksJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckUsersSavedTracksCborValue[] = "ids=track-id";

void sendSpotifyCheckUsersSavedTracksCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.saved.check", kCheckUsersSavedTracksCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.saved.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.saved.check");
}

void handleSpotifyCheckUsersSavedTracksCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.saved.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.saved.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.saved.check.response payload");
    return;
  }

  if (kCheckUsersSavedTracksCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckUsersSavedTracksCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.audio-features.get returns audio features for one track.

Request
{
  "spotify.audio-features.get" : "track-id"
}
Example response
{
  "spotify.audio-features.get.response" : {
    "result" : {
      "id" : "track-id",
      "tempo" : 123.0
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetTracksAudioFeaturesJsonValue[] = "track-id";

void sendSpotifyGetTracksAudioFeaturesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.audio-features.get", kGetTracksAudioFeaturesJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.audio-features.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-features.get");
}

void handleSpotifyGetTracksAudioFeaturesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.audio-features.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-features.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-features.get.response payload");
    return;
  }

  if (kGetTracksAudioFeaturesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTracksAudioFeaturesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetTracksAudioFeaturesCborValue[] = "track-id";

void sendSpotifyGetTracksAudioFeaturesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.audio-features.get", kGetTracksAudioFeaturesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.audio-features.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-features.get");
}

void handleSpotifyGetTracksAudioFeaturesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.audio-features.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-features.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-features.get.response payload");
    return;
  }

  if (kGetTracksAudioFeaturesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTracksAudioFeaturesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.audio-features.get-many returns audio features for supplied tracks.

Request
{
  "spotify.audio-features.get-many" : "ids=track-1,track-2"
}
Example response
{
  "spotify.audio-features.get-many.response" : {
    "result" : {
      "audio_features" : [ {
        "id" : "track-1",
        "tempo" : 120.0
      }, {
        "id" : "track-2",
        "tempo" : 128.0
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetSeveralTracksAudioFeaturesJsonValue[] = "ids=track-1,track-2";

void sendSpotifyGetSeveralTracksAudioFeaturesJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.audio-features.get.several", kGetSeveralTracksAudioFeaturesJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.audio-features.get.several request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-features.get.several");
}

void handleSpotifyGetSeveralTracksAudioFeaturesJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.audio-features.get.several.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-features.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-features.get.several.response payload");
    return;
  }

  if (kGetSeveralTracksAudioFeaturesJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralTracksAudioFeaturesJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetSeveralTracksAudioFeaturesCborValue[] = "ids=track-1,track-2";

void sendSpotifyGetSeveralTracksAudioFeaturesCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.audio-features.get.several", kGetSeveralTracksAudioFeaturesCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.audio-features.get.several request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-features.get.several");
}

void handleSpotifyGetSeveralTracksAudioFeaturesCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.audio-features.get.several.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-features.get.several response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-features.get.several.response payload");
    return;
  }

  if (kGetSeveralTracksAudioFeaturesCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetSeveralTracksAudioFeaturesCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.audio-analysis.get returns audio analysis for a track.

Request
{
  "spotify.audio-analysis.get" : "track-id"
}
Example response
{
  "endpointId" : "test-endpoint-id",
  "spotify.audio-analysis.get.response" : {
    "result" : {
      "bars" : [ ],
      "beats" : [ ],
      "sections" : [ ],
      "segments" : [ ],
      "tatums" : [ ]
    }
  }
}
JSON example
constexpr char kGetTracksAudioAnalysisJsonValue[] = "track-id";

void sendSpotifyGetTracksAudioAnalysisJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.audio-analysis.get", kGetTracksAudioAnalysisJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.audio-analysis.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-analysis.get");
}

void handleSpotifyGetTracksAudioAnalysisJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.audio-analysis.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-analysis.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-analysis.get.response payload");
    return;
  }

  if (kGetTracksAudioAnalysisJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTracksAudioAnalysisJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetTracksAudioAnalysisCborValue[] = "track-id";

void sendSpotifyGetTracksAudioAnalysisCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.audio-analysis.get", kGetTracksAudioAnalysisCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.audio-analysis.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.audio-analysis.get");
}

void handleSpotifyGetTracksAudioAnalysisCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.audio-analysis.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.audio-analysis.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.audio-analysis.get.response payload");
    return;
  }

  if (kGetTracksAudioAnalysisCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetTracksAudioAnalysisCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.recommendations.list returns track recommendations.

Request
{
  "spotify.recommendations.list" : "seed_tracks=track-1"
}
Example response
{
  "spotify.recommendations.list.response" : {
    "result" : {
      "seeds" : [ ],
      "tracks" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-1",
        "name" : "First Track"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetRecommendationsJsonValue[] = "seed_tracks=track-1";

void sendSpotifyGetRecommendationsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.tracks.recommendations", kGetRecommendationsJsonValue, body)) {
    Serial.println("Unable to build spotify.tracks.recommendations request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.tracks.recommendations");
}

void handleSpotifyGetRecommendationsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<6144> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.tracks.recommendations.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.recommendations response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.recommendations.response payload");
    return;
  }

  if (kGetRecommendationsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetRecommendationsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetRecommendationsCborValue[] = "seed_tracks=track-1";

void sendSpotifyGetRecommendationsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.tracks.recommendations", kGetRecommendationsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.tracks.recommendations request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.tracks.recommendations");
}

void handleSpotifyGetRecommendationsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(6144);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.tracks.recommendations.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.tracks.recommendations response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.tracks.recommendations.response payload");
    return;
  }

  if (kGetRecommendationsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetRecommendationsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Users

spotify.users.get-current returns the current user’s profile.

Request
{
  "spotify.users.get-current" : ""
}
Example response
{
  "spotify.users.get-current.response" : {
    "result" : {
      "display_name" : "Sample User",
      "id" : "user-id",
      "images" : [ ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetCurrentUsersProfileJsonValue[] = "";

void sendSpotifyGetCurrentUsersProfileJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.current.get", kGetCurrentUsersProfileJsonValue, body)) {
    Serial.println("Unable to build spotify.users.current.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.current.get");
}

void handleSpotifyGetCurrentUsersProfileJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.current.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.current.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.current.get.response payload");
    return;
  }

  if (kGetCurrentUsersProfileJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCurrentUsersProfileJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetCurrentUsersProfileCborValue[] = "";

void sendSpotifyGetCurrentUsersProfileCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.current.get", kGetCurrentUsersProfileCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.current.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.current.get");
}

void handleSpotifyGetCurrentUsersProfileCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.current.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.current.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.current.get.response payload");
    return;
  }

  if (kGetCurrentUsersProfileCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetCurrentUsersProfileCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.users.get returns a user’s public profile.

Request
{
  "spotify.users.get" : "user-id"
}
Example response
{
  "spotify.users.get.response" : {
    "result" : {
      "display_name" : "Public User",
      "id" : "user-id",
      "images" : [ ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersProfileJsonValue[] = "user-id";

void sendSpotifyGetUsersProfileJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.get", kGetUsersProfileJsonValue, body)) {
    Serial.println("Unable to build spotify.users.get request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.get");
}

void handleSpotifyGetUsersProfileJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.get.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.get response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.get.response payload");
    return;
  }

  if (kGetUsersProfileJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersProfileJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersProfileCborValue[] = "user-id";

void sendSpotifyGetUsersProfileCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.get", kGetUsersProfileCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.get request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.get");
}

void handleSpotifyGetUsersProfileCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.get.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.get response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.get.response payload");
    return;
  }

  if (kGetUsersProfileCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersProfileCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.users.list-top-items returns the current user’s top items.

Request
{
  "spotify.users.list-top-items" : "tracks?limit=5"
}
Example response
{
  "spotify.users.list-top-items.response" : {
    "result" : {
      "href" : null,
      "limit" : null,
      "next" : null,
      "offset" : null,
      "previous" : null,
      "total" : null,
      "items" : [ {
        "album" : {
          "album_type" : null,
          "total_tracks" : null,
          "available_markets" : [ ],
          "external_urls" : null,
          "href" : null,
          "id" : "album-id",
          "images" : [ ],
          "name" : "Sample Album",
          "release_date" : null,
          "release_date_precision" : null,
          "type" : null,
          "uri" : null,
          "artists" : [ ]
        },
        "artists" : [ {
          "id" : "artist-id",
          "name" : "Sample Artist"
        } ],
        "available_markets" : [ ],
        "id" : "track-id",
        "name" : "Top Track"
      } ]
    }
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kGetUsersTopItemsJsonValue[] = "tracks?limit=5";

void sendSpotifyGetUsersTopItemsJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.top-items", kGetUsersTopItemsJsonValue, body)) {
    Serial.println("Unable to build spotify.users.top-items request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.top-items");
}

void handleSpotifyGetUsersTopItemsJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.top-items.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.top-items response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.top-items.response payload");
    return;
  }

  if (kGetUsersTopItemsJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersTopItemsJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kGetUsersTopItemsCborValue[] = "tracks?limit=5";

void sendSpotifyGetUsersTopItemsCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.top-items", kGetUsersTopItemsCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.top-items request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.top-items");
}

void handleSpotifyGetUsersTopItemsCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.top-items.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.top-items response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.top-items.response payload");
    return;
  }

  if (kGetUsersTopItemsCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kGetUsersTopItemsCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.users.follow follows artists or users.

Request
{
  "spotify.users.follow" : "type=artist&ids=artist-id"
}
Example response
{
  "spotify.users.follow.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kFollowArtistsOrUsersJsonValue[] = "type=artist&ids=artist-id";

void sendSpotifyFollowArtistsOrUsersJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.follow", kFollowArtistsOrUsersJsonValue, body)) {
    Serial.println("Unable to build spotify.users.follow request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.follow");
}

void handleSpotifyFollowArtistsOrUsersJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.follow.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.follow response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.follow.response payload");
    return;
  }

  if (kFollowArtistsOrUsersJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kFollowArtistsOrUsersJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kFollowArtistsOrUsersCborValue[] = "type=artist&ids=artist-id";

void sendSpotifyFollowArtistsOrUsersCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.follow", kFollowArtistsOrUsersCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.follow request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.follow");
}

void handleSpotifyFollowArtistsOrUsersCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.follow.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.follow response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.follow.response payload");
    return;
  }

  if (kFollowArtistsOrUsersCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kFollowArtistsOrUsersCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.users.unfollow unfollows artists or users.

Request
{
  "spotify.users.unfollow" : "type=user&ids=user-id"
}
Example response
{
  "spotify.users.unfollow.response" : {
    "result" : "OK"
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kUnfollowArtistsOrUsersJsonValue[] = "type=user&ids=user-id";

void sendSpotifyUnfollowArtistsOrUsersJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.unfollow", kUnfollowArtistsOrUsersJsonValue, body)) {
    Serial.println("Unable to build spotify.users.unfollow request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.unfollow");
}

void handleSpotifyUnfollowArtistsOrUsersJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.unfollow.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.unfollow response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.unfollow.response payload");
    return;
  }

  if (kUnfollowArtistsOrUsersJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUnfollowArtistsOrUsersJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kUnfollowArtistsOrUsersCborValue[] = "type=user&ids=user-id";

void sendSpotifyUnfollowArtistsOrUsersCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.unfollow", kUnfollowArtistsOrUsersCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.unfollow request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.unfollow");
}

void handleSpotifyUnfollowArtistsOrUsersCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.unfollow.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.unfollow response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.unfollow.response payload");
    return;
  }

  if (kUnfollowArtistsOrUsersCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kUnfollowArtistsOrUsersCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

spotify.users.check-following checks whether artists or users are followed.

Request
{
  "spotify.users.check-following" : "type=artist&ids=artist-id"
}
Example response
{
  "spotify.users.check-following.response" : {
    "result" : [ false ]
  },
  "endpointId" : "test-endpoint-id"
}
JSON example
constexpr char kCheckIfUserFollowsArtistsOrUsersJsonValue[] = "type=artist&ids=artist-id";

void sendSpotifyCheckIfUserFollowsArtistsOrUsersJson(WebSocketsClient& socket) {
  String body;
  if (!offbeat::spotify::examples::json::buildRequest("spotify.users.follow.check", kCheckIfUserFollowsArtistsOrUsersJsonValue, body)) {
    Serial.println("Unable to build spotify.users.follow.check request");
    return;
  }

  socket.sendTXT(body);
  Serial.println("REQUEST_SENT=spotify.users.follow.check");
}

void handleSpotifyCheckIfUserFollowsArtistsOrUsersJsonResponse(uint8_t* payload, size_t length) {
  StaticJsonDocument<4096> document;
  JsonObjectConst root;
  JsonVariantConst result;
  offbeat::spotify::examples::json::ParseStatus status =
      offbeat::spotify::examples::json::parseResult(
          payload, length, document, "spotify.users.follow.check.response", root, result);

  if (status == offbeat::spotify::examples::json::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.follow.check response");
    return;
  }

  if (status == offbeat::spotify::examples::json::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.follow.check.response payload");
    return;
  }

  if (kCheckIfUserFollowsArtistsOrUsersJsonValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckIfUserFollowsArtistsOrUsersJsonValue);
  }
  offbeat::spotify::examples::json::printSummary(result);
  offbeat::spotify::examples::json::printEndpoint(root);
  Serial.println("TEST:PASS");
}
CBOR example
constexpr char kCheckIfUserFollowsArtistsOrUsersCborValue[] = "type=artist&ids=artist-id";

void sendSpotifyCheckIfUserFollowsArtistsOrUsersCbor(WebSocketsClient& socket) {
  uint8_t encoded[768];
  size_t encodedLength = offbeat::spotify::examples::cbor::buildRequest("spotify.users.follow.check", kCheckIfUserFollowsArtistsOrUsersCborValue, encoded, sizeof(encoded));
  if (encodedLength == 0) {
    Serial.println("Unable to build spotify.users.follow.check request");
    return;
  }

  socket.sendBIN(encoded, encodedLength);
  Serial.println("REQUEST_SENT=spotify.users.follow.check");
}

void handleSpotifyCheckIfUserFollowsArtistsOrUsersCborResponse(uint8_t* payload, size_t length) {
  CborBuffer buffer(4096);
  cn_cbor* root = NULL;
  cn_cbor* result = NULL;
  offbeat::spotify::examples::cbor::ParseStatus status =
      offbeat::spotify::examples::cbor::parseResult(
          payload, length, buffer, "spotify.users.follow.check.response", root, result);

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kInvalidPayload) {
    Serial.println("Unable to parse spotify.users.follow.check response");
    return;
  }

  if (status == offbeat::spotify::examples::cbor::ParseStatus::kMissingResponse) {
    Serial.println("No spotify.users.follow.check.response payload");
    return;
  }

  if (kCheckIfUserFollowsArtistsOrUsersCborValue[0] != '\0') {
    Serial.print("Request value: ");
    Serial.println(kCheckIfUserFollowsArtistsOrUsersCborValue);
  }
  offbeat::spotify::examples::cbor::printSummary(result);
  offbeat::spotify::examples::cbor::printEndpoint(root);
  Serial.println("TEST:PASS");
}

== Deprecated commands

Deprecated aliases remain available during the compatibility window. Their responses retain the deprecated response key. Each alias has generated REST Docs snippets named after the normalized alias plus -deprecated; migrate to the canonical command listed in its context above.

Examples