Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invoke "/rfid" endpoint #260

Merged
merged 3 commits into from
Aug 31, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 213 additions & 69 deletions src/Web.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ static void handleGetWiFiConfig(AsyncWebServerRequest *request);
static void handlePostWiFiConfig(AsyncWebServerRequest *request, JsonVariant &json);
static void handleCoverImageRequest(AsyncWebServerRequest *request);
static void handleWiFiScanRequest(AsyncWebServerRequest *request);
static void handleGetRFIDRequest(AsyncWebServerRequest *request);
static void handlePostRFIDRequest(AsyncWebServerRequest *request, JsonVariant &json);
static void handleDeleteRFIDRequest(AsyncWebServerRequest *request);
static void handleGetInfo(AsyncWebServerRequest *request);
static void handleGetSettings(AsyncWebServerRequest *request);
static void handlePostSettings(AsyncWebServerRequest *request, JsonVariant &json);


static bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile);

static void onWebsocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
static void settingsToJSON(JsonObject obj, String section);
static bool JSONToSettings(JsonObject obj);
Expand Down Expand Up @@ -155,6 +155,85 @@ class OneParamRewrite : public AsyncWebRewrite
};


// List all key in NVS for a given namespace
// callback function is called for every key with userdefined data object
bool listNVSKeys(const char *_namespace, void* data, bool (*callback)(const char * key, void* data)) {
Led_SetPause(true); // Workaround to prevent exceptions due to Neopixel-signalisation while NVS-write
esp_partition_iterator_t pi; // Iterator for find
const esp_partition_t *nvs; // Pointer to partition struct
esp_err_t result = ESP_OK;
const char *partname = "nvs";
uint8_t pagenr = 0; // Page number in NVS
uint8_t i; // Index in Entry 0..125
uint8_t bm; // Bitmap for an entry
uint32_t offset = 0; // Offset in nvs partition
uint8_t namespace_ID; // Namespace ID found

pi = esp_partition_find(ESP_PARTITION_TYPE_DATA, // Get partition iterator for
ESP_PARTITION_SUBTYPE_ANY, // this partition
partname);
if (pi) {
nvs = esp_partition_get(pi); // Get partition struct
esp_partition_iterator_release(pi); // Release the iterator
Log_Printf(LOGLEVEL_DEBUG, "Partition %s found, %d bytes", partname, nvs->size);
} else {
Log_Printf(LOGLEVEL_ERROR, "Partition %s not found!", partname);
return NULL;
}
namespace_ID = FindNsID(nvs, _namespace); // Find ID of our namespace in NVS
while (offset < nvs->size) {
result = esp_partition_read(nvs, offset, // Read 1 page in nvs partition
&buf,
sizeof(nvs_page));
if (result != ESP_OK) {
Log_Println("Error reading NVS!", LOGLEVEL_ERROR);
return false;
}

i = 0;

while (i < 126) {
bm = (buf.Bitmap[i / 4] >> ((i % 4) * 2)) & 0x03; // Get bitmap for this entry
if (bm == 2) {
if ((namespace_ID == 0xFF) || // Show all if ID = 0xFF
(buf.Entry[i].Ns == namespace_ID)) { // otherwise just my namespace
if (isNumber(buf.Entry[i].Key)) {
if (!callback(buf.Entry[i].Key, data)) {
return false;
}
}
}
i += buf.Entry[i].Span; // Next entry
} else {
i++;
}
}
offset += sizeof(nvs_page); // Prepare to read next page in nvs
pagenr++;
}
Led_SetPause(false);

return true;
}

// callback for writing a NVS entry to file
bool DumpNvsToSdCallback(const char * key, void* data) {
String s = gPrefsRfid.getString(key);
File *file = (File *) data;
file->printf("%s%s%s%s\n", stringOuterDelimiter, key, stringOuterDelimiter, s.c_str());
return true;
}

// Dumps all RFID-entries from NVS into a file on SD-card
bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile) {
File file = gFSystem.open(_destFile, FILE_WRITE);
if (!file) {
return false;
}
bool success = listNVSKeys(_namespace, &file, DumpNvsToSdCallback);
file.close();
return success;
}

// First request will return 0 results unless you start scan from somewhere else (loop/setup)
// Do not request more often than 3-5 seconds
Expand Down Expand Up @@ -335,6 +414,11 @@ void webserverStart(void) {
System_UpdateActivityTimer();
});

// RFID
wServer.on("/rfid", HTTP_GET, handleGetRFIDRequest);
wServer.addHandler(new AsyncCallbackJsonWebHandler("/rfid", handlePostRFIDRequest));
wServer.addRewrite(new OneParamRewrite("/rfid/{id}", "/rfid?id={id}") );
wServer.on("/rfid", HTTP_DELETE, handleDeleteRFIDRequest);

// WiFi scan
wServer.on("/wifiscan", HTTP_GET, handleWiFiScanRequest);
Expand Down Expand Up @@ -1490,6 +1574,132 @@ void handlePostWiFiConfig(AsyncWebServerRequest *request, JsonVariant &json){
}
}

static bool tagIdToJSON(String tagId, JsonObject entry) {
String s = gPrefsRfid.getString(tagId.c_str(), "-1"); // Try to lookup rfidId in NVS
if (!s.compareTo("-1")) {
return false;
}
char _file[255];
uint32_t _lastPlayPos = 0;
uint16_t _trackLastPlayed = 0;
uint32_t _mode = 1;
char *token;
uint8_t i = 1;
token = strtok((char *)s.c_str(), stringDelimiter);
while (token != NULL) { // Try to extract data from string after lookup
if (i == 1) {
strncpy(_file, token, sizeof(_file) / sizeof(_file[0]));
} else if (i == 2) {
_lastPlayPos = strtoul(token, NULL, 10);
} else if (i == 3) {
_mode = strtoul(token, NULL, 10);
} else if (i == 4) {
_trackLastPlayed = strtoul(token, NULL, 10);
}
i++;
token = strtok(NULL, stringDelimiter);
}
entry["id"] = tagId;
if (_mode >= 100) {
entry["modId"] = _mode;
} else {
entry["fileOrUrl"] = _file;
entry["playMode"] = _mode;
entry["lastPlayPos"] = _lastPlayPos;
entry["trackLastPlayed"] = _trackLastPlayed;
}
return true;
}

// callback for writing a NVS entry to JSON
bool DumpNvsToJSONCallback(const char * key, void* data) {
JsonArray *myArr = (JsonArray*) data;
JsonObject obj = myArr->createNestedObject();
return tagIdToJSON(key, obj);
}

static void handleGetRFIDRequest(AsyncWebServerRequest *request) {
String tagId = "";
if (request->hasParam("id")) {
tagId = request->getParam("id")->value();
}
if (tagId == "") {
// return all RFID assignments
AsyncJsonResponse *response = new AsyncJsonResponse(true);
JsonArray Arr = response->getRoot();
if (listNVSKeys("rfidTags", &Arr, DumpNvsToJSONCallback)) {
response->setLength();
request->send(response);
} else {
request->send(500, "error reading entries from NVS");
}
} else {
if (gPrefsRfid.isKey(tagId.c_str())) {
// return single RFID assignment
AsyncJsonResponse *response = new AsyncJsonResponse(false);
JsonObject obj = response->getRoot();
tagIdToJSON(tagId, obj);
response->setLength();
request->send(response);
} else {
// RFID assignment not found
request->send(404);
}
}
}

static void handlePostRFIDRequest(AsyncWebServerRequest *request, JsonVariant &json) {
const JsonObject& jsonObj = json.as<JsonObject>();

String tagId = jsonObj["id"];
String fileOrUrl = jsonObj["fileOrUrl"];
if (fileOrUrl.isEmpty()) {
fileOrUrl = "0";
}
char _fileOrUrlAscii[MAX_FILEPATH_LENTGH];
convertFilenameToAscii(fileOrUrl, _fileOrUrlAscii);
uint8_t _playModeOrModId;
if (jsonObj.containsKey("modId")) {
_playModeOrModId = jsonObj["modId"];
} else {
_playModeOrModId = jsonObj["playMode"];
}
if (_playModeOrModId <= 0) {
Log_Println("rfidAssign: Invalid playMode or modId", LOGLEVEL_ERROR);
request->send(500, "text/plain; charset=utf-8", "rfidAssign: Invalid playMode or modId");
return;
}
char rfidString[275];
snprintf(rfidString, sizeof(rfidString) / sizeof(rfidString[0]), "%s%s%s0%s%u%s0", stringDelimiter, _fileOrUrlAscii, stringDelimiter, stringDelimiter, _playModeOrModId, stringDelimiter);
gPrefsRfid.putString(tagId.c_str(), rfidString);

String s = gPrefsRfid.getString(tagId.c_str(), "-1");
if (s.compareTo(rfidString)) {
request->send(500, "text/plain; charset=utf-8", "rfidAssign: cannot save assignment to NVS");
return;
}
Web_DumpNvsToSd("rfidTags", backupFile); // Store backup-file every time when a new rfid-tag is programmed
}

static void handleDeleteRFIDRequest(AsyncWebServerRequest *request) {
String tagId = "";
if (request->hasParam("id")) {
tagId = request->getParam("id")->value();
}
if ((tagId != "") && (gPrefsRfid.isKey(tagId.c_str()))) {
// stop playback, tag to delete might be in use
Cmd_Action(CMD_STOP);
if (gPrefsRfid.remove(tagId.c_str())) {
request->send(200, "text/plain; charset=utf-8", tagId + " removed successfuly");
} else {
request->send(500, "text/plain; charset=utf-8", "error removing tag from NVS");
}
} else {
request->send(404, "text/plain; charset=utf-8", "error removing tag from NVS: Tag not exists");
}
}



// Takes stream from file-upload and writes payload into a temporary sd-file.
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
Expand Down Expand Up @@ -1577,72 +1787,6 @@ void Web_DumpSdToNvs(const char *_filename) {
gFSystem.remove(_filename);
}

// Dumps all RFID-entries from NVS into a file on SD-card
bool Web_DumpNvsToSd(const char *_namespace, const char *_destFile) {
Led_SetPause(true); // Workaround to prevent exceptions due to Neopixel-signalisation while NVS-write
esp_partition_iterator_t pi; // Iterator for find
const esp_partition_t *nvs; // Pointer to partition struct
esp_err_t result = ESP_OK;
const char *partname = "nvs";
uint8_t pagenr = 0; // Page number in NVS
uint8_t i; // Index in Entry 0..125
uint8_t bm; // Bitmap for an entry
uint32_t offset = 0; // Offset in nvs partition
uint8_t namespace_ID; // Namespace ID found

pi = esp_partition_find(ESP_PARTITION_TYPE_DATA, // Get partition iterator for
ESP_PARTITION_SUBTYPE_ANY, // this partition
partname);
if (pi) {
nvs = esp_partition_get(pi); // Get partition struct
esp_partition_iterator_release(pi); // Release the iterator
Log_Printf(LOGLEVEL_DEBUG, "Partition %s found, %d bytes", partname, nvs->size);
} else {
Log_Printf(LOGLEVEL_ERROR, "Partition %s not found!", partname);
return NULL;
}
namespace_ID = FindNsID(nvs, _namespace); // Find ID of our namespace in NVS
File backupFile = gFSystem.open(_destFile, FILE_WRITE);
if (!backupFile) {
return false;
}
while (offset < nvs->size) {
result = esp_partition_read(nvs, offset, // Read 1 page in nvs partition
&buf,
sizeof(nvs_page));
if (result != ESP_OK) {
Log_Println("Error reading NVS!", LOGLEVEL_ERROR);
return false;
}

i = 0;

while (i < 126) {
bm = (buf.Bitmap[i / 4] >> ((i % 4) * 2)) & 0x03; // Get bitmap for this entry
if (bm == 2) {
if ((namespace_ID == 0xFF) || // Show all if ID = 0xFF
(buf.Entry[i].Ns == namespace_ID)) { // otherwise just my namespace
if (isNumber(buf.Entry[i].Key)) {
String s = gPrefsRfid.getString((const char *)buf.Entry[i].Key);
backupFile.printf("%s%s%s%s\n", stringOuterDelimiter, buf.Entry[i].Key, stringOuterDelimiter, s.c_str());
}
}
i += buf.Entry[i].Span; // Next entry
} else {
i++;
}
}
offset += sizeof(nvs_page); // Prepare to read next page in nvs
pagenr++;
}

backupFile.close();
Led_SetPause(false);

return true;
}


// handle album cover image request
static void handleCoverImageRequest(AsyncWebServerRequest *request) {

Expand Down