1360 lines
48 KiB
C++
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
|