status: spotify_error, the last Spotify session error and when it happened

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 19:59:40 +10:00
parent 2e136ddb8c
commit 66e72cebcc
4 changed files with 19 additions and 1 deletions
+2
View File
@@ -58,6 +58,8 @@ typedef struct {
bool spotify_now_playing(spotify_track_t *t); bool spotify_now_playing(spotify_track_t *t);
// Current state for status.spotify_state. // Current state for status.spotify_state.
const char *spotify_state(void); const char *spotify_state(void);
// Last session error (exception text and uptime) for status.spotify_error. False if none yet.
bool spotify_last_error(char *out, size_t len);
#ifdef __cplusplus #ifdef __cplusplus
} }
+12
View File
@@ -16,6 +16,7 @@
#include "SpircHandler.h" #include "SpircHandler.h"
#include "TrackPlayer.h" #include "TrackPlayer.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "mdns.h" #include "mdns.h"
@@ -39,6 +40,8 @@ static std::atomic<bool> s_got_blob{false};
static std::atomic<bool> s_in_session{false}; // session() running (its tasks may exist) static std::atomic<bool> s_in_session{false}; // session() running (its tasks may exist)
static std::atomic<int> s_volume{65535}; // player volume in Spotify units (0..65535) static std::atomic<int> s_volume{65535}; // player volume in Spotify units (0..65535)
static std::atomic<const char *> s_state{"disabled"}; static std::atomic<const char *> s_state{"disabled"};
static std::mutex s_error_mutex;
static std::string s_error; // last session error, kept for the status
static std::atomic<uint64_t> s_pcm_bytes{0}; static std::atomic<uint64_t> s_pcm_bytes{0};
static std::shared_ptr<cspot::SpircHandler> s_handler; // running session, else null static std::shared_ptr<cspot::SpircHandler> s_handler; // running session, else null
static spotify_pcm_cb_t s_pcm_cb; static spotify_pcm_cb_t s_pcm_cb;
@@ -230,6 +233,13 @@ const char *spotify_state(void)
return s_state.load(); return s_state.load();
} }
bool spotify_last_error(char *out, size_t len)
{
std::lock_guard<std::mutex> lock(s_error_mutex);
snprintf(out, len, "%s", s_error.c_str());
return !s_error.empty();
}
/* ----- cspot/bell logging -> esp_log (UART + syslog) ----- */ /* ----- cspot/bell logging -> esp_log (UART + syslog) ----- */
// cspot logs through bell::bellGlobalLogger; without one, CSPOT_LOG dereferences NULL. // cspot logs through bell::bellGlobalLogger; without one, CSPOT_LOG dereferences NULL.
@@ -494,6 +504,8 @@ static void spotify_task(void *arg)
} catch (const std::exception &e) { } catch (const std::exception &e) {
s_state = "error"; s_state = "error";
ESP_LOGE(TAG, "session ended: %s", e.what()); ESP_LOGE(TAG, "session ended: %s", e.what());
std::lock_guard<std::mutex> lock(s_error_mutex);
s_error = std::string(e.what()) + " (at " + std::to_string(esp_timer_get_time() / 1000000) + " s)";
} }
s_handler.reset(); s_handler.reset();
s_in_session = false; s_in_session = false;
+1 -1
View File
@@ -33,7 +33,7 @@ source (cspot Spotify Connect | HLS player) -> decode (Vorbis/AAC/MP3) -> SRC 44
## Project config group ## Project config group
- source: {mode: spotify|hls|auto|tone|pink|off, spotify_name, spotify_bitrate, hls_url, autoplay, gain_db, failover_delay_s, failover_on_pause, spotify_client_id, spotify_client_secret} - source: {mode: spotify|hls|auto|tone|pink|off, spotify_name, spotify_bitrate, hls_url, autoplay, gain_db, failover_delay_s, failover_on_pause, spotify_client_id, spotify_client_secret}
- spotify_client_id / spotify_client_secret: each user's own Spotify developer app (developer.spotify.com, Premium account), needed by the maintained cspot fork (philippe44/cspot) since Spotify's 2025 API restrictions. The secret is write-only (never returned by GET /api/config). spotify_state reports "no client credentials" while either is missing. - spotify_client_id / spotify_client_secret: each user's own Spotify developer app (developer.spotify.com, Premium account), needed by the maintained cspot fork (philippe44/cspot) since Spotify's 2025 API restrictions. The secret is write-only (never returned by GET /api/config). spotify_state reports "no client credentials" while either is missing.
- Project status fields: active_source, source_state, spotify_state, buffer_ms - Project status fields: active_source, source_state, spotify_state, buffer_ms; spotify_error (last session error with the uptime it happened at, e.g. "Can't connect to spotify servers (at 412 s)"; only present after one)
- mode "tone": the core's 1 kHz / -18 dBFS test tone, phase-locked to PTP (commissioning, e.g. Riedel import tests). "pink": the core's pink noise, -18 dBFS RMS (peaks about -6 dBFS), same on L and R (level/EQ checks). Neither is affected by volume or gain. "off": silence. - mode "tone": the core's 1 kHz / -18 dBFS test tone, phase-locked to PTP (commissioning, e.g. Riedel import tests). "pink": the core's pink noise, -18 dBFS RMS (peaks about -6 dBFS), same on L and R (level/EQ checks). Neither is affected by volume or gain. "off": silence.
## Player control API (project routes) ## Player control API (project routes)
+4
View File
@@ -390,6 +390,10 @@ static void player_status(cJSON *st)
cJSON_AddStringToObject(st, "active_source", SRC_NAME[src]); cJSON_AddStringToObject(st, "active_source", SRC_NAME[src]);
cJSON_AddStringToObject(st, "source_state", state); cJSON_AddStringToObject(st, "source_state", state);
cJSON_AddStringToObject(st, "spotify_state", spotify_state()); cJSON_AddStringToObject(st, "spotify_state", spotify_state());
char err[96];
if (spotify_last_error(err, sizeof(err))) {
cJSON_AddStringToObject(st, "spotify_error", err);
}
cJSON_AddNumberToObject(st, "buffer_ms", (double)(audio_ring_level() * 1000 / RATE)); cJSON_AddNumberToObject(st, "buffer_ms", (double)(audio_ring_level() * 1000 / RATE));
} }