9 Commits

5 changed files with 83 additions and 7 deletions

View File

@@ -14,6 +14,12 @@ via the constant operation_mode. Valid choices are:
- 0: interconnected (HTTP API and WiFi, no buttons); useful for bigger projects
- 1: standalone (Buttons, no WiFi and HTTP API); useful if you want to build a small player
Another two "operating modes" have been introduced as sometimes you want to change between the above
operation modes without having to recompile everything.
- 2: interconnected first (if any button is pressed at startup, while the led is blinking, the standalone mode is chosen. otherwise, interconnected mode is used)
- 3: standalone first (just the reverse of mode 2)
Any questions? Hopefully a look into the **Wiki** will help.
## API demo
@@ -78,6 +84,7 @@ Features, already implemented, or still in progress for the v1.0.0 release!
- [X] /api/v1/system/restore_state/{on,off,get}
- [X] /api/v1/system/restore_playing/{on,off,get}
- [X] /api/v1/system/tell_address/{on,off,get}
- [X] /api/v1/system/tell_address/lang/{en,de,get}
- [X] /api/v1/system/version
- [X] /api/v1/system/wifi/change
- [X] /api/v1/system/wifi/get_ssid

View File

@@ -29,9 +29,9 @@ For more information, please refer to <http://unlicense.org/>
// define all constants
const String version_tag = "v0.4.1-dev"; // version tag (with -dev appendix for dev versions)
const String version_tag = "v0.4.2-dev"; // version tag (with -dev appendix for dev versions)
const String version = "NetSpeaker " + version_tag; // version string used e.g. for access point ssid when wifi couldn't connect
const int operation_mode = 3; // 0: interconnected (no buttons, but api and wifi); 1: standalone (no wifi; no api);
const int operation_mode = 0; // 0: interconnected (no buttons, but api and wifi); 1: standalone (no wifi; no api);
// 2: choose by pressing any button at startup (not pressed: interconnected; pressed: standalone)
// 3: same as 2 but if not pressed: standalone; pressed: interconnected
const int SD_CS = 5; // BOARD SPECIFIC
@@ -75,7 +75,8 @@ const char PREFERENCES_KEY_PLAYING[] = "playing"; // prefe
const char PREFERENCES_KEY_MUTED[] = "muted"; // preferences key name for info if currently muted
const char PREFERENCES_KEY_PLAYLIST_PATH[] = "playlist"; // preferences key name for playlist path (for state restore)
const char PREFERENCES_KEY_PLAYLIST_INDEX[] = "pl-index"; // preferences key name for current playlist index (for state restore)
const char PREFERENCES_KEY_TELL_ADDRESS_AT_STARTUP[] = "tell_address"; // preferences key name for current playlist index (for state restore)
const char PREFERENCES_KEY_TELL_ADDRESS_AT_STARTUP[] = "tell_address"; // preferences key name for turning the telling of the NetSpeaker at startup on or off
const char PREFERENCES_KEY_TELL_ADDRESS_LANG[] = "tellAddressLang"; // preferences key name to determine what the language of the address telling at startup should be (valid: en, de)
// all other variables needed
Audio audio; // Audio object (for playing audio, decoding mp3, ...)
@@ -242,6 +243,7 @@ void setup() {
}
void loop() {
if (operation_mode == 0 || operationModeChosen == 0) { // things only need to be done if in interconnected mode
setupWiFi(); // if connection was lost

View File

@@ -30,9 +30,34 @@ void setupAudio() {
void tellAddressInfo() {
// TODO: make this Preference accessible via the Web API
if(configuration.getBool(PREFERENCES_KEY_TELL_ADDRESS_AT_STARTUP, true) && apON == false) { // only tell the name if the key is set (do it by default) and connected to wifi (for internet)
String tell_address_string = "Connected. Head over to " + WiFi.localIP().toString() + " or " + configuration.getString(PREFERENCES_KEY_NETWORK_NAME, default_network_name) + " dot local";
String tellAddressLang = configuration.getString(PREFERENCES_KEY_TELL_ADDRESS_LANG, "en");
String tell_address_string;
IPAddress ip = WiFi.localIP();
if(tellAddressLang == "en") {
tell_address_string = "Connected. Head over to ";
tell_address_string += configuration.getString(PREFERENCES_KEY_NETWORK_NAME, default_network_name);
tell_address_string += " dot local.";
tell_address_string += " Or "; // this is needed for a little gap between the hostname and ip telling
tell_address_string += String(ip[0]) + " dot ";
tell_address_string += String(ip[1]) + " dot ";
tell_address_string += String(ip[2]) + " dot ";
tell_address_string += String(ip[3]);
} else if (tellAddressLang == "de") {
tell_address_string = "Verbunden. Gehe zu ";
tell_address_string += configuration.getString(PREFERENCES_KEY_NETWORK_NAME, default_network_name);
tell_address_string += " punkt local.";
tell_address_string += " Oder "; // this is needed for a little gap between the hostname and ip telling
tell_address_string += String(ip[0]) + " punkt ";
tell_address_string += String(ip[1]) + " punkt ";
tell_address_string += String(ip[2]) + " punkt ";
tell_address_string += String(ip[3]);
} else {
Serial.println(F("[FATAL] PLEASE CHOOSE A VALID TELL_STRING LANGUAGE (valid options: en; de). SLEEPING FOREVER."));
indicate_system_error(); // halt system and blink readyPin
}
Serial.printf("[SETUP] Telling address now. String: %s\n", tell_address_string);
audio.connecttospeech(tell_address_string.c_str(), "en");
audio.connecttospeech(tell_address_string.c_str(), tellAddressLang.c_str());
wait_after_eof_speech = true; // to prevent the eof_speech handler of the Audio.h library (defined below) to play next audio (as there's none at startup!)
while(!eof_speech_reached) { audio.loop(); } // wait till end of speech
wait_after_eof_speech = false; // turn calling nextAudio on again (the default behaviour)

View File

@@ -46,6 +46,21 @@ bool isStringConvertable2Int(String toIntStr) {
}
return true;
}
bool isValidNetworkName(String to_validate) {
// if too long
if (to_validate.length() > 63) return false; // see here under labels: https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4
// check if it only contains a-z, A-Z, 0-9 and -
char currentChar;
for (int i = 0; i < to_validate.length(); i++) {
currentChar = (char)to_validate[i];
if (!isAlphaNumeric(currentChar) && currentChar != '-') {
return false; // if not, return false
}
}
// then return true
return true;
}
/* ----------------------
---- API FUNCTIONS ----
@@ -464,14 +479,16 @@ void api_v1_system_networkname_change() {
if (api_server.argName(i) == "network_name") {
arg_given = true;
newNetworkName = api_server.arg(i);
break;
}
}
// define what to do if nothing really changed (or no argument was given)
if (newNetworkName == currentNetworkName || !arg_given) {
if (!isValidNetworkName(newNetworkName) || !arg_given) {
send_api_json(false, "\"network_name\": \"" + currentNetworkName + "\", \"apply_changes\": false"); // return with no success ("false")
return;
}
// if the network name hasn't changed, just return success (as no further action's required)
if (newNetworkName == currentNetworkName) send_api_json(true, "\"network_name\": \"" + newNetworkName + "\", \"apply_changes\": " + (apply_changes ? "true" : "false"));
configuration.putString(PREFERENCES_KEY_NETWORK_NAME, newNetworkName);
Serial.printf("[INFO] Changed network name from \"%s\" to \"%s\".\n", currentNetworkName, newNetworkName);
@@ -526,6 +543,8 @@ void api_v1_system_restore_playing() {
content += configuration.getBool(PREFERENCES_KEY_RESTORE_PLAYING, false) ? "true" : "false";
send_api_json(success, content); // generate json and send it
}
void api_v1_system_tell_address() {
String option = api_server.pathArg(0);
bool success = true;
@@ -548,6 +567,28 @@ void api_v1_system_tell_address() {
send_api_json(success, content); // generate json and send it
}
void api_v1_system_tell_address_lang() {
String option = api_server.pathArg(0);
bool success = true;
Serial.printf("[HTTP] [API] 200 - '/api/v1/system/tell_address/lang/%s'\n", option);
if (option == "get") {
// just here that calling .../get wont respond 404
} else if (option == "en") {
configuration.putString(PREFERENCES_KEY_TELL_ADDRESS_LANG, "en");
} else if (option == "de") {
configuration.putString(PREFERENCES_KEY_TELL_ADDRESS_LANG, "de");
} else {
success = false;
}
String content = "\"tell_address_lang\": \""; // prepare the http response
content += configuration.getString(PREFERENCES_KEY_TELL_ADDRESS_LANG, "en") + "\"";
send_api_json(success, content); // generate json and send it
}
void api_v1_system_version() {
Serial.printf("[HTTP] [API] 200 - '/api/v1/system/version'\n");
@@ -639,6 +680,7 @@ void setupWeb() {
api_server.on(UriBraces("/api/v1/system/restore_state/{}"), api_v1_system_restore_state);
api_server.on(UriBraces("/api/v1/system/restore_playing/{}"), api_v1_system_restore_playing);
api_server.on(UriBraces("/api/v1/system/tell_address/{}"), api_v1_system_tell_address);
api_server.on(UriBraces("/api/v1/system/tell_address/lang/{}"), api_v1_system_tell_address_lang);
api_server.on("/api/v1/system/version", api_v1_system_version);
api_server.on("/api/v1/system/wifi/change", api_v1_system_wifi_change);
api_server.on("/api/v1/system/wifi/get_ssid", api_v1_system_wifi_getssid);