CanGrow/include/Webserver/Page_grow.h

1000 lines
34 KiB
C

/*
*
* include/Webserver/Page_grow.h - grow page header file
*
*
*
*/
#include "Page_grow_HTML.h"
/* subnav processor */
const byte WEB_GROW_SUBNAV_GENERAL = 1;
const byte WEB_GROW_SUBNAV_LIGHT = 2;
const byte WEB_GROW_SUBNAV_AIR = 3;
const byte WEB_GROW_SUBNAV_WATER = 4;
const byte WEB_GROW_SUBNAV_DASHBOARD = 5;
bool Test_WebPage_grow_SUBNAV(const String& var) {
if(
(var == "SUBNAV") ||
(var == "ACTIVE_SUBNAV_GENERAL") ||
(var == "ACTIVE_SUBNAV_LIGHT") ||
(var == "ACTIVE_SUBNAV_AIR") ||
(var == "ACTIVE_SUBNAV_WATER") ||
(var == "ACTIVE_SUBNAV_DASHBOARD")) {
return true;
} else {
return false;
}
}
/*
* Proc_WebPage_grow_SUBNAV - subnav processor for grow
* this function works as same as AddHeaderFooter from Common.h
* byte activeSubnav:
* 1 - Output
* 2 - Update
* 3 - Restart
* 4 - Wipe
*/
String Proc_WebPage_grow_SUBNAV(const String& var, byte activeSubnav = 0) {
String activeSubnav_ClassName = "activeNav";
if(var == "SUBNAV") {
return String(Page_grow_HTML_SUBNAV);
} else if((var == "ACTIVE_SUBNAV_GENERAL") && (activeSubnav == WEB_GROW_SUBNAV_GENERAL)) {
return activeSubnav_ClassName;
} else if((var == "ACTIVE_SUBNAV_LIGHT") && (activeSubnav == WEB_GROW_SUBNAV_LIGHT)) {
return activeSubnav_ClassName;
} else if((var == "ACTIVE_SUBNAV_AIR") && (activeSubnav == WEB_GROW_SUBNAV_AIR)) {
return activeSubnav_ClassName;
} else if((var == "ACTIVE_SUBNAV_WATER") && (activeSubnav == WEB_GROW_SUBNAV_WATER)) {
return activeSubnav_ClassName;
} else if((var == "ACTIVE_SUBNAV_DASHBOARD") && (activeSubnav == WEB_GROW_SUBNAV_DASHBOARD)) {
return activeSubnav_ClassName;
} else {
return String();
}
}
/*******************************************************************************
* Main grow page
*/
// https://techtutorialsx.com/2018/07/23/esp32-arduino-http-server-template-processing-with-multiple-placeholders/
String Proc_WebPage_grow(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:grow(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, 1);
} else if(Test_WebPage_grow_SUBNAV(var)) {
return Proc_WebPage_grow_SUBNAV(var, WEB_GROW_SUBNAV_GENERAL);
} else if(var == "GROWNAME") {
return String(config.grow.name);
} else if(var == "GROWSTART_EPOCH") {
/* check if there is an reasonable grow start time */
if(config.grow.start > 1711922400) {
return String(config.grow.start);
} else {
return String(); //String(now());
}
} else if(var == "GROWSTART") {
if(config.grow.start > 1711922400) {
return Str_Epoch2Date(config.grow.start);
} else {
return String(); //Str_Epoch2Date(now());
}
} else if(var == "DAYS_VEG") {
return String(config.grow.daysVeg);
} else if(var == "DAYS_BLOOM") {
return String(config.grow.daysBloom);
} else {
return String();
}
}
String Proc_WebPage_grow_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_grow()
* 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_grow(var);
}
}
String Proc_WebPage_grow_POST_ERR(const String& var) {
if(var == "SAVE_MSG") {
return String(Common_HTML_SAVE_MSG_ERR);
} else {
return Proc_WebPage_grow(var);
}
}
/* WebPage function */
void WebPage_grow(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:grow]";
/* Which kind of Request */
if(request->method() == HTTP_POST) {
if(request->hasParam("name", true)) {
const AsyncWebParameter* param = request->getParam("name", true);
strlcpy(config.grow.name, param->value().c_str(), sizeof(config.grow.name));
}
if(request->hasParam("start", true)) {
const AsyncWebParameter* param = request->getParam("start", true);
config.grow.start = param->value().toInt();
}
if(request->hasParam("daysVeg", true)) {
const AsyncWebParameter* param = request->getParam("daysVeg", true);
config.grow.daysVeg = param->value().toInt();
}
if(request->hasParam("daysBloom", true)) {
const AsyncWebParameter* param = request->getParam("daysBloom", true);
config.grow.daysBloom = param->value().toInt();
}
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_grow_HTML, Proc_WebPage_grow_POST);
} else {
Log.error(F("%s ERROR while saving config" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_grow_HTML, Proc_WebPage_grow_POST_ERR);
}
} else {
request->send_P(200, TEXT_HTML, Page_grow_HTML, Proc_WebPage_grow);
}
}
/*******************************************************************************
* grow light page
*/
String Proc_WebPage_grow_light(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:light(Proc)]";
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var, 1);
} else if(Test_WebPage_grow_SUBNAV(var)) {
return Proc_WebPage_grow_SUBNAV(var, WEB_GROW_SUBNAV_LIGHT);
} else if(var == "LIGHT") {
String html;
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.light.configured[i] == true) {
/* form */
html += F("<form method='post' action='/grow/light/'>");
/* OutputId */
html += F("<input type='hidden' name='output' value='");
html += i;
html += F("' required>");
html += F("<h2>");
html += config.system.output.name[i];
html += F("</h2>");
/* Device */
html += F("<u>Device:</u><br><b>");
html += FPSTR(Output_Device_descr[config.system.output.device[i]]);
html += F(" (");
html += FPSTR(Output_Type_descr[config.system.output.type[i]]);
html += F(")</b><br>");
/* Sunrise */
html += F("<u>Sunrise</u><br><b>Vegetation </b><input class='inputShort' type='number' name='sunriseHourVeg' min='0' max='23' value='");
html += config.grow.light.sunriseHourVeg[i];
html += F("' required><b>:</b> <input class='inputShort' type='number' name='sunriseMinuteVeg' min='0' max='59' value='");
html += config.grow.light.sunriseMinuteVeg[i];
html += F("' required><br>");
/* when bloon days is set to 0, disable it */
if(config.grow.daysBloom > 0) {
html += F("<b>Bloom </b><input class='inputShort' type='number' name='sunriseHourBloom' min='0' max='23' value='");
html += config.grow.light.sunriseHourBloom[i];
html += F("' required><b>:</b> <input class='inputShort' type='number' name='sunriseMinuteBloom' min='0' max='59' value='");
html += config.grow.light.sunriseMinuteBloom[i];
html += F("' required><br>");
}
/* Sunset */
html += F("<u>Sunset</u><br><b>Vegetation </b><input class='inputShort' type='number' name='sunsetHourVeg' min='0' max='23' value='");
html += config.grow.light.sunsetHourVeg[i];
html += F("' required><b>:</b> <input class='inputShort' type='number' name='sunsetMinuteVeg' min='0' max='59' value='");
html += config.grow.light.sunsetMinuteVeg[i];
html += F("' required><br>");
/* when bloon days is set to 0, disable it */
if(config.grow.daysBloom > 0) {
html += F("<b>Bloom </b><input class='inputShort' type='number' name='sunsetHourBloom' min='0' max='23' value='");
html += config.grow.light.sunsetHourBloom[i];
html += F("' required><b>:</b> <input class='inputShort' type='number' name='sunsetMinuteBloom' min='0' max='59' value='");
html += config.grow.light.sunsetMinuteBloom[i];
html += F("' required><br>");
}
/* power */
/* if no pwm, show simple bool select */
if(Output_Check_PWM(i) == true) {
html += F("<u>Power</u><br><input type='range' name='power' min='0' max='255' value='");
html += config.grow.light.power[i];
html += F("'/> &#37;<br>");
/* fade */
html += F("<u>Fade sunset/sunrise</u><br><select name='fade' required>");
html += Html_SelectOpt_bool(config.grow.light.fade[i]);
html += F("'/></select><br>");
/* fade duration */
html += F("<u>Fade duration</u><br><input class='inputShort' type='number' name='fadeDuration' min='1' max='255' value='");
html += config.grow.light.fadeDuration[i];
html += F("' required> Minutes<br>");
} else {
html += F("<u>Power</u><br><select name='power' required>");
html += Html_SelectOpt_bool(config.grow.light.power[i], "On", "Off");
html += F("'/></select><br>");
}
/* submit button */
html += F("<input type='submit' value='&#x1F4BE; Save settings' style='margin-top: 8px;'></form>");
/* HR HORIZONTAL LINE TO SIGNAL END */
html += F("<hr>");
}
}
return html;
} else {
return String();
}
}
String Proc_WebPage_grow_light_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_grow()
* 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_grow_light(var);
}
}
/* WebPage function */
void WebPage_grow_light(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:light]";
/* Which kind of Request */
if(request->method() == HTTP_POST) {
byte OutputId;
if(request->hasParam("output", true)) {
const AsyncWebParameter* param = request->getParam("output", true);
OutputId = param->value().toInt();
config.grow.light.output[OutputId] = param->value().toInt();
}
if(request->hasParam("sunriseHourVeg", true)) {
const AsyncWebParameter* param = request->getParam("sunriseHourVeg", true);
config.grow.light.sunriseHourVeg[OutputId] = param->value().toInt();
}
if(request->hasParam("sunriseMinuteVeg", true)) {
const AsyncWebParameter* param = request->getParam("sunriseMinuteVeg", true);
config.grow.light.sunriseMinuteVeg[OutputId] = param->value().toInt();
}
if(request->hasParam("sunriseHourBloom", true)) {
const AsyncWebParameter* param = request->getParam("sunriseHourBloom", true);
config.grow.light.sunriseHourBloom[OutputId] = param->value().toInt();
}
if(request->hasParam("sunriseMinuteBloom", true)) {
const AsyncWebParameter* param = request->getParam("sunriseMinuteBloom", true);
config.grow.light.sunriseMinuteBloom[OutputId] = param->value().toInt();
}
if(request->hasParam("sunsetHourVeg", true)) {
const AsyncWebParameter* param = request->getParam("sunsetHourVeg", true);
config.grow.light.sunsetHourVeg[OutputId] = param->value().toInt();
}
if(request->hasParam("sunsetMinuteVeg", true)) {
const AsyncWebParameter* param = request->getParam("sunsetMinuteVeg", true);
config.grow.light.sunsetMinuteVeg[OutputId] = param->value().toInt();
}
if(request->hasParam("sunsetHourBloom", true)) {
const AsyncWebParameter* param = request->getParam("sunsetHourBloom", true);
config.grow.light.sunsetHourBloom[OutputId] = param->value().toInt();
}
if(request->hasParam("sunsetMinuteBloom", true)) {
const AsyncWebParameter* param = request->getParam("sunsetMinuteBloom", true);
config.grow.light.sunsetMinuteBloom[OutputId] = param->value().toInt();
}
if(request->hasParam("power", true)) {
const AsyncWebParameter* param = request->getParam("power", true);
config.grow.light.power[OutputId] = param->value().toInt();
}
if(request->hasParam("fade", true)) {
const AsyncWebParameter* param = request->getParam("fade", true);
config.grow.light.fade[OutputId] = param->value().toInt();
}
if(request->hasParam("fadeDuration", true)) {
const AsyncWebParameter* param = request->getParam("fadeDuration", true);
config.grow.light.fadeDuration[OutputId] = param->value().toInt();
}
SaveConfig();
Log.notice(F("%s config saved" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_grow_light_HTML, Proc_WebPage_grow_light_POST);
} else {
request->send_P(200, TEXT_HTML, Page_grow_light_HTML, Proc_WebPage_grow_light);
}
}
/*******************************************************************************
* grow air page
*/
String Proc_WebPage_grow_air(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:air(Proc)]";
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var, 1);
} else if(Test_WebPage_grow_SUBNAV(var)) {
return Proc_WebPage_grow_SUBNAV(var, WEB_GROW_SUBNAV_AIR);
} else if(var == "AIR") {
String html;
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.air.configured[i] == true) {
/* form */
html += F("<form method='post' action='/grow/air/'>");
/* OutputId */
html += F("<input type='hidden' name='output' value='");
html += i;
html += F("' required>");
html += F("<h2>");
html += config.system.output.name[i];
html += F("</h2>");
/* Device */
html += F("<u>Device:</u><br><b>");
html += FPSTR(Output_Device_descr[config.system.output.device[i]]);
html += F(" (");
html += FPSTR(Output_Type_descr[config.system.output.type[i]]);
html += F(")</b><br>");
/* speed / power */
/* if no pwm, show simple bool select */
if(Output_Check_PWM(i) == true) {
/* <input type='range' id='PinLEDPWM' name='PinLEDPWM' min='0' max='255' value='255'/> %<br> */
if(config.system.output.device[i] == OUTPUT_DEVICE_FAN) {
html += F("<u>Speed</u>");
} else {
html += F("<u>Power</u>");
}
html += F("<br><input type='range' name='power' min='0' max='255' value='");
html += config.grow.air.power[i];
html += F("'/> &#37;<br>");
} else {
html += F("<u>Power</u><br><select name='power' required>");
//<input type='range' name='power' min='0' max='100' value='");
html += Html_SelectOpt_bool(config.grow.air.power[i], "On", "Off");
html += F("'/></select><br>");
}
/* hidden inputs for SensorId and ReadId*/
html += F("<input type='hidden' name='controlSensor' value='");
html += config.grow.air.controlSensor[i];
html += F("' id='controlSensor");
html += i;
html += F("'>");
html += F("<input type='hidden' name='controlRead' value='");
html += config.grow.air.controlRead[i];
html += F("' id='controlRead");
html += i;
html += F("'>");
/* controledBy */
html += F("<u>Controled by</u><br><select name='controlBy' id='ctrl");
html += i;
html += F("'onChange=\"GrowSelectControlSensorRead('ctrl");
html += i;
html += F("', 'controlSensor");
html += i;
html += F("', 'controlRead");
html += i;
html += F("');\"><option value='255:255' selected >---</option>");
/* iterate through available sensors and offer useful values */
byte count = 0;
for(byte j = 0; j < Max_Sensors; j++) {
/* if sensor is configured */
if(config.system.sensor.type[j] > 0) {
/* we want to offer humidity, temperature, gas resistance */
for(byte k = 0; k < Max_Sensors_Read; k++) {
if(SensorIndex[config.system.sensor.type[j]].read[k] > 0) {
if((SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_TEMP) ||
(SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_HUMIDITY) ||
(SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_GAS_RESISTANCE)) {
html += F("<option value='");
/* put SensorId and ReadId into one colon sperated string. This we seperate later within javascript */
html += j; // SensorId
html += F(":");
html += k; // ReadId
html += F("'");
if((config.grow.air.controlSensor[i] == j) && (config.grow.air.controlRead[i] == k))
html += F(" selected");
html += F(">");
html += config.system.sensor.name[j];
html += F(" - ");
html += FPSTR(Sensor_Read_descr[SensorIndex[config.system.sensor.type[j]].read[k]]);
html += F(" (");
html += Sensor_getCalibratedValue(j, k);
html += F(" ");
/* put unit into string */
String unit = FPSTR(Sensor_Read_unit[SensorIndex[config.system.sensor.type[j]].read[k]]);
/* to be able to replace % sign, which is already used by ESPAsyncWebserver's template engine
* with html code for it */
html += F(" ");
unit.replace(F("%"), F("&#37;"));
html += unit;
html += F(")</option>");
count++;
}
}
}
}
}
html += F("</select><br>");
/* controlMode */
html += F("<u>Control mode</u><br><select name='controlMode' ><option value='' selected>---</option>");
//<input type='range' name='power' min='0' max='100' value='");
html += Html_SelectOpt_array(CONTROL_AIR_MODE__TOTAL, Control_Air_Mode_descr, config.grow.air.controlMode[i]);
html += F("'/> &#37;</select><br>");
/* min */
html += F("<u>Min</u><br><input type='number' name='min' step='0.1' value='");
html += config.grow.air.min[i];
html += F("' required><br>");
/* max */
html += F("<u>Max</u><br><input type='number' name='max' step='0.1' value='");
html += config.grow.air.max[i];
html += F("' required><br>");
/* submit button */
html += F("<input type='submit' value='&#x1F4BE; Save settings' style='margin-top: 8px;'></form>");
/* HR HORIZONTAL LINE TO SIGNAL END */
html += F("<hr>");
}
}
return html;
} else {
return String();
}
}
String Proc_WebPage_grow_air_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_grow()
* 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_grow_air(var);
}
}
/* WebPage function */
void WebPage_grow_air(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:air]";
/* Which kind of Request */
if(request->method() == HTTP_POST) {
byte OutputId;
if(request->hasParam("output", true)) {
const AsyncWebParameter* param = request->getParam("output", true);
OutputId = param->value().toInt();
config.grow.air.output[OutputId] = param->value().toInt();
}
if(request->hasParam("power", true)) {
const AsyncWebParameter* param = request->getParam("power", true);
config.grow.air.power[OutputId] = param->value().toInt();
}
if(request->hasParam("controlSensor", true)) {
const AsyncWebParameter* param = request->getParam("controlSensor", true);
config.grow.air.controlSensor[OutputId] = param->value().toInt();
}
if(request->hasParam("controlRead", true)) {
const AsyncWebParameter* param = request->getParam("controlRead", true);
config.grow.air.controlRead[OutputId] = param->value().toInt();
}
//byte controlBy;
//if(request->hasParam("controlBy", true)) {
//const AsyncWebParameter* param = request->getParam("controlBy", true);
//controlBy = param->value().toInt();
//}
if(request->hasParam("controlMode", true)) {
const AsyncWebParameter* param = request->getParam("controlMode", true);
config.grow.air.controlMode[OutputId] = param->value().toInt();
}
if(request->hasParam("min", true)) {
const AsyncWebParameter* param = request->getParam("min", true);
config.grow.air.min[OutputId] = param->value().toFloat();
}
if(request->hasParam("max", true)) {
const AsyncWebParameter* param = request->getParam("max", true);
config.grow.air.max[OutputId] = param->value().toFloat();
}
SaveConfig();
Log.notice(F("%s config saved" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_grow_air_HTML, Proc_WebPage_grow_air_POST);
} else {
request->send_P(200, TEXT_HTML, Page_grow_air_HTML, Proc_WebPage_grow_air);
}
}
/*******************************************************************************
* grow water page
*/
String Proc_WebPage_grow_water(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:water(Proc)]";
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var, 1);
} else if(Test_WebPage_grow_SUBNAV(var)) {
return Proc_WebPage_grow_SUBNAV(var, WEB_GROW_SUBNAV_WATER);
} else if(var == "WATER") {
String html;
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.water.configured[i] == true) {
/* form */
html += F("<form method='post' action='/grow/water/'>");
/* OutputId */
html += F("<input type='hidden' name='output' value='");
html += i;
html += F("' required>");
html += F("<h2>");
html += config.system.output.name[i];
html += F("</h2>");
/* Device */
html += F("<u>Device:</u><br><b>");
html += FPSTR(Output_Device_descr[config.system.output.device[i]]);
html += F(" (");
html += FPSTR(Output_Type_descr[config.system.output.type[i]]);
html += F(")</b><br>");
/*
* struct Config_Grow_Water {
bool configured[Max_Outputs];
byte output[Max_Outputs];
byte controlSensor[Max_Outputs];
byte controlRead[Max_Outputs];
byte controlMode[Max_Outputs];
byte pumpOn[Max_Sensors];
byte min[Max_Sensors];
byte max[Max_Sensors];
byte interval[Max_Sensors];
byte intervalUnit[Max_Sensors];
};
*/
/* min */
html += F("<u>Pump On</u><br><input class='inputShort' type='number' name='onTime' min='0' max='255' value='");
html += config.grow.water.onTime[i];
html += F("' required> Seconds<br>");
/* hidden inputs for SensorId and ReadId*/
html += F("<input type='hidden' name='controlSensor' value='");
html += config.grow.water.controlSensor[i];
html += F("' id='controlSensor");
html += i;
html += F("'>");
html += F("<input type='hidden' name='controlRead' value='");
html += config.grow.water.controlRead[i];
html += F("' id='controlRead");
html += i;
html += F("'>");
/* controlMode */
html += F("<u>Control mode</u><br><select name='controlMode'><option value='' selected>---</option>");
//<input type='range' name='power' min='0' max='100' value='");
html += Html_SelectOpt_array(CONTROL_WATER_MODE__TOTAL, Control_Water_Mode_descr, config.grow.water.controlMode[i]);
html += F("</select><br>");
/* interval */
html += F("<u>Interval</u><br><input class='inputShort' type='number' name='interval' min='0' max='255' value='");
html += config.grow.water.interval[i];
html += F("' required> ");
/* intervalUnit */
html += F("<select name='intervalUnit'>");
//<input type='range' name='power' min='0' max='100' value='");
//html += Html_SelectOpt_bool(config.grow.water.intervalUnit[i]);
/* iterate through time scale units */
for(byte j = 0; j <= TIMESCALE_WEEK; j++) {
html += F("<option value='");
html += j;
html += F("'");
if(config.grow.water.intervalUnit[i] == j)
html += F(" selected");
html += F(">");
html += FPSTR(Timescale_descr[j]);
html += F("</option>");
}
html += F("</select><br>");
/* controledBy */
html += F("<u>Controled by</u><br><select name='controlBy' id='ctrl");
html += i;
html += F("'onChange=\"GrowSelectControlSensorRead('ctrl");
html += i;
html += F("', 'controlSensor");
html += i;
html += F("', 'controlRead");
html += i;
html += F("');\"><option value='255:255' selected >---</option>");
/* iterate through available sensors and offer useful values */
byte count = 0;
for(byte j = 0; j < Max_Sensors; j++) {
/* if sensor is configured */
if(config.system.sensor.type[j] > 0) {
/* we want to offer humidity, temperature, gas resistance */
for(byte k = 0; k < Max_Sensors_Read; k++) {
if(SensorIndex[config.system.sensor.type[j]].read[k] > 0) {
if((SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_SOILMOISTURE) || ((SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_RAW) && (config.system.sensor.rawConvert[j][k] == SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE))) {
html += F("<option value='");
/* put SensorId and ReadId into one colon sperated string. This we seperate later within javascript */
html += j; // SensorId
html += F(":");
html += k; // ReadId
html += F("'");
if((config.grow.water.controlSensor[i] == j) && (config.grow.water.controlRead[i] == k))
html += F(" selected");
html += F(">");
html += config.system.sensor.name[j];
html += F(" - (");
html += k;
html += F(") ");
if((SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_RAW) && (config.system.sensor.rawConvert[j][k] == SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE)) {
html += FPSTR(Sensor_Convert_Raw_descr[config.system.sensor.rawConvert[j][k]]);
} else {
html += FPSTR(Sensor_Read_descr[SensorIndex[config.system.sensor.type[j]].read[k]]);
}
html += F(" (");
html += Sensor_getCalibratedValue(j, k);
html += F(" ");
/* put unit into string */
String unit;
if((SensorIndex[config.system.sensor.type[j]].read[k] == SENSOR_READ_TYPE_RAW) && (config.system.sensor.rawConvert[j][k] == SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE)) {
unit = FPSTR(Sensor_Convert_Raw_unit[config.system.sensor.rawConvert[j][k]]);
} else {
unit = FPSTR(Sensor_Read_unit[SensorIndex[config.system.sensor.type[j]].read[k]]);
}
/* to be able to replace % sign, which is already used by ESPAsyncWebserver's template engine
* with html code for it */
html += F(" ");
unit.replace(F("%"), F("&#37;"));
html += unit;
html += F(")</option>");
count++;
}
}
}
}
}
html += F("</select><br>");
/* min */
html += F("<u>Min</u><br><input class='inputShort' type='number' name='min' min='0' max='255' value='");
html += config.grow.water.min[i];
html += F("' required><br>");
/* max */
html += F("<u>Max</u><br><input class='inputShort' type='number' name='max' min='0' max='255' value='");
html += config.grow.water.max[i];
html += F("' required><br>");
/* submit button */
html += F("<input type='submit' value='&#x1F4BE; Save settings' style='margin-top: 8px;'></form>");
/* HR HORIZONTAL LINE TO SIGNAL END */
html += F("<hr>");
}
}
return html;
} else {
return String();
}
}
String Proc_WebPage_grow_water_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_grow()
* 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_grow_water(var);
}
}
/* WebPage function */
void WebPage_grow_water(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:water]";
/* Which kind of Request */
if(request->method() == HTTP_POST) {
byte OutputId;
if(request->hasParam("output", true)) {
const AsyncWebParameter* param = request->getParam("output", true);
OutputId = param->value().toInt();
config.grow.water.output[OutputId] = param->value().toInt();
}
if(request->hasParam("onTime", true)) {
const AsyncWebParameter* param = request->getParam("onTime", true);
config.grow.water.onTime[OutputId] = param->value().toInt();
}
if(request->hasParam("controlSensor", true)) {
const AsyncWebParameter* param = request->getParam("controlSensor", true);
config.grow.water.controlSensor[OutputId] = param->value().toInt();
}
if(request->hasParam("controlRead", true)) {
const AsyncWebParameter* param = request->getParam("controlRead", true);
config.grow.water.controlRead[OutputId] = param->value().toInt();
}
if(request->hasParam("controlMode", true)) {
const AsyncWebParameter* param = request->getParam("controlMode", true);
config.grow.water.controlMode[OutputId] = param->value().toInt();
}
if(request->hasParam("min", true)) {
const AsyncWebParameter* param = request->getParam("min", true);
config.grow.water.min[OutputId] = param->value().toInt();
}
if(request->hasParam("max", true)) {
const AsyncWebParameter* param = request->getParam("max", true);
config.grow.water.max[OutputId] = param->value().toInt();
}
if(request->hasParam("interval", true)) {
const AsyncWebParameter* param = request->getParam("interval", true);
config.grow.water.interval[OutputId] = param->value().toInt();
}
if(request->hasParam("intervalUnit", true)) {
const AsyncWebParameter* param = request->getParam("intervalUnit", true);
config.grow.water.intervalUnit[OutputId] = param->value().toInt();
}
SaveConfig();
Log.notice(F("%s config saved" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_grow_water_HTML, Proc_WebPage_grow_water_POST);
} else {
request->send_P(200, TEXT_HTML, Page_grow_water_HTML, Proc_WebPage_grow_water);
}
}
/*******************************************************************************
* grow dashboards page
*/
String Proc_WebPage_grow_dashboard(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:dashboard(Proc)]";
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var, 1);
} else if(Test_WebPage_grow_SUBNAV(var)) {
return Proc_WebPage_grow_SUBNAV(var, WEB_GROW_SUBNAV_DASHBOARD);
} else if(var == "DASHBOARD") {
String html;
return html;
} else {
return String();
}
}
String Proc_WebPage_grow_dashboard_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_grow()
* 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_grow_dashboard(var);
}
}
/* WebPage function */
void WebPage_grow_dashboard(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:grow:dashboard]";
/* Which kind of Request */
if(request->method() == HTTP_POST) {
SaveConfig();
Log.notice(F("%s config saved" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_grow_dashboard_HTML, Proc_WebPage_grow_dashboard_POST);
} else {
request->send_P(200, TEXT_HTML, Page_grow_dashboard_HTML, Proc_WebPage_grow_dashboard);
}
}