2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @file webui.cpp
|
|
|
|
*
|
2024-05-30 23:48:26 +02:00
|
|
|
* @brief Implementation file for web-based user interface (WebUI) functions in the DE-Timer application.
|
2024-05-30 23:38:05 +02:00
|
|
|
*
|
|
|
|
* This file contains the implementation of functions related to the initialization and processing of the
|
|
|
|
* web-based user interface (WebUI). It includes the setup of LittleFS, handling of firmware version checks,
|
|
|
|
* initialization of mDNS, setup of web server routes, and handling of various HTTP events.
|
|
|
|
*
|
|
|
|
* @author Marcel Peterkau
|
|
|
|
* @date 09.01.2024
|
|
|
|
*/
|
|
|
|
|
2022-04-14 22:48:11 +02:00
|
|
|
#include "webui.h"
|
|
|
|
|
|
|
|
AsyncWebServer webServer(80);
|
|
|
|
|
2023-04-13 00:32:50 +02:00
|
|
|
const char *PARAM_MESSAGE = "message";
|
2023-02-20 13:51:54 +01:00
|
|
|
|
2024-05-31 03:03:47 +02:00
|
|
|
batteryType_t batterytypePreselect; /**< Preselect Memory for change Batterytype */
|
|
|
|
|
2022-04-14 22:48:11 +02:00
|
|
|
String processor(const String &var);
|
|
|
|
void WebserverNotFound_Callback(AsyncWebServerRequest *request);
|
2023-02-20 13:51:54 +01:00
|
|
|
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
|
|
|
|
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
|
|
|
|
void WebServerEEJSON_Callback(AsyncWebServerRequest *request);
|
|
|
|
void GetFlashVersion(char *buff, size_t buff_size);
|
2022-04-14 22:48:11 +02:00
|
|
|
|
2023-04-13 00:32:50 +02:00
|
|
|
AsyncWebSocket webSocket("/ws");
|
|
|
|
|
|
|
|
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
|
|
|
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len);
|
2024-05-30 23:38:05 +02:00
|
|
|
void Websocket_RefreshClientData_DTCs(uint32_t client_id);
|
|
|
|
void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping = false);
|
|
|
|
void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping = false);
|
|
|
|
void Websocket_HandleButtons(uint8_t *data);
|
|
|
|
void Websocket_HandleSettings(uint8_t *data);
|
|
|
|
void parseWebsocketString(char *data, char *identifierBuffer, size_t identifierBufferSize, char *valueBuffer, size_t valueBufferSize);
|
|
|
|
int findIndexByString(const char *searchString, const char *const *array, int arraySize);
|
|
|
|
|
|
|
|
/**
|
2024-05-30 23:48:26 +02:00
|
|
|
* @brief Initializes the web-based user interface (WebUI) for the DE-Timer application.
|
2024-05-30 23:38:05 +02:00
|
|
|
*
|
|
|
|
* This function sets up the necessary components for the WebUI, including mounting LittleFS,
|
|
|
|
* performing flash version checks, initializing mDNS, and configuring the web server with
|
|
|
|
* routes and event handlers. If any errors occur during setup, appropriate diagnostic messages
|
|
|
|
* are pushed to the debugging system, and potential error conditions are recorded as Diagnostic
|
|
|
|
* Trouble Codes (DTCs).
|
|
|
|
*
|
|
|
|
* @note This function should be called during the initialization phase of the application.
|
|
|
|
*/
|
2022-04-14 22:48:11 +02:00
|
|
|
void initWebUI()
|
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
// Attempt to mount LittleFS
|
2022-04-14 22:48:11 +02:00
|
|
|
if (!LittleFS.begin())
|
|
|
|
{
|
2023-04-13 00:32:50 +02:00
|
|
|
Debug_pushMessage("An Error has occurred while mounting LittleFS\n");
|
2024-05-30 23:38:05 +02:00
|
|
|
MaintainDTC(DTC_FLASHFS_ERROR, true);
|
2022-04-14 22:48:11 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Retrieve the flash version
|
2023-02-20 13:51:54 +01:00
|
|
|
GetFlashVersion(globals.FlashVersion, sizeof(globals.FlashVersion));
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Compare the flash version with the required version
|
2023-04-13 00:32:50 +02:00
|
|
|
char buffer[6];
|
|
|
|
snprintf(buffer, sizeof(buffer), "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor);
|
|
|
|
if (strcmp(globals.FlashVersion, buffer))
|
2023-02-20 13:51:54 +01:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
MaintainDTC(DTC_FLASHFS_VERSION_ERROR, true);
|
2023-02-20 13:51:54 +01:00
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Initialize mDNS and add service
|
2024-05-31 12:49:06 +02:00
|
|
|
MDNS.begin(globals.DeviceNameId);
|
2023-02-20 13:51:54 +01:00
|
|
|
MDNS.addService("http", "tcp", 80);
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Set up WebSocket event handler and attach to web server
|
2023-04-13 00:32:50 +02:00
|
|
|
webSocket.onEvent(WebsocketEvent_Callback);
|
|
|
|
webServer.addHandler(&webSocket);
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Serve static files and define routes
|
2022-04-14 22:48:11 +02:00
|
|
|
webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
|
2024-05-30 23:38:05 +02:00
|
|
|
webServer.serveStatic("/index.htm", LittleFS, "/index.htm").setCacheControl("max-age=360000");
|
2022-04-14 22:48:11 +02:00
|
|
|
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
|
|
|
{ request->redirect("/index.htm"); });
|
|
|
|
webServer.onNotFound(WebserverNotFound_Callback);
|
2023-02-20 13:51:54 +01:00
|
|
|
webServer.on("/eejson", HTTP_GET, WebServerEEJSON_Callback);
|
|
|
|
webServer.on(
|
|
|
|
"/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverFirmwareUpdate_Callback);
|
|
|
|
webServer.on(
|
|
|
|
"/eeRestore", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverEERestore_Callback);
|
2022-04-14 22:48:11 +02:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
// Start the web server
|
2022-04-14 22:48:11 +02:00
|
|
|
webServer.begin();
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
2024-05-30 23:48:26 +02:00
|
|
|
* @brief Processes the web server functionality for the DE-Timer application.
|
2024-05-30 23:38:05 +02:00
|
|
|
*
|
|
|
|
* This function performs periodic processing tasks for the web server, including cleaning up
|
|
|
|
* WebSocket clients and refreshing client data when WebSocket connections are active. It ensures
|
|
|
|
* that WebSocket client data related to Diagnostic Trouble Codes (DTCs) and system status is
|
|
|
|
* updated at regular intervals.
|
|
|
|
*
|
|
|
|
* @note This function should be called in the main loop of the application.
|
|
|
|
*/
|
2023-04-13 00:32:50 +02:00
|
|
|
void Webserver_Process()
|
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
static uint32_t previousMillis = 0;
|
2022-04-14 22:48:11 +02:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
webSocket.cleanupClients();
|
2022-04-14 22:48:11 +02:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
if ((webSocket.count() > 0) && (millis() - previousMillis >= 10000))
|
2022-04-14 22:48:11 +02:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Websocket_RefreshClientData_DTCs(0);
|
|
|
|
Websocket_RefreshClientData_Status(0);
|
|
|
|
previousMillis = millis();
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
2024-05-30 23:48:26 +02:00
|
|
|
* @brief Shuts down the web server functionality for the DE-Timer application.
|
2024-05-30 23:38:05 +02:00
|
|
|
*
|
|
|
|
* This function closes all WebSocket connections and terminates the web server. It is intended
|
|
|
|
* to be called when the application is being shut down or when there is a need to deactivate the
|
|
|
|
* web server.
|
|
|
|
*
|
|
|
|
* @details This function ensures a graceful shutdown of the web server by closing all active
|
|
|
|
* WebSocket connections and ending the web server instance.
|
|
|
|
*
|
|
|
|
* @note This function should be called before shutting down the application to properly
|
|
|
|
* deactivate the web server.
|
|
|
|
*/
|
|
|
|
void Webserver_Shutdown()
|
2022-04-14 22:48:11 +02:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
if (webSocket.count() > 0)
|
|
|
|
webSocket.closeAll();
|
|
|
|
webServer.end();
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Callback function for handling HTTP 404 (Not Found) errors on the web server.
|
|
|
|
*
|
|
|
|
* This function is invoked when an HTTP request results in a 404 error (Not Found). It sends
|
|
|
|
* a simple "Not found" text response with an HTTP status code of 404.
|
|
|
|
*
|
|
|
|
* @param request Pointer to the AsyncWebServerRequest object representing the HTTP request.
|
|
|
|
*/
|
2022-04-14 22:48:11 +02:00
|
|
|
void WebserverNotFound_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
request->send(404, "text/html", "Not found");
|
2022-05-15 16:37:34 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Reads the flash version information from a file in LittleFS.
|
|
|
|
*
|
|
|
|
* This function reads the flash version information stored in a file named "version" in the
|
|
|
|
* LittleFS filesystem. It opens the file, reads the content until a carriage return ('\r') is
|
|
|
|
* encountered, and stores the result in the provided buffer. The buffer is null-terminated.
|
|
|
|
*
|
|
|
|
* @param buff Pointer to the buffer where the flash version information will be stored.
|
|
|
|
* @param buff_size Size of the buffer.
|
|
|
|
*/
|
2023-02-20 13:51:54 +01:00
|
|
|
void GetFlashVersion(char *buff, size_t buff_size)
|
|
|
|
{
|
|
|
|
File this_file = LittleFS.open("version", "r");
|
|
|
|
if (!this_file)
|
|
|
|
{ // failed to open the file, retrn empty result
|
|
|
|
buff[0] = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this_file.available())
|
|
|
|
{
|
|
|
|
int bytes_read;
|
|
|
|
bytes_read = this_file.readBytesUntil('\r', buff, buff_size - 1);
|
|
|
|
buff[bytes_read] = '\0';
|
|
|
|
}
|
|
|
|
this_file.close();
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Callback function for handling firmware updates via the web server.
|
|
|
|
*
|
|
|
|
* This function is invoked during the firmware update process when a new firmware file
|
|
|
|
* is received. It handles the update process using the ESPAsyncHTTPUpdate library. The update
|
|
|
|
* process involves checking the firmware type, initializing the update, writing data, and finalizing
|
|
|
|
* the update. If the update is successful, it triggers a system shutdown.
|
|
|
|
*
|
|
|
|
* @param request Pointer to the AsyncWebServerRequest object.
|
|
|
|
* @param filename The name of the file being updated.
|
|
|
|
* @param index The index of the file being updated.
|
|
|
|
* @param data Pointer to the data buffer.
|
|
|
|
* @param len The length of the data buffer.
|
|
|
|
* @param final Boolean indicating if this is the final chunk of data.
|
|
|
|
*/
|
2023-02-20 13:51:54 +01:00
|
|
|
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!index)
|
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Debug_pushMessage("Update\n");
|
2023-02-20 13:51:54 +01:00
|
|
|
size_t content_len = request->contentLength();
|
|
|
|
int cmd = (filename.indexOf(".fs") > -1) ? U_FS : U_FLASH;
|
|
|
|
Update.runAsync(true);
|
|
|
|
if (!Update.begin(content_len, cmd))
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Update.write(data, len) != len)
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-13 00:32:50 +02:00
|
|
|
Debug_pushMessage("Progress: %d%%\n", (Update.progress() * 100) / Update.size());
|
2023-02-20 13:51:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (final)
|
|
|
|
{
|
|
|
|
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
|
|
|
|
response->addHeader("Refresh", "20");
|
|
|
|
response->addHeader("Location", "/");
|
|
|
|
request->send(response);
|
|
|
|
if (!Update.end(true))
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-13 00:32:50 +02:00
|
|
|
Debug_pushMessage("Update complete\n");
|
2023-02-20 13:51:54 +01:00
|
|
|
globals.systemStatus = sysStat_Shutdown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Callback function for handling EEPROM restore via the web server.
|
|
|
|
*
|
|
|
|
* This function is invoked during the EEPROM restore process when a new EEPROM file
|
|
|
|
* is received. It handles the restore process by reading the data from the received file,
|
|
|
|
* deserializing the JSON data, and updating the configuration and persistence data accordingly.
|
|
|
|
* If the restore is successful, it triggers a system shutdown.
|
|
|
|
*
|
|
|
|
* @param request Pointer to the AsyncWebServerRequest object.
|
|
|
|
* @param filename The name of the file being restored.
|
|
|
|
* @param index The index of the file being restored.
|
|
|
|
* @param data Pointer to the data buffer.
|
|
|
|
* @param len The length of the data buffer.
|
|
|
|
* @param final Boolean indicating if this is the final chunk of data.
|
|
|
|
*/
|
2023-02-20 13:51:54 +01:00
|
|
|
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
|
|
{
|
|
|
|
bool ee_done = false;
|
2023-04-13 00:32:50 +02:00
|
|
|
static bool validext = false;
|
|
|
|
static char *buffer = NULL;
|
|
|
|
static uint32_t read_ptr = 0;
|
|
|
|
DeserializationError error;
|
2023-02-20 13:51:54 +01:00
|
|
|
|
|
|
|
if (!index)
|
|
|
|
{
|
|
|
|
validext = (filename.indexOf(".ee.json") > -1);
|
2023-04-13 00:32:50 +02:00
|
|
|
if (validext)
|
|
|
|
{
|
|
|
|
buffer = (char *)malloc(1536);
|
|
|
|
read_ptr = 0;
|
|
|
|
if (buffer == NULL)
|
2024-05-30 23:38:05 +02:00
|
|
|
Debug_pushMessage("malloc() failed for EEPROM-Restore\n");
|
2023-04-13 00:32:50 +02:00
|
|
|
}
|
2023-02-20 13:51:54 +01:00
|
|
|
}
|
|
|
|
|
2023-04-13 00:32:50 +02:00
|
|
|
if (buffer != NULL)
|
2023-02-20 13:51:54 +01:00
|
|
|
{
|
2023-04-13 00:32:50 +02:00
|
|
|
memcpy(buffer + read_ptr, data, len);
|
|
|
|
read_ptr = read_ptr + len;
|
2023-02-20 13:51:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (final)
|
|
|
|
{
|
2023-04-13 00:32:50 +02:00
|
|
|
if (buffer != NULL)
|
|
|
|
{
|
|
|
|
Serial.print(buffer);
|
2024-05-30 23:38:05 +02:00
|
|
|
JsonDocument json;
|
|
|
|
error = deserializeJson(json, buffer);
|
2023-04-13 00:32:50 +02:00
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
Debug_pushMessage("deserializeJson() failed: %s\n", error.f_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
ConfigData.batteryType = (batteryType_t)json["config"]["batteryType"].as<int>();
|
|
|
|
ConfigData.active_faction_on_reboot = json["config"]["active_faction_on_reboot"].as<bool>();
|
|
|
|
strncpy(ConfigData.Faction_1_Name, json["config"]["Faction_1_Name"].as<const char *>(), sizeof(ConfigData.Faction_1_Name));
|
|
|
|
strncpy(ConfigData.Faction_2_Name, json["config"]["Faction_2_Name"].as<const char *>(), sizeof(ConfigData.Faction_2_Name));
|
|
|
|
strncpy(ConfigData.Faction_3_Name, json["config"]["Faction_3_Name"].as<const char *>(), sizeof(ConfigData.Faction_3_Name));
|
|
|
|
strncpy(ConfigData.wifi_client_ssid, json["config"]["wifi_client_ssid"].as<const char *>(), sizeof(ConfigData.wifi_client_ssid));
|
|
|
|
strncpy(ConfigData.wifi_client_password, json["config"]["wifi_client_password"].as<const char *>(), sizeof(ConfigData.wifi_client_password));
|
|
|
|
|
|
|
|
PersistenceData.writeCycleCounter = json["persis"]["writeCycleCounter"].as<uint16_t>();
|
|
|
|
PersistenceData.activeFaction = (Factions_t)json["persis"]["activeFaction"].as<int>();
|
|
|
|
PersistenceData.faction_1_timer = json["persis"]["faction_1_timer"].as<uint32_t>();
|
|
|
|
PersistenceData.faction_2_timer = json["persis"]["faction_2_timer"].as<uint32_t>();
|
|
|
|
PersistenceData.faction_3_timer = json["persis"]["faction_3_timer"].as<uint32_t>();
|
|
|
|
PersistenceData.checksum = json["persis"]["checksum"].as<uint32_t>();
|
2023-04-13 00:32:50 +02:00
|
|
|
|
|
|
|
ee_done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buffer);
|
|
|
|
|
2023-02-20 13:51:54 +01:00
|
|
|
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
|
|
|
|
response->addHeader("Refresh", "20");
|
|
|
|
response->addHeader("Location", "/");
|
|
|
|
request->send(response);
|
2023-04-13 00:32:50 +02:00
|
|
|
|
2023-02-20 13:51:54 +01:00
|
|
|
if (ee_done)
|
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Debug_pushMessage("Update complete\n");
|
2023-02-20 13:51:54 +01:00
|
|
|
globals.systemStatus = sysStat_Shutdown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Callback function for handling EEPROM JSON request via the web server.
|
|
|
|
*
|
|
|
|
* This function is invoked when a request for EEPROM JSON data is received. It constructs a JSON
|
|
|
|
* response containing information about the firmware, configuration, and persistence data.
|
|
|
|
*
|
|
|
|
* @param request Pointer to the AsyncWebServerRequest object.
|
|
|
|
*/
|
2023-02-20 13:51:54 +01:00
|
|
|
void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
2024-05-30 23:38:05 +02:00
|
|
|
JsonDocument json;
|
|
|
|
JsonObject info = json["info"].to<JsonObject>();
|
2023-02-20 13:51:54 +01:00
|
|
|
|
|
|
|
char buffer[16];
|
|
|
|
|
2024-05-31 12:49:06 +02:00
|
|
|
info["DeviceNameId"] = globals.DeviceNameId;
|
2023-04-13 00:32:50 +02:00
|
|
|
sprintf(buffer, "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor);
|
2024-05-30 23:38:05 +02:00
|
|
|
info["FW-Version"] = buffer;
|
|
|
|
info["FS-Version"] = globals.FlashVersion;
|
2023-04-13 00:32:50 +02:00
|
|
|
snprintf_P(buffer, sizeof(buffer), "%s", constants.GitHash);
|
2024-05-30 23:38:05 +02:00
|
|
|
info["Git-Hash"] = buffer;
|
2023-02-20 13:51:54 +01:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
JsonObject config = json["config"].to<JsonObject>();
|
|
|
|
generateJsonObject_ConfigData(config);
|
|
|
|
JsonObject persis = json["persis"].to<JsonObject>();
|
|
|
|
generateJsonObject_PersistenceData(persis);
|
2023-02-20 13:51:54 +01:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
JsonObject eepart = json["eepart"].to<JsonObject>();
|
2023-02-20 13:51:54 +01:00
|
|
|
sprintf(buffer, "0x%04X", globals.eePersistanceAdress);
|
|
|
|
eepart["PersistanceAddress"] = buffer;
|
|
|
|
|
|
|
|
serializeJsonPretty(json, *response);
|
|
|
|
|
|
|
|
response->addHeader("Content-disposition", "attachment; filename=backup.ee.json");
|
|
|
|
|
|
|
|
request->send(response);
|
2023-04-13 00:32:50 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Callback function for handling WebSocket events.
|
|
|
|
*
|
|
|
|
* This function is invoked when events occur in the WebSocket communication, such as client connection,
|
|
|
|
* disconnection, reception of data, and others. It dispatches the events to the appropriate handlers.
|
|
|
|
*
|
|
|
|
* @param server Pointer to the AsyncWebSocket object.
|
|
|
|
* @param client Pointer to the AsyncWebSocketClient object representing the WebSocket client.
|
|
|
|
* @param type Type of WebSocket event.
|
|
|
|
* @param arg Event-specific argument.
|
|
|
|
* @param data Pointer to the received data (if applicable).
|
|
|
|
* @param len Length of the received data.
|
|
|
|
*/
|
2023-04-13 00:32:50 +02:00
|
|
|
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case WS_EVT_CONNECT:
|
|
|
|
Debug_pushMessage("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
|
2024-05-30 23:38:05 +02:00
|
|
|
Websocket_RefreshClientData_Status(client->id(), true);
|
|
|
|
Websocket_RefreshClientData_Static(client->id(), true);
|
|
|
|
Websocket_RefreshClientData_DTCs(client->id());
|
2023-04-13 00:32:50 +02:00
|
|
|
break;
|
|
|
|
case WS_EVT_DISCONNECT:
|
|
|
|
Debug_pushMessage("WebSocket client #%u disconnected\n", client->id());
|
|
|
|
break;
|
|
|
|
case WS_EVT_DATA:
|
|
|
|
Websocket_HandleMessage(arg, data, len);
|
|
|
|
break;
|
|
|
|
case WS_EVT_PONG:
|
|
|
|
case WS_EVT_ERROR:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Handles WebSocket messages received from clients.
|
|
|
|
*
|
|
|
|
* This function processes WebSocket messages, such as starting or stopping debugging,
|
|
|
|
* and provides appropriate responses.
|
|
|
|
*
|
|
|
|
* @param arg Pointer to the WebSocket frame information.
|
|
|
|
* @param data Pointer to the received data.
|
|
|
|
* @param len Length of the received data.
|
|
|
|
*/
|
2023-04-13 00:32:50 +02:00
|
|
|
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
|
|
|
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT)
|
|
|
|
{
|
|
|
|
data[len] = 0;
|
2024-05-30 23:38:05 +02:00
|
|
|
Debug_pushMessage("Websocket-Message (len: %d): %s\n", len, (char *)data);
|
2023-04-13 00:32:50 +02:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
if (strncmp((char *)data, "btn-", strlen("btn-")) == 0)
|
2023-04-13 00:32:50 +02:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Websocket_HandleButtons(data + strlen("btn-"));
|
2023-04-13 00:32:50 +02:00
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
else if (strncmp((char *)data, "set-", strlen("set-")) == 0)
|
2023-04-13 00:32:50 +02:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Websocket_HandleSettings(data + strlen("set-"));
|
2023-04-13 00:32:50 +02:00
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
else
|
2023-04-13 00:32:50 +02:00
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
Debug_pushMessage("Got unknown Websocket-Message '%s' from client\n", (char *)data);
|
2023-04-13 00:32:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Handle button commands received via WebSocket.
|
|
|
|
*
|
|
|
|
* This function parses a WebSocket string representing button commands, extracts
|
|
|
|
* the identifier and value components, and performs corresponding actions based on
|
|
|
|
* the received commands.
|
|
|
|
*
|
|
|
|
* @param data The WebSocket data containing button commands.
|
|
|
|
*/
|
|
|
|
void Websocket_HandleButtons(uint8_t *data)
|
|
|
|
{
|
|
|
|
char identifier[32];
|
|
|
|
char value[32];
|
|
|
|
|
|
|
|
parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value));
|
2024-05-31 03:03:47 +02:00
|
|
|
if (strcmp(identifier, "reset-timer") == 0)
|
|
|
|
{
|
|
|
|
PersistenceData.activeFaction = NONE;
|
|
|
|
PersistenceData.faction_1_timer = 0;
|
|
|
|
PersistenceData.faction_2_timer = 0;
|
|
|
|
PersistenceData.faction_3_timer = 0;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "debugstart") == 0)
|
2024-05-30 23:38:05 +02:00
|
|
|
{
|
|
|
|
SetDebugportStatus(dbg_Webui, enabled);
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "debugstop") == 0)
|
|
|
|
{
|
|
|
|
SetDebugportStatus(dbg_Webui, disabled);
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "settingssave") == 0)
|
|
|
|
{
|
2024-05-31 03:03:47 +02:00
|
|
|
ConfigData.batteryType = batterytypePreselect;
|
2024-05-30 23:38:05 +02:00
|
|
|
globals.requestEEAction = EE_CFG_SAVE;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "reboot") == 0)
|
|
|
|
{
|
|
|
|
globals.systemStatus = sysStat_Shutdown;
|
|
|
|
}
|
2024-06-06 20:28:04 +02:00
|
|
|
else if (strcmp(identifier, "set-faction1") == 0)
|
|
|
|
{
|
|
|
|
PersistenceData.activeFaction = FACTION_1;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "set-faction2") == 0)
|
|
|
|
{
|
|
|
|
PersistenceData.activeFaction = FACTION_2;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "set-faction3") == 0)
|
|
|
|
{
|
|
|
|
PersistenceData.activeFaction = FACTION_3;
|
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Debug_pushMessage("Got unknown Button-id '%s' from ws-client\n", identifier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Handle settings commands received via WebSocket.
|
|
|
|
*
|
|
|
|
* This function parses a WebSocket string representing settings commands, extracts
|
|
|
|
* the identifier and value components, and updates the system settings accordingly.
|
|
|
|
*
|
|
|
|
* @param data The WebSocket data containing settings commands.
|
|
|
|
*/
|
|
|
|
void Websocket_HandleSettings(uint8_t *data)
|
|
|
|
{
|
|
|
|
char identifier[32];
|
|
|
|
char value[63];
|
|
|
|
|
|
|
|
parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value));
|
|
|
|
|
2024-05-31 03:03:47 +02:00
|
|
|
if (strcmp(identifier, "active_faction_on_reboot") == 0)
|
|
|
|
{
|
|
|
|
ConfigData.active_faction_on_reboot = value[0] == '1' ? true : false;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "name_faction1") == 0)
|
|
|
|
{
|
|
|
|
strncpy(ConfigData.Faction_1_Name, value, sizeof(ConfigData.Faction_1_Name));
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "name_faction2") == 0)
|
|
|
|
{
|
|
|
|
strncpy(ConfigData.Faction_2_Name, value, sizeof(ConfigData.Faction_2_Name));
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "name_faction3") == 0)
|
|
|
|
{
|
|
|
|
strncpy(ConfigData.Faction_3_Name, value, sizeof(ConfigData.Faction_3_Name));
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "batterytype") == 0)
|
|
|
|
{
|
|
|
|
int index = findIndexByString(value, BatteryString, BatteryString_Elements);
|
|
|
|
batterytypePreselect = (batteryType_t)index;
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "wifi-ssid") == 0)
|
2024-05-30 23:38:05 +02:00
|
|
|
{
|
|
|
|
strncpy(ConfigData.wifi_client_ssid, value, sizeof(ConfigData.wifi_client_ssid));
|
|
|
|
}
|
|
|
|
else if (strcmp(identifier, "wifi-password") == 0)
|
|
|
|
{
|
|
|
|
strncpy(ConfigData.wifi_client_password, value, sizeof(ConfigData.wifi_client_password));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Debug_pushMessage("Got unknown Settings-id and value '%s' from ws-client\n", identifier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Pushes live debug messages to all WebSocket clients.
|
|
|
|
*
|
|
|
|
* This function sends a live debug message to all connected WebSocket clients.
|
|
|
|
*
|
|
|
|
* @param Message The debug message to be sent.
|
|
|
|
*/
|
2023-04-13 00:32:50 +02:00
|
|
|
void Websocket_PushLiveDebug(String Message)
|
|
|
|
{
|
2024-05-30 23:38:05 +02:00
|
|
|
webSocket.textAll("DEBUG:" + Message);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Refreshes client data related to Diagnostic Trouble Codes (DTCs) on WebSocket clients.
|
|
|
|
*
|
|
|
|
* This function constructs a DTC-related string and sends it to a specific WebSocket client or
|
|
|
|
* broadcasts it to all connected WebSocket clients.
|
|
|
|
*
|
|
|
|
* @param client_id The ID of the WebSocket client to which the data should be sent. If 0, the data
|
|
|
|
* will be broadcasted to all connected clients.
|
|
|
|
*/
|
|
|
|
void Websocket_RefreshClientData_DTCs(uint32_t client_id)
|
|
|
|
{
|
|
|
|
String temp = "DTC:";
|
|
|
|
|
|
|
|
// Build DTC-String
|
|
|
|
if (globals.hasDTC != true)
|
|
|
|
{
|
|
|
|
temp.concat(String(DTC_NO_DTC) + ";");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
|
|
|
|
{
|
|
|
|
if (DTCStorage[i].Number < DTC_LAST_DTC)
|
|
|
|
{
|
|
|
|
temp.concat(String(DTCStorage[i].timestamp) + ",");
|
|
|
|
temp.concat(String(DTCStorage[i].Number) + ",");
|
|
|
|
temp.concat(String(getSeverityForDTC(DTCStorage[i].Number)) + ",");
|
|
|
|
temp.concat(String(DTCStorage[i].active) + ",");
|
|
|
|
temp.concat(String(DTCStorage[i].debugVal) + ";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (client_id > 0)
|
|
|
|
{
|
|
|
|
webSocket.text(client_id, temp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
webSocket.textAll(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Refreshes client data related to system status and relevant parameters on WebSocket clients.
|
|
|
|
*
|
|
|
|
* This function constructs a status-related string and sends it to a specific WebSocket client or
|
|
|
|
* broadcasts it to all connected WebSocket clients. It also sends a mapping of the status parameters.
|
|
|
|
*
|
|
|
|
* @param client_id The ID of the WebSocket client to which the data should be sent. If 0, the data
|
|
|
|
* will be broadcasted to all connected clients.
|
|
|
|
* @param send_mapping Flag indicating whether to send the parameter mapping to the client(s).
|
|
|
|
*/
|
|
|
|
void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
|
|
|
|
{
|
2024-06-02 23:37:04 +02:00
|
|
|
if (send_mapping)
|
|
|
|
{
|
|
|
|
const char mapping[] = "MAPPING_STATUS:"
|
|
|
|
"batterylevel;"
|
|
|
|
"systemstatus;"
|
|
|
|
"activefaction;"
|
|
|
|
"time_faction1;"
|
|
|
|
"time_faction2;"
|
|
|
|
"time_faction3;";
|
|
|
|
|
|
|
|
if (client_id > 0)
|
|
|
|
webSocket.text(client_id, mapping);
|
|
|
|
else
|
|
|
|
webSocket.textAll(mapping);
|
|
|
|
|
|
|
|
Debug_pushMessage("send MAPPING_STATUS WS-Client Data\n");
|
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
|
2024-06-02 23:37:04 +02:00
|
|
|
char dataString[200] = {0}; // Maximal 200 Zeichen für den Data-String
|
|
|
|
|
|
|
|
sprintf(dataString, "STATUS:%d;%s;%d;%ld;%ld;%ld;",
|
|
|
|
globals.battery_level,
|
|
|
|
globals.systemStatustxt,
|
|
|
|
PersistenceData.activeFaction,
|
|
|
|
PersistenceData.faction_1_timer,
|
|
|
|
PersistenceData.faction_2_timer,
|
|
|
|
PersistenceData.faction_3_timer);
|
2024-05-30 23:38:05 +02:00
|
|
|
|
|
|
|
if (client_id > 0)
|
2024-06-02 23:37:04 +02:00
|
|
|
{
|
|
|
|
webSocket.text(client_id, dataString);
|
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
else
|
2024-06-02 23:37:04 +02:00
|
|
|
{
|
|
|
|
webSocket.textAll(dataString);
|
|
|
|
}
|
2024-05-30 23:38:05 +02:00
|
|
|
}
|
|
|
|
|
2024-06-02 23:37:04 +02:00
|
|
|
|
2024-05-30 23:38:05 +02:00
|
|
|
/**
|
|
|
|
* @brief Refreshes client data related to static configuration parameters on WebSocket clients.
|
|
|
|
*
|
|
|
|
* This function constructs a static configuration-related string and sends it to a specific WebSocket client or
|
|
|
|
* broadcasts it to all connected WebSocket clients. It also sends a mapping of the static configuration parameters.
|
|
|
|
*
|
|
|
|
* @param client_id The ID of the WebSocket client to which the data should be sent. If 0, the data
|
|
|
|
* will be broadcasted to all connected clients.
|
|
|
|
* @param send_mapping Flag indicating whether to send the parameter mapping to the client(s).
|
|
|
|
*/
|
|
|
|
void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
|
|
|
|
{
|
|
|
|
|
2024-06-02 23:37:04 +02:00
|
|
|
Debug_pushMessage("send STATIC WS-Client Data\n");
|
2024-05-30 23:38:05 +02:00
|
|
|
if (send_mapping)
|
|
|
|
{
|
|
|
|
const char mapping[] = "MAPPING_STATIC:"
|
2024-05-31 12:49:06 +02:00
|
|
|
"devicename;"
|
2024-05-30 23:38:05 +02:00
|
|
|
"active_faction_on_reboot;"
|
|
|
|
"batteryType;"
|
|
|
|
"name_faction1;"
|
|
|
|
"name_faction2;"
|
|
|
|
"name_faction3;"
|
|
|
|
"wifi-ssid;"
|
2024-05-31 18:51:05 +02:00
|
|
|
"wifi-pass;"
|
|
|
|
"fw-version;"
|
|
|
|
"flash-version;"
|
2024-06-06 20:28:04 +02:00
|
|
|
"git-revision;";
|
2024-05-30 23:38:05 +02:00
|
|
|
|
|
|
|
if (client_id > 0)
|
|
|
|
webSocket.text(client_id, mapping);
|
|
|
|
else
|
|
|
|
webSocket.textAll(mapping);
|
2024-06-02 23:37:04 +02:00
|
|
|
|
|
|
|
Debug_pushMessage("send MAPPING_STATIC WS-Client Data\n");
|
2024-05-30 23:38:05 +02:00
|
|
|
}
|
|
|
|
|
2024-06-02 23:37:38 +02:00
|
|
|
char dataString[200] = {0}; // Maximal 200 Zeichen für den Data-String
|
2024-05-31 18:51:05 +02:00
|
|
|
|
|
|
|
sprintf(dataString, "STATIC:%s;%d;%d;%s;%s;%s;%s;%s;%d.%02d;%s;%s;",
|
|
|
|
globals.DeviceName,
|
|
|
|
ConfigData.active_faction_on_reboot,
|
|
|
|
ConfigData.batteryType,
|
|
|
|
ConfigData.Faction_1_Name,
|
|
|
|
ConfigData.Faction_2_Name,
|
|
|
|
ConfigData.Faction_3_Name,
|
|
|
|
ConfigData.wifi_client_ssid,
|
|
|
|
ConfigData.wifi_client_password,
|
|
|
|
constants.FW_Version_major,
|
|
|
|
constants.FW_Version_minor,
|
2024-06-02 23:37:38 +02:00
|
|
|
globals.FlashVersion,
|
2024-05-31 18:51:05 +02:00
|
|
|
constants.GitHash);
|
2024-05-30 23:38:05 +02:00
|
|
|
|
|
|
|
if (client_id > 0)
|
|
|
|
{
|
2024-05-31 18:51:05 +02:00
|
|
|
webSocket.text(client_id, dataString);
|
2024-05-30 23:38:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-05-31 18:51:05 +02:00
|
|
|
webSocket.textAll(dataString);
|
2024-05-30 23:38:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Parse a WebSocket string into identifier and value components.
|
|
|
|
*
|
|
|
|
* This function takes a WebSocket string, separates it into identifier and value
|
|
|
|
* components using the ":" delimiter, and stores them in the specified buffers.
|
|
|
|
* If no ":" is found, the entire string is considered as the value, and the
|
|
|
|
* identifier buffer is set to an empty string.
|
|
|
|
*
|
|
|
|
* @param data The WebSocket string to parse.
|
|
|
|
* @param identifierBuffer The buffer to store the identifier component.
|
|
|
|
* @param identifierBufferSize The size of the identifier buffer.
|
|
|
|
* @param valueBuffer The buffer to store the value component.
|
|
|
|
* @param valueBufferSize The size of the value buffer.
|
|
|
|
*/
|
|
|
|
void parseWebsocketString(char *data, char *identifierBuffer, size_t identifierBufferSize,
|
|
|
|
char *valueBuffer, size_t valueBufferSize)
|
|
|
|
{
|
|
|
|
// Zerlegen des Strings anhand des Trennzeichens ":"
|
|
|
|
char *token = strtok(data, ":");
|
|
|
|
|
|
|
|
// Falls der erste Teil des Strings vorhanden ist
|
|
|
|
if (token != NULL)
|
|
|
|
{
|
|
|
|
// Kopieren des ersten Teils in den Buffer für Identifier
|
|
|
|
strncpy(identifierBuffer, token, identifierBufferSize - 1);
|
|
|
|
identifierBuffer[identifierBufferSize - 1] = '\0'; // Null-Terminierung sicherstellen
|
|
|
|
|
|
|
|
// Weitere Aufrufe von strtok, um den nächsten Teil zu erhalten
|
|
|
|
token = strtok(NULL, ":");
|
|
|
|
|
|
|
|
// Falls der zweite Teil des Strings vorhanden ist
|
|
|
|
if (token != NULL)
|
|
|
|
{
|
|
|
|
// Kopieren des zweiten Teils in den Buffer für Value
|
|
|
|
strncpy(valueBuffer, token, valueBufferSize - 1);
|
|
|
|
valueBuffer[valueBufferSize - 1] = '\0'; // Null-Terminierung sicherstellen
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Kein zweiter Teil vorhanden, setzen Sie den Buffer für Value auf leer
|
|
|
|
valueBuffer[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Der erste Teil des Strings fehlt, setzen Sie den Buffer für Identifier auf leer
|
|
|
|
identifierBuffer[0] = '\0';
|
|
|
|
|
|
|
|
// Der gesamte String wird als Value betrachtet
|
|
|
|
strncpy(valueBuffer, data, valueBufferSize - 1);
|
|
|
|
valueBuffer[valueBufferSize - 1] = '\0'; // Null-Terminierung sicherstellen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Find the index of a string in an array.
|
|
|
|
*
|
|
|
|
* This function searches for the given string in the provided array and returns
|
|
|
|
* the index of the first occurrence. If the string is not found, it returns -1.
|
|
|
|
*
|
|
|
|
* @param searchString The string to search for in the array.
|
|
|
|
* @param array The array of strings to search within.
|
|
|
|
* @param arraySize The size of the array.
|
|
|
|
*
|
|
|
|
* @return The index of the first occurrence of the string in the array,
|
|
|
|
* or -1 if the string is not found.
|
|
|
|
*/
|
|
|
|
int findIndexByString(const char *searchString, const char *const *array, int arraySize)
|
|
|
|
{
|
|
|
|
// Durchlaufe das Array und vergleiche jeden String
|
|
|
|
for (int i = 0; i < arraySize; ++i)
|
|
|
|
{
|
|
|
|
if (strcmp(array[i], searchString) == 0)
|
|
|
|
{
|
|
|
|
// String gefunden, gib den Index zurück
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// String nicht gefunden, gib -1 zurück
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Pushes a notification to all WebSocket clients.
|
|
|
|
*
|
|
|
|
* This function sends a live debug message to all connected WebSocket clients.
|
|
|
|
*
|
|
|
|
* @param Message The debug message to be sent.
|
|
|
|
* @param type The type of notification (info, success, warning, error).
|
|
|
|
* - Use NotificationType_t::info for informational messages.
|
|
|
|
* - Use NotificationType_t::success for successful operation messages.
|
|
|
|
* - Use NotificationType_t::warning for warning messages.
|
|
|
|
* - Use NotificationType_t::error for error messages.
|
|
|
|
*/
|
|
|
|
void Websocket_PushNotification(String Message, NotificationType_t type)
|
|
|
|
{
|
|
|
|
String typeString = "";
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case info:
|
|
|
|
typeString = "info";
|
|
|
|
break;
|
|
|
|
case success:
|
|
|
|
typeString = "success";
|
|
|
|
break;
|
|
|
|
case warning:
|
|
|
|
typeString = "warning";
|
|
|
|
break;
|
|
|
|
case error:
|
|
|
|
typeString = "danger";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
webSocket.textAll("NOTIFY:" + typeString + ";" + Message);
|
|
|
|
Debug_pushMessage("Sending Notification to WebUI: %s\n", typeString);
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|