PCB lives now in its own git repo https://git.la10cy.net/DeltaLima/CanGrow-12V-PCB
1721 lines
59 KiB
C
1721 lines
59 KiB
C
/*
|
|
*
|
|
* include/Webserver/Page_system.h - system settings page header file
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#include "Page_system_HTML.h"
|
|
|
|
/* global runtime variables */
|
|
|
|
/* VERY VERY DIRTY WORKAROUND
|
|
* I have the problem, that I cannot pass a parameter I receive from a http
|
|
* request to it's template processor. In my case i want to edit an output,
|
|
* the user should click an edit button on the system/output overview page.
|
|
* I am lazy so i want to reuse the output_add page, because it is quite
|
|
* kinda exactly the same. so i want to call GET /system/output/add?edit=ID
|
|
* I have searched and came to the conclusion, that at this point i see no
|
|
* other way then giving the parameter I need, the outputId, to an global
|
|
* variable, so the template processor can read it.
|
|
*/
|
|
byte tmpParam_editOutputId = 255;
|
|
byte tmpParam_editSensorId = 255;
|
|
byte tmpParam_calibrateSensorId = 255;
|
|
|
|
|
|
/* subnav processor */
|
|
const byte WEB_SYSTEM_SUBNAV_GENERAL = 1;
|
|
const byte WEB_SYSTEM_SUBNAV_SENSOR = 2;
|
|
const byte WEB_SYSTEM_SUBNAV_OUTPUT = 3;
|
|
const byte WEB_SYSTEM_SUBNAV_UPDATE = 4;
|
|
const byte WEB_SYSTEM_SUBNAV_RESTART = 5;
|
|
const byte WEB_SYSTEM_SUBNAV_WIPE = 6;
|
|
|
|
bool Test_WebPage_system_SUBNAV(const String& var) {
|
|
if(
|
|
(var == "SUBNAV") ||
|
|
(var == "ACTIVE_SUBNAV_GENERAL") ||
|
|
(var == "ACTIVE_SUBNAV_SENSOR") ||
|
|
(var == "ACTIVE_SUBNAV_OUTPUT") ||
|
|
(var == "ACTIVE_SUBNAV_UPDATE") ||
|
|
(var == "ACTIVE_SUBNAV_RESTART") ||
|
|
(var == "ACTIVE_SUBNAV_WIPE")) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Proc_WebPage_system_SUBNAV - subnav processor for system
|
|
* this function works as same as AddHeaderFooter from Common.h
|
|
* byte activeSubnav:
|
|
* 1 - Output
|
|
* 2 - Update
|
|
* 3 - Restart
|
|
* 4 - Wipe
|
|
*/
|
|
String Proc_WebPage_system_SUBNAV(const String& var, byte activeSubnav = 0) {
|
|
String activeSubnav_ClassName = "activeNav";
|
|
if(var == "SUBNAV") {
|
|
return String(Page_system_HTML_SUBNAV);
|
|
} else if((var == "ACTIVE_SUBNAV_GENERAL") && (activeSubnav == WEB_SYSTEM_SUBNAV_GENERAL)) {
|
|
return activeSubnav_ClassName;
|
|
} else if((var == "ACTIVE_SUBNAV_SENSOR") && (activeSubnav == WEB_SYSTEM_SUBNAV_SENSOR)) {
|
|
return activeSubnav_ClassName;
|
|
} else if((var == "ACTIVE_SUBNAV_OUTPUT") && (activeSubnav == WEB_SYSTEM_SUBNAV_OUTPUT)) {
|
|
return activeSubnav_ClassName;
|
|
} else if((var == "ACTIVE_SUBNAV_UPDATE") && (activeSubnav == WEB_SYSTEM_SUBNAV_UPDATE)) {
|
|
return activeSubnav_ClassName;
|
|
} else if((var == "ACTIVE_SUBNAV_RESTART") && (activeSubnav == WEB_SYSTEM_SUBNAV_RESTART)) {
|
|
return activeSubnav_ClassName;
|
|
} else if((var == "ACTIVE_SUBNAV_WIPE") && (activeSubnav == WEB_SYSTEM_SUBNAV_WIPE)) {
|
|
return activeSubnav_ClassName;
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Main system page
|
|
*/
|
|
// https://techtutorialsx.com/2018/07/23/esp32-arduino-http-server-template-processing-with-multiple-placeholders/
|
|
String Proc_WebPage_system(const String& var) {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system(Proc)]";
|
|
/* This is a processor function, which returns a string.
|
|
* We check if var contains one of our placeholders from the template.
|
|
* If we hit a placeholder, we just return the String we want.
|
|
*
|
|
* TestHeaderFooter() Is kinda a processor too, but only checks for
|
|
* header specific placeholders.
|
|
*/
|
|
|
|
//Log.verbose(F("%s var: %s" CR), LogLoc, var);
|
|
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_GENERAL);
|
|
} else if(var == "NTPOFFSET") {
|
|
return String(config.system.ntpOffset);
|
|
|
|
} else if(var == "MAINTDUR") {
|
|
return String(config.system.maintenanceDuration);
|
|
} else if(var == "ESP32CAM") {
|
|
return String(config.system.esp32cam);
|
|
} else if(var == "HTTPLOGSERIAL") {
|
|
return Html_SelectOpt_bool(config.system.httpLogSerial);
|
|
} else if(var == "RTC_STATUS") {
|
|
/* show warn sign if rtcError is true (there was an error), otherwise green checkmark */
|
|
if(config.system.rtc > 0) {
|
|
if(rtcError == true) {
|
|
return F(" ⚠️ ");
|
|
} else {
|
|
return F(" ✅ ");
|
|
}
|
|
} else {
|
|
return String();
|
|
}
|
|
} else if(var == "RTC_AVAILABLE") {
|
|
return Html_SelectOpt_array(RTCs_total, RTCs_descr, config.system.rtc);
|
|
} else if(var == "TIME2FS") {
|
|
return Html_SelectOpt_bool(config.system.time2fs);
|
|
} else if(var == "PWMFREQ") {
|
|
return String(config.system.pwmFreq);
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_POST(const String& var) {
|
|
/* This is the processor for POST
|
|
* Its exactly the same, just looking for SAVE_MSG string.
|
|
* If nothing matches, it calles the main Proc_WebPage_system()
|
|
* processor function, so all the other stuff like header and so
|
|
* on get replaced
|
|
*/
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system(var);
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_POST_ERR(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG_ERR);
|
|
} else {
|
|
return Proc_WebPage_system(var);
|
|
}
|
|
}
|
|
|
|
/* WebPage function */
|
|
void WebPage_system(AsyncWebServerRequest *request) {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system]";
|
|
|
|
/* when changing httpLogSerial it requires a restart to take effect
|
|
* for this we keep the old val to compare it if it got changed
|
|
* to notice user for a restart */
|
|
bool old_httpLogSerial = config.system.httpLogSerial;
|
|
byte old_rtc = config.system.rtc;
|
|
short old_ntpOffset;
|
|
|
|
/* Which kind of Request */
|
|
if(request->method() == HTTP_POST) {
|
|
|
|
if(request->hasParam("ntp", true)) {
|
|
const AsyncWebParameter* param = request->getParam("ntp", true);
|
|
config.system.ntp = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("ntpOffset", true)) {
|
|
const AsyncWebParameter* param = request->getParam("ntpOffset", true);
|
|
//Log.verbose(F("%s POST[%s]: %s" CR), LogLoc, param->value().c_str());
|
|
old_ntpOffset = config.system.ntpOffset;
|
|
config.system.ntpOffset = param->value().toInt();
|
|
if((config.system.ntp == true) && (old_ntpOffset != config.system.ntpOffset)) {
|
|
// trigger ntp offset update
|
|
updateNtpOffset = true;
|
|
}
|
|
|
|
}
|
|
|
|
if(request->hasParam("maintenanceDuration", true)) {
|
|
const AsyncWebParameter* param = request->getParam("maintenanceDuration", true);
|
|
config.system.maintenanceDuration = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("esp32cam", true)) {
|
|
const AsyncWebParameter* param = request->getParam("esp32cam", true);
|
|
//config.system.esp32cam = param->value().toInt();
|
|
strlcpy(config.system.esp32cam, param->value().c_str(), sizeof(config.system.esp32cam));
|
|
}
|
|
|
|
if(request->hasParam("httpLogSerial", true)) {
|
|
const AsyncWebParameter* param = request->getParam("httpLogSerial", true);
|
|
config.system.httpLogSerial = param->value().toInt();
|
|
if( old_httpLogSerial != config.system.httpLogSerial) {
|
|
needRestart = true;
|
|
}
|
|
}
|
|
|
|
|
|
if(request->hasParam("rtc", true)) {
|
|
const AsyncWebParameter* param = request->getParam("rtc", true);
|
|
config.system.rtc = param->value().toInt();
|
|
if( old_rtc != config.system.rtc) {
|
|
needRestart = true;
|
|
if(config.system.rtc > 0)
|
|
rtcError = true;
|
|
}
|
|
}
|
|
|
|
if(request->hasParam("time2fs", true)) {
|
|
const AsyncWebParameter* param = request->getParam("time2fs", true);
|
|
config.system.time2fs = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("pwmFreq", true)) {
|
|
const AsyncWebParameter* param = request->getParam("pwmFreq", true);
|
|
config.system.pwmFreq = param->value().toInt();
|
|
#ifdef ESP8266
|
|
/* set pwm frequency global for ESP8266.
|
|
* ESP32 pwm frequency setting is done withing CanGrow_Output / Init */
|
|
analogWriteFreq(config.system.pwmFreq);
|
|
#endif
|
|
}
|
|
|
|
if(SaveConfig()) {
|
|
// we need a restart to apply the new settings
|
|
|
|
Log.notice(F("%s config saved" CR), LogLoc);
|
|
|
|
request->send_P(200, "text/html", Page_system_HTML, Proc_WebPage_system_POST);
|
|
} else {
|
|
Log.error(F("%s ERROR while saving config" CR), LogLoc);
|
|
request->send_P(200, TEXT_HTML, Page_system_HTML, Proc_WebPage_system_POST_ERR);
|
|
}
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_HTML, Proc_WebPage_system);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage restart
|
|
*/
|
|
String Proc_WebPage_system_restart(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_RESTART);
|
|
} else if(var == "RESTART_MSG") {
|
|
return String(Page_system_restart_HTML_RESTART_MSG);
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_restart_POST(const String& var) {
|
|
if(var == "RESTART_MSG") {
|
|
return String(Page_system_restart_HTML_RESTART_MSG_POST);
|
|
} else {
|
|
return Proc_WebPage_system_restart(var);
|
|
}
|
|
}
|
|
|
|
void WebPage_system_restart(AsyncWebServerRequest *request) {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:restart]";
|
|
if(request->method() == HTTP_POST) {
|
|
if(request->hasParam("confirmed", true)) {
|
|
doRestart = false;
|
|
}
|
|
//request->send_P(200, TEXT_HTML, Page_system_restart_HTML, Proc_WebPage_system_restart_POST);
|
|
|
|
/* Add custom header for redirect after timeout
|
|
* https://github.com/mathieucarbou/ESPAsyncWebServer?tab=readme-ov-file#send-large-webpage-from-progmem-containing-templates-and-extra-headers */
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", Page_system_restart_HTML, Proc_WebPage_system_restart_POST);
|
|
/* return Refresh header to redirect to root page after restart */
|
|
if(config.wifi.dhcp == true) {
|
|
response->addHeader("Refresh","20; url=http://" + WiFi.localIP().toString());
|
|
} else {
|
|
response->addHeader("Refresh","20; url=http://" + String(IP2Char(config.wifi.ip)));
|
|
}
|
|
|
|
request->send(response);
|
|
|
|
if(request->hasParam("confirmed", true)) {
|
|
Log.notice(F("%s POST[confirmed]: is set, triggering restart" CR), LogLoc);
|
|
|
|
// set global var doRestart to true causes a restart
|
|
doRestart = true;
|
|
}
|
|
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_restart_HTML, Proc_WebPage_system_restart);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage update
|
|
*/
|
|
|
|
// https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/docs/index.md#setting-up-the-server
|
|
void WebPage_system_update_ApplyUpdate(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:update:ApplyUpdate]";
|
|
if(!index){
|
|
Log.notice(F("%s Update Start: %s" CR), LogLoc, filename.c_str());
|
|
|
|
// https://github.com/me-no-dev/ESPAsyncWebServer/issues/455#issuecomment-451728099
|
|
// workaround for bug with ESP32
|
|
#ifdef ESP8266
|
|
Update.runAsync(true);
|
|
#endif
|
|
if(!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)){
|
|
Update.printError(Serial);
|
|
}
|
|
}
|
|
if(!Update.hasError()){
|
|
if(Update.write(data, len) != len){
|
|
Update.printError(Serial);
|
|
}
|
|
}
|
|
if(final){
|
|
if(Update.end(true)){
|
|
Log.notice(F("%s Update Success: %uB" CR), LogLoc, index+len);
|
|
} else {
|
|
Log.error(F("%s FAILED Update:" CR), LogLoc);
|
|
Update.printError(Serial);
|
|
}
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_update(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_UPDATE);
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
/* After an update.bin file was uploaded*/
|
|
String Proc_WebPage_system_update_POST(const String& var) {
|
|
if(var == "CONFIGWIFI_IP") {
|
|
if(config.wifi.dhcp == true) {
|
|
return WiFi.localIP().toString();
|
|
} else {
|
|
return String(IP2Char(config.wifi.ip));
|
|
}
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
void WebPage_system_update(AsyncWebServerRequest *request) {
|
|
if(request->method() == HTTP_POST) {
|
|
doRestart = !Update.hasError();
|
|
// when doRestart is true, deliver Page_system_update_HTML_POST
|
|
// otherwise Page_system_update_HTML_POST_FAILED
|
|
AsyncWebServerResponse *response = request->beginResponse_P(200, TEXT_HTML, doRestart?Page_system_update_HTML_POST:Page_system_update_HTML_POST_FAILED, Proc_WebPage_system_update_POST);
|
|
response->addHeader(F("Connection"), F("close"));
|
|
request->send(response);
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_update_HTML, Proc_WebPage_system_update);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage wipe
|
|
*/
|
|
String Proc_WebPage_system_wipe(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_WIPE);
|
|
} else if(var == "WIPE_MSG") {
|
|
return String(Page_system_wipe_HTML_WIPE_MSG);
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_wipe_POST(const String& var) {
|
|
if(var == "WIPE_MSG") {
|
|
return String(Page_system_wipe_HTML_WIPE_MSG_POST);
|
|
} else {
|
|
return Proc_WebPage_system_wipe(var);
|
|
}
|
|
}
|
|
|
|
void WebPage_system_wipe(AsyncWebServerRequest *request) {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:wipe]";
|
|
if(request->method() == HTTP_POST) {
|
|
request->send_P(200, TEXT_HTML, Page_system_wipe_HTML, Proc_WebPage_system_wipe_POST);
|
|
|
|
if(request->hasParam("confirmed", true)) {
|
|
Log.notice(F("%s POST[confirmed]: is set, triggering wipe / factory reset" CR), LogLoc);
|
|
LFS_Format();
|
|
Log.notice(F("%s triggering restart" CR), LogLoc);
|
|
doRestart = true;
|
|
}
|
|
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_wipe_HTML, Proc_WebPage_system_wipe);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage output
|
|
*/
|
|
String Proc_WebPage_system_output(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_OUTPUT);
|
|
} else if(var == "ADD_DISABLED") {
|
|
/* check if there is a free Output Id. Give_Free_OutputId returns 255 if no id available, otherwise it
|
|
* gives us the next free id. Here we check if the given ID is greater then Max_Outputs. This will also
|
|
* reflect a valid result, if there is a free id left or not. */
|
|
if(Give_Free_OutputId() > Max_Outputs ) {
|
|
return F("disabled force_hide");
|
|
} else {
|
|
return String();
|
|
}
|
|
|
|
} else if(var == "TR_TD") {
|
|
// build table body
|
|
// i dont know a better way at the moment. if you do, please tell me!
|
|
String html;
|
|
for(byte i=0; i < Max_Outputs; i++) {
|
|
if(config.system.output.type[i] > 0) {
|
|
|
|
html += F("<tr><td>");
|
|
/* show warn sign if outputStatus is false (uninitialized), otherwise green checkmark */
|
|
if(outputStatus[i] == false) {
|
|
html += F(" ⚠️");
|
|
} else {
|
|
html += F(" ✅");
|
|
}
|
|
/* bit spacing after the status icon */
|
|
html += F(" ");
|
|
html += F("</td><td>");
|
|
html += i;
|
|
|
|
html += F("</td><td>");
|
|
html += config.system.output.name[i];
|
|
html += F("</td><td>");
|
|
html += FPSTR(Output_Type_descr[config.system.output.type[i]]);
|
|
|
|
if((config.system.output.type[i] == OUTPUT_TYPE_GPIO) || ( (config.system.output.type[i] == OUTPUT_TYPE_I2C) && (config.system.output.i2c_type[i] > 0) )) {
|
|
html += F(" (");
|
|
|
|
switch(config.system.output.type[i]) {
|
|
case OUTPUT_TYPE_GPIO:
|
|
html += GPIOindex[config.system.output.gpio[i]].gpio;
|
|
break;
|
|
|
|
case OUTPUT_TYPE_I2C:
|
|
html += OutputI2Cindex[config.system.output.i2c_type[i]].name;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
html += F(")");
|
|
}
|
|
|
|
html += F("</td><td>");
|
|
html += FPSTR(Output_Device_descr[config.system.output.device[i]]);
|
|
html += F("</td><td>");
|
|
|
|
if(config.system.output.enabled[i] > 0) {
|
|
html += F(" 🟢 ");
|
|
} else {
|
|
html += F(" 🔴 ");
|
|
}
|
|
|
|
html += F("</td><td>");
|
|
|
|
// edit button
|
|
html += F("<form class='linkForm' action='/system/output/add' method='get'>");
|
|
html += F("<input type='hidden' name='edit' value='");
|
|
html += i;
|
|
html += F("'>");
|
|
html += F("<input type='submit' value='✏️' title='Edit'></form> ");
|
|
|
|
|
|
// delete button
|
|
html += F("<form class='linkForm' action='/system/output/' method='post'>");
|
|
html += F("<input type='hidden' name='delete_output' value='");
|
|
html += i;
|
|
html += F("'>");
|
|
html += F("<input type='submit' value='❌' onclick=\"return confirmDelete('");
|
|
html += config.system.output.name[i];;
|
|
html += F("')\" title='Delete'></form>");
|
|
|
|
html += F("</td></tr>");
|
|
}
|
|
}
|
|
|
|
return html;
|
|
} else{
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_output_POST(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system_output(var);
|
|
}
|
|
}
|
|
|
|
void WebPage_system_output(AsyncWebServerRequest *request) {
|
|
if(request->method() == HTTP_POST) {
|
|
if(request->hasParam("delete_output", true)) {
|
|
byte outputId;
|
|
|
|
const AsyncWebParameter* param = request->getParam("delete_output", true);
|
|
|
|
outputId = param->value().toInt();
|
|
|
|
/* remove grow objects */
|
|
Output_Device_Grow_AddRemove(outputId, 1);
|
|
|
|
// we ensure that every field is empty
|
|
config.system.output.type[outputId] = 0;
|
|
config.system.output.device[outputId] = 0;
|
|
// set every field of char array to 0x00 with memset
|
|
memset(config.system.output.name[outputId], '\0', sizeof config.system.output.name[outputId]);
|
|
config.system.output.enabled[outputId] = 0;
|
|
config.system.output.gpio[outputId] = 0;
|
|
config.system.output.gpio_pwm[outputId] = 0;
|
|
config.system.output.invert[outputId] = 0;
|
|
config.system.output.i2c_type[outputId] = 0;
|
|
memset(config.system.output.webcall_host[outputId], '\0', sizeof config.system.output.webcall_host[outputId]);
|
|
memset(config.system.output.webcall_path_on[outputId], '\0', sizeof config.system.output.webcall_path_on[outputId]);
|
|
memset(config.system.output.webcall_path_off[outputId], '\0', sizeof config.system.output.webcall_path_off[outputId]);
|
|
|
|
#ifdef DEBUG
|
|
SaveConfig(true);
|
|
#endif
|
|
SaveConfig();
|
|
}
|
|
|
|
request->send_P(200, TEXT_HTML, Page_system_output_HTML, Proc_WebPage_system_output_POST);
|
|
|
|
|
|
} else {
|
|
if(request->hasParam("success")) {
|
|
// when GET param success is present, we use the _POST processor for the save message
|
|
request->send_P(200, TEXT_HTML, Page_system_output_HTML, Proc_WebPage_system_output_POST);
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_output_HTML, Proc_WebPage_system_output);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Subpage output add
|
|
*/
|
|
|
|
|
|
/* returns select <option> list of available output types */
|
|
String Html_SelOpt_type_WebPage_system_output_i2c_add(byte selectId = 255) {
|
|
String html;
|
|
// go through all available Output I2C modules, skip 0 because it means unconfigured
|
|
for(byte i = 1; i <= OutputI2Cindex_length; i++) {
|
|
html += F("<option value='");
|
|
html += i;
|
|
html += F("'");
|
|
if(i == selectId) {
|
|
html += F(" selected");
|
|
}
|
|
html += F(">");
|
|
html += OutputI2Cindex[i].name;
|
|
html += F("</option>");
|
|
}
|
|
return html;
|
|
}
|
|
|
|
String Js_I2cAddr_Array_WebPage_system_output_i2c_add() {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:output:add:i2c:Js_I2cAddr_Array]";
|
|
/* bit hacky, bit dirty, but may work
|
|
* here we return a 2-dimensional javascript array. the returned stuff
|
|
* gets directly injected in the template into a js function
|
|
*/
|
|
String js;
|
|
|
|
/* iterate through all OutputI2Cindex in index*/
|
|
for(byte i = 1; i <= OutputI2Cindex_length; i++) {
|
|
// name
|
|
js += F("[");
|
|
/* iterate through all available addresses */
|
|
for(byte j = 0; j < OutputI2Cindex[i].max; j++) {
|
|
js += F("['0x");
|
|
js += String(Output_I2C_Addr_Init_Update(i, j, 0, OUPUT_I2C_AIU_MODE_ADDR), HEX);
|
|
// value
|
|
js += F("', '");
|
|
js += j;
|
|
// used
|
|
|
|
js += F("', [");
|
|
/* check I2C module ports available */
|
|
for(byte k = 0; k < OUTPUT_TYPE_I2C_MAX_PORTS; k++) {
|
|
|
|
/* When this Port of the module offers a value */
|
|
if(OutputI2Cindex[i].port[k] > 0) {
|
|
js += F("'");
|
|
/* check if I2C module and port are used in config */
|
|
for(byte l = 0; l < Max_Outputs; l++) {
|
|
if((config.system.output.type[l] == OUTPUT_TYPE_I2C) && (config.system.output.i2c_type[l] == i) &&
|
|
(config.system.output.i2c_addr[l] == j) && (config.system.output.i2c_port[l] == k)) {
|
|
js += 1;
|
|
/* exit loop here */
|
|
l = Max_Outputs + 1;
|
|
}
|
|
}
|
|
js += F("',");
|
|
}
|
|
|
|
}
|
|
|
|
js += F("]],");
|
|
}
|
|
js += F("],\n");
|
|
}
|
|
|
|
return js;
|
|
}
|
|
|
|
|
|
|
|
|
|
String Proc_WebPage_system_output_add(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_OUTPUT);
|
|
} else if(var == "ACTION") {
|
|
return F("➕ Add");
|
|
|
|
} else if(var == "OUTPUT_ID") {
|
|
// we check which id is free. A free ID as type == 0
|
|
return String(Give_Free_OutputId());
|
|
|
|
|
|
} else if(var == "OUTPUT_TYPE") {
|
|
return Html_SelectOpt_array(OUTPUT_TYPE__TOTAL, Output_Type_descr);
|
|
|
|
} else if(var == "OUTPUT_DEVICE") {
|
|
return Html_SelectOpt_array(OUTPUT_DEVICE__TOTAL, Output_Device_descr);
|
|
|
|
} else if(var == "OUTPUT_ENABLED") {
|
|
return Html_SelectOpt_bool();
|
|
|
|
} else if(var == "INVERT") {
|
|
return Html_SelectOpt_bool();
|
|
} else if(var == "GPIO_INDEX") {
|
|
return Html_SelectOpt_GPIOindex();
|
|
|
|
} else if(var == "GPIO_PWM") {
|
|
return Html_SelectOpt_bool();
|
|
|
|
} else if(var == "I2C_TYPE") {
|
|
return Html_SelOpt_type_WebPage_system_output_i2c_add();
|
|
} else if(var == "REPLACE_I2CADDR_JS") {
|
|
|
|
return Js_I2cAddr_Array_WebPage_system_output_i2c_add();
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_output_addEdit(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_OUTPUT);
|
|
} else if(var == "ACTION") {
|
|
return F("✏️ Edit");
|
|
|
|
} else if(var == "EDIT_MODE") {
|
|
return F("editmode");
|
|
} else if(var == "OUTPUT_ID") {
|
|
// return the outputId we got from GET .../add?edit=ID
|
|
// dirty workaround to put this in a global variable
|
|
return String(tmpParam_editOutputId);
|
|
|
|
} else if(var == "OUTPUT_TYPE") {
|
|
return Html_SelectOpt_array(OUTPUT_TYPE__TOTAL, Output_Type_descr, config.system.output.type[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "OUTPUT_DEVICE") {
|
|
return Html_SelectOpt_array(OUTPUT_DEVICE__TOTAL, Output_Device_descr, config.system.output.device[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "OUTPUT_NAME") {
|
|
// "escape" % character, because it would break the template processor.
|
|
// tasmote webcall for example has percentage char in its path
|
|
String outputName = config.system.output.name[tmpParam_editOutputId];;
|
|
outputName.replace(F("%"), F("%"));
|
|
return outputName;
|
|
|
|
} else if(var == "OUTPUT_ENABLED") {
|
|
return Html_SelectOpt_bool(config.system.output.enabled[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "INVERT") {
|
|
return Html_SelectOpt_bool(config.system.output.invert[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "GPIO_INDEX") {
|
|
return Html_SelectOpt_GPIOindex(config.system.output.gpio[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "GPIO_PWM") {
|
|
return Html_SelectOpt_bool(config.system.output.gpio_pwm[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "I2C_TYPE") {
|
|
//return String(config.system.output.i2c_type[tmpParam_editOutputId]);
|
|
//Html_SelectOpt_array(OutputI2Cindex_length, OutputI2Cindex[].name, config.system.output.i2c_type[tmpParam_editOutputId]);
|
|
return Html_SelOpt_type_WebPage_system_output_i2c_add(config.system.output.i2c_type[tmpParam_editOutputId]);
|
|
} else if(var == "REPLACE_I2CADDR_JS") {
|
|
|
|
return Js_I2cAddr_Array_WebPage_system_output_i2c_add();
|
|
|
|
} else if(var == "I2C_SAVED") {
|
|
|
|
/* Add bit javascript to ensure the saved value is selected */
|
|
String js;
|
|
js += F("showSelect('type_sel', 'type_', 'hidden'); SystemOutputAddselectRequired('type_sel');");
|
|
|
|
//js += F("document.getElementById('i2c_type').value='");
|
|
//js += config.system.output.i2c_type[tmpParam_editOutputId];
|
|
//js += F("';\n");
|
|
|
|
js += F("SystemOutputAdd_replaceI2cAddr('i2c_type', 'i2c_addr');");
|
|
|
|
js += F("document.getElementById('i2c_addr').value='");
|
|
js += config.system.output.i2c_addr[tmpParam_editOutputId];
|
|
js += F("';\n");
|
|
|
|
js += F("SystemOutputAdd_replaceI2cPort('i2c_type', 'i2c_addr', 'i2c_port');\n");
|
|
|
|
js += F("document.getElementById('i2c_port').value='");
|
|
js += config.system.output.i2c_port[tmpParam_editOutputId];
|
|
js += F("';\n");
|
|
|
|
|
|
//js += "SystemOutputAdd_replaceI2cPort('i2c_type', 'i2c_addr', 'i2c_port');";
|
|
return js;
|
|
|
|
|
|
} else if(var == "WEBCALL_HOST") {
|
|
return String(config.system.output.webcall_host[tmpParam_editOutputId]);
|
|
|
|
} else if(var == "WEBCALL_PATH_ON") {
|
|
String webcallPathOn = config.system.output.webcall_path_on[tmpParam_editOutputId];
|
|
webcallPathOn.replace(F("%"), F("%"));
|
|
return webcallPathOn;
|
|
|
|
} else if(var == "WEBCALL_PATH_OFF") {
|
|
String webcallPathOff = config.system.output.webcall_path_off[tmpParam_editOutputId];
|
|
webcallPathOff.replace(F("%"), F("%"));
|
|
return webcallPathOff;
|
|
|
|
} else if(
|
|
((var == "CLASS_TYPE_1") && (config.system.output.type[tmpParam_editOutputId] == 1)) ||
|
|
((var == "CLASS_TYPE_2") && (config.system.output.type[tmpParam_editOutputId] == 2)) ||
|
|
((var == "CLASS_TYPE_3") && (config.system.output.type[tmpParam_editOutputId] == 3))) {
|
|
// add class 'visible' which overwrites display with flex!important and justify center
|
|
return F("visible");
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_output_add_POST(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system_output_add(var);
|
|
}
|
|
}
|
|
|
|
|
|
void WebPage_system_output_add(AsyncWebServerRequest *request) {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:output:add]";
|
|
if(request->method() == HTTP_POST) {
|
|
|
|
byte outputId;
|
|
byte outputType;
|
|
//byte outputType_old;
|
|
|
|
byte outputDevice_old;
|
|
|
|
|
|
if(request->hasParam("outputId", true)) {
|
|
const AsyncWebParameter* param = request->getParam("outputId", true);
|
|
outputId = param->value().toInt();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(request->hasParam("type", true)) {
|
|
const AsyncWebParameter* param = request->getParam("type", true);
|
|
// put info config struct
|
|
config.system.output.type[outputId] = param->value().toInt();
|
|
// remember the value in own var to work later with here
|
|
//outputType = param->value().toInt();
|
|
}
|
|
|
|
/* save outputDevice_old */
|
|
outputDevice_old = config.system.output.device[outputId];
|
|
|
|
if(request->hasParam("device", true)) {
|
|
const AsyncWebParameter* param = request->getParam("device", true);
|
|
|
|
byte outputDevice = param->value().toInt();
|
|
|
|
/* check if output type has changed. if so, delete old Output
|
|
* Grow Devices and recreate them with new type later */
|
|
//if((outputType != outputType_old) || (outputDevice != outputDevice_old))
|
|
if(outputDevice != outputDevice_old) {
|
|
#ifdef DEBUG
|
|
Log.verbose(F("%s - device changed, delete old Grow object" CR), LogLoc);
|
|
#endif
|
|
Output_Device_Grow_AddRemove(outputId, 1);
|
|
//config.grow.light.configured[outputId] = false;
|
|
}
|
|
/* finally write the new device type into the config */
|
|
config.system.output.device[outputId] = param->value().toInt();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(request->hasParam("name", true)) {
|
|
const AsyncWebParameter* param = request->getParam("name", true);
|
|
strlcpy(config.system.output.name[outputId], param->value().c_str(), sizeof(config.system.output.name[outputId]));
|
|
}
|
|
|
|
if(request->hasParam("enabled", true)) {
|
|
const AsyncWebParameter* param = request->getParam("enabled", true);
|
|
config.system.output.enabled[outputId] = param->value().toInt();
|
|
}
|
|
|
|
|
|
if(request->hasParam("invert", true)) {
|
|
const AsyncWebParameter* param = request->getParam("invert", true);
|
|
config.system.output.invert[outputId] = param->value().toInt();
|
|
}
|
|
|
|
// only fill the type related config vars
|
|
switch(config.system.output.type[outputId]) {
|
|
// GPIO
|
|
case OUTPUT_TYPE_GPIO:
|
|
if(request->hasParam("gpio", true)) {
|
|
byte old_gpio = config.system.output.gpio[outputId];
|
|
const AsyncWebParameter* param = request->getParam("gpio", true);
|
|
config.system.output.gpio[outputId] = param->value().toInt();
|
|
if(old_gpio != config.system.output.gpio[outputId])
|
|
needRestart = true;
|
|
|
|
}
|
|
|
|
if(request->hasParam("gpio_pwm", true)) {
|
|
const AsyncWebParameter* param = request->getParam("gpio_pwm", true);
|
|
config.system.output.gpio_pwm[outputId] = param->value().toInt();
|
|
}
|
|
break;
|
|
|
|
// I2C
|
|
case OUTPUT_TYPE_I2C:
|
|
if(request->hasParam("i2c_type", true)) {
|
|
byte old_i2c_type = config.system.output.i2c_type[outputId];
|
|
const AsyncWebParameter* param = request->getParam("i2c_type", true);
|
|
config.system.output.i2c_type[outputId] = param->value().toInt();
|
|
if(old_i2c_type != config.system.output.i2c_type[outputId])
|
|
needRestart = true;
|
|
outputStatus[outputId] = false;
|
|
}
|
|
|
|
if(request->hasParam("i2c_addr", true)) {
|
|
byte old_i2c_addr = config.system.output.i2c_addr[outputId];
|
|
const AsyncWebParameter* param = request->getParam("i2c_addr", true);
|
|
config.system.output.i2c_addr[outputId] = param->value().toInt();
|
|
if(old_i2c_addr != config.system.output.i2c_addr[outputId])
|
|
needRestart = true;
|
|
outputStatus[outputId] = false;
|
|
}
|
|
|
|
if(request->hasParam("i2c_port", true)) {
|
|
const AsyncWebParameter* param = request->getParam("i2c_port", true);
|
|
config.system.output.i2c_port[outputId] = param->value().toInt();
|
|
}
|
|
break;
|
|
// Webcall
|
|
case OUTPUT_TYPE_WEB:
|
|
if(request->hasParam("webcall_host", true)) {
|
|
const AsyncWebParameter* param = request->getParam("webcall_host", true);
|
|
strlcpy(config.system.output.webcall_host[outputId], param->value().c_str(), sizeof(config.system.output.webcall_host[outputId]));
|
|
}
|
|
|
|
if(request->hasParam("webcall_path_on", true)) {
|
|
const AsyncWebParameter* param = request->getParam("webcall_path_on", true);
|
|
strlcpy(config.system.output.webcall_path_on[outputId], param->value().c_str(), sizeof(config.system.output.webcall_path_on[outputId]));
|
|
}
|
|
|
|
if(request->hasParam("webcall_path_off", true)) {
|
|
const AsyncWebParameter* param = request->getParam("webcall_path_off", true);
|
|
strlcpy(config.system.output.webcall_path_off[outputId], param->value().c_str(), sizeof(config.system.output.webcall_path_off[outputId]));
|
|
}
|
|
|
|
/* reset in any case the webcall fail counter to trigger update retry */
|
|
outputWebcallFailed[outputId] = 0;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
|
|
/* create grow objects */
|
|
if(request->hasParam("editmode")) {
|
|
//Log.verbose(F("%s - has edit" CR), LogLoc);
|
|
if(config.system.output.device[outputId] != outputDevice_old) {
|
|
#ifdef DEBUG
|
|
Log.verbose(F("%s - device changed, Recreate Grow object" CR), LogLoc);
|
|
#endif
|
|
// remove - already done few lines before
|
|
//Output_Device_Grow_AddRemove(outputId, 1);
|
|
// add empty
|
|
Output_Device_Grow_AddRemove(outputId, 0);
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
Log.verbose(F("%s - no edit" CR), LogLoc);
|
|
#endif
|
|
Output_Device_Grow_AddRemove(outputId, 0);
|
|
}
|
|
#ifdef DEBUG
|
|
SaveConfig(true);
|
|
#endif
|
|
SaveConfig();
|
|
// request->send_P(200, "text/html", Page_system_output_add_HTML, Proc_WebPage_system_output_add_POST);
|
|
// I like it more when user gets redirected to the output overview after saving
|
|
request->redirect(F("/system/output/?success"));
|
|
} else {
|
|
|
|
/* When in edit mode */
|
|
if(request->hasParam("edit")) {
|
|
const AsyncWebParameter* param = request->getParam("edit");
|
|
tmpParam_editOutputId = param->value().toInt();
|
|
request->send_P(200, TEXT_HTML, Page_system_output_add_HTML, Proc_WebPage_system_output_addEdit);
|
|
/* when just adding a new output, check if there are free IDs */
|
|
} else if(Give_Free_OutputId() > Max_Outputs) {
|
|
/* if not, send error */
|
|
request->send_P(200, TEXT_HTML, Page_system_output_add_HTML_NO_ID_AVAILABLE, Proc_WebPage_system_output_add);
|
|
} else {
|
|
/* otherwise let the user create new output */
|
|
request->send_P(200, TEXT_HTML, Page_system_output_add_HTML, Proc_WebPage_system_output_add);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage sensor
|
|
*/
|
|
|
|
String Proc_WebPage_system_sensor(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_SENSOR);
|
|
} else if(var == "ADD_DISABLED") {
|
|
if(Give_Free_SensorId() > Max_Outputs ) {
|
|
return F("disabled force_hide");
|
|
} else {
|
|
return String();
|
|
}
|
|
} else if(var == "TR_TD") {
|
|
// build table body
|
|
// i dont know a better way at the moment. if you do, please tell me!
|
|
String html;
|
|
for(byte i=0; i < Max_Sensors; i++) {
|
|
if(config.system.sensor.type[i] > 0) {
|
|
|
|
html += F("<tr><td>");
|
|
/* show warn sign if sensorStatus is false (uninitialized), otherwise green checkmark */
|
|
if(sensorStatus[i] == false) {
|
|
html += F(" ⚠️");
|
|
} else {
|
|
html += F(" ✅");
|
|
}
|
|
/* bit spacing after the status icon */
|
|
html += F(" ");
|
|
html += F("</td><td>");
|
|
/* sens*/
|
|
html += i;
|
|
|
|
html += F("</td><td>");
|
|
html += config.system.sensor.name[i];
|
|
html += F("</td><td>");
|
|
html += SensorIndex[config.system.sensor.type[i]].name;
|
|
|
|
/* when GPIO pin or I2C sensor is configured (1 is int adc), shot the Pin / addr in overview */
|
|
if((
|
|
(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) && config.system.sensor.gpio[i][0] > 0) ||
|
|
(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_ONEWIRE) ||
|
|
(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_TWOWIRE) ||
|
|
(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C)) {
|
|
html += F(" (");
|
|
|
|
if(config.system.sensor.gpio[i][0] > 0) {
|
|
html += GPIOindex[config.system.sensor.gpio[i][0]].gpio;
|
|
|
|
if(config.system.sensor.gpio[i][1] > 0) {
|
|
html += F("/");
|
|
html += GPIOindex[config.system.sensor.gpio[i][1]].gpio;
|
|
}
|
|
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
|
|
html += F("0x");
|
|
html += String(Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR), HEX);
|
|
}
|
|
html += F(")");
|
|
}
|
|
|
|
html += F("</td><td>");
|
|
|
|
// calibrate button
|
|
html += F("<form class='linkForm' action='/system/sensor/calibrate' method='get'>");
|
|
html += F("<input type='hidden' name='calibrate' value='");
|
|
html += i;
|
|
html += F("'>");
|
|
html += F("<input type='submit' value='🎛️' title='Calibrate'></form> ");
|
|
|
|
// edit button
|
|
html += F("<form class='linkForm' action='/system/sensor/add' method='get'>");
|
|
html += F("<input type='hidden' name='edit' value='");
|
|
html += i;
|
|
html += F("'>");
|
|
html += F("<input type='submit' value='✏️' title='Edit'></form> ");
|
|
|
|
// delete button
|
|
html += F("<form class='linkForm' action='/system/sensor/' method='post'>");
|
|
html += F("<input type='hidden' name='delete_sensor' value='");
|
|
html += i;
|
|
html += F("'>");
|
|
html += F("<input type='submit' value='❌' onclick=\"return confirmDelete('");
|
|
html += config.system.sensor.name[i];;
|
|
html += F("')\" title='Delete'></form>");
|
|
|
|
html += F("</td></tr>");
|
|
}
|
|
}
|
|
|
|
return html;
|
|
} else{
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_sensor_POST(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system_sensor(var);
|
|
}
|
|
}
|
|
|
|
void WebPage_system_sensor(AsyncWebServerRequest *request) {
|
|
if(request->method() == HTTP_POST) {
|
|
if(request->hasParam("delete_sensor", true)) {
|
|
byte sensorId;
|
|
|
|
const AsyncWebParameter* param = request->getParam("delete_sensor", true);
|
|
sensorId = param->value().toInt();
|
|
|
|
// we ensure that every field is empty
|
|
config.system.sensor.type[sensorId] = 0;
|
|
memset(config.system.sensor.name[sensorId], '\0', sizeof config.system.sensor.name[sensorId]);
|
|
|
|
/* go through all GPIOs */
|
|
for(byte i = 0; i < Max_Sensors_GPIO; i++) {
|
|
config.system.sensor.gpio[sensorId][i] = 0;
|
|
}
|
|
|
|
config.system.sensor.i2c_addr[sensorId] = 0;
|
|
sensorStatus[sensorId];
|
|
|
|
SaveConfig();
|
|
}
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_HTML, Proc_WebPage_system_sensor_POST);
|
|
|
|
} else {
|
|
if(request->hasParam("success")) {
|
|
// when GET param success is present, we use the _POST processor for the save message
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_HTML, Proc_WebPage_system_sensor_POST);
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_HTML, Proc_WebPage_system_sensor);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage sensor add
|
|
*/
|
|
|
|
/* returns select <option> list of available output types */
|
|
String Html_SelOpt_type_WebPage_system_sensor_add(byte selectId = 255) {
|
|
String html;
|
|
// go through all available Output Devices, skip 0 because it means unconfigured
|
|
for(byte i = 1; i <= SensorIndex_length; i++) {
|
|
html += F("<option value='");
|
|
html += i;
|
|
html += F("'");
|
|
if(i == selectId) {
|
|
html += F(" selected");
|
|
}
|
|
html += F(">");
|
|
html += SensorIndex[i].name;
|
|
html += F("</option>");
|
|
}
|
|
return html;
|
|
}
|
|
|
|
String Js_I2cAddr_Array_WebPage_system_sensor_add() {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:sensor:add:Js_I2cAddr_Array]";
|
|
/* bit hacky, bit dirty, but may work
|
|
* here we return a 2-dimensional javascript array. the returned stuff
|
|
* gets directly injected in the template into a js function
|
|
*/
|
|
String js;
|
|
|
|
/* iterate through all SensorIds in index*/
|
|
for(byte i = 1; i <= SensorIndex_length; i++) {
|
|
// name
|
|
js += F("[");
|
|
/* iterate through all available addresses */
|
|
for(byte j = 0; j < SensorIndex[i].max; j++) {
|
|
js += F("['0x");
|
|
js += String(Sensor_Addr_Init_Update(i, j, SENSOR_AIU_MODE_ADDR), HEX);
|
|
// value
|
|
js += F("', '");
|
|
js += j;
|
|
// used
|
|
bool used;
|
|
js += F("', '");
|
|
/* check if addr is used */
|
|
for(byte k = 0; k < Max_Sensors; k++) {
|
|
if(config.system.sensor.type[k] == i ) {
|
|
if(config.system.sensor.i2c_addr[k] == j) {
|
|
js += F("1");
|
|
// exit loop here
|
|
k = Max_Sensors + 1;
|
|
} //else {
|
|
//js += "0";
|
|
//}
|
|
|
|
}
|
|
}
|
|
js += F("'],");
|
|
}
|
|
js += F("],\n");
|
|
}
|
|
|
|
return js;
|
|
}
|
|
|
|
String Proc_WebPage_system_sensor_add(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_SENSOR);
|
|
} else if(var == "ACTION") {
|
|
return F("➕ Add");
|
|
|
|
} else if(var == "SENSOR_ID") {
|
|
// we check which id is free. A free ID as type == 0
|
|
return String(Give_Free_SensorId());
|
|
|
|
|
|
} else if(var == "SENSOR_TYPE") {
|
|
return Html_SelOpt_type_WebPage_system_sensor_add();
|
|
|
|
} else if(var == "GPIO_INDEX") {
|
|
return Html_SelectOpt_GPIOindex(255, true);
|
|
|
|
} else if(var == "ESP_PLATFORM") {
|
|
#ifdef ESP8266
|
|
return F("8266");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
return F("32");
|
|
#endif
|
|
} else if(var == "REPLACE_I2CADDR_JS") {
|
|
|
|
return Js_I2cAddr_Array_WebPage_system_sensor_add();
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_sensor_addEdit(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_SENSOR);
|
|
} else if(var == "ACTION") {
|
|
return F("✏️ Edit");
|
|
|
|
} else if(var == "SENSOR_ID") {
|
|
// return the sensorId we got from GET .../add?edit=ID
|
|
// dirty workaround to put this in a global variable
|
|
return String(tmpParam_editSensorId);
|
|
|
|
} else if(var == "SENSOR_TYPE") {
|
|
return Html_SelOpt_type_WebPage_system_sensor_add(config.system.sensor.type[tmpParam_editSensorId]);
|
|
|
|
} else if(var == "SENSOR_NAME") {
|
|
// "escape" % character, because it would break the template processor.
|
|
// tasmote webcall for example has percentage char in its path
|
|
String sensorName = config.system.sensor.name[tmpParam_editSensorId];;
|
|
sensorName.replace(F("%"), F("%"));
|
|
return sensorName;
|
|
|
|
} else if(var == "GPIO_INDEX") {
|
|
return Html_SelectOpt_GPIOindex(config.system.sensor.gpio[tmpParam_editSensorId][0], true);
|
|
|
|
} else if(var == "ESP_PLATFORM") {
|
|
#ifdef ESP8266
|
|
return F("8266");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
return F("32");
|
|
#endif
|
|
} else if(var == "REPLACE_I2CADDR_JS") {
|
|
|
|
return Js_I2cAddr_Array_WebPage_system_sensor_add();
|
|
|
|
} else if(var == "I2C_SAVED") {
|
|
|
|
/* Add bit javascript to ensure the saved value is selected */
|
|
String js;
|
|
js += F("SystemSensorAddGpioI2cSel('type_sel');SystemSensor_replaceAddr('type_sel', 'i2c_addr');");
|
|
js += F("document.getElementById('i2c_addr').value='");
|
|
js += config.system.sensor.i2c_addr[tmpParam_editSensorId];
|
|
js += F("';");
|
|
return js;
|
|
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_sensor_add_POST(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system_sensor_add(var);
|
|
}
|
|
}
|
|
|
|
|
|
void WebPage_system_sensor_add(AsyncWebServerRequest *request) {
|
|
if(request->method() == HTTP_POST) {
|
|
byte sensorId;
|
|
//byte sensorType;
|
|
if(request->hasParam("sensorId", true)) {
|
|
const AsyncWebParameter* param = request->getParam("sensorId", true);
|
|
sensorId = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("type", true)) {
|
|
const AsyncWebParameter* param = request->getParam("type", true);
|
|
byte old_type = config.system.sensor.type[sensorId];
|
|
// put info config struct
|
|
config.system.sensor.type[sensorId] = param->value().toInt();
|
|
/* when config changed to a different sensor which is not internal ADC, then need restart */
|
|
if((config.system.sensor.type[sensorId] != old_type) && (config.system.sensor.type[sensorId] != 1 ))
|
|
needRestart = true;
|
|
|
|
}
|
|
|
|
|
|
if(request->hasParam("name", true)) {
|
|
const AsyncWebParameter* param = request->getParam("name", true);
|
|
strlcpy(config.system.sensor.name[sensorId], param->value().c_str(), sizeof(config.system.sensor.name[sensorId]));
|
|
}
|
|
|
|
if(request->hasParam("i2c_addr", true)) {
|
|
const AsyncWebParameter* param = request->getParam("i2c_addr", true);
|
|
//strlcpy(config.system.sensor.i2c_addr[sensorId], param->value().c_str(), sizeof(config.system.sensor.i2c_addr[sensorId]));
|
|
byte old_i2c_addr = config.system.sensor.i2c_addr[sensorId];
|
|
config.system.sensor.i2c_addr[sensorId] = param->value().toInt();
|
|
/* when i2c address changes, we need a restart
|
|
* TODO or re-initialise the sensor(s) (possible?) */
|
|
// check if sensor is I2C one and i2c_addr differs from before, otherwise we dont care whats inside here
|
|
|
|
if((SensorIndex[config.system.sensor.type[sensorId]].type == SENSOR_TYPE_I2C) && (config.system.sensor.i2c_addr[sensorId] != old_i2c_addr)) {
|
|
needRestart = true;
|
|
/* disable sensor, otherwise ESP will crash */
|
|
sensorStatus[sensorId] = false;
|
|
}
|
|
}
|
|
|
|
if(request->hasParam("gpio", true)) {
|
|
const AsyncWebParameter* param = request->getParam("gpio", true);
|
|
byte old_gpio = config.system.sensor.gpio[sensorId][0];
|
|
config.system.sensor.gpio[sensorId][0] = param->value().toInt();
|
|
/* when internal ADC we can initialize the sensors here */
|
|
if((SensorIndex[config.system.sensor.type[sensorId]].type == SENSOR_TYPE_INTADC) && (config.system.sensor.gpio[sensorId][0] != old_gpio)) {
|
|
|
|
/* TODO Crashes on ESP8266 sometimes, idk why, is OK on ESP32 */
|
|
#ifdef ESP32
|
|
Sensor_Addr_Init_Update(config.system.sensor.type[sensorId], config.system.sensor.gpio[sensorId][0], SENSOR_AIU_MODE_INIT);
|
|
sensorStatus[sensorId] = true;
|
|
#endif
|
|
|
|
#ifdef ESP8266
|
|
/* because on ESP8266 it crashes sometimes, we force a restart until this get fixed */
|
|
needRestart = true;
|
|
sensorStatus[sensorId] = false;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
SaveConfig();
|
|
|
|
// request->send_P(200, "text/html", Page_system_output_add_HTML, Proc_WebPage_system_output_add_POST);
|
|
// I like it more when user gets redirected to the output overview after saving
|
|
request->redirect("/system/sensor/?success");
|
|
} else {
|
|
|
|
/* when in edit mode */
|
|
if(request->hasParam("edit")) {
|
|
const AsyncWebParameter* param = request->getParam("edit");
|
|
tmpParam_editSensorId = param->value().toInt();
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_add_HTML, Proc_WebPage_system_sensor_addEdit);
|
|
/* if we want to add new sensor, check if a sensor id is available. if not send error */
|
|
} else if(Give_Free_SensorId() > Max_Sensors) {
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_add_HTML_NO_ID_AVAILABLE, Proc_WebPage_system_sensor_add);
|
|
/* Otherwise let the user create new sensor */
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_add_HTML, Proc_WebPage_system_sensor_add);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* Subpage sensor calibrate
|
|
*/
|
|
|
|
/*******************************************************************
|
|
String Js_I2cAddr_Array_WebPage_system_sensor_calibrate() {
|
|
const static char LogLoc[] PROGMEM = "[Webserver:system:sensor:add:Js_I2cAddr_Array]";
|
|
|
|
String js;
|
|
|
|
|
|
for(byte i = 1; i <= SensorIndex_length; i++) {
|
|
// name
|
|
js += F("[");
|
|
|
|
for(byte j = 0; j < SensorIndex[i].max; j++) {
|
|
js += F("['0x");
|
|
js += String(Sensor_Addr_Init_Update(i, j, SENSOR_AIU_MODE_ADDR), HEX);
|
|
// value
|
|
js += F("', '");
|
|
js += j;
|
|
// used
|
|
bool used;
|
|
js += F("', '");
|
|
|
|
for(byte k = 0; k < Max_Sensors; k++) {
|
|
if(config.system.sensor.type[k] == i ) {
|
|
if(config.system.sensor.i2c_addr[k] == j) {
|
|
js += F("1");
|
|
// exit loop here
|
|
k = Max_Sensors + 1;
|
|
} //else {
|
|
//js += "0";
|
|
//}
|
|
|
|
}
|
|
}
|
|
js += F("'],");
|
|
}
|
|
js += F("],\n");
|
|
}
|
|
|
|
return js;
|
|
}
|
|
|
|
|
|
String Proc_WebPage_system_sensor_calibrate3(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_SENSOR);
|
|
} else if(var == "ACTION") {
|
|
return F("➕ Add");
|
|
|
|
} else if(var == "SENSOR_ID") {
|
|
// we check which id is free. A free ID as type == 0
|
|
return String(Give_Free_SensorId());
|
|
|
|
|
|
} else if(var == "SENSOR_TYPE") {
|
|
return Html_SelOpt_type_WebPage_system_sensor_add();
|
|
|
|
} else if(var == "GPIO_INDEX") {
|
|
return Html_SelectOpt_GPIOindex(255, true);
|
|
|
|
} else if(var == "ESP_PLATFORM") {
|
|
#ifdef ESP8266
|
|
return F("8266");
|
|
#endif
|
|
|
|
#ifdef ESP32
|
|
return F("32");
|
|
#endif
|
|
} else if(var == "REPLACE_I2CADDR_JS") {
|
|
|
|
return Js_I2cAddr_Array_WebPage_system_sensor_add();
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
*******************************************************************/
|
|
|
|
String Proc_WebPage_system_sensor_calibrate(const String& var) {
|
|
if(TestHeaderFooter(var)) {
|
|
return AddHeaderFooter(var, 2);
|
|
} else if(Test_WebPage_system_SUBNAV(var)) {
|
|
return Proc_WebPage_system_SUBNAV(var, WEB_SYSTEM_SUBNAV_SENSOR);
|
|
} else if(var == "SENSOR_ID") {
|
|
// return the sensorId we got from GET .../add?edit=ID
|
|
// dirty workaround to put this in a global variable
|
|
return String(tmpParam_calibrateSensorId);
|
|
|
|
} else if(var == "SENSOR_NAME") {
|
|
// "escape" % character, because it would break the template processor.
|
|
// tasmote webcall for example has percentage char in its path
|
|
String sensorName = config.system.sensor.name[tmpParam_calibrateSensorId];;
|
|
sensorName.replace(F("%"), F("%"));
|
|
return sensorName;
|
|
|
|
} else if(var == "SENSOR_READING") {
|
|
String html;
|
|
/* TODO the way this page is built is ulgy IMHO
|
|
* maybe i will replace everything dynamic at this place with javascript? or i can make
|
|
* more / better use of the ESPAsync template engine */
|
|
html += F("<script>SensorJsonRefresh(); var refreshJson = setInterval(SensorJsonRefresh, 1000);</script>");
|
|
for(byte i = 0; i < Max_Sensors_Read; i++) {
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i] > 0) {
|
|
html += F("<form method='post' action='/system/sensor/calibrate'>");
|
|
html += F("<input type='hidden' name='sensorId' value='");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += F("'/>");
|
|
html += F("<input type='hidden' name='readId' value='");
|
|
html += i;
|
|
html += F("'/>");
|
|
|
|
html += F("<u>Reading ");
|
|
html += i;
|
|
html += F(":</u><br><b>");
|
|
html += FPSTR(Sensor_Read_descr[SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i]]);
|
|
html += F("</b> (");
|
|
html += F("<span class='sensorReading' id='raw-");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += "-";
|
|
html += i;
|
|
html += F("'>");
|
|
|
|
/* is sensor internal ADC ? */
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].type == SENSOR_TYPE_INTADC) {
|
|
/* reading type RAW ? */
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i] == SENSOR_READ_TYPE_RAW) {
|
|
/* get RAW value */
|
|
html += (int)Sensor_getValue( config.system.sensor.type[tmpParam_calibrateSensorId], config.system.sensor.gpio[tmpParam_calibrateSensorId][0]);
|
|
} else {
|
|
/* print calibrated value if not RAW */
|
|
html += Sensor_getCalibratedValue(tmpParam_calibrateSensorId, i);
|
|
//html += Sensor_getValue( config.system.sensor.type[tmpParam_calibrateSensorId], config.system.sensor.gpio[tmpParam_calibrateSensorId][0]);
|
|
}
|
|
} else if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].type == SENSOR_TYPE_I2C) {
|
|
/* same stuff for i2c sensor */
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i] == SENSOR_READ_TYPE_RAW) {
|
|
html += (int)Sensor_getValue( config.system.sensor.type[tmpParam_calibrateSensorId], config.system.sensor.i2c_addr[tmpParam_calibrateSensorId], i);
|
|
} else {
|
|
html += Sensor_getCalibratedValue(tmpParam_calibrateSensorId, i);
|
|
html += " ";
|
|
String unit;
|
|
unit += FPSTR(Sensor_Read_unit[SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i]]);
|
|
unit.replace(F("%"), F("%"));
|
|
html += unit;
|
|
//html += Sensor_getValue( config.system.sensor.type[tmpParam_calibrateSensorId], config.system.sensor.i2c_addr[tmpParam_calibrateSensorId], i);
|
|
}
|
|
}
|
|
|
|
html += F("</span>)");
|
|
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i] == SENSOR_READ_TYPE_RAW) {
|
|
html += F("<script>var raw_");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += F("_");
|
|
html += i;
|
|
html += F(" = setInterval(rawRefresh, 1000, ");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += F(", ");
|
|
html += i;
|
|
html += F(", 'raw-');</script>");
|
|
}
|
|
|
|
html += F("<br>");
|
|
|
|
if(SensorIndex[config.system.sensor.type[tmpParam_calibrateSensorId]].read[i] == SENSOR_READ_TYPE_RAW) {
|
|
html += F("<u>Convert RAW value to:</u><br>");
|
|
html += F("<select name='rawConvert'><option value='0' >---</option>");
|
|
for(byte j = 1; j <= SENSOR_CONVERT_RAW_TYPE__TOTAL; j++) {
|
|
html += F("<option value='");
|
|
html += j;
|
|
|
|
html += F("'");
|
|
if(config.system.sensor.rawConvert[tmpParam_calibrateSensorId][i] == j)
|
|
html += F(" selected");
|
|
html += F(">");
|
|
html += FPSTR(Sensor_Convert_Raw_descr[j]);
|
|
html += F("</option>");
|
|
}
|
|
html += F("</select><br>");
|
|
|
|
/* when raw convert is set, display converted reading */
|
|
if(config.system.sensor.rawConvert[tmpParam_calibrateSensorId][i] > 0) {
|
|
//html += F("<span class='sensorReading'>");
|
|
html += F("<span class='sensorReading' id='reading-");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += "-";
|
|
html += i;
|
|
html += F("'>");
|
|
|
|
html += Sensor_getCalibratedValue(tmpParam_calibrateSensorId, i);
|
|
html += " ";
|
|
String unit;
|
|
unit += FPSTR(Sensor_Convert_Raw_unit[config.system.sensor.rawConvert[tmpParam_calibrateSensorId][i]]);
|
|
unit.replace(F("%"), F("%"));
|
|
html += unit;
|
|
html += F("</span>");
|
|
|
|
html += F("<script>var reading_");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += F("_");
|
|
html += i;
|
|
html += F(" = setInterval(sensorRefresh, 1000, ");
|
|
html += tmpParam_calibrateSensorId;
|
|
html += F(", ");
|
|
html += i;
|
|
html += F(", 'reading-');</script>");
|
|
|
|
html += F("<br>");
|
|
}
|
|
|
|
html += F("<u>Low:</u><br>");
|
|
html += F("<input type='number' name='low' value='");
|
|
html += config.system.sensor.low[tmpParam_calibrateSensorId][i];
|
|
html += F("'/><br>");
|
|
|
|
html += F("<u>High:</u><br>");
|
|
html += F("<input type='number' name='high' value='");
|
|
html += config.system.sensor.high[tmpParam_calibrateSensorId][i];
|
|
html += F("'/><br>");
|
|
|
|
} else {
|
|
|
|
html += F("<u>Offset:</u><br>");
|
|
html += F("<input type='number' step='0.01' name='offset' value='");
|
|
html += config.system.sensor.offset[tmpParam_calibrateSensorId][i];
|
|
html += F("'/><br>");
|
|
|
|
}
|
|
html += F("<br><input type='submit' value='💾 Save settings'></form>");
|
|
html += F("<hr>\n\n");
|
|
}
|
|
}
|
|
|
|
return html;
|
|
|
|
} else {
|
|
return String();
|
|
}
|
|
}
|
|
|
|
String Proc_WebPage_system_sensor_calibrate_POST(const String& var) {
|
|
if(var == "SAVE_MSG") {
|
|
return String(Common_HTML_SAVE_MSG);
|
|
} else {
|
|
return Proc_WebPage_system_sensor_calibrate(var);
|
|
}
|
|
}
|
|
|
|
|
|
void WebPage_system_sensor_calibrate(AsyncWebServerRequest *request) {
|
|
if(request->method() == HTTP_POST) {
|
|
byte sensorId;
|
|
byte readId;
|
|
//byte sensorType;
|
|
if(request->hasParam("sensorId", true)) {
|
|
const AsyncWebParameter* param = request->getParam("sensorId", true);
|
|
sensorId = param->value().toInt();
|
|
tmpParam_calibrateSensorId = sensorId;
|
|
}
|
|
|
|
if(request->hasParam("readId", true)) {
|
|
const AsyncWebParameter* param = request->getParam("readId", true);
|
|
readId = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("offset", true)) {
|
|
const AsyncWebParameter* param = request->getParam("offset", true);
|
|
config.system.sensor.offset[sensorId][readId] = param->value().toFloat();
|
|
}
|
|
|
|
if(request->hasParam("low", true)) {
|
|
const AsyncWebParameter* param = request->getParam("low", true);
|
|
config.system.sensor.low[sensorId][readId] = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("high", true)) {
|
|
const AsyncWebParameter* param = request->getParam("high", true);
|
|
config.system.sensor.high[sensorId][readId] = param->value().toInt();
|
|
}
|
|
|
|
if(request->hasParam("rawConvert", true)) {
|
|
const AsyncWebParameter* param = request->getParam("rawConvert", true);
|
|
config.system.sensor.rawConvert[sensorId][readId] = param->value().toInt();
|
|
}
|
|
|
|
SaveConfig();
|
|
|
|
request->send_P(200, "text/html", Page_system_sensor_calibrate_HTML, Proc_WebPage_system_sensor_calibrate_POST);
|
|
// I like it more when user gets redirected to the output overview after saving
|
|
//request->redirect("/system/sensor/?success");
|
|
} else {
|
|
|
|
/* when in edit mode */
|
|
if(request->hasParam("calibrate")) {
|
|
const AsyncWebParameter* param = request->getParam("calibrate");
|
|
tmpParam_calibrateSensorId = param->value().toInt();
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_calibrate_HTML, Proc_WebPage_system_sensor_calibrate);
|
|
/* if we want to add new sensor, check if a sensor id is available. if not send error */
|
|
} else {
|
|
request->send_P(200, TEXT_HTML, Page_system_sensor_calibrate_HTML, Proc_WebPage_system_sensor_calibrate);
|
|
}
|
|
}
|
|
}
|
|
|