PCB lives now in its own git repo https://git.la10cy.net/DeltaLima/CanGrow-12V-PCB
1000 lines
34 KiB
C
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("'/> %<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='💾 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("'/> %<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("%"));
|
|
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("'/> %</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='💾 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("%"));
|
|
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='💾 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);
|
|
}
|
|
}
|