gravitymon/src/webserver.cpp
2022-07-01 21:11:10 +02:00

1360 lines
48 KiB
C++

/*
MIT License
Copyright (c) 2021-22 Magnus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <calc.hpp>
#include <config.hpp>
#include <gyro.hpp>
#include <helper.hpp>
#include <main.hpp>
#include <pushtarget.hpp>
#include <resources.hpp>
#include <templating.hpp>
#include <tempsensor.hpp>
#include <webserver.hpp>
#include <wifi.hpp>
WebServerHandler myWebServerHandler; // My wrapper class fr webserver functions
extern bool sleepModeActive;
extern bool sleepModeAlwaysSkip;
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleConfig() {
LOG_PERF_START("webserver-api-config");
Log.notice(F("WEB : webServer callback for /api/config(get)." CR));
DynamicJsonDocument doc(CFG_JSON_BUFSIZE);
myConfig.createJson(doc);
doc[PARAM_PASS] = ""; // dont show the wifi password
doc[PARAM_PASS2] = "";
double angle = 0;
if (myGyro.hasValue()) angle = myGyro.getAngle();
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, tempC);
doc[PARAM_ANGLE] = reduceFloatPrecision(angle);
doc[PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
// Format the adjustment so we get rid of rounding errors
if (myConfig.isTempF())
// We want the delta value (32F = 0C).
doc[PARAM_TEMP_ADJ] =
reduceFloatPrecision(convertCtoF(myConfig.getTempSensorAdjC()) - 32, 1);
else
doc[PARAM_TEMP_ADJ] = reduceFloatPrecision(myConfig.getTempSensorAdjC(), 1);
if (myConfig.isGravityTempAdj()) {
gravity =
gravityTemperatureCorrectionC(gravity, tempC, myConfig.getTempFormat());
}
if (myConfig.isGravityPlato()) {
doc[PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
} else {
doc[PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4);
}
doc[PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
FloatHistoryLog runLog(RUNTIME_FILENAME);
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
#if defined(ESP8266)
doc[PARAM_PLATFORM] = "esp8266";
#else
doc[PARAM_PLATFORM] = "esp32";
#endif
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(CFG_JSON_BUFSIZE);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-config");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleUpload() {
LOG_PERF_START("webserver-api-upload");
Log.notice(F("WEB : webServer callback for /api/upload(get)." CR));
DynamicJsonDocument doc(300);
doc["index"] = checkHtmlFile(WebServerHandler::HTML_INDEX);
doc["config"] = checkHtmlFile(WebServerHandler::HTML_CONFIG);
doc["calibration"] = checkHtmlFile(WebServerHandler::HTML_CALIBRATION);
doc["format"] = checkHtmlFile(WebServerHandler::HTML_FORMAT);
doc["about"] = checkHtmlFile(WebServerHandler::HTML_ABOUT);
doc["test"] = checkHtmlFile(WebServerHandler::HTML_TEST);
#if defined(ESP8266)
JsonArray files = doc.createNestedArray(PARAM_FILES);
// Show files in the filessytem at startup
FSInfo fs;
LittleFS.info(fs);
Dir dir = LittleFS.openDir("/");
while (dir.next()) {
JsonObject obj = files.createNestedObject();
obj[PARAM_FILE_NAME] = dir.fileName();
obj[PARAM_FILE_SIZE] = dir.fileSize();
}
#else // defined(ESP32)
JsonArray files = doc.createNestedArray(PARAM_FILES);
File dir = LittleFS.open("/");
while (true) {
File entry = dir.openNextFile();
if (!entry) {
// no more files
break;
}
if (!entry.isDirectory()) {
JsonObject obj = files.createNestedObject();
obj[PARAM_FILE_NAME] = entry.name();
obj[PARAM_FILE_SIZE] = entry.size();
}
entry.close();
}
dir.close();
#endif
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(300);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-upload");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleUploadFile() {
LOG_PERF_START("webserver-api-upload-file");
Log.verbose(F("WEB : webServer callback for /api/upload(post)." CR));
HTTPUpload& upload = _server->upload();
String f = upload.filename;
bool validFilename = false;
bool firmware = false;
if (f.equalsIgnoreCase("index.min.htm") ||
f.equalsIgnoreCase("calibration.min.htm") ||
f.equalsIgnoreCase("config.min.htm") ||
f.equalsIgnoreCase("format.min.htm") ||
f.equalsIgnoreCase("test.min.htm") ||
f.equalsIgnoreCase("about.min.htm")) {
validFilename = true;
}
if (f.endsWith(".bin")) {
validFilename = true;
firmware = true;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(
F("WEB : webServer callback for /api/upload, receiving file %s, %d(%d) "
"valid=%s, firmware=%s." CR),
f.c_str(), upload.currentSize, upload.totalSize,
validFilename ? "yes" : "no", firmware ? "yes" : "no");
#endif
if (firmware) {
// Handle firmware update, hardcode since function return wrong value.
// (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
uint32_t maxSketchSpace = MAX_SKETCH_SPACE;
if (upload.status == UPLOAD_FILE_START) {
_uploadReturn = 200;
Log.notice(F("WEB : Start firmware upload, max sketch size %d kb." CR),
maxSketchSpace / 1024);
if (!Update.begin(maxSketchSpace, U_FLASH, PIN_LED)) {
ErrorFileLog errLog;
errLog.addEntry(
F("WEB : Not enough space to store for this firmware."));
_uploadReturn = 500;
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
Log.notice(F("WEB : Writing firmware upload %d (%d)." CR),
upload.totalSize, maxSketchSpace);
if (upload.totalSize > maxSketchSpace) {
Log.error(F("WEB : Firmware file is to large." CR));
_uploadReturn = 500;
} else if (Update.write(upload.buf, upload.currentSize) !=
upload.currentSize) {
Log.warning(F("WEB : Firmware write was unsuccessful." CR));
_uploadReturn = 500;
}
} else if (upload.status == UPLOAD_FILE_END) {
Log.notice(F("WEB : Finish firmware upload." CR));
if (Update.end(true)) {
_server->send(200);
delay(500);
ESP_RESET();
} else {
ErrorFileLog errLog;
errLog.addEntry("WEB : Failed to finish firmware flashing error=" +
String(Update.getError()));
_uploadReturn = 500;
}
} else {
Update.end();
Log.notice(F("WEB : Firmware flashing aborted." CR));
_uploadReturn = 500;
}
delay(0);
} else {
// Handle HTML file upload
if (upload.status == UPLOAD_FILE_START) {
_uploadReturn = 200;
Log.notice(F("WEB : Start html upload." CR));
if (validFilename) _uploadFile = LittleFS.open(f, "w");
} else if (upload.status == UPLOAD_FILE_WRITE) {
Log.notice(F("WEB : Writing html upload." CR));
if (_uploadFile) _uploadFile.write(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
Log.notice(F("WEB : Finish html upload." CR));
if (_uploadFile) {
_uploadFile.close();
Log.notice(F("WEB : Html file uploaded %d bytes." CR),
upload.totalSize);
}
_server->sendHeader("Location", "/");
_server->send(303);
} else {
_server->send(500, "text/plain", "Couldn't upload html file.");
}
}
LOG_PERF_STOP("webserver-api-upload-file");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleCalibrate() {
LOG_PERF_START("webserver-api-calibrate");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/calibrate." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-calibrate");
return;
}
myGyro.calibrateSensor();
_server->send(200, "text/plain", "Device calibrated");
LOG_PERF_STOP("webserver-api-calibrate");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleFactoryDefaults() {
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/factory." CR));
if (!id.compareTo(myConfig.getID())) {
_server->send(200, "text/plain",
"Removing configuration and restarting...");
LittleFS.remove(CFG_FILENAME);
LittleFS.remove(CFG_HW_FILENAME);
LittleFS.remove(ERR_FILENAME);
LittleFS.remove(RUNTIME_FILENAME);
LittleFS.remove(TPL_FNAME_HTTP1);
LittleFS.remove(TPL_FNAME_HTTP2);
LittleFS.remove(TPL_FNAME_INFLUXDB);
LittleFS.remove(TPL_FNAME_MQTT);
LittleFS.end();
delay(500);
ESP_RESET();
} else {
_server->send(400, "text/plain", "Unknown ID.");
}
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleStatus() {
LOG_PERF_START("webserver-api-status");
Log.notice(F("WEB : webServer callback for /api/status(get)." CR));
DynamicJsonDocument doc(512);
double angle = 0;
if (myGyro.hasValue()) angle = myGyro.getAngle();
double tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
double gravity = calculateGravity(angle, tempC);
doc[PARAM_ID] = myConfig.getID();
doc[PARAM_ANGLE] = reduceFloatPrecision(angle);
if (myConfig.isGravityTempAdj()) {
gravity = gravityTemperatureCorrectionC(gravity, tempC);
}
if (myConfig.isGravityPlato()) {
doc[PARAM_GRAVITY] = reduceFloatPrecision(convertToPlato(gravity), 1);
} else {
doc[PARAM_GRAVITY] = reduceFloatPrecision(gravity, 4);
}
doc[PARAM_TEMP_C] = reduceFloatPrecision(tempC, 1);
doc[PARAM_TEMP_F] = reduceFloatPrecision(convertCtoF(tempC), 1);
doc[PARAM_BATTERY] = reduceFloatPrecision(myBatteryVoltage.getVoltage());
doc[PARAM_TEMPFORMAT] = String(myConfig.getTempFormat());
doc[PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
doc[PARAM_SLEEP_MODE] = sleepModeAlwaysSkip;
doc[PARAM_RSSI] = WiFi.RSSI();
doc[PARAM_SLEEP_INTERVAL] = myConfig.getSleepInterval();
doc[PARAM_TOKEN] = myConfig.getToken();
doc[PARAM_TOKEN2] = myConfig.getToken2();
doc[PARAM_APP_VER] = CFG_APPVER;
doc[PARAM_APP_BUILD] = CFG_GITREV;
doc[PARAM_MDNS] = myConfig.getMDNS();
doc[PARAM_SSID] = WiFi.SSID();
FloatHistoryLog runLog(RUNTIME_FILENAME);
doc[PARAM_RUNTIME_AVERAGE] = reduceFloatPrecision(
runLog.getAverage() ? runLog.getAverage() / 1000 : 0, 1);
#if defined(ESP8266)
doc[PARAM_PLATFORM] = "esp8266";
#else
doc[PARAM_PLATFORM] = "esp32";
#endif
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(300);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-status");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleClearWIFI() {
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/clearwifi." CR));
if (!id.compareTo(myConfig.getID())) {
_server->send(200, "text/plain",
"Clearing WIFI credentials and doing reset...");
myConfig.setWifiPass("", 0);
myConfig.setWifiSSID("", 0);
myConfig.setWifiPass("", 1);
myConfig.setWifiSSID("", 1);
myConfig.saveFile();
delay(1000);
WiFi.disconnect(); // Clear credentials
ESP_RESET();
} else {
_server->send(400, "text/plain", "Unknown ID.");
}
}
//
// Used to force the device to never sleep.
//
void WebServerHandler::webHandleStatusSleepmode() {
LOG_PERF_START("webserver-api-sleepmode");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/status/sleepmode(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-sleepmode");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->arg(PARAM_SLEEP_MODE).equalsIgnoreCase("true"))
sleepModeAlwaysSkip = true;
else
sleepModeAlwaysSkip = false;
_server->send(200, "text/plain", "Sleep mode updated");
LOG_PERF_STOP("webserver-api-sleepmode");
}
//
// Update device settings.
//
void WebServerHandler::webHandleConfigDevice() {
LOG_PERF_START("webserver-api-config-device");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/device(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-device");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->hasArg(PARAM_MDNS))
myConfig.setMDNS(_server->arg(PARAM_MDNS).c_str());
if (_server->hasArg(PARAM_TEMPFORMAT))
myConfig.setTempFormat(_server->arg(PARAM_TEMPFORMAT).charAt(0));
if (_server->hasArg(PARAM_SLEEP_INTERVAL))
myConfig.setSleepInterval(_server->arg(PARAM_SLEEP_INTERVAL).c_str());
myConfig.saveFile();
_server->sendHeader("Location", "/config.htm#collapseDevice", true);
_server->send(302, "text/plain", "Device config updated");
LOG_PERF_STOP("webserver-api-config-device");
}
//
// Update push settings.
//
void WebServerHandler::webHandleConfigPush() {
LOG_PERF_START("webserver-api-config-push");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/push(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-push");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->hasArg(PARAM_TOKEN))
myConfig.setToken(_server->arg(PARAM_TOKEN).c_str());
if (_server->hasArg(PARAM_TOKEN2))
myConfig.setToken2(_server->arg(PARAM_TOKEN2).c_str());
if (_server->hasArg(PARAM_PUSH_HTTP))
myConfig.setHttpUrl(_server->arg(PARAM_PUSH_HTTP).c_str());
if (_server->hasArg(PARAM_PUSH_HTTP_H1))
myConfig.setHttpHeader(_server->arg(PARAM_PUSH_HTTP_H1).c_str(), 0);
if (_server->hasArg(PARAM_PUSH_HTTP_H2))
myConfig.setHttpHeader(_server->arg(PARAM_PUSH_HTTP_H2).c_str(), 1);
if (_server->hasArg(PARAM_PUSH_HTTP2))
myConfig.setHttp2Url(_server->arg(PARAM_PUSH_HTTP2).c_str());
if (_server->hasArg(PARAM_PUSH_HTTP2_H1))
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H1).c_str(), 0);
if (_server->hasArg(PARAM_PUSH_HTTP2_H2))
myConfig.setHttp2Header(_server->arg(PARAM_PUSH_HTTP2_H2).c_str(), 1);
if (_server->hasArg(PARAM_PUSH_HTTP3))
myConfig.setHttp3Url(_server->arg(PARAM_PUSH_HTTP3).c_str());
if (_server->hasArg(PARAM_PUSH_INFLUXDB2))
myConfig.setInfluxDb2PushUrl(_server->arg(PARAM_PUSH_INFLUXDB2).c_str());
if (_server->hasArg(PARAM_PUSH_INFLUXDB2_ORG))
myConfig.setInfluxDb2PushOrg(
_server->arg(PARAM_PUSH_INFLUXDB2_ORG).c_str());
if (_server->hasArg(PARAM_PUSH_INFLUXDB2_BUCKET))
myConfig.setInfluxDb2PushBucket(
_server->arg(PARAM_PUSH_INFLUXDB2_BUCKET).c_str());
if (_server->hasArg(PARAM_PUSH_INFLUXDB2_AUTH))
myConfig.setInfluxDb2PushToken(
_server->arg(PARAM_PUSH_INFLUXDB2_AUTH).c_str());
if (_server->hasArg(PARAM_PUSH_MQTT))
myConfig.setMqttUrl(_server->arg(PARAM_PUSH_MQTT).c_str());
if (_server->hasArg(PARAM_PUSH_MQTT_PORT))
myConfig.setMqttPort(_server->arg(PARAM_PUSH_MQTT_PORT).c_str());
if (_server->hasArg(PARAM_PUSH_MQTT_USER))
myConfig.setMqttUser(_server->arg(PARAM_PUSH_MQTT_USER).c_str());
if (_server->hasArg(PARAM_PUSH_MQTT_PASS))
myConfig.setMqttPass(_server->arg(PARAM_PUSH_MQTT_PASS).c_str());
myConfig.saveFile();
String section("/config.htm#");
section += _server->arg("section");
_server->sendHeader("Location", section.c_str(), true);
_server->send(302, "text/plain", "Push config updated");
LOG_PERF_STOP("webserver-api-config-push");
}
//
// Get string with all received arguments. Used for debugging only.
//
String WebServerHandler::getRequestArguments() {
String debug;
for (int i = 0; i < _server->args(); i++) {
if (!_server->argName(i).equals(
"plain")) { // this contains all the arguments, we dont need that.
if (debug.length()) debug += ", ";
debug += _server->argName(i);
debug += "=";
debug += _server->arg(i);
}
}
return debug;
}
//
// Update gravity settings.
//
void WebServerHandler::webHandleConfigGravity() {
LOG_PERF_START("webserver-api-config-gravity");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/gravity(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-gravity");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->hasArg(PARAM_GRAVITY_FORMAT))
myConfig.setGravityFormat(_server->arg(PARAM_GRAVITY_FORMAT).charAt(0));
if (_server->hasArg(PARAM_GRAVITY_FORMULA))
myConfig.setGravityFormula(_server->arg(PARAM_GRAVITY_FORMULA).c_str());
if (_server->hasArg(PARAM_GRAVITY_TEMP_ADJ))
myConfig.setGravityTempAdj(
_server->arg(PARAM_GRAVITY_TEMP_ADJ).equalsIgnoreCase("on") ? true
: false);
else
myConfig.setGravityTempAdj(false);
myConfig.saveFile();
_server->sendHeader("Location", "/config.htm#collapseGravity", true);
_server->send(302, "text/plain", "Gravity config updated");
LOG_PERF_STOP("webserver-api-config-gravity");
}
//
// Update hardware settings.
//
void WebServerHandler::webHandleConfigHardware() {
LOG_PERF_START("webserver-api-config-hardware");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/hardware(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-hardware");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->hasArg(PARAM_VOLTAGEFACTOR))
myConfig.setVoltageFactor(_server->arg(PARAM_VOLTAGEFACTOR).toFloat());
if (_server->hasArg(PARAM_TEMP_ADJ)) {
if (myConfig.isTempC()) {
myConfig.setTempSensorAdjC(_server->arg(PARAM_TEMP_ADJ));
} else {
// Data is delta so we add 32 in order to conver to C.
myConfig.setTempSensorAdjF(_server->arg(PARAM_TEMP_ADJ), 32);
}
}
if (_server->hasArg(PARAM_BLE))
myConfig.setColorBLE(_server->arg(PARAM_BLE).c_str());
if (_server->hasArg(PARAM_OTA))
myConfig.setOtaURL(_server->arg(PARAM_OTA).c_str());
if (_server->hasArg(PARAM_GYRO_TEMP))
myConfig.setGyroTemp(
_server->arg(PARAM_GYRO_TEMP).equalsIgnoreCase("on") ? true : false);
else
myConfig.setGyroTemp(false);
if (_server->hasArg(PARAM_STORAGE_SLEEP))
myConfig.setStorageSleep(
_server->arg(PARAM_STORAGE_SLEEP).equalsIgnoreCase("on") ? true : false);
else
myConfig.setStorageSleep(false);
myConfig.saveFile();
_server->sendHeader("Location", "/config.htm#collapseHardware", true);
_server->send(302, "text/plain", "Hardware config updated");
LOG_PERF_STOP("webserver-api-config-hardware");
}
//
// Update advanced settings.
//
void WebServerHandler::webHandleConfigAdvancedWrite() {
LOG_PERF_START("webserver-api-config-advanced");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/advaced(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-advanced");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
if (_server->hasArg(PARAM_HW_GYRO_READ_COUNT))
myAdvancedConfig.setGyroReadCount(
_server->arg(PARAM_HW_GYRO_READ_COUNT).toInt());
// if (_server->hasArg(PARAM_HW_GYRO_READ_DELAY))
// myAdvancedConfig.setGyroReadDelay(
// _server->arg(PARAM_HW_GYRO_READ_DELAY).toInt());
if (_server->hasArg(PARAM_HW_GYRO_MOVING_THREASHOLD))
myAdvancedConfig.setGyroSensorMovingThreashold(
_server->arg(PARAM_HW_GYRO_MOVING_THREASHOLD).toInt());
if (_server->hasArg(PARAM_HW_FORMULA_DEVIATION))
myAdvancedConfig.setMaxFormulaCreationDeviation(
_server->arg(PARAM_HW_FORMULA_DEVIATION).toFloat());
if (_server->hasArg(PARAM_HW_FORMULA_CALIBRATION_TEMP))
myAdvancedConfig.SetDefaultCalibrationTemp(
_server->arg(PARAM_HW_FORMULA_CALIBRATION_TEMP).toFloat());
if (_server->hasArg(PARAM_HW_WIFI_PORTAL_TIMEOUT))
myAdvancedConfig.setWifiPortalTimeout(
_server->arg(PARAM_HW_WIFI_PORTAL_TIMEOUT).toInt());
if (_server->hasArg(PARAM_HW_WIFI_CONNECT_TIMEOUT))
myAdvancedConfig.setWifiConnectTimeout(
_server->arg(PARAM_HW_WIFI_CONNECT_TIMEOUT).toInt());
if (_server->hasArg(PARAM_HW_PUSH_TIMEOUT))
myAdvancedConfig.setPushTimeout(
_server->arg(PARAM_HW_PUSH_TIMEOUT).toInt());
if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP1))
myAdvancedConfig.setPushIntervalHttp1(
_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP1).toInt());
if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP2))
myAdvancedConfig.setPushIntervalHttp2(
_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP2).toInt());
if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_HTTP3))
myAdvancedConfig.setPushIntervalHttp3(
_server->arg(PARAM_HW_PUSH_INTERVAL_HTTP3).toInt());
if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_INFLUX))
myAdvancedConfig.setPushIntervalInflux(
_server->arg(PARAM_HW_PUSH_INTERVAL_INFLUX).toInt());
if (_server->hasArg(PARAM_HW_PUSH_INTERVAL_MQTT))
myAdvancedConfig.setPushIntervalMqtt(
_server->arg(PARAM_HW_PUSH_INTERVAL_MQTT).toInt());
if (_server->hasArg(PARAM_HW_TEMPSENSOR_RESOLUTION))
myAdvancedConfig.setTempSensorResolution(
_server->arg(PARAM_HW_TEMPSENSOR_RESOLUTION).toInt());
if (_server->hasArg(PARAM_HW_IGNORE_LOW_ANGLES))
myAdvancedConfig.setIgnoreLowAnges(
_server->arg(PARAM_HW_IGNORE_LOW_ANGLES).equalsIgnoreCase("on")
? true
: false);
else
myAdvancedConfig.setIgnoreLowAnges(false);
myAdvancedConfig.saveFile();
_server->sendHeader("Location", "/config.htm#collapseAdvanced", true);
_server->send(302, "text/plain", "Advanced config updated");
LOG_PERF_STOP("webserver-api-config-advanced");
}
//
// Read advanced settings
//
void WebServerHandler::webHandleConfigAdvancedRead() {
LOG_PERF_START("webserver-api-config-advanced");
Log.notice(F("WEB : webServer callback for /api/config/advanced(get)." CR));
DynamicJsonDocument doc(512);
doc[PARAM_HW_GYRO_READ_COUNT] = myAdvancedConfig.getGyroReadCount();
// doc[PARAM_HW_GYRO_READ_DELAY] = myAdvancedConfig.getGyroReadDelay();
doc[PARAM_HW_GYRO_MOVING_THREASHOLD] =
myAdvancedConfig.getGyroSensorMovingThreashold();
doc[PARAM_HW_FORMULA_DEVIATION] =
myAdvancedConfig.getMaxFormulaCreationDeviation();
doc[PARAM_HW_WIFI_PORTAL_TIMEOUT] = myAdvancedConfig.getWifiPortalTimeout();
doc[PARAM_HW_WIFI_CONNECT_TIMEOUT] = myAdvancedConfig.getWifiConnectTimeout();
doc[PARAM_HW_PUSH_TIMEOUT] = myAdvancedConfig.getPushTimeout();
doc[PARAM_HW_FORMULA_CALIBRATION_TEMP] =
myAdvancedConfig.getDefaultCalibrationTemp();
doc[PARAM_HW_PUSH_INTERVAL_HTTP1] = myAdvancedConfig.getPushIntervalHttp1();
doc[PARAM_HW_PUSH_INTERVAL_HTTP2] = myAdvancedConfig.getPushIntervalHttp2();
doc[PARAM_HW_PUSH_INTERVAL_HTTP3] = myAdvancedConfig.getPushIntervalHttp3();
doc[PARAM_HW_PUSH_INTERVAL_INFLUX] = myAdvancedConfig.getPushIntervalInflux();
doc[PARAM_HW_PUSH_INTERVAL_MQTT] = myAdvancedConfig.getPushIntervalMqtt();
doc[PARAM_HW_TEMPSENSOR_RESOLUTION] =
myAdvancedConfig.getTempSensorResolution();
doc[PARAM_HW_IGNORE_LOW_ANGLES] = myAdvancedConfig.isIgnoreLowAnges();
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(512);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-config-advanced");
}
//
// Callback from webServer when / has been accessed.
//
void WebServerHandler::webHandleFormulaRead() {
LOG_PERF_START("webserver-api-formula-read");
Log.notice(F("WEB : webServer callback for /api/formula(get)." CR));
DynamicJsonDocument doc(512);
const RawFormulaData& fd = myConfig.getFormulaData();
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
doc[PARAM_ID] = myConfig.getID();
doc[PARAM_ANGLE] = reduceFloatPrecision(myGyro.getAngle());
doc[PARAM_GRAVITY_FORMAT] = String(myConfig.getGravityFormat());
doc[PARAM_GRAVITY_FORMULA] = "";
doc[PARAM_ERROR] = "";
switch (_lastFormulaCreateError) {
case ERR_FORMULA_INTERNAL:
doc[PARAM_ERROR] = "Internal error creating formula.";
break;
case ERR_FORMULA_NOTENOUGHVALUES:
doc[PARAM_ERROR] = "Not enough values to create formula.";
break;
case ERR_FORMULA_UNABLETOFFIND:
doc[PARAM_ERROR] = "Unable to find an accurate formula based on input.";
break;
default:
doc[PARAM_GRAVITY_FORMULA] = myConfig.getGravityFormula();
break;
}
doc["a1"] = reduceFloatPrecision(fd.a[0], 2);
doc["a2"] = reduceFloatPrecision(fd.a[1], 2);
doc["a3"] = reduceFloatPrecision(fd.a[2], 2);
doc["a4"] = reduceFloatPrecision(fd.a[3], 2);
doc["a5"] = reduceFloatPrecision(fd.a[4], 2);
doc["a6"] = reduceFloatPrecision(fd.a[5], 2);
doc["a7"] = reduceFloatPrecision(fd.a[6], 2);
doc["a8"] = reduceFloatPrecision(fd.a[7], 2);
doc["a9"] = reduceFloatPrecision(fd.a[8], 2);
doc["a10"] = reduceFloatPrecision(fd.a[9], 2);
if (myConfig.isGravityPlato()) {
doc["g1"] = reduceFloatPrecision(convertToPlato(fd.g[0]), 1);
doc["g2"] = reduceFloatPrecision(convertToPlato(fd.g[1]), 1);
doc["g3"] = reduceFloatPrecision(convertToPlato(fd.g[2]), 1);
doc["g4"] = reduceFloatPrecision(convertToPlato(fd.g[3]), 1);
doc["g5"] = reduceFloatPrecision(convertToPlato(fd.g[4]), 1);
doc["g6"] = reduceFloatPrecision(convertToPlato(fd.g[5]), 1);
doc["g7"] = reduceFloatPrecision(convertToPlato(fd.g[6]), 1);
doc["g8"] = reduceFloatPrecision(convertToPlato(fd.g[7]), 1);
doc["g9"] = reduceFloatPrecision(convertToPlato(fd.g[8]), 1);
doc["g10"] = reduceFloatPrecision(convertToPlato(fd.g[9]), 1);
} else {
doc["g1"] = reduceFloatPrecision(fd.g[0], 4);
doc["g2"] = reduceFloatPrecision(fd.g[1], 4);
doc["g3"] = reduceFloatPrecision(fd.g[2], 4);
doc["g4"] = reduceFloatPrecision(fd.g[3], 4);
doc["g5"] = reduceFloatPrecision(fd.g[4], 4);
doc["g6"] = reduceFloatPrecision(fd.g[5], 4);
doc["g7"] = reduceFloatPrecision(fd.g[6], 4);
doc["g8"] = reduceFloatPrecision(fd.g[7], 4);
doc["g9"] = reduceFloatPrecision(fd.g[8], 4);
doc["g10"] = reduceFloatPrecision(fd.g[9], 4);
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(256);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-formula-read");
}
//
// Update format template
//
void WebServerHandler::webHandleConfigFormatWrite() {
LOG_PERF_START("webserver-api-config-format-write");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/config/format(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-config-format-write");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
bool success = false;
// Only one option is posted so we done need to check them all.
if (_server->hasArg(PARAM_FORMAT_HTTP1)) {
success = writeFile(TPL_FNAME_HTTP1, _server->arg(PARAM_FORMAT_HTTP1));
} else if (_server->hasArg(PARAM_FORMAT_HTTP2)) {
success = writeFile(TPL_FNAME_HTTP2, _server->arg(PARAM_FORMAT_HTTP2));
} else if (_server->hasArg(PARAM_FORMAT_HTTP3)) {
success = writeFile(TPL_FNAME_HTTP3, _server->arg(PARAM_FORMAT_HTTP3));
} else if (_server->hasArg(PARAM_FORMAT_INFLUXDB)) {
success =
writeFile(TPL_FNAME_INFLUXDB, _server->arg(PARAM_FORMAT_INFLUXDB));
} else if (_server->hasArg(PARAM_FORMAT_MQTT)) {
success = writeFile(TPL_FNAME_MQTT, _server->arg(PARAM_FORMAT_MQTT));
}
if (success) {
_server->sendHeader("Location", "/format.htm", true);
_server->send(302, "text/plain", "Format updated");
} else {
ErrorFileLog errLog;
errLog.addEntry(F("WEB : Unable to store format file"));
_server->send(400, "text/plain", "Unable to store format in file.");
}
LOG_PERF_STOP("webserver-api-config-format-write");
}
//
// Get format with real data
//
void WebServerHandler::webHandleTestPush() {
LOG_PERF_START("webserver-api-test-push");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/test/push." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-test-push");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
float angle = myGyro.getAngle();
float tempC = myTempSensor.getTempC(myConfig.isGyroTemp());
float gravitySG = calculateGravity(angle, tempC);
float corrGravitySG = gravityTemperatureCorrectionC(gravitySG, tempC);
TemplatingEngine engine;
engine.initialize(angle, gravitySG, corrGravitySG, tempC, 2.1);
const String& type = _server->arg(PARAM_PUSH_FORMAT);
PushTarget push;
bool enabled = false;
if (!type.compareTo(PARAM_FORMAT_HTTP1) && myConfig.isHttpActive()) {
push.sendHttp1(engine, myConfig.isHttpSSL());
enabled = true;
} else if (!type.compareTo(PARAM_FORMAT_HTTP2) && myConfig.isHttp2Active()) {
push.sendHttp2(engine, myConfig.isHttp2SSL());
enabled = true;
} else if (!type.compareTo(PARAM_FORMAT_HTTP3) && myConfig.isHttp3Active()) {
push.sendHttp3(engine, myConfig.isHttp3SSL());
enabled = true;
} else if (!type.compareTo(PARAM_FORMAT_INFLUXDB) &&
myConfig.isInfluxDb2Active()) {
push.sendInfluxDb2(engine, myConfig.isInfluxSSL());
enabled = true;
} else if (!type.compareTo(PARAM_FORMAT_MQTT) && myConfig.isMqttActive()) {
push.sendMqtt(engine, myConfig.isMqttSSL());
enabled = true;
}
DynamicJsonDocument doc(100);
doc[PARAM_PUSH_ENABLED] = enabled;
doc[PARAM_PUSH_SUCCESS] = push.getLastSuccess();
doc[PARAM_PUSH_CODE] = push.getLastCode();
String out;
out.reserve(100);
serializeJson(doc, out);
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-test-push");
}
//
// Write file to disk, if there is no data then delete the current file (if it
// exists) = reset to default.
//
bool WebServerHandler::writeFile(String fname, String data) {
if (data.length()) {
data = urldecode(data);
File file = LittleFS.open(fname, "w");
if (file) {
Log.notice(F("WEB : Storing template data in %s." CR), fname.c_str());
#if defined(ESP8266)
file.write(data.c_str());
#else // defined (ESP32)
file.write((unsigned char*)data.c_str(), data.length());
#endif
file.close();
return true;
}
} else {
Log.notice(
F("WEB : No template data to store in %s, reverting to default." CR),
fname.c_str());
LittleFS.remove(fname);
return true;
}
return false;
}
//
// Read file from disk
//
String WebServerHandler::readFile(String fname) {
File file = LittleFS.open(fname, "r");
if (file) {
char buf[file.size() + 1];
memset(&buf[0], 0, file.size() + 1);
file.readBytes(&buf[0], file.size());
file.close();
Log.notice(F("WEB : Read template data from %s." CR), fname.c_str());
return String(&buf[0]);
}
return "";
}
//
// Get format templates
//
void WebServerHandler::webHandleConfigFormatRead() {
LOG_PERF_START("webserver-api-config-format-read");
Log.notice(F("WEB : webServer callback for /api/config/formula(get)." CR));
DynamicJsonDocument doc(2048);
doc[PARAM_ID] = myConfig.getID();
String s = readFile(TPL_FNAME_HTTP1);
if (s.length())
doc[PARAM_FORMAT_HTTP1] = urlencode(s);
else
doc[PARAM_FORMAT_HTTP1] = urlencode(String(&iSpindleFormat[0]));
s = readFile(TPL_FNAME_HTTP2);
if (s.length())
doc[PARAM_FORMAT_HTTP2] = urlencode(s);
else
doc[PARAM_FORMAT_HTTP2] = urlencode(String(&iSpindleFormat[0]));
s = readFile(TPL_FNAME_HTTP3);
if (s.length())
doc[PARAM_FORMAT_HTTP3] = urlencode(s);
else
doc[PARAM_FORMAT_HTTP3] = urlencode(String(&iHttpGetFormat[0]));
s = readFile(TPL_FNAME_INFLUXDB);
if (s.length())
doc[PARAM_FORMAT_INFLUXDB] = urlencode(s);
else
doc[PARAM_FORMAT_INFLUXDB] = urlencode(String(&influxDbFormat[0]));
s = readFile(TPL_FNAME_MQTT);
if (s.length())
doc[PARAM_FORMAT_MQTT] = urlencode(s);
else
doc[PARAM_FORMAT_MQTT] = urlencode(String(&mqttFormat[0]));
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
serializeJson(doc, Serial);
Serial.print(CR);
#endif
String out;
out.reserve(2048);
serializeJson(doc, out);
_server->send(200, "application/json", out.c_str());
LOG_PERF_STOP("webserver-api-config-format-read");
}
//
// Update hardware settings.
//
void WebServerHandler::webHandleFormulaWrite() {
LOG_PERF_START("webserver-api-formula-write");
String id = _server->arg(PARAM_ID);
Log.notice(F("WEB : webServer callback for /api/formula(post)." CR));
if (!id.equalsIgnoreCase(myConfig.getID())) {
Log.error(F("WEB : Wrong ID received %s, expected %s" CR), id.c_str(),
myConfig.getID());
_server->send(400, "text/plain", "Invalid ID.");
LOG_PERF_STOP("webserver-api-formula-write");
return;
}
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : %s." CR), getRequestArguments().c_str());
#endif
RawFormulaData fd;
fd.a[0] = _server->arg("a1").toDouble();
fd.a[1] = _server->arg("a2").toDouble();
fd.a[2] = _server->arg("a3").toDouble();
fd.a[3] = _server->arg("a4").toDouble();
fd.a[4] = _server->arg("a5").toDouble();
fd.a[5] = _server->arg("a6").toDouble();
fd.a[6] = _server->arg("a7").toDouble();
fd.a[7] = _server->arg("a8").toDouble();
fd.a[8] = _server->arg("a9").toDouble();
fd.a[9] = _server->arg("a10").toDouble();
if (myConfig.isGravityPlato()) {
fd.g[0] = convertToSG(_server->arg("g1").toDouble());
fd.g[1] = convertToSG(_server->arg("g2").toDouble());
fd.g[2] = convertToSG(_server->arg("g3").toDouble());
fd.g[3] = convertToSG(_server->arg("g4").toDouble());
fd.g[4] = convertToSG(_server->arg("g5").toDouble());
fd.g[5] = convertToSG(_server->arg("g6").toDouble());
fd.g[6] = convertToSG(_server->arg("g7").toDouble());
fd.g[7] = convertToSG(_server->arg("g8").toDouble());
fd.g[8] = convertToSG(_server->arg("g9").toDouble());
fd.g[9] = convertToSG(_server->arg("g10").toDouble());
} else {
fd.g[0] = _server->arg("g1").toDouble();
fd.g[1] = _server->arg("g2").toDouble();
fd.g[2] = _server->arg("g3").toDouble();
fd.g[3] = _server->arg("g4").toDouble();
fd.g[4] = _server->arg("g5").toDouble();
fd.g[5] = _server->arg("g6").toDouble();
fd.g[6] = _server->arg("g7").toDouble();
fd.g[7] = _server->arg("g8").toDouble();
fd.g[8] = _server->arg("g9").toDouble();
fd.g[9] = _server->arg("g10").toDouble();
}
fd.g[0] = 1; // force first point to SG gravity of water
myConfig.setFormulaData(fd);
int e;
char buf[100];
e = createFormula(fd, &buf[0], sizeof(buf), 2);
if (e) {
// If we fail with order=2 try with 3
Log.warning(F("WEB : Failed to find formula with order 3." CR), e);
e = createFormula(fd, &buf[0], sizeof(buf), 3);
}
if (e) {
// If we fail with order=3 try with 4
Log.warning(F("WEB : Failed to find formula with order 4." CR), e);
e = createFormula(fd, &buf[0], sizeof(buf), 4);
}
if (e) {
// If we fail with order=4 then we mark it as failed
Log.error(
F("WEB : Unable to find formula based on provided values err=%d." CR),
e);
_lastFormulaCreateError = e;
} else {
// Save the formula as succesful
Log.info(F("WEB : Found valid formula: '%s'" CR), &buf[0]);
myConfig.setGravityFormula(buf);
_lastFormulaCreateError = 0;
}
myConfig.saveFile();
_server->sendHeader("Location", "/calibration.htm", true);
_server->send(302, "text/plain", "Formula updated");
LOG_PERF_STOP("webserver-api-formula-write");
}
//
// Helper function to check if files exist on file system.
//
const char* WebServerHandler::getHtmlFileName(HtmlFile item) {
Log.notice(F("WEB : Looking up filename for %d." CR), item);
switch (item) {
case HtmlFile::HTML_INDEX:
return "index.min.htm";
case HtmlFile::HTML_CONFIG:
return "config.min.htm";
case HtmlFile::HTML_CALIBRATION:
return "calibration.min.htm";
case HtmlFile::HTML_FORMAT:
return "format.min.htm";
case HtmlFile::HTML_ABOUT:
return "about.min.htm";
case HtmlFile::HTML_TEST:
return "test.min.htm";
}
return "";
}
//
// Helper function to check if files exist on file system.
//
bool WebServerHandler::checkHtmlFile(HtmlFile item) {
const char* fn = getHtmlFileName(item);
#if LOG_LEVEL == 6 && !defined(WEB_DISABLE_LOGGING)
Log.verbose(F("WEB : Checking for file %s." CR), fn);
#endif
// TODO: We might need to add more checks here like zero file size etc. But
// for now we only check if the file exist.
return LittleFS.exists(fn);
}
//
// Handler for page not found
//
void WebServerHandler::webHandlePageNotFound() {
Log.error(F("WEB : URL not found %s received." CR), _server->uri().c_str());
_server->send(404, "text/plain", F("URL not found"));
}
//
// Setup the Web Server callbacks and start it
//
bool WebServerHandler::setupWebServer() {
Log.notice(F("WEB : Configuring web server." CR));
_server = new ESP8266WebServer();
MDNS.begin(myConfig.getMDNS());
MDNS.addService("http", "tcp", 80);
// Show files in the filessytem at startup
#if defined(ESP8266)
FSInfo fs;
LittleFS.info(fs);
Log.notice(F("WEB : File system Total=%d, Used=%d." CR), fs.totalBytes,
fs.usedBytes);
Dir dir = LittleFS.openDir("/");
while (dir.next()) {
Log.notice(F("WEB : File=%s, %d bytes" CR), dir.fileName().c_str(),
dir.fileSize());
if (!dir.fileSize()) {
Log.notice(F("WEB : Empty file detected, removing file." CR));
LittleFS.remove(dir.fileName().c_str());
}
}
#else // defined( ESP32 )
File root = LittleFS.open("/");
File f = root.openNextFile();
while (f) {
Log.notice(F("WEB : File=%s, %d bytes" CR), f.name(), f.size());
if (!f.size()) {
Log.notice(F("WEB : Empty file detected, removing file." CR));
LittleFS.remove(f.name());
}
f = root.openNextFile();
}
f.close();
root.close();
#endif
// Static content
#if defined(EMBED_HTML)
_server->on("/", std::bind(&WebServerHandler::webReturnIndexHtm, this));
_server->on("/index.htm",
std::bind(&WebServerHandler::webReturnIndexHtm, this));
_server->on("/config.htm",
std::bind(&WebServerHandler::webReturnConfigHtm, this));
_server->on("/calibration.htm",
std::bind(&WebServerHandler::webReturnCalibrationHtm, this));
_server->on("/format.htm",
std::bind(&WebServerHandler::webReturnFormatHtm, this));
_server->on("/about.htm",
std::bind(&WebServerHandler::webReturnAboutHtm, this));
_server->on("/test.htm",
std::bind(&WebServerHandler::webReturnTestHtm, this));
#else
// Check if the html files exist, if so serve them, else show the static
// upload page.
if (checkHtmlFile(HTML_INDEX) && checkHtmlFile(HTML_CONFIG) &&
checkHtmlFile(HTML_CALIBRATION) && checkHtmlFile(HTML_FORMAT) &&
checkHtmlFile(HTML_ABOUT) && checkHtmlFile(HTML_TEST)) {
Log.notice(F("WEB : All html files exist, starting in normal mode." CR));
_server->serveStatic("/", LittleFS, "/index.min.htm");
_server->serveStatic("/index.htm", LittleFS, "/index.min.htm");
_server->serveStatic("/config.htm", LittleFS, "/config.min.htm");
_server->serveStatic("/about.htm", LittleFS, "/about.min.htm");
_server->serveStatic("/test.htm", LittleFS, "/test.min.htm");
_server->serveStatic("/calibration.htm", LittleFS, "/calibration.min.htm");
_server->serveStatic("/format.htm", LittleFS, "/format.min.htm");
// Also add the static upload view in case we we have issues that needs to
// be fixed.
_server->on("/upload.htm",
std::bind(&WebServerHandler::webReturnUploadHtm, this));
} else {
Log.error(F("WEB : Missing html files, starting with upload UI." CR));
_server->on("/", std::bind(&WebServerHandler::webReturnUploadHtm, this));
}
#endif
_server->on("/firmware.htm",
std::bind(&WebServerHandler::webReturnFirmwareHtm, this));
_server->serveStatic("/log", LittleFS, ERR_FILENAME);
_server->serveStatic("/runtime", LittleFS, RUNTIME_FILENAME);
// Dynamic content
_server->on(
"/api/config", HTTP_GET,
std::bind(&WebServerHandler::webHandleConfig, this)); // Get config.json
_server->on("/api/formula", HTTP_GET,
std::bind(&WebServerHandler::webHandleFormulaRead,
this)); // Get formula.json (calibration page)
_server->on("/api/formula", HTTP_POST,
std::bind(&WebServerHandler::webHandleFormulaWrite,
this)); // Get formula.json (calibration page)
_server->on("/api/calibrate", HTTP_POST,
std::bind(&WebServerHandler::webHandleCalibrate,
this)); // Run calibration routine (param id)
_server->on("/api/factory", HTTP_GET,
std::bind(&WebServerHandler::webHandleFactoryDefaults,
this)); // Reset the device
_server->on("/api/status", HTTP_GET,
std::bind(&WebServerHandler::webHandleStatus,
this)); // Get the status.json
_server->on("/api/clearwifi", HTTP_GET,
std::bind(&WebServerHandler::webHandleClearWIFI,
this)); // Clear wifi settings
_server->on(
"/api/upload", HTTP_GET,
std::bind(&WebServerHandler::webHandleUpload, this)); // Get upload.json
_server->on("/api/upload", HTTP_POST,
std::bind(&WebServerHandler::webReturnOK, this),
std::bind(&WebServerHandler::webHandleUploadFile,
this)); // File upload data
_server->on("/api/status/sleepmode", HTTP_POST,
std::bind(&WebServerHandler::webHandleStatusSleepmode,
this)); // Change sleep mode
_server->on("/api/config/device", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigDevice,
this)); // Change device settings
_server->on("/api/config/push", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigPush,
this)); // Change push settings
_server->on("/api/config/gravity", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigGravity,
this)); // Change gravity settings
_server->on("/api/config/hardware", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigHardware,
this)); // Change hardware settings
_server->on("/api/config/format", HTTP_GET,
std::bind(&WebServerHandler::webHandleConfigFormatRead,
this)); // Change template formats
_server->on("/api/config/format", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigFormatWrite,
this)); // Change template formats
_server->on("/api/config/advanced", HTTP_GET,
std::bind(&WebServerHandler::webHandleConfigAdvancedRead,
this)); // Read advanced settings
_server->on("/api/config/advanced", HTTP_POST,
std::bind(&WebServerHandler::webHandleConfigAdvancedWrite,
this)); // Change advanced params
_server->on("/api/test/push", HTTP_GET,
std::bind(&WebServerHandler::webHandleTestPush,
this)); //
_server->onNotFound(
std::bind(&WebServerHandler::webHandlePageNotFound, this));
_server->begin();
Log.notice(F("WEB : Web server started." CR));
return true;
}
//
// called from main loop
//
void WebServerHandler::loop() {
#if defined(ESP8266)
MDNS.update();
#endif
_server->handleClient();
}
// EOF