Skip to content

Commit 342816e

Browse files
Batch of enhancements and fixes: optional user.ini support, filesystem check, improved Spotify messaging
1 parent 0b3c106 commit 342816e

File tree

13 files changed

+955
-170
lines changed

13 files changed

+955
-170
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.pio
22
.vscode
33
src/compile_time.h
4+
data/user.ini

data/user.ini.template

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
;
2+
; User settings for Spotify Controller. Make sure to Upload Filesystem
3+
; Image after updating.
4+
;
5+
; ----------------------------------------------------------------------
6+
; Privacy Levels:
7+
; 0 - None (credentials in clear text)
8+
; 1 - Good (encrypted, reusable across devices)
9+
; 2 - Better (encrypted, tied to this device)
10+
; ----------------------------------------------------------------------
11+
[vault]
12+
privacy_level = 0
13+
; ----------------------------------------------------------------------
14+
; WiFi:
15+
; ----------------------------------------------------------------------
16+
[wifi]
17+
ssid = Your WiFi Name Here
18+
password = Your WiFi Password Here
19+
; ----------------------------------------------------------------------
20+
; Spotify:
21+
; ----------------------------------------------------------------------
22+
[spotify]
23+
client_id = Enter your Spotify Client ID here
24+
client_secret = Enter your Spotify Client Secret here
25+
; ----------------------------------------------------------------------
26+
; System Settings:
27+
; Timezone format - see
28+
; https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
29+
; ----------------------------------------------------------------------
30+
[system]
31+
timezone = CST6CDT,M3.2.0,M11.1.0

src/SCFileIO.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,34 +63,36 @@ SCFileIO::~SCFileIO()
6363
**
6464
** ===================================================================
6565
*/
66-
void SCFileIO::initialize()
66+
bool SCFileIO::initialize()
6767
{
6868
if (!xSemaphoreFileIO)
6969
{
7070
xSemaphoreFileIO = xSemaphoreCreateMutex();
7171
if (!xSemaphoreFileIO)
7272
{
7373
spLogE(LOGTAG_FILEIO, "Failed to create semaphore!");
74-
return;
74+
return false;
7575
}
7676
}
7777

7878
if (!takeSemaphore())
7979
{
8080
spLogE(LOGTAG_FILEIO, "Failed to acquire semaphore for initialize().");
81-
return;
81+
return false;
8282
}
8383

8484
if (LittleFS.begin())
8585
{
8686
spLogI(LOGTAG_FILEIO, "Flash FS available!");
8787
giveSemaphore();
8888
listFiles();
89+
return true;
8990
}
9091
else
9192
{
9293
giveSemaphore();
9394
spLogE(LOGTAG_FILEIO, "Flash FS initialization failed!");
95+
return false;
9496
}
9597

9698

src/SCFileIO.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SCFileIO
3232
static SCFileIO& getInstance();
3333

3434
// Initializes the semaphore. Must be called before using the class.
35-
void initialize();
35+
bool initialize();
3636

3737
// Opens a file safely using LittleFS
3838
File open(const char *path, const char *mode);

src/SpotifyPlayer.cpp

Lines changed: 56 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -29,57 +29,6 @@
2929

3030
#include <TJpg_Decoder.h> // Ensure you include the required decoder library
3131

32-
// HTTP client setup remains as is in your project
33-
34-
int SpotifyPlayer::getAlbumReleaseYear()
35-
{
36-
// // if (!_pCurrentTrack->albumUri || strlen(_pCurrentTrack->albumUri) == 0)
37-
// if (_currentlyPlayingMetadata.albumUri == nullptr)
38-
// {
39-
// spLogE(LOGTAG_PLAYER, "Album URI is empty.");
40-
// return -1; // Indicate error
41-
// }
42-
43-
// // Extract album ID from albumUri (typically of the form spotify:album:<id>)
44-
// String albumId = String(_currentlyPlayingMetadata.albumUri).substring(14); // Skip "spotify:album:"
45-
46-
// String url = "https://api.spotify.com/v1/albums/" + albumId + "?market=US";
47-
48-
// HTTPClient http;
49-
// WiFiClientSecure wifiClient;
50-
// //wifiClient.setInsecure(); // TODO: Remove... just debugging...
51-
// wifiClient.setCACert(spotify_server_cert); // Ensure proper certificate
52-
53-
// http.begin(wifiClient, url);
54-
// http.addHeader("Authorization", "Bearer " + _spotifyRefreshToken); // Use your Spotify token
55-
56-
// int httpCode = http.GET();
57-
// if (httpCode != 200)
58-
// {
59-
// spLogE(LOGTAG_PLAYER, "Failed to get album details, HTTP code: %d", httpCode);
60-
// http.end();
61-
// return -1;
62-
// }
63-
64-
// // Parse JSON response
65-
// String payload = http.getString();
66-
// http.end();
67-
68-
// DynamicJsonDocument doc(1024);
69-
// deserializeJson(doc, payload);
70-
71-
// // Get release_date (e.g., "2020-05-01")
72-
// const char* releaseDate = doc["release_date"];
73-
// if (releaseDate && strlen(releaseDate) >= 4)
74-
// {
75-
// String year = String(releaseDate).substring(0, 4);
76-
// return year.toInt();
77-
// }
78-
79-
// // spLogE(LOGTAG_PLAYER, "Release date not found in album details.");
80-
return -1; // Indicate error
81-
}
82-
8332
// Spotify related
8433
#define SP_SPOTIFY_MARKET "IE"
8534

@@ -127,9 +76,21 @@ SpotifyPlayer& SpotifyPlayer::getInstance()
12776

12877
void SpotifyPlayer::initialize(QueueHandle_t *pScuiQueue)
12978
{
130-
// _pUI = pUI;
13179
_pScuiQueue = pScuiQueue;
132-
initSpotify();
80+
81+
// keep reference since SpotifyArduino is dependent on values
82+
// remaining in memory
83+
_spotifyClientId = Vault::getInstance().getSpotifyClientID();
84+
_spotifyClientSecret = Vault::getInstance().getSpotifyClientSecret();
85+
86+
_pSpotify = new SpotifyArduino(
87+
client,
88+
_spotifyClientId.c_str(),
89+
_spotifyClientSecret.c_str()
90+
);
91+
// client is defined in ThingPulse/spotify.h
92+
client.setCACert(spotify_server_cert);
93+
13394
}
13495

13596
/*
@@ -170,30 +131,48 @@ void SpotifyPlayer::startBackgroundRefreshes()
170131

171132
/*
172133
** ===================================================================
173-
** verifyRefreshToken()
134+
** isRefreshTokenAvailable()
174135
**
175-
** Returns true if previously saved token found; false otherwise.
136+
** Returns whether the refresh token is available. This will also
137+
** internally initialize the token if it exists.
176138
** ===================================================================
177139
*/
178-
bool SpotifyPlayer::verifyRefreshToken()
140+
bool SpotifyPlayer::isRefreshTokenAvailable()
179141
{
180-
_spotifyRefreshToken = SCFileIO::getInstance().readFsString(SPOTIFY_REFRESH_TOKEN_FILE_NAME);
181-
if (_spotifyRefreshToken == "")
182-
{
183-
spLogI(LOGTAG_PLAYER, "No Spotify refresh token found. Requesting one through the browser via auth code.");
142+
_spotifyRefreshToken = SCFileIO::getInstance().readFsString(SPOTIFY_REFRESH_TOKEN_FILE_NAME);
143+
144+
if (_spotifyRefreshToken == "")
145+
{
146+
spLogI(LOGTAG_PLAYER, "Spotify refresh token not found.");
147+
return false;
148+
}
149+
else
150+
{
151+
spLogI(LOGTAG_PLAYER, "Spotify refresh token found.");
152+
return true;
153+
}
154+
}
184155

185-
String spotifyAuthCode = fetchSpotifyAuthCode();
186-
_spotifyRefreshToken = spotify.requestAccessTokens(spotifyAuthCode.c_str(), SPOTIFY_REDIRECT_URI);
187-
SCFileIO::getInstance().saveFsString(SPOTIFY_REFRESH_TOKEN_FILE_NAME, _spotifyRefreshToken);
156+
/*
157+
** ===================================================================
158+
** requestRefreshToken()
159+
**
160+
** Requests a fresh refresh token. A refresh token is a security
161+
** credential that allows client applications to obtain new access
162+
** tokens without requiring users to reauthorize the application.
163+
** ===================================================================
164+
*/
165+
bool SpotifyPlayer::requestRefreshToken()
166+
{
188167

189-
return false;
168+
spLogI(LOGTAG_PLAYER, "Requesting Spotify refresh token through the browser via auth code.");
169+
170+
String spotifyAuthCode = fetchSpotifyAuthCode();
171+
_spotifyRefreshToken = _pSpotify->requestAccessTokens(spotifyAuthCode.c_str(), SPOTIFY_REDIRECT_URI);
172+
SCFileIO::getInstance().saveFsString(SPOTIFY_REFRESH_TOKEN_FILE_NAME, _spotifyRefreshToken);
173+
174+
return true;
190175

191-
}
192-
else
193-
{
194-
spLogI(LOGTAG_PLAYER, "Using previously saved Spotify refresh token.");
195-
return true;
196-
}
197176
}
198177

199178
/*
@@ -217,8 +196,8 @@ void SpotifyPlayer::login()
217196
// - keeps track of the refresh token and its TTL internally
218197
// - automatically renews the actual access token using the refresh token
219198
// -> see SpotifyArduino.h#autoTokenRefresh and SpotifyArduino::checkAndRefreshAccessToken() (called before every API function)
220-
spotify.setRefreshToken(_spotifyRefreshToken.c_str());
221-
spotify.refreshAccessToken();
199+
_pSpotify->setRefreshToken(_spotifyRefreshToken.c_str());
200+
_pSpotify->refreshAccessToken();
222201
spLogI(LOGTAG_PLAYER, "Authentication against Spotify done. Refresh token: %s", _spotifyRefreshToken.c_str());
223202
}
224203

@@ -273,7 +252,7 @@ void SpotifyPlayer::nextSong()
273252
static_cast<int>(TFTColor::SC_NetworkInProgress));
274253
if (xSemaphoreTake(_xSemaphoreNetwork, portMAX_DELAY))
275254
{
276-
if (spotify.nextTrack())
255+
if (_pSpotify->nextTrack())
277256
{
278257
//_pUI->drawStatusBox(TFTColor::SC_NetworkSuccess);
279258
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
@@ -316,7 +295,7 @@ void SpotifyPlayer::previousSong()
316295

317296
if (xSemaphoreTake(_xSemaphoreNetwork, portMAX_DELAY))
318297
{
319-
if (spotify.previousTrack())
298+
if (_pSpotify->previousTrack())
320299
{
321300
// _pUI->drawStatusBox(TFTColor::SC_NetworkSuccess);
322301
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
@@ -362,7 +341,7 @@ void SpotifyPlayer::pauseSong()
362341
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
363342
"Making Call",
364343
static_cast<int>(TFTColor::SC_NetworkInProgress));
365-
if (spotify.pause())
344+
if (_pSpotify->pause())
366345
{
367346
// _pUI->drawStatusBox(TFTColor::SC_NetworkSuccess);
368347
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
@@ -385,7 +364,7 @@ void SpotifyPlayer::pauseSong()
385364
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
386365
"Making Call",
387366
static_cast<int>(TFTColor::SC_NetworkInProgress));
388-
if (spotify.play())
367+
if (_pSpotify->play())
389368
{
390369
// _pUI->drawStatusBox(TFTColor::SC_NetworkSuccess);
391370
postScuiMessage(SCUIMessageType::UM_STATUS_BOX,
@@ -435,7 +414,7 @@ void SpotifyPlayer::refreshCurrentTrack()
435414
spLogI(LOGTAG_MULTITASK, "Invoking spotify.getCurrentlyPlaying(...)");
436415
// vTaskDelay(pdMS_TO_TICKS(200));
437416
Monitor::start(MONITOR_ID_SPOTIFY_GET_CURRENTLY_PLAYING, LOGTAG_METRICS, "spotify.getCurrentlyPlaying(...)");
438-
status = spotify.getCurrentlyPlaying(SpotifyPlayer::getCurrentlyPlayingCallback, SP_SPOTIFY_MARKET);
417+
status = _pSpotify->getCurrentlyPlaying(SpotifyPlayer::getCurrentlyPlayingCallback, SP_SPOTIFY_MARKET);
439418
Monitor::stop(MONITOR_ID_SPOTIFY_GET_CURRENTLY_PLAYING);
440419
xSemaphoreGive(_xSemaphoreNetwork);
441420
}

src/SpotifyPlayer.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@
2121
#include "PlayingMetadata.h"
2222
#include "scui.h"
2323

24+
// Forward declaration to avoid including full spotify.h in the header
25+
class SpotifyArduino;
2426
class SpotifyPlayer {
2527
public:
2628
// Public method to access the singleton instance
2729
static SpotifyPlayer& getInstance();
2830

2931
// initialization related
3032
void initialize(QueueHandle_t *pScuiQueue);
31-
bool verifyRefreshToken();
33+
bool isRefreshTokenAvailable();
34+
bool requestRefreshToken();
3235
String getNodeName();
3336
void login();
3437
void startBackgroundRefreshes();
@@ -63,6 +66,9 @@ class SpotifyPlayer {
6366
SemaphoreHandle_t _xSemaphoreNetwork = xSemaphoreCreateMutex();
6467
SemaphoreHandle_t _xSemaphoreDataCopy = xSemaphoreCreateMutex();
6568
TaskHandle_t _refreshTaskHandle;
69+
SpotifyArduino *_pSpotify = nullptr;
70+
String _spotifyClientId;
71+
String _spotifyClientSecret;
6672

6773
// Methods
6874

@@ -82,6 +88,5 @@ class SpotifyPlayer {
8288
void postScuiMessage(SCUIMessageType type, const String& str, int num);
8389

8490
void saveCache();
85-
int getAlbumReleaseYear();
8691

8792
};

src/ThingPulse/connectivity.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,24 @@
2020
#include <WiFi.h>
2121

2222
#include "connectivity.h"
23-
#include "../settings.h"
24-
#include "ThingPulse/util.h"
23+
#include "ThingPulse/util.h"
24+
#include "../Vault.h"
2525

2626
/*
2727
** ===================================================================
28-
** WiFi settings
28+
** startWiFi()
29+
** Attempt to connect to the configured WiFi network
2930
** ===================================================================
3031
*/
31-
const char *SSIDEnc = "SSID Goes Here";
32-
const char *WIFI_PWD_Enc = "WiFi Password Goes Here";
32+
void startWiFi() {
3333

34+
String ssid = Vault::getInstance().getSSID();
3435

35-
// Updated to use encrypted network credentials
36-
void startWiFi() {
37-
static const char k[] = "42";
38-
char decryptedSSID[50];
39-
char decryptedWIFI_PWD[50];
40-
simpleDecrypt(SSIDEnc, k, decryptedSSID);
41-
simpleDecrypt(WIFI_PWD_Enc, k , decryptedWIFI_PWD);
42-
WiFi.begin(decryptedSSID, decryptedWIFI_PWD);
43-
log_i("Connecting to WiFi '%s'...", decryptedSSID);
36+
// log_i("WiFi ssid/pw '%s' '%s'", ssid.c_str(), Vault::getInstance().getWiFiPassword().c_str());
37+
38+
WiFi.begin(ssid, Vault::getInstance().getWiFiPassword());
39+
40+
log_i("Connecting to WiFi '%s'...", ssid);
4441
while (WiFi.status() != WL_CONNECTED) {
4542
//log_i(".");
4643
Serial.print(".");

0 commit comments

Comments
 (0)