mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
2424 lines
75 KiB
Arduino
2424 lines
75 KiB
Arduino
//********************************************************************************
|
|
// Web Interface init
|
|
//********************************************************************************
|
|
void WebServerInit()
|
|
{
|
|
// Prepare webserver pages
|
|
WebServer.on("/", handle_root);
|
|
WebServer.on("/config", handle_config);
|
|
WebServer.on("/hardware", handle_hardware);
|
|
WebServer.on("/devices", handle_devices);
|
|
WebServer.on("/log", handle_log);
|
|
WebServer.on("/tools", handle_tools);
|
|
WebServer.on("/i2cscanner", handle_i2cscanner);
|
|
WebServer.on("/wifiscanner", handle_wifiscanner);
|
|
WebServer.on("/login", handle_login);
|
|
WebServer.on("/control", handle_control);
|
|
WebServer.on("/download", handle_download);
|
|
WebServer.on("/upload", HTTP_GET, handle_upload);
|
|
WebServer.on("/upload", HTTP_POST, handle_upload_post, handleFileUpload);
|
|
WebServer.onNotFound(handleNotFound);
|
|
#if FEATURE_SPIFFS
|
|
WebServer.on("/filelist", handle_filelist);
|
|
#else
|
|
WebServer.on("/esp.css", handle_css);
|
|
#endif
|
|
WebServer.on("/advanced", handle_advanced);
|
|
WebServer.on("/setup", handle_setup);
|
|
WebServer.on("/json", handle_json);
|
|
WebServer.on("/rules", handle_rules);
|
|
|
|
if (ESP.getFlashChipRealSize() > 524288)
|
|
httpUpdater.setup(&WebServer);
|
|
|
|
#if FEATURE_SSDP
|
|
if (Settings.UseSSDP)
|
|
{
|
|
WebServer.on("/ssdp.xml", HTTP_GET, []() {
|
|
SSDP_schema(WebServer.client());
|
|
});
|
|
SSDP_begin();
|
|
}
|
|
#endif
|
|
|
|
WebServer.begin();
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add top menu
|
|
//********************************************************************************
|
|
void addHeader(boolean showMenu, String& str)
|
|
{
|
|
boolean cssfile = false;
|
|
|
|
str += F("<script language=\"javascript\"><!--\n");
|
|
str += F("function dept_onchange(frmselect) {frmselect.submit();}\n");
|
|
str += F("//--></script>");
|
|
str += F("<head><title>");
|
|
str += Settings.Name;
|
|
str += F("</title>");
|
|
|
|
#if FEATURE_SPIFFS
|
|
File f = SPIFFS.open("esp.css", "r");
|
|
if (f)
|
|
{
|
|
cssfile = true;
|
|
f.close();
|
|
}
|
|
#else
|
|
if (Settings.CustomCSS)
|
|
cssfile = true;
|
|
#endif
|
|
|
|
if (!cssfile)
|
|
{
|
|
str += F("<style>");
|
|
str += F("* {font-family:sans-serif; font-size:12pt;}");
|
|
str += F("h1 {font-size:16pt; color:black;}");
|
|
str += F("h6 {font-size:10pt; color:black; text-align:center;}");
|
|
str += F(".button-menu {background-color:#ffffff; color:blue; margin: 10px; text-decoration:none}");
|
|
str += F(".button-link {padding:5px 15px; background-color:#0077dd; color:#fff; border:solid 1px #fff; text-decoration:none}");
|
|
str += F(".button-menu:hover {background:#ddddff;}");
|
|
str += F(".button-link:hover {background:#369;}");
|
|
str += F("th {padding:10px; background-color:black; color:#ffffff;}");
|
|
str += F("td {padding:7px;}");
|
|
str += F("table {color:black;}");
|
|
str += F(".div_l {float: left;}");
|
|
str += F(".div_r {float: right; margin: 2px; padding: 1px 10px; border-radius: 7px; background-color:#080; color:white;}");
|
|
str += F(".div_br {clear: both;}");
|
|
str += F("</style>");
|
|
}
|
|
else
|
|
str += F("<link rel=\"stylesheet\" type=\"text/css\" href=\"esp.css\">");
|
|
|
|
str += F("</head>");
|
|
|
|
str += F("<h1>Welcome to ESP Easy: ");
|
|
str += Settings.Name;
|
|
|
|
#if FEATURE_SPIFFS
|
|
f = SPIFFS.open("esp.png", "r");
|
|
if (f)
|
|
{
|
|
str += F("<img src=\"esp.png\" width=50 height=50 align=right >");
|
|
f.close();
|
|
}
|
|
#endif
|
|
|
|
str += F("</h1>");
|
|
|
|
if (showMenu)
|
|
{
|
|
str += F("<BR><a class=\"button-menu\" href=\".\">Main</a>");
|
|
str += F("<a class=\"button-menu\" href=\"config\">Config</a>");
|
|
str += F("<a class=\"button-menu\" href=\"hardware\">Hardware</a>");
|
|
str += F("<a class=\"button-menu\" href=\"devices\">Devices</a>");
|
|
if (Settings.UseRules)
|
|
str += F("<a class=\"button-menu\" href=\"rules\">Rules</a>");
|
|
str += F("<a class=\"button-menu\" href=\"tools\">Tools</a><BR><BR>");
|
|
}
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add footer to web page
|
|
//********************************************************************************
|
|
void addFooter(String& str)
|
|
{
|
|
str += F("<h6>Powered by www.esp8266.nu</h6></body>");
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface root page
|
|
//********************************************************************************
|
|
void handle_root() {
|
|
|
|
// if Wifi setup, launch setup wizard
|
|
if (wifiSetup)
|
|
{
|
|
WebServer.send(200, "text/html", "<meta HTTP-EQUIV='REFRESH' content='0; url=http://192.168.4.1/setup'>");
|
|
//WebServer.send(200, "text/html", "<a class=\"button-menu\" href=\"setup\">Setup</a>");
|
|
return;
|
|
}
|
|
|
|
if (!isLoggedIn()) return;
|
|
|
|
int freeMem = ESP.getFreeHeap();
|
|
String sCommand = WebServer.arg("cmd");
|
|
|
|
if ((strcasecmp_P(sCommand.c_str(), PSTR("wifidisconnect")) != 0) && (strcasecmp_P(sCommand.c_str(), PSTR("reboot")) != 0))
|
|
{
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
printToWeb = true;
|
|
printWebString = "";
|
|
ExecuteCommand(sCommand.c_str());
|
|
|
|
IPAddress ip = WiFi.localIP();
|
|
IPAddress gw = WiFi.gatewayIP();
|
|
|
|
reply += printWebString;
|
|
reply += F("<form>");
|
|
reply += F("<table><TH>System Info<TH><TH>");
|
|
|
|
#if FEATURE_TIME
|
|
if (Settings.UseNTP)
|
|
{
|
|
reply += F("<TR><TD>System Time:<TD>");
|
|
reply += hour();
|
|
reply += ":";
|
|
if (minute() < 10)
|
|
reply += "0";
|
|
reply += minute();
|
|
}
|
|
#endif
|
|
|
|
reply += F("<TR><TD>Uptime:<TD>");
|
|
reply += wdcounter / 2;
|
|
reply += F(" minutes");
|
|
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
{
|
|
reply += F("<TR><TD>Wifi RSSI:<TD>");
|
|
reply += WiFi.RSSI();
|
|
reply += F(" dB");
|
|
}
|
|
|
|
char str[20];
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
|
reply += F("<TR><TD>IP:<TD>");
|
|
reply += str;
|
|
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), gw[0], gw[1], gw[2], gw[3]);
|
|
reply += F("<TR><TD>GW:<TD>");
|
|
reply += str;
|
|
|
|
reply += F("<TR><TD>Build:<TD>");
|
|
reply += BUILD;
|
|
|
|
reply += F("<TR><TD>Unit:<TD>");
|
|
reply += Settings.Unit;
|
|
|
|
reply += F("<TR><TD>STA MAC:<TD>");
|
|
uint8_t mac[] = {0, 0, 0, 0, 0, 0};
|
|
uint8_t* macread = WiFi.macAddress(mac);
|
|
char macaddress[20];
|
|
sprintf_P(macaddress, PSTR("%02x:%02x:%02x:%02x:%02x:%02x"), macread[0], macread[1], macread[2], macread[3], macread[4], macread[5]);
|
|
reply += macaddress;
|
|
|
|
reply += F("<TR><TD>AP MAC:<TD>");
|
|
macread = WiFi.softAPmacAddress(mac);
|
|
sprintf_P(macaddress, PSTR("%02x:%02x:%02x:%02x:%02x:%02x"), macread[0], macread[1], macread[2], macread[3], macread[4], macread[5]);
|
|
reply += macaddress;
|
|
|
|
reply += F("<TR><TD>ESP Chip ID:<TD>");
|
|
reply += ESP.getChipId();
|
|
|
|
reply += F("<TR><TD>Flash Chip ID:<TD>");
|
|
reply += ESP.getFlashChipId();
|
|
|
|
reply += F("<TR><TD>Flash Size:<TD>");
|
|
reply += ESP.getFlashChipRealSize()/1024; //ESP.getFlashChipSize();
|
|
reply += F(" kB");
|
|
|
|
reply += F("<TR><TD>Free Mem:<TD>");
|
|
reply += freeMem;
|
|
|
|
reply += F("<TR><TD>Boot cause:<TD>");
|
|
switch (lastBootCause)
|
|
{
|
|
case BOOT_CAUSE_MANUAL_REBOOT:
|
|
reply += F("Manual reboot");
|
|
break;
|
|
case BOOT_CAUSE_COLD_BOOT:
|
|
reply += F("Cold boot");
|
|
break;
|
|
case BOOT_CAUSE_EXT_WD:
|
|
reply += F("External Watchdog");
|
|
break;
|
|
}
|
|
|
|
reply += F("<TR><TH>Node List:<TH>IP<TH>Age<TR><TD><TD>");
|
|
for (byte x = 0; x < UNIT_MAX; x++)
|
|
{
|
|
if (Nodes[x].ip[0] != 0)
|
|
{
|
|
if (x == Settings.Unit)
|
|
reply += F("<font color='blue'>");
|
|
char url[80];
|
|
sprintf_P(url, PSTR("<a href='http://%u.%u.%u.%u'>%u.%u.%u.%u</a>"), Nodes[x].ip[0], Nodes[x].ip[1], Nodes[x].ip[2], Nodes[x].ip[3], Nodes[x].ip[0], Nodes[x].ip[1], Nodes[x].ip[2], Nodes[x].ip[3]);
|
|
reply += F("<TR><TD>Unit ");
|
|
reply += x;
|
|
reply += F(":<TD>");
|
|
reply += url;
|
|
reply += F("<TD>");
|
|
reply += Nodes[x].age;
|
|
if (x == Settings.Unit)
|
|
reply += F("</font color>");
|
|
}
|
|
}
|
|
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
else
|
|
{
|
|
// have to disconnect or reboot from within the main loop
|
|
// because the webconnection is still active at this point
|
|
// disconnect here could result into a crash/reboot...
|
|
if (strcasecmp_P(sCommand.c_str(), PSTR("wifidisconnect")) == 0)
|
|
{
|
|
String log = F("WIFI : Disconnecting...");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
cmd_within_mainloop = CMD_WIFI_DISCONNECT;
|
|
}
|
|
|
|
if (strcasecmp_P(sCommand.c_str(), PSTR("reboot")) == 0)
|
|
{
|
|
String log = F(" : Rebooting...");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
cmd_within_mainloop = CMD_REBOOT;
|
|
}
|
|
|
|
WebServer.send(200, "text/html", "OK");
|
|
}
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface config page
|
|
//********************************************************************************
|
|
void handle_config() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char tmpString[64];
|
|
|
|
String name = WebServer.arg("name");
|
|
String password = WebServer.arg("password");
|
|
String ssid = WebServer.arg("ssid");
|
|
String key = WebServer.arg("key");
|
|
String usedns = WebServer.arg("usedns");
|
|
String controllerip = WebServer.arg("controllerip");
|
|
String controllerhostname = WebServer.arg("controllerhostname");
|
|
String controllerport = WebServer.arg("controllerport");
|
|
String protocol = WebServer.arg("protocol");
|
|
String controlleruser = WebServer.arg("controlleruser");
|
|
String controllerpassword = WebServer.arg("controllerpassword");
|
|
String sensordelay = WebServer.arg("delay");
|
|
String deepsleep = WebServer.arg("deepsleep");
|
|
String espip = WebServer.arg("espip");
|
|
String espgateway = WebServer.arg("espgateway");
|
|
String espsubnet = WebServer.arg("espsubnet");
|
|
String espdns = WebServer.arg("espdns");
|
|
String unit = WebServer.arg("unit");
|
|
String apkey = WebServer.arg("apkey");
|
|
|
|
if (ssid[0] != 0)
|
|
{
|
|
strncpy(Settings.Name, name.c_str(), sizeof(Settings.Name));
|
|
strncpy(SecuritySettings.Password, password.c_str(), sizeof(SecuritySettings.Password));
|
|
strncpy(SecuritySettings.WifiSSID, ssid.c_str(), sizeof(SecuritySettings.WifiSSID));
|
|
strncpy(SecuritySettings.WifiKey, key.c_str(), sizeof(SecuritySettings.WifiKey));
|
|
strncpy(SecuritySettings.WifiAPKey, apkey.c_str(), sizeof(SecuritySettings.WifiAPKey));
|
|
|
|
if (Settings.Protocol != protocol.toInt())
|
|
{
|
|
Settings.Protocol = protocol.toInt();
|
|
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
|
|
Settings.ControllerPort = Protocol[ProtocolIndex].defaultPort;
|
|
if (Protocol[ProtocolIndex].usesMQTT)
|
|
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_TEMPLATE, 0, dummyString);
|
|
}
|
|
else
|
|
{
|
|
if (Settings.Protocol != 0)
|
|
{
|
|
Settings.UseDNS = usedns.toInt();
|
|
if (Settings.UseDNS)
|
|
{
|
|
strncpy(Settings.ControllerHostName, controllerhostname.c_str(), sizeof(Settings.ControllerHostName));
|
|
getIPfromHostName();
|
|
}
|
|
else
|
|
{
|
|
if (controllerip.length() != 0)
|
|
{
|
|
controllerip.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.Controller_IP);
|
|
}
|
|
}
|
|
|
|
Settings.ControllerPort = controllerport.toInt();
|
|
strncpy(SecuritySettings.ControllerUser, controlleruser.c_str(), sizeof(SecuritySettings.ControllerUser));
|
|
strncpy(SecuritySettings.ControllerPassword, controllerpassword.c_str(), sizeof(SecuritySettings.ControllerPassword));
|
|
}
|
|
}
|
|
|
|
Settings.Delay = sensordelay.toInt();
|
|
Settings.deepSleep = (deepsleep == "on");
|
|
espip.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.IP);
|
|
espgateway.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.Gateway);
|
|
espsubnet.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.Subnet);
|
|
espdns.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.DNS);
|
|
Settings.Unit = unit.toInt();
|
|
SaveSettings();
|
|
}
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
reply += F("<form name='frmselect' method='post'><table>");
|
|
reply += F("<TH>Main Settings<TH><TR><TD>Name:<TD><input type='text' name='name' value='");
|
|
Settings.Name[25] = 0;
|
|
reply += Settings.Name;
|
|
reply += F("'><TR><TD>Admin Password:<TD><input type='text' name='password' value='");
|
|
SecuritySettings.Password[25] = 0;
|
|
reply += SecuritySettings.Password;
|
|
reply += F("'><TR><TD>SSID:<TD><input type='text' name='ssid' value='");
|
|
reply += SecuritySettings.WifiSSID;
|
|
reply += F("'><TR><TD>WPA Key:<TD><input type='text' maxlength='63' name='key' value='");
|
|
reply += SecuritySettings.WifiKey;
|
|
|
|
reply += F("'><TR><TD>WPA AP Mode Key:<TD><input type='text' maxlength='63' name='apkey' value='");
|
|
reply += SecuritySettings.WifiAPKey;
|
|
|
|
reply += F("'><TR><TD>Unit nr:<TD><input type='text' name='unit' value='");
|
|
reply += Settings.Unit;
|
|
|
|
reply += F("'><TR><TD>Protocol:");
|
|
byte choice = Settings.Protocol;
|
|
reply += F("<TD><select name='protocol' LANGUAGE=javascript onchange=\"return dept_onchange(frmselect)\" >");
|
|
reply += F("<option value='0'>- Standalone -</option>");
|
|
for (byte x = 0; x <= protocolCount; x++)
|
|
{
|
|
reply += F("<option value='");
|
|
reply += Protocol[x].Number;
|
|
reply += "'";
|
|
if (choice == Protocol[x].Number)
|
|
reply += F(" selected");
|
|
reply += ">";
|
|
|
|
String ProtocolName = "";
|
|
CPlugin_ptr[x](CPLUGIN_GET_DEVICENAME, 0, ProtocolName);
|
|
reply += ProtocolName;
|
|
reply += F("</option>");
|
|
}
|
|
reply += F("</select>");
|
|
reply += F("<a class=\"button-link\" href=\"http://www.esp8266.nu/index.php/EasyProtocols\" target=\"_blank\">?</a>");
|
|
|
|
|
|
char str[20];
|
|
|
|
if (Settings.Protocol)
|
|
{
|
|
byte choice = Settings.UseDNS;
|
|
String options[2];
|
|
options[0] = F("Use IP address");
|
|
options[1] = F("Use Hostname");
|
|
int optionValues[2];
|
|
optionValues[0] = 0;
|
|
optionValues[1] = 1;
|
|
reply += F("<TR><TD>Locate Controller:<TD><select name='usedns' LANGUAGE=javascript onchange=\"return dept_onchange(frmselect)\" >");
|
|
for (byte x = 0; x < 2; x++)
|
|
{
|
|
reply += F("<option value='");
|
|
reply += optionValues[x];
|
|
reply += "'";
|
|
if (choice == optionValues[x])
|
|
reply += F(" selected");
|
|
reply += ">";
|
|
reply += options[x];
|
|
reply += F("</option>");
|
|
}
|
|
reply += F("</select>");
|
|
|
|
if (Settings.UseDNS)
|
|
{
|
|
reply += F("<TR><TD>Controller Hostname:<TD><input type='text' name='controllerhostname' size='64' value='");
|
|
reply += Settings.ControllerHostName;
|
|
}
|
|
else
|
|
{
|
|
reply += F("<TR><TD>Controller IP:<TD><input type='text' name='controllerip' value='");
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]);
|
|
reply += str;
|
|
}
|
|
|
|
reply += F("'><TR><TD>Controller Port:<TD><input type='text' name='controllerport' value='");
|
|
reply += Settings.ControllerPort;
|
|
|
|
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
|
|
if (Protocol[ProtocolIndex].usesAccount)
|
|
{
|
|
reply += F("'><TR><TD>Controller User:<TD><input type='text' name='controlleruser' value='");
|
|
reply += SecuritySettings.ControllerUser;
|
|
}
|
|
|
|
if (Protocol[ProtocolIndex].usesPassword)
|
|
{
|
|
reply += F("'><TR><TD>Controller Password:<TD><input type='text' name='controllerpassword' value='");
|
|
reply += SecuritySettings.ControllerPassword;
|
|
}
|
|
reply += F("'>");
|
|
}
|
|
|
|
reply += F("<TR><TD>Sensor Delay:<TD><input type='text' name='delay' value='");
|
|
reply += Settings.Delay;
|
|
reply += F("'><TR><TD>Sleep Mode:<TD>");
|
|
if (Settings.deepSleep)
|
|
reply += F("<input type=checkbox name='deepsleep' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='deepsleep'>");
|
|
|
|
reply += F("<a class=\"button-link\" href=\"http://www.esp8266.nu/index.php/SleepMode\" target=\"_blank\">?</a>");
|
|
|
|
reply += F("<TR><TH>Optional Settings<TH>");
|
|
|
|
reply += F("<TR><TD>ESP IP:<TD><input type='text' name='espip' value='");
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.IP[0], Settings.IP[1], Settings.IP[2], Settings.IP[3]);
|
|
reply += str;
|
|
|
|
reply += F("'><TR><TD>ESP GW:<TD><input type='text' name='espgateway' value='");
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.Gateway[0], Settings.Gateway[1], Settings.Gateway[2], Settings.Gateway[3]);
|
|
reply += str;
|
|
|
|
reply += F("'><TR><TD>ESP Subnet:<TD><input type='text' name='espsubnet' value='");
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.Subnet[0], Settings.Subnet[1], Settings.Subnet[2], Settings.Subnet[3]);
|
|
reply += str;
|
|
|
|
reply += F("'><TR><TD>ESP DNS:<TD><input type='text' name='espdns' value='");
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.DNS[0], Settings.DNS[1], Settings.DNS[2], Settings.DNS[3]);
|
|
reply += str;
|
|
|
|
reply += F("'><TR><TD><TD><input class=\"button-link\" type='submit' value='Submit'>");
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface hardware page
|
|
//********************************************************************************
|
|
void handle_hardware() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String pin_i2c_sda = WebServer.arg("psda");
|
|
String pin_i2c_scl = WebServer.arg("pscl");
|
|
String pin_status_led = WebServer.arg("pled");
|
|
|
|
if (pin_i2c_sda.length() != 0)
|
|
{
|
|
Settings.Pin_i2c_sda = pin_i2c_sda.toInt();
|
|
Settings.Pin_i2c_scl = pin_i2c_scl.toInt();
|
|
Settings.Pin_status_led = pin_status_led.toInt();
|
|
Settings.PinBootStates[0] = WebServer.arg("p0").toInt();
|
|
Settings.PinBootStates[2] = WebServer.arg("p2").toInt();
|
|
Settings.PinBootStates[4] = WebServer.arg("p4").toInt();
|
|
Settings.PinBootStates[5] = WebServer.arg("p5").toInt();
|
|
Settings.PinBootStates[9] = WebServer.arg("p9").toInt();
|
|
Settings.PinBootStates[10] = WebServer.arg("p10").toInt();
|
|
Settings.PinBootStates[12] = WebServer.arg("p12").toInt();
|
|
Settings.PinBootStates[13] = WebServer.arg("p13").toInt();
|
|
Settings.PinBootStates[14] = WebServer.arg("p14").toInt();
|
|
Settings.PinBootStates[15] = WebServer.arg("p15").toInt();
|
|
Settings.PinBootStates[16] = WebServer.arg("p16").toInt();
|
|
|
|
SaveSettings();
|
|
}
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
reply += F("<form method='post'><table><TH>Hardware Settings<TH><TR><TD>");
|
|
reply += F("<TR><TD>Wifi Status Led:<TD>");
|
|
addPinSelect(false, reply, "pled", Settings.Pin_status_led);
|
|
reply += F("<TR><TD>SDA:<TD>");
|
|
addPinSelect(true, reply, "psda", Settings.Pin_i2c_sda);
|
|
reply += F("<TR><TD>SCL:<TD>");
|
|
addPinSelect(true, reply, "pscl", Settings.Pin_i2c_scl);
|
|
|
|
reply += F("<TR><TD>GPIO boot states:<TD>");
|
|
reply += F("<TR><TD>Pin mode 0:<TD>");
|
|
addPinStateSelect(reply, "p0", Settings.PinBootStates[0]);
|
|
reply += F("<TR><TD>Pin mode 2:<TD>");
|
|
addPinStateSelect(reply, "p2", Settings.PinBootStates[2]);
|
|
reply += F("<TR><TD>Pin mode 4:<TD>");
|
|
addPinStateSelect(reply, "p4", Settings.PinBootStates[4]);
|
|
reply += F("<TR><TD>Pin mode 5:<TD>");
|
|
addPinStateSelect(reply, "p5", Settings.PinBootStates[5]);
|
|
reply += F("<TR><TD>Pin mode 9:<TD>");
|
|
addPinStateSelect(reply, "p9", Settings.PinBootStates[9]);
|
|
reply += F("<TR><TD>Pin mode 10:<TD>");
|
|
addPinStateSelect(reply, "p10", Settings.PinBootStates[10]);
|
|
reply += F("<TR><TD>Pin mode 12:<TD>");
|
|
addPinStateSelect(reply, "p12", Settings.PinBootStates[12]);
|
|
reply += F("<TR><TD>Pin mode 13:<TD>");
|
|
addPinStateSelect(reply, "p13", Settings.PinBootStates[13]);
|
|
reply += F("<TR><TD>Pin mode 14:<TD>");
|
|
addPinStateSelect(reply, "p14", Settings.PinBootStates[14]);
|
|
reply += F("<TR><TD>Pin mode 15:<TD>");
|
|
addPinStateSelect(reply, "p15", Settings.PinBootStates[15]);
|
|
reply += F("<TR><TD>Pin mode 16:<TD>");
|
|
addPinStateSelect(reply, "p16", Settings.PinBootStates[16]);
|
|
|
|
reply += F("<TR><TD><TD><input class=\"button-link\" type='submit' value='Submit'><TR><TD>");
|
|
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add a GPIO pin select dropdown list
|
|
//********************************************************************************
|
|
void addPinStateSelect(String& str, String name, int choice)
|
|
{
|
|
String options[4];
|
|
options[0] = F("Default");
|
|
options[1] = F("Output Low");
|
|
options[2] = F("Output High");
|
|
options[3] = F("Input");
|
|
int optionValues[4];
|
|
optionValues[0] = 0;
|
|
optionValues[1] = 1;
|
|
optionValues[2] = 2;
|
|
optionValues[3] = 3;
|
|
|
|
str += F("<select name='");
|
|
str += name;
|
|
str += "'>";
|
|
for (byte x = 0; x < 4; x++)
|
|
{
|
|
str += F("<option value='");
|
|
str += optionValues[x];
|
|
str += "'";
|
|
if (choice == optionValues[x])
|
|
str += F(" selected");
|
|
str += ">";
|
|
str += options[x];
|
|
str += F("</option>");
|
|
}
|
|
str += F("</select>");
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface device page
|
|
//********************************************************************************
|
|
void handle_devices() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char tmpString[41];
|
|
struct EventStruct TempEvent;
|
|
|
|
String taskindex = WebServer.arg("index");
|
|
String taskdevicenumber = WebServer.arg("taskdevicenumber");
|
|
String taskdevicetimer = WebServer.arg("taskdevicetimer");
|
|
String taskdeviceid = WebServer.arg("taskdeviceid");
|
|
String taskdevicepin1 = WebServer.arg("taskdevicepin1");
|
|
String taskdevicepin2 = WebServer.arg("taskdevicepin2");
|
|
String taskdevicepin3 = WebServer.arg("taskdevicepin3");
|
|
String taskdevicepin1pullup = WebServer.arg("taskdevicepin1pullup");
|
|
String taskdevicepin1inversed = WebServer.arg("taskdevicepin1inversed");
|
|
String taskdevicename = WebServer.arg("taskdevicename");
|
|
String taskdeviceport = WebServer.arg("taskdeviceport");
|
|
String taskdeviceformula[VARS_PER_TASK];
|
|
String taskdevicevaluename[VARS_PER_TASK];
|
|
String taskdevicesenddata = WebServer.arg("taskdevicesenddata");
|
|
String taskdeviceglobalsync = WebServer.arg("taskdeviceglobalsync");
|
|
|
|
for (byte varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
|
{
|
|
char argc[25];
|
|
String arg = "taskdeviceformula";
|
|
arg += varNr + 1;
|
|
arg.toCharArray(argc, 25);
|
|
taskdeviceformula[varNr] = WebServer.arg(argc);
|
|
|
|
arg = "taskdevicevaluename";
|
|
arg += varNr + 1;
|
|
arg.toCharArray(argc, 25);
|
|
taskdevicevaluename[varNr] = WebServer.arg(argc);
|
|
}
|
|
|
|
String edit = WebServer.arg("edit");
|
|
byte page = WebServer.arg("page").toInt();
|
|
if (page == 0)
|
|
page = 1;
|
|
byte setpage = WebServer.arg("setpage").toInt();
|
|
if (setpage > 0)
|
|
{
|
|
if (setpage <= (TASKS_MAX / 4))
|
|
page = setpage;
|
|
else
|
|
page = TASKS_MAX / 4;
|
|
}
|
|
|
|
byte index = taskindex.toInt();
|
|
|
|
byte DeviceIndex = 0;
|
|
|
|
if (edit.toInt() != 0)
|
|
{
|
|
if (Settings.TaskDeviceNumber[index - 1] != taskdevicenumber.toInt()) // change of device, clear all other values
|
|
{
|
|
taskClear(index - 1, false); // clear settings, but do not save
|
|
Settings.TaskDeviceNumber[index - 1] = taskdevicenumber.toInt();
|
|
}
|
|
else if (taskdevicenumber.toInt() != 0)
|
|
{
|
|
Settings.TaskDeviceNumber[index - 1] = taskdevicenumber.toInt();
|
|
DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[index - 1]);
|
|
|
|
if (taskdevicetimer.toInt() > 0)
|
|
Settings.TaskDeviceTimer[index - 1] = taskdevicetimer.toInt();
|
|
else
|
|
Settings.TaskDeviceTimer[index - 1] = Settings.Delay;
|
|
|
|
taskdevicename.toCharArray(tmpString, 26);
|
|
strcpy(ExtraTaskSettings.TaskDeviceName, tmpString);
|
|
Settings.TaskDevicePort[index - 1] = taskdeviceport.toInt();
|
|
if (Settings.TaskDeviceNumber[index - 1] != 0)
|
|
Settings.TaskDeviceID[index - 1] = taskdeviceid.toInt();
|
|
else
|
|
Settings.TaskDeviceID[index - 1] = 0;
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_SINGLE)
|
|
{
|
|
Settings.TaskDevicePin1[index - 1] = taskdevicepin1.toInt();
|
|
}
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_DUAL)
|
|
{
|
|
Settings.TaskDevicePin1[index - 1] = taskdevicepin1.toInt();
|
|
Settings.TaskDevicePin2[index - 1] = taskdevicepin2.toInt();
|
|
}
|
|
|
|
if (taskdevicepin3.length() != 0)
|
|
Settings.TaskDevicePin3[index - 1] = taskdevicepin3.toInt();
|
|
|
|
if (Device[DeviceIndex].PullUpOption)
|
|
Settings.TaskDevicePin1PullUp[index - 1] = (taskdevicepin1pullup == "on");
|
|
|
|
if (Device[DeviceIndex].InverseLogicOption)
|
|
Settings.TaskDevicePin1Inversed[index - 1] = (taskdevicepin1inversed == "on");
|
|
|
|
if (Device[DeviceIndex].SendDataOption)
|
|
Settings.TaskDeviceSendData[index - 1] = (taskdevicesenddata == "on");
|
|
|
|
if (Device[DeviceIndex].GlobalSyncOption)
|
|
Settings.TaskDeviceGlobalSync[index - 1] = (taskdeviceglobalsync == "on");
|
|
|
|
// Send task info if set global
|
|
if (Settings.TaskDeviceGlobalSync[index - 1])
|
|
{
|
|
SendUDPTaskInfo(0, index - 1, index - 1);
|
|
}
|
|
|
|
for (byte varNr = 0; varNr < Device[DeviceIndex].ValueCount; varNr++)
|
|
{
|
|
taskdeviceformula[varNr].toCharArray(tmpString, 41);
|
|
strcpy(ExtraTaskSettings.TaskDeviceFormula[varNr], tmpString);
|
|
}
|
|
|
|
// task value names handling.
|
|
TempEvent.TaskIndex = index - 1;
|
|
for (byte varNr = 0; varNr < Device[DeviceIndex].ValueCount; varNr++)
|
|
{
|
|
taskdevicevaluename[varNr].toCharArray(tmpString, 26);
|
|
strcpy(ExtraTaskSettings.TaskDeviceValueNames[varNr], tmpString);
|
|
}
|
|
TempEvent.TaskIndex = index - 1;
|
|
PluginCall(PLUGIN_WEBFORM_SAVE, &TempEvent, dummyString);
|
|
PluginCall(PLUGIN_INIT, &TempEvent, dummyString);
|
|
}
|
|
SaveTaskSettings(index - 1);
|
|
SaveSettings();
|
|
}
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
|
|
// show all tasks as table
|
|
reply += F("<table cellpadding='4' border='1' frame='box' rules='all'><TH>");
|
|
reply += F("<a class=\"button-link\" href=\"devices?setpage=");
|
|
if (page > 1)
|
|
reply += page - 1;
|
|
else
|
|
reply += page;
|
|
reply += F("\"><</a>");
|
|
reply += F("<a class=\"button-link\" href=\"devices?setpage=");
|
|
if (page < (TASKS_MAX / 4))
|
|
reply += page + 1;
|
|
else
|
|
reply += page;
|
|
reply += F("\">></a>");
|
|
|
|
reply += F("<TH>Task<TH>Device<TH>Name<TH>Port<TH>IDX/Variable<TH>GPIO<TH>Values");
|
|
|
|
String deviceName;
|
|
|
|
for (byte x = (page - 1) * 4; x < ((page) * 4); x++)
|
|
{
|
|
LoadTaskSettings(x);
|
|
DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[x]);
|
|
TempEvent.TaskIndex = x;
|
|
|
|
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
|
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString);
|
|
|
|
deviceName = "";
|
|
if (Settings.TaskDeviceNumber[x] != 0)
|
|
Plugin_ptr[DeviceIndex](PLUGIN_GET_DEVICENAME, &TempEvent, deviceName);
|
|
|
|
reply += F("<TR><TD>");
|
|
reply += F("<a class=\"button-link\" href=\"devices?index=");
|
|
reply += x + 1;
|
|
reply += F("&page=");
|
|
reply += page;
|
|
reply += F("\">Edit</a>");
|
|
reply += F("<TD>");
|
|
reply += x + 1;
|
|
reply += F("<TD>");
|
|
reply += deviceName;
|
|
reply += F("<TD>");
|
|
reply += ExtraTaskSettings.TaskDeviceName;
|
|
reply += F("<TD>");
|
|
|
|
byte customConfig = false;
|
|
customConfig = PluginCall(PLUGIN_WEBFORM_SHOW_CONFIG, &TempEvent, reply);
|
|
if (!customConfig)
|
|
if (Device[DeviceIndex].Ports != 0)
|
|
reply += Settings.TaskDevicePort[x];
|
|
|
|
reply += F("<TD>");
|
|
|
|
if (Settings.TaskDeviceID[x] != 0)
|
|
reply += Settings.TaskDeviceID[x];
|
|
|
|
reply += F("<TD>");
|
|
|
|
if (Settings.TaskDeviceDataFeed[x] == 0)
|
|
{
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_I2C)
|
|
{
|
|
reply += F("GPIO-");
|
|
reply += Settings.Pin_i2c_sda;
|
|
reply += F("<BR>GPIO-");
|
|
reply += Settings.Pin_i2c_scl;
|
|
}
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_ANALOG)
|
|
reply += F("ADC (TOUT)");
|
|
|
|
if (Settings.TaskDevicePin1[x] != -1)
|
|
{
|
|
reply += F("GPIO-");
|
|
reply += Settings.TaskDevicePin1[x];
|
|
}
|
|
|
|
if (Settings.TaskDevicePin2[x] != -1)
|
|
{
|
|
reply += F("<BR>GPIO-");
|
|
reply += Settings.TaskDevicePin2[x];
|
|
}
|
|
|
|
if (Settings.TaskDevicePin3[x] != -1)
|
|
{
|
|
reply += F("<BR>GPIO-");
|
|
reply += Settings.TaskDevicePin3[x];
|
|
}
|
|
}
|
|
|
|
reply += F("<TD>");
|
|
byte customValues = false;
|
|
customValues = PluginCall(PLUGIN_WEBFORM_SHOW_VALUES, &TempEvent, reply);
|
|
if (!customValues)
|
|
{
|
|
for (byte varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
|
{
|
|
if ((Settings.TaskDeviceNumber[x] != 0) and (varNr < Device[DeviceIndex].ValueCount))
|
|
{
|
|
if (varNr > 0)
|
|
reply += F("<div class=\"div_br\"></div>");
|
|
reply += F("<div class=\"div_l\">");
|
|
reply += ExtraTaskSettings.TaskDeviceValueNames[varNr];
|
|
reply += F(":</div><div class=\"div_r\">");
|
|
reply += UserVar[x * VARS_PER_TASK + varNr];
|
|
reply += "</div>";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
reply += F("</table>");
|
|
|
|
// Show edit form if a specific entry is chosen with the edit button
|
|
if (index != 0)
|
|
{
|
|
LoadTaskSettings(index - 1);
|
|
DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[index - 1]);
|
|
TempEvent.TaskIndex = index - 1;
|
|
|
|
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
|
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString);
|
|
|
|
reply += F("<BR><BR><form name='frmselect' method='post'><table><TH>Task Settings<TH>Value");
|
|
|
|
reply += F("<TR><TD>Device:<TD>");
|
|
addDeviceSelect(reply, "taskdevicenumber", Settings.TaskDeviceNumber[index - 1]);
|
|
|
|
if (Settings.TaskDeviceNumber[index - 1] != 0 )
|
|
{
|
|
reply += F("<a class=\"button-link\" href=\"http://www.esp8266.nu/index.php/plugin");
|
|
reply += Settings.TaskDeviceNumber[index - 1];
|
|
reply += F("\" target=\"_blank\">?</a>");
|
|
|
|
reply += F("<TR><TD>Name:<TD><input type='text' maxlength='25' name='taskdevicename' value='");
|
|
reply += ExtraTaskSettings.TaskDeviceName;
|
|
reply += F("'>");
|
|
|
|
if (Device[DeviceIndex].TimerOption)
|
|
{
|
|
reply += F("<TR><TD>Delay:<TD><input type='text' name='taskdevicetimer' value='");
|
|
reply += Settings.TaskDeviceTimer[index - 1];
|
|
reply += F("'>");
|
|
}
|
|
|
|
if (!Device[DeviceIndex].Custom)
|
|
{
|
|
reply += F("<TR><TD>IDX / Var:<TD><input type='text' name='taskdeviceid' value='");
|
|
reply += Settings.TaskDeviceID[index - 1];
|
|
reply += F("'>");
|
|
}
|
|
|
|
if (!Device[DeviceIndex].Custom && Settings.TaskDeviceDataFeed[index - 1] == 0)
|
|
{
|
|
if (Device[DeviceIndex].Ports != 0)
|
|
{
|
|
reply += F("<TR><TD>Port:<TD><input type='text' name='taskdeviceport' value='");
|
|
reply += Settings.TaskDevicePort[index - 1];
|
|
reply += F("'>");
|
|
}
|
|
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_SINGLE || Device[DeviceIndex].Type == DEVICE_TYPE_DUAL)
|
|
{
|
|
reply += F("<TR><TD>1st GPIO:<TD>");
|
|
addPinSelect(false, reply, "taskdevicepin1", Settings.TaskDevicePin1[index - 1]);
|
|
}
|
|
if (Device[DeviceIndex].Type == DEVICE_TYPE_DUAL)
|
|
{
|
|
reply += F("<TR><TD>2nd GPIO:<TD>");
|
|
addPinSelect(false, reply, "taskdevicepin2", Settings.TaskDevicePin2[index - 1]);
|
|
}
|
|
|
|
if (Device[DeviceIndex].PullUpOption)
|
|
{
|
|
reply += F("<TR><TD>Pull UP:<TD>");
|
|
if (Settings.TaskDevicePin1PullUp[index - 1])
|
|
reply += F("<input type=checkbox name=taskdevicepin1pullup checked>");
|
|
else
|
|
reply += F("<input type=checkbox name=taskdevicepin1pullup>");
|
|
}
|
|
|
|
if (Device[DeviceIndex].InverseLogicOption)
|
|
{
|
|
reply += F("<TR><TD>Inversed:<TD>");
|
|
if (Settings.TaskDevicePin1Inversed[index - 1])
|
|
reply += F("<input type=checkbox name=taskdevicepin1inversed checked>");
|
|
else
|
|
reply += F("<input type=checkbox name=taskdevicepin1inversed>");
|
|
}
|
|
}
|
|
|
|
PluginCall(PLUGIN_WEBFORM_LOAD, &TempEvent, reply);
|
|
|
|
if (Device[DeviceIndex].SendDataOption)
|
|
{
|
|
reply += F("<TR><TD>Send Data:<TD>");
|
|
if (Settings.TaskDeviceSendData[index - 1])
|
|
reply += F("<input type=checkbox name=taskdevicesenddata checked>");
|
|
else
|
|
reply += F("<input type=checkbox name=taskdevicesenddata>");
|
|
}
|
|
|
|
if (Device[DeviceIndex].GlobalSyncOption && Settings.TaskDeviceDataFeed[index - 1] == 0 && Settings.UDPPort != 0)
|
|
{
|
|
reply += F("<TR><TD>Global Sync:<TD>");
|
|
if (Settings.TaskDeviceGlobalSync[index - 1])
|
|
reply += F("<input type=checkbox name=taskdeviceglobalsync checked>");
|
|
else
|
|
reply += F("<input type=checkbox name=taskdeviceglobalsync>");
|
|
}
|
|
|
|
if (!Device[DeviceIndex].Custom)
|
|
{
|
|
reply += F("<TR><TH>Optional Settings<TH>Value");
|
|
|
|
if (Device[DeviceIndex].FormulaOption)
|
|
{
|
|
for (byte varNr = 0; varNr < Device[DeviceIndex].ValueCount; varNr++)
|
|
{
|
|
reply += F("<TR><TD>Formula ");
|
|
reply += ExtraTaskSettings.TaskDeviceValueNames[varNr];
|
|
reply += F(":<TD><input type='text' maxlength='25' name='taskdeviceformula");
|
|
reply += varNr + 1;
|
|
reply += F("' value='");
|
|
reply += ExtraTaskSettings.TaskDeviceFormula[varNr];
|
|
reply += F("'>");
|
|
if (varNr == 0)
|
|
reply += F("<a class=\"button-link\" href=\"http://www.esp8266.nu/index.php/EasyFormula\" target=\"_blank\">?</a>");
|
|
}
|
|
}
|
|
|
|
for (byte varNr = 0; varNr < Device[DeviceIndex].ValueCount; varNr++)
|
|
{
|
|
reply += F("<TR><TD>Value Name ");
|
|
reply += varNr + 1;
|
|
reply += F(":<TD><input type='text' maxlength='25' name='taskdevicevaluename");
|
|
reply += varNr + 1;
|
|
reply += F("' value='");
|
|
reply += ExtraTaskSettings.TaskDeviceValueNames[varNr];
|
|
reply += F("'>");
|
|
}
|
|
}
|
|
|
|
}
|
|
reply += F("<TR><TD><TD><a class=\"button-link\" href=\"devices\">Close</a>");
|
|
reply += F("<input class=\"button-link\" type='submit' value='Submit'>");
|
|
reply += F("<input type='hidden' name='edit' value='1'>");
|
|
reply += F("<input type='hidden' name='page' value='1'>");
|
|
reply += F("</table></form>");
|
|
}
|
|
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
|
|
byte sortedIndex[DEVICES_MAX + 1];
|
|
//********************************************************************************
|
|
// Add a device select dropdown list
|
|
//********************************************************************************
|
|
void addDeviceSelect(String& str, String name, int choice)
|
|
{
|
|
// first get the list in alphabetic order
|
|
for (byte x = 0; x <= deviceCount; x++)
|
|
sortedIndex[x] = x;
|
|
sortDeviceArray();
|
|
|
|
String deviceName;
|
|
|
|
str += F("<select name='");
|
|
str += name;
|
|
//str += "'>";
|
|
str += "' LANGUAGE=javascript onchange=\"return dept_onchange(frmselect)\">";
|
|
|
|
str += F("<option value='0'></option>");
|
|
for (byte x = 0; x <= deviceCount; x++)
|
|
{
|
|
byte index = sortedIndex[x];
|
|
if (Plugin_id[index] != 0)
|
|
Plugin_ptr[index](PLUGIN_GET_DEVICENAME, 0, deviceName);
|
|
str += F("<option value='");
|
|
str += Device[index].Number;
|
|
str += "'";
|
|
if (choice == Device[index].Number)
|
|
str += F(" selected");
|
|
str += ">";
|
|
str += deviceName;
|
|
str += F("</option>");
|
|
}
|
|
str += F("</select>");
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Device Sort routine, switch array entries
|
|
//********************************************************************************
|
|
void switchArray(byte value)
|
|
{
|
|
byte temp;
|
|
temp = sortedIndex[value - 1];
|
|
sortedIndex[value - 1] = sortedIndex[value];
|
|
sortedIndex[value] = temp;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Device Sort routine, compare two array entries
|
|
//********************************************************************************
|
|
byte arrayLessThan(char *ptr_1, char *ptr_2)
|
|
{
|
|
char check1;
|
|
char check2;
|
|
|
|
int i = 0;
|
|
while (i < strlen(ptr_1)) // For each character in string 1, starting with the first:
|
|
{
|
|
check1 = (char)ptr_1[i]; // get the same char from string 1 and string 2
|
|
|
|
if (strlen(ptr_2) < i) // If string 2 is shorter, then switch them
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
check2 = (char)ptr_2[i];
|
|
|
|
if (check2 > check1)
|
|
{
|
|
return 1; // String 2 is greater; so switch them
|
|
}
|
|
if (check2 < check1)
|
|
{
|
|
return 0; // String 2 is LESS; so DONT switch them
|
|
}
|
|
// OTHERWISE they're equal so far; check the next char !!
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Device Sort routine, actual sorting
|
|
//********************************************************************************
|
|
void sortDeviceArray()
|
|
{
|
|
String deviceName;
|
|
char deviceName1[41];
|
|
char deviceName2[41];
|
|
int innerLoop ;
|
|
int mainLoop ;
|
|
|
|
for ( mainLoop = 1; mainLoop <= deviceCount; mainLoop++)
|
|
{
|
|
innerLoop = mainLoop;
|
|
while (innerLoop >= 1)
|
|
{
|
|
Plugin_ptr[sortedIndex[innerLoop]](PLUGIN_GET_DEVICENAME, 0, deviceName);
|
|
deviceName.toCharArray(deviceName1, 26);
|
|
Plugin_ptr[sortedIndex[innerLoop - 1]](PLUGIN_GET_DEVICENAME, 0, deviceName);
|
|
deviceName.toCharArray(deviceName2, 26);
|
|
|
|
if (arrayLessThan(deviceName1, deviceName2) == 1)
|
|
{
|
|
switchArray(innerLoop);
|
|
}
|
|
innerLoop--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add a GPIO pin select dropdown list
|
|
//********************************************************************************
|
|
void addPinSelect(boolean forI2C, String& str, String name, int choice)
|
|
{
|
|
String options[14];
|
|
options[0] = F(" ");
|
|
options[1] = F("GPIO-0");
|
|
options[2] = F("GPIO-1");
|
|
options[3] = F("GPIO-2");
|
|
options[4] = F("GPIO-3");
|
|
options[5] = F("GPIO-4");
|
|
options[6] = F("GPIO-5");
|
|
options[7] = F("GPIO-9");
|
|
options[8] = F("GPIO-10");
|
|
options[9] = F("GPIO-12");
|
|
options[10] = F("GPIO-13");
|
|
options[11] = F("GPIO-14");
|
|
options[12] = F("GPIO-15");
|
|
options[13] = F("GPIO-16");
|
|
int optionValues[14];
|
|
optionValues[0] = -1;
|
|
optionValues[1] = 0;
|
|
optionValues[2] = 1;
|
|
optionValues[3] = 2;
|
|
optionValues[4] = 3;
|
|
optionValues[5] = 4;
|
|
optionValues[6] = 5;
|
|
optionValues[7] = 9;
|
|
optionValues[8] = 10;
|
|
optionValues[9] = 12;
|
|
optionValues[10] = 13;
|
|
optionValues[11] = 14;
|
|
optionValues[12] = 15;
|
|
optionValues[13] = 16;
|
|
str += F("<select name='");
|
|
str += name;
|
|
str += "'>";
|
|
for (byte x = 0; x < 14; x++)
|
|
{
|
|
str += F("<option value='");
|
|
str += optionValues[x];
|
|
str += "'";
|
|
if (!forI2C && ((optionValues[x] == Settings.Pin_i2c_sda) || (optionValues[x] == Settings.Pin_i2c_scl)))
|
|
str += F(" disabled");
|
|
if (Settings.UseSerial && ((optionValues[x] == 1) || (optionValues[x] == 3)))
|
|
str += F(" disabled");
|
|
if (choice == optionValues[x])
|
|
str += F(" selected");
|
|
str += ">";
|
|
str += options[x];
|
|
str += F("</option>");
|
|
}
|
|
str += F("</select>");
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add a task select dropdown list
|
|
//********************************************************************************
|
|
void addTaskSelect(String& str, String name, int choice)
|
|
{
|
|
struct EventStruct TempEvent;
|
|
String deviceName;
|
|
|
|
str += F("<select name='");
|
|
str += name;
|
|
str += F("' LANGUAGE=javascript onchange=\"return dept_onchange(frmselect)\">");
|
|
|
|
for (byte x = 0; x < TASKS_MAX; x++)
|
|
{
|
|
deviceName = "";
|
|
if (Settings.TaskDeviceNumber[x] != 0 )
|
|
{
|
|
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[x]);
|
|
|
|
if (Plugin_id[DeviceIndex] != 0)
|
|
Plugin_ptr[DeviceIndex](PLUGIN_GET_DEVICENAME, &TempEvent, deviceName);
|
|
}
|
|
LoadTaskSettings(x);
|
|
str += F("<option value='");
|
|
str += x;
|
|
str += "'";
|
|
if (choice == x)
|
|
str += " selected";
|
|
if (Settings.TaskDeviceNumber[x] == 0)
|
|
str += " disabled";
|
|
str += ">";
|
|
str += x + 1;
|
|
str += " - ";
|
|
str += deviceName;
|
|
str += " - ";
|
|
str += ExtraTaskSettings.TaskDeviceName;
|
|
str += "</option>";
|
|
}
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Add a Value select dropdown list, based on TaskIndex
|
|
//********************************************************************************
|
|
void addTaskValueSelect(String& str, String name, int choice, byte TaskIndex)
|
|
{
|
|
str += F("<select name='");
|
|
str += name;
|
|
str += "'>";
|
|
|
|
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
|
|
|
|
for (byte x = 0; x < Device[DeviceIndex].ValueCount; x++)
|
|
{
|
|
str += F("<option value='");
|
|
str += x;
|
|
str += "'";
|
|
if (choice == x)
|
|
str += " selected";
|
|
str += ">";
|
|
str += ExtraTaskSettings.TaskDeviceValueNames[x];
|
|
str += "</option>";
|
|
}
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface log page
|
|
//********************************************************************************
|
|
void handle_log() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char *TempString = (char*)malloc(80);
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("<script language='JavaScript'>function RefreshMe(){window.location = window.location}setTimeout('RefreshMe()', 3000);</script>");
|
|
reply += F("<table><TH>Log<TR><TD>");
|
|
|
|
if (logcount != -1)
|
|
{
|
|
byte counter = logcount;
|
|
do
|
|
{
|
|
counter++;
|
|
if (counter > 9)
|
|
counter = 0;
|
|
if (Logging[counter].timeStamp > 0)
|
|
{
|
|
reply += Logging[counter].timeStamp;
|
|
reply += " : ";
|
|
reply += Logging[counter].Message;
|
|
reply += F("<BR>");
|
|
}
|
|
} while (counter != logcount);
|
|
}
|
|
reply += F("</table>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
free(TempString);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface debug page
|
|
//********************************************************************************
|
|
void handle_tools() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String webrequest = WebServer.arg("cmd");
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
reply += F("<form>");
|
|
reply += F("<table><TH>Tools<TH>");
|
|
reply += F("<TR><TD>System<TD><a class=\"button-link\" href=\"/?cmd=reboot\">Reboot</a>");
|
|
reply += F("<a class=\"button-link\" href=\"log\">Log</a>");
|
|
reply += F("<a class=\"button-link\" href=\"advanced\">Advanced</a><BR><BR>");
|
|
reply += F("<TR><TD>Wifi<TD><a class=\"button-link\" href=\"/?cmd=wificonnect\">Connect</a>");
|
|
reply += F("<a class=\"button-link\" href=\"/?cmd=wifidisconnect\">Disconnect</a>");
|
|
reply += F("<a class=\"button-link\" href=\"/wifiscanner\">Scan</a><BR><BR>");
|
|
reply += F("<TR><TD>Interfaces<TD><a class=\"button-link\" href=\"/i2cscanner\">I2C Scan</a><BR><BR>");
|
|
reply += F("<TR><TD>Settings<TD><a class=\"button-link\" href=\"/upload\">Load</a>");
|
|
reply += F("<a class=\"button-link\" href=\"/download\">Save</a>");
|
|
if (ESP.getFlashChipRealSize() > 524288)
|
|
{
|
|
reply += F("<TR><TD>Firmware<TD><a class=\"button-link\" href=\"/update\">Load</a>");
|
|
reply += F("<a class=\"button-link\" href=\"http://www.esp8266.nu/index.php/EasyOTA\" target=\"_blank\">?</a>");
|
|
}
|
|
#if FEATURE_SPIFFS
|
|
reply += F("<a class=\"button-link\" href=\"/filelist\">List</a><BR><BR>");
|
|
#else
|
|
reply += F("<BR><BR>");
|
|
#endif
|
|
|
|
reply += F("<TR><TD>Command<TD>");
|
|
reply += F("<input type='text' name='cmd' value='");
|
|
reply += webrequest;
|
|
reply += F("'><TR><TD><TD><input class=\"button-link\" type='submit' value='Submit'><TR><TD>");
|
|
|
|
printToWeb = true;
|
|
printWebString = "<BR>";
|
|
|
|
struct EventStruct TempEvent;
|
|
parseCommandString(&TempEvent, webrequest);
|
|
TempEvent.Source = VALUE_SOURCE_HTTP;
|
|
if (!PluginCall(PLUGIN_WRITE, &TempEvent, webrequest))
|
|
ExecuteCommand(webrequest.c_str());
|
|
|
|
reply += printWebString;
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface I2C scanner
|
|
//********************************************************************************
|
|
void handle_i2cscanner() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char *TempString = (char*)malloc(80);
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("<table><TH>I2C Addresses in use<TH>Known devices");
|
|
|
|
byte error, address;
|
|
int nDevices;
|
|
nDevices = 0;
|
|
for (address = 1; address <= 127; address++ )
|
|
{
|
|
Wire.beginTransmission(address);
|
|
error = Wire.endTransmission();
|
|
if (error == 0)
|
|
{
|
|
reply += "<TR><TD>0x";
|
|
reply += String(address, HEX);
|
|
reply += "<TD>";
|
|
switch (address)
|
|
{
|
|
case 0x20:
|
|
case 0x27:
|
|
case 0x3F:
|
|
reply += F("PCF8574, MCP23017, LCD Modules");
|
|
break;
|
|
case 0x23:
|
|
reply += F("BH1750 Lux Sensor");
|
|
break;
|
|
case 0x24:
|
|
reply += F("PN532 RFID Reader");
|
|
break;
|
|
case 0x39:
|
|
reply += F("TLS2561 Lux Sensor");
|
|
break;
|
|
case 0x3C:
|
|
case 0x3D:
|
|
reply += F("OLED SSD1306 Display");
|
|
break;
|
|
case 0x40:
|
|
reply += F("SI7021 Temp/Hum Sensor, INA219, PCA9685");
|
|
break;
|
|
case 0x48:
|
|
reply += F("PCF8591 ADC");
|
|
break;
|
|
case 0x68:
|
|
reply += F("DS1307 RTC");
|
|
break;
|
|
case 0x76:
|
|
reply += F("BME280");
|
|
break;
|
|
case 0x77:
|
|
reply += F("BMP085");
|
|
break;
|
|
case 0x7f:
|
|
reply += F("Arduino Pro Mini IO Extender");
|
|
break;
|
|
}
|
|
nDevices++;
|
|
}
|
|
else if (error == 4)
|
|
{
|
|
reply += F("<TR><TD>Unknow error at address 0x");
|
|
reply += String(address, HEX);
|
|
}
|
|
}
|
|
|
|
if (nDevices == 0)
|
|
reply += F("<TR>No I2C devices found");
|
|
|
|
reply += F("</table>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
free(TempString);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface Wifi scanner
|
|
//********************************************************************************
|
|
void handle_wifiscanner() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char *TempString = (char*)malloc(80);
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("<table><TH>Access Points:<TH>RSSI");
|
|
|
|
int n = WiFi.scanNetworks();
|
|
if (n == 0)
|
|
reply += F("No Access Points found");
|
|
else
|
|
{
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
reply += F("<TR><TD>");
|
|
reply += WiFi.SSID(i);
|
|
reply += "<TD>";
|
|
reply += WiFi.RSSI(i);
|
|
}
|
|
}
|
|
|
|
|
|
reply += F("</table>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
free(TempString);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface login page
|
|
//********************************************************************************
|
|
void handle_login() {
|
|
|
|
String webrequest = WebServer.arg("password");
|
|
char command[80];
|
|
command[0] = 0;
|
|
webrequest.toCharArray(command, 80);
|
|
|
|
String reply = "";
|
|
reply += F("<form method='post'>");
|
|
reply += F("<table><TR><TD>Password<TD>");
|
|
reply += F("<input type='password' name='password' value='");
|
|
reply += webrequest;
|
|
reply += F("'><TR><TD><TD><input class=\"button-link\" type='submit' value='Submit'><TR><TD>");
|
|
reply += F("</table></form>");
|
|
|
|
if (webrequest.length() != 0)
|
|
{
|
|
// compare with stored password and set timer if there's a match
|
|
if ((strcasecmp(command, SecuritySettings.Password) == 0) || (SecuritySettings.Password[0] == 0))
|
|
{
|
|
WebLoggedIn = true;
|
|
WebLoggedInTimer = 0;
|
|
reply = F("<script language='JavaScript'>window.location = '.'</script>");
|
|
}
|
|
else
|
|
{
|
|
reply += F("Invalid password!");
|
|
}
|
|
}
|
|
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface control page (no password!)
|
|
//********************************************************************************
|
|
void handle_control() {
|
|
|
|
String webrequest = WebServer.arg("cmd");
|
|
|
|
struct EventStruct TempEvent;
|
|
parseCommandString(&TempEvent, webrequest);
|
|
TempEvent.Source = VALUE_SOURCE_HTTP;
|
|
|
|
printToWeb = true;
|
|
printWebString = "";
|
|
String reply = "";
|
|
|
|
if (!PluginCall(PLUGIN_WRITE, &TempEvent, webrequest))
|
|
reply += F("Unknown or restricted command!");
|
|
|
|
reply += printWebString;
|
|
|
|
if (printToWebJSON)
|
|
WebServer.send(200, "application/json", reply);
|
|
else
|
|
WebServer.send(200, "text/html", reply);
|
|
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
printToWebJSON = false;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface JSON page (no password!)
|
|
//********************************************************************************
|
|
|
|
boolean handle_json()
|
|
{
|
|
String tasknr = WebServer.arg("tasknr");
|
|
String reply = "";
|
|
|
|
if (tasknr.length() == 0)
|
|
{
|
|
reply += F("{\"System\":{\n");
|
|
reply += F("\"Build\": ");
|
|
reply += BUILD;
|
|
reply += F(",\n\"Unit\": ");
|
|
reply += Settings.Unit;
|
|
reply += F(",\n\"Uptime\": ");
|
|
reply += wdcounter / 2;
|
|
reply += F(",\n\"Free RAM\": ");
|
|
reply += ESP.getFreeHeap();
|
|
reply += F("\n},\n");
|
|
}
|
|
|
|
byte taskNr = tasknr.toInt();
|
|
byte firstTaskIndex = 0;
|
|
byte lastTaskIndex = TASKS_MAX - 1;
|
|
if (taskNr != 0 )
|
|
{
|
|
firstTaskIndex = taskNr - 1;
|
|
lastTaskIndex = taskNr - 1;
|
|
}
|
|
|
|
byte lastActiveTaskIndex = 0;
|
|
for (byte TaskIndex = firstTaskIndex; TaskIndex <= lastTaskIndex; TaskIndex++)
|
|
if (Settings.TaskDeviceNumber[TaskIndex])
|
|
lastActiveTaskIndex = TaskIndex;
|
|
|
|
if (taskNr == 0 )
|
|
reply += F("\"Sensors\":[\n");
|
|
for (byte TaskIndex = firstTaskIndex; TaskIndex <= lastTaskIndex; TaskIndex++)
|
|
{
|
|
if (Settings.TaskDeviceNumber[TaskIndex])
|
|
{
|
|
byte BaseVarIndex = TaskIndex * VARS_PER_TASK;
|
|
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
|
|
LoadTaskSettings(TaskIndex);
|
|
reply += F("{\n");
|
|
|
|
reply += F("\"TaskName\": \"");
|
|
reply += ExtraTaskSettings.TaskDeviceName;
|
|
reply += F("\"");
|
|
if (Device[DeviceIndex].ValueCount != 0)
|
|
reply += F(",");
|
|
reply += F("\n");
|
|
|
|
for (byte x = 0; x < Device[DeviceIndex].ValueCount; x++)
|
|
{
|
|
reply += F("\"");
|
|
reply += ExtraTaskSettings.TaskDeviceValueNames[x];
|
|
reply += F("\": ");
|
|
reply += UserVar[BaseVarIndex + x];
|
|
if (x < (Device[DeviceIndex].ValueCount - 1))
|
|
reply += F(",");
|
|
reply += F("\n");
|
|
}
|
|
reply += F("}");
|
|
if (TaskIndex != lastActiveTaskIndex)
|
|
reply += F(",");
|
|
reply += F("\n");
|
|
}
|
|
}
|
|
if (taskNr == 0 )
|
|
reply += F("]}\n");
|
|
|
|
WebServer.send(200, "application/json", reply);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface config page
|
|
//********************************************************************************
|
|
void handle_advanced() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
char tmpString[81];
|
|
|
|
String mqttsubscribe = WebServer.arg("mqttsubscribe");
|
|
String mqttpublish = WebServer.arg("mqttpublish");
|
|
String messagedelay = WebServer.arg("messagedelay");
|
|
String ip = WebServer.arg("ip");
|
|
String syslogip = WebServer.arg("syslogip");
|
|
String ntphost = WebServer.arg("ntphost");
|
|
String timezone = WebServer.arg("timezone");
|
|
String dst = WebServer.arg("dst");
|
|
String sysloglevel = WebServer.arg("sysloglevel");
|
|
String udpport = WebServer.arg("udpport");
|
|
String useserial = WebServer.arg("useserial");
|
|
String serialloglevel = WebServer.arg("serialloglevel");
|
|
String webloglevel = WebServer.arg("webloglevel");
|
|
String baudrate = WebServer.arg("baudrate");
|
|
#if !FEATURE_SPIFFS
|
|
String customcss = WebServer.arg("customcss");
|
|
#endif
|
|
String usentp = WebServer.arg("usentp");
|
|
String wdi2caddress = WebServer.arg("wdi2caddress");
|
|
String usessdp = WebServer.arg("usessdp");
|
|
String edit = WebServer.arg("edit");
|
|
String wireclockstretchlimit = WebServer.arg("wireclockstretchlimit");
|
|
|
|
if (edit.length() != 0)
|
|
{
|
|
mqttsubscribe.toCharArray(tmpString, 81);
|
|
strcpy(Settings.MQTTsubscribe, tmpString);
|
|
mqttpublish.toCharArray(tmpString, 81);
|
|
strcpy(Settings.MQTTpublish, tmpString);
|
|
Settings.MessageDelay = messagedelay.toInt();
|
|
Settings.IP_Octet = ip.toInt();
|
|
ntphost.toCharArray(tmpString, 64);
|
|
strcpy(Settings.NTPHost, tmpString);
|
|
Settings.TimeZone = timezone.toInt();
|
|
syslogip.toCharArray(tmpString, 26);
|
|
str2ip(tmpString, Settings.Syslog_IP);
|
|
Settings.UDPPort = udpport.toInt();
|
|
Settings.SyslogLevel = sysloglevel.toInt();
|
|
Settings.UseSerial = (useserial == "on");
|
|
Settings.SerialLogLevel = serialloglevel.toInt();
|
|
Settings.WebLogLevel = webloglevel.toInt();
|
|
Settings.BaudRate = baudrate.toInt();
|
|
#if !FEATURE_SPIFFS
|
|
Settings.CustomCSS = (customcss == "on");
|
|
#endif
|
|
Settings.UseNTP = (usentp == "on");
|
|
Settings.DST = (dst == "on");
|
|
Settings.WDI2CAddress = wdi2caddress.toInt();
|
|
Settings.UseSSDP = (usessdp == "on");
|
|
#if ESP_CORE >= 210
|
|
Settings.WireClockStretchLimit = wireclockstretchlimit.toInt();
|
|
#endif
|
|
SaveSettings();
|
|
}
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
char str[20];
|
|
|
|
reply += F("<form method='post'><table>");
|
|
reply += F("<TH>Advanced Settings<TH>Value");
|
|
|
|
reply += F("<TR><TD>Subscribe Template:<TD><input type='text' name='mqttsubscribe' size=80 value='");
|
|
reply += Settings.MQTTsubscribe;
|
|
|
|
reply += F("'><TR><TD>Publish Template:<TD><input type='text' name='mqttpublish' size=80 value='");
|
|
reply += Settings.MQTTpublish;
|
|
|
|
reply += F("'><TR><TD>Message Delay (ms):<TD><input type='text' name='messagedelay' value='");
|
|
reply += Settings.MessageDelay;
|
|
|
|
reply += F("'><TR><TD>Fixed IP Octet:<TD><input type='text' name='ip' value='");
|
|
reply += Settings.IP_Octet;
|
|
reply += F("'>");
|
|
|
|
#if FEATURE_TIME
|
|
reply += F("<TR><TD>Use NTP:<TD>");
|
|
if (Settings.UseNTP)
|
|
reply += F("<input type=checkbox name='usentp' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='usentp'>");
|
|
|
|
reply += F("<TR><TD>NTP Hostname:<TD><input type='text' name='ntphost' size=64 value='");
|
|
reply += Settings.NTPHost;
|
|
|
|
reply += F("'><TR><TD>Timezone Offset:<TD><input type='text' name='timezone' size=2 value='");
|
|
reply += Settings.TimeZone;
|
|
reply += F("'>");
|
|
|
|
reply += F("<TR><TD>DST:<TD>");
|
|
if (Settings.DST)
|
|
reply += F("<input type=checkbox name='dst' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='dst'>");
|
|
|
|
#endif
|
|
|
|
reply += F("<TR><TD>Syslog IP:<TD><input type='text' name='syslogip' value='");
|
|
str[0] = 0;
|
|
sprintf_P(str, PSTR("%u.%u.%u.%u"), Settings.Syslog_IP[0], Settings.Syslog_IP[1], Settings.Syslog_IP[2], Settings.Syslog_IP[3]);
|
|
reply += str;
|
|
|
|
reply += F("'><TR><TD>Syslog Level:<TD><input type='text' name='sysloglevel' value='");
|
|
reply += Settings.SyslogLevel;
|
|
|
|
reply += F("'><TR><TD>UDP port:<TD><input type='text' name='udpport' value='");
|
|
reply += Settings.UDPPort;
|
|
reply += F("'>");
|
|
|
|
reply += F("<TR><TD>Enable Serial port:<TD>");
|
|
if (Settings.UseSerial)
|
|
reply += F("<input type=checkbox name='useserial' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='useserial'>");
|
|
|
|
reply += F("<TR><TD>Serial log Level:<TD><input type='text' name='serialloglevel' value='");
|
|
reply += Settings.SerialLogLevel;
|
|
|
|
reply += F("'><TR><TD>Web log Level:<TD><input type='text' name='webloglevel' value='");
|
|
reply += Settings.WebLogLevel;
|
|
|
|
reply += F("'><TR><TD>Baud Rate:<TD><input type='text' name='baudrate' value='");
|
|
reply += Settings.BaudRate;
|
|
reply += F("'>");
|
|
|
|
reply += F("<TR><TD>WD I2C Address:<TD><input type='text' name='wdi2caddress' value='");
|
|
reply += Settings.WDI2CAddress;
|
|
reply += F("'>");
|
|
|
|
#if !FEATURE_SPIFFS
|
|
reply += F("<TR><TD>Custom CSS:<TD>");
|
|
if (Settings.CustomCSS)
|
|
reply += F("<input type=checkbox name='customcss' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='customcss'>");
|
|
#endif
|
|
|
|
#if FEATURE_SSDP
|
|
reply += F("<TR><TD>Use SSDP:<TD>");
|
|
if (Settings.UseSSDP)
|
|
reply += F("<input type=checkbox name='usessdp' checked>");
|
|
else
|
|
reply += F("<input type=checkbox name='usessdp'>");
|
|
#endif
|
|
|
|
#if ESP_CORE >= 210
|
|
reply += F("<TR><TD>I2C ClockStretchLimit:<TD><input type='text' name='wireclockstretchlimit' value='");
|
|
reply += Settings.WireClockStretchLimit;
|
|
reply += F("'>");
|
|
#endif
|
|
|
|
reply += F("<TR><TD><TD><input class=\"button-link\" type='submit' value='Submit'>");
|
|
reply += F("<input type='hidden' name='edit' value='1'>");
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Login state check
|
|
//********************************************************************************
|
|
boolean isLoggedIn()
|
|
{
|
|
if (SecuritySettings.Password[0] == 0)
|
|
WebLoggedIn = true;
|
|
|
|
if (!WebLoggedIn)
|
|
{
|
|
WebServer.sendContent(F("HTTP/1.1 302 \r\nLocation: /login\r\n"));
|
|
}
|
|
else
|
|
{
|
|
WebLoggedInTimer = 0;
|
|
}
|
|
|
|
return WebLoggedIn;
|
|
}
|
|
|
|
|
|
#if FEATURE_SPIFFS
|
|
//********************************************************************************
|
|
// Web Interface download page
|
|
//********************************************************************************
|
|
void handle_download()
|
|
{
|
|
if (!isLoggedIn()) return;
|
|
|
|
File dataFile = SPIFFS.open("config.txt", "r");
|
|
if (!dataFile)
|
|
return;
|
|
|
|
WebServer.sendHeader("Content-Disposition", "attachment; filename=config.txt");
|
|
WebServer.streamFile(dataFile, "application/octet-stream");
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface upload page
|
|
//********************************************************************************
|
|
byte uploadResult = 0;
|
|
void handle_upload() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String edit = WebServer.arg("edit");
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
|
|
if (edit.length() != 0)
|
|
{
|
|
if (uploadResult == 1)
|
|
{
|
|
reply += F("Upload OK!<BR>You may need to reboot to apply all settings...");
|
|
LoadSettings();
|
|
}
|
|
|
|
if (uploadResult == 2)
|
|
reply += F("<font color=\"red\">Upload file invalid!</font>");
|
|
|
|
if (uploadResult == 3)
|
|
reply += F("<font color=\"red\">No filename!</font>");
|
|
}
|
|
|
|
reply += F("<form enctype=\"multipart/form-data\" method=\"post\"><p>Upload settings file:<br><input type=\"file\" name=\"datafile\" size=\"40\"></p><div><input class=\"button-link\" type='submit' value='Upload'></div><input type='hidden' name='edit' value='1'></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
|
|
//********************************************************************************
|
|
// Web Interface upload handler
|
|
//********************************************************************************
|
|
File uploadFile;
|
|
void handleFileUpload() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
static boolean valid = false;
|
|
String log = "";
|
|
|
|
HTTPUpload& upload = WebServer.upload();
|
|
|
|
if (upload.filename.c_str()[0] == 0)
|
|
{
|
|
uploadResult = 3;
|
|
return;
|
|
}
|
|
|
|
if (upload.status == UPLOAD_FILE_START)
|
|
{
|
|
log = F("Upload: START, filename: ");
|
|
log += upload.filename;
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
valid = false;
|
|
uploadResult = 0;
|
|
}
|
|
else if (upload.status == UPLOAD_FILE_WRITE)
|
|
{
|
|
// first data block, if this is the config file, check PID/Version
|
|
if (upload.totalSize == 0)
|
|
{
|
|
if (strcasecmp(upload.filename.c_str(), "config.txt") == 0)
|
|
{
|
|
struct TempStruct {
|
|
unsigned long PID;
|
|
int Version;
|
|
} Temp;
|
|
for (int x = 0; x < sizeof(struct TempStruct); x++)
|
|
{
|
|
byte b = upload.buf[x];
|
|
memcpy((byte*)&Temp + x, &b, 1);
|
|
}
|
|
if (Temp.Version == VERSION && Temp.PID == ESP_PROJECT_PID)
|
|
valid = true;
|
|
}
|
|
else
|
|
{
|
|
// other files are always valid...
|
|
valid = true;
|
|
}
|
|
if (valid)
|
|
{
|
|
// once we're safe, remove file and create empty one...
|
|
SPIFFS.remove((char *)upload.filename.c_str());
|
|
uploadFile = SPIFFS.open(upload.filename.c_str(), "w");
|
|
}
|
|
}
|
|
if (uploadFile) uploadFile.write(upload.buf, upload.currentSize);
|
|
log = F("Upload: WRITE, Bytes: ");
|
|
log += upload.currentSize;
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
else if (upload.status == UPLOAD_FILE_END)
|
|
{
|
|
if (uploadFile) uploadFile.close();
|
|
log = F("Upload: END, Size: ");
|
|
log += upload.totalSize;
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
|
|
if (valid)
|
|
uploadResult = 1;
|
|
else
|
|
uploadResult = 2;
|
|
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface server web file from SPIFFS
|
|
//********************************************************************************
|
|
bool loadFromSPIFFS(String path) {
|
|
if (!isLoggedIn()) return false;
|
|
|
|
String dataType = F("text/plain");
|
|
if (path.endsWith("/")) path += "index.htm";
|
|
|
|
if (path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
|
|
else if (path.endsWith(".htm")) dataType = F("text/html");
|
|
else if (path.endsWith(".css")) dataType = F("text/css");
|
|
else if (path.endsWith(".js")) dataType = F("application/javascript");
|
|
else if (path.endsWith(".png")) dataType = F("image/png");
|
|
else if (path.endsWith(".gif")) dataType = F("image/gif");
|
|
else if (path.endsWith(".jpg")) dataType = F("image/jpeg");
|
|
else if (path.endsWith(".ico")) dataType = F("image/x-icon");
|
|
else if (path.endsWith(".txt")) dataType = F("application/octet-stream");
|
|
|
|
path = path.substring(1);
|
|
File dataFile = SPIFFS.open(path.c_str(), "r");
|
|
|
|
if (!dataFile)
|
|
return false;
|
|
|
|
if (path.endsWith(".txt"))
|
|
WebServer.sendHeader("Content-Disposition", "attachment;");
|
|
WebServer.streamFile(dataFile, dataType);
|
|
|
|
dataFile.close();
|
|
return true;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface file list)
|
|
//********************************************************************************
|
|
void handle_filelist() {
|
|
|
|
String fdelete = WebServer.arg("delete");
|
|
|
|
if (fdelete.length() > 0)
|
|
{
|
|
SPIFFS.remove(fdelete);
|
|
}
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("<table border='1'><TH><TH>Filename:<TH>Size");
|
|
|
|
Dir dir = SPIFFS.openDir("/");
|
|
while (dir.next())
|
|
{
|
|
reply += F("<TR><TD>");
|
|
if (dir.fileName() != "config.txt" && dir.fileName() != "security.txt")
|
|
{
|
|
reply += F("<a class=\"button-link\" href=\"filelist?delete=");
|
|
reply += dir.fileName();
|
|
reply += F("\">Del</a>");
|
|
}
|
|
|
|
reply += F("<TD><a href=\"");
|
|
reply += dir.fileName();
|
|
reply += F("\">");
|
|
reply += dir.fileName();
|
|
reply += F("</a>");
|
|
File f = dir.openFile("r");
|
|
reply += F("<TD>");
|
|
reply += f.size();
|
|
}
|
|
reply += F("</table></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
#else
|
|
|
|
// Without spiffs support, we will use our own handlers to manage a 40kb flash area
|
|
// it uses the same space as where SPIFFS would reside
|
|
// Layout:
|
|
// 32kB settings as used in "config.txt"
|
|
// 4kb security settings. These cannot be downloaded/uploaded.
|
|
// 4kb rules
|
|
// the config.txt can be interchanged between using spiffs or this custom method
|
|
|
|
//********************************************************************************
|
|
// Web Interface download page
|
|
//********************************************************************************
|
|
void handle_download() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
uint32_t _sectorStart = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
uint32_t _sectorEnd = _sectorStart + 32; //((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
uint8_t* data = new uint8_t[FLASH_EEPROM_SIZE];
|
|
|
|
WiFiClient client = WebServer.client();
|
|
/*client.print("HTTP/1.1 200 OK\r\n");
|
|
client.print("Content-Disposition: attachment; filename=config.txt\r\n");
|
|
client.print("Content-Type: application/octet-stream\r\n");
|
|
client.print("Content-Length: 32768\r\n");
|
|
client.print("Connection: close\r\n");
|
|
client.print("Access-Control-Allow-Origin: *\r\n");
|
|
client.print("\r\n");
|
|
*/
|
|
WebServer.setContentLength(32768);
|
|
WebServer.sendHeader("Content-Disposition", "attachment; filename=config.txt");
|
|
WebServer.send(200, "application/octet-stream", "");
|
|
|
|
for (uint32_t _sector = _sectorStart; _sector < _sectorEnd; _sector++)
|
|
{
|
|
// load entire sector from flash into memory
|
|
noInterrupts();
|
|
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(data), FLASH_EEPROM_SIZE);
|
|
interrupts();
|
|
client.write((const char*)data, 2048);
|
|
client.write((const char*)data + 2028, 2048);
|
|
}
|
|
delete [] data;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface download page
|
|
//********************************************************************************
|
|
void handle_css() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
int size = 0;
|
|
uint32_t _sector = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
uint8_t* data = new uint8_t[FLASH_EEPROM_SIZE];
|
|
_sector += 9;
|
|
|
|
// load entire sector from flash into memory
|
|
noInterrupts();
|
|
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(data), FLASH_EEPROM_SIZE);
|
|
interrupts();
|
|
|
|
// check size of css file content
|
|
for (int x = 0; x < 4096; x++)
|
|
if (data[x] == 0)
|
|
{
|
|
size = x;
|
|
break;
|
|
}
|
|
WiFiClient client = WebServer.client();
|
|
WebServer.setContentLength(size);
|
|
WebServer.send(200, "text/css", "");
|
|
client.write((const char*)data, size);
|
|
delete [] data;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface upload page
|
|
//********************************************************************************
|
|
void handle_upload() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("<form enctype=\"multipart/form-data\" method=\"post\"><p>Upload settings:<br><input type=\"file\" name=\"datafile\" size=\"40\"></p><div><input class=\"button-link\" type='submit' value='Upload'></div></form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
|
|
//********************************************************************************
|
|
// Web Interface upload page
|
|
//********************************************************************************
|
|
void handle_upload_post() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String reply = "";
|
|
addHeader(true, reply);
|
|
reply += F("Upload finished");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
printWebString = "";
|
|
printToWeb = false;
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Upload handler
|
|
//********************************************************************************
|
|
void handleFileUpload()
|
|
{
|
|
if (!isLoggedIn()) return;
|
|
String log = "";
|
|
static byte filetype = 0;
|
|
static byte page = 0;
|
|
static uint8_t* data;
|
|
int uploadSize = 0;
|
|
HTTPUpload& upload = WebServer.upload();
|
|
|
|
if (upload.status == UPLOAD_FILE_START)
|
|
{
|
|
filetype = 0;
|
|
if (strcasecmp(upload.filename.c_str(), "config.txt") == 0)
|
|
filetype = 1;
|
|
if (strcasecmp(upload.filename.c_str(), "esp.css") == 0)
|
|
{
|
|
filetype = 2;
|
|
Settings.CustomCSS = true;
|
|
}
|
|
if (strcasecmp(upload.filename.c_str(), "rules.txt") == 0)
|
|
{
|
|
filetype = 3;
|
|
}
|
|
log = F("Upload start ");
|
|
log += (char *)upload.filename.c_str();
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
page = 0;
|
|
data = new uint8_t[FLASH_EEPROM_SIZE];
|
|
}
|
|
|
|
if (upload.status == UPLOAD_FILE_WRITE && filetype != 0)
|
|
{
|
|
uploadSize = upload.currentSize;
|
|
|
|
int base = 0;
|
|
if (page % 2)
|
|
base += 2048;
|
|
memcpy((byte*)data + base, upload.buf, upload.currentSize);
|
|
if (filetype == 2 || filetype == 3)
|
|
data[upload.currentSize + upload.totalSize] = 0; // eof marker
|
|
|
|
if ((page % 2) || (filetype == 2) || (filetype == 3))
|
|
{
|
|
byte sectorOffset = 0;
|
|
if (filetype == 2)
|
|
sectorOffset = 9;
|
|
if (filetype == 3)
|
|
sectorOffset = 10;
|
|
uint32_t _sector = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
_sector += page / 2;
|
|
_sector += sectorOffset;
|
|
noInterrupts();
|
|
if (spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK)
|
|
if (spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(data), FLASH_EEPROM_SIZE) == SPI_FLASH_RESULT_OK)
|
|
{
|
|
//Serial.println("flash save ok");
|
|
}
|
|
interrupts();
|
|
delay(10);
|
|
}
|
|
page++;
|
|
}
|
|
|
|
if (upload.status == UPLOAD_FILE_END)
|
|
{
|
|
log = F("Upload end");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
delete [] data;
|
|
if (filetype == 1)
|
|
LoadSettings();
|
|
if (filetype == 2)
|
|
SaveSettings();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface handle other requests
|
|
//********************************************************************************
|
|
void handleNotFound() {
|
|
|
|
if (wifiSetup)
|
|
{
|
|
WebServer.send(200, "text/html", "<meta HTTP-EQUIV='REFRESH' content='0; url=http://192.168.4.1/setup'>");
|
|
//WebServer.send(200, "text/html", "<a class=\"button-menu\" href=\"setup\">Setup</a>");
|
|
return;
|
|
}
|
|
|
|
if (!isLoggedIn()) return;
|
|
#if FEATURE_SPIFFS
|
|
if (loadFromSPIFFS(WebServer.uri())) return;
|
|
#endif
|
|
String message = "URI: ";
|
|
message += WebServer.uri();
|
|
message += "\nMethod: ";
|
|
message += (WebServer.method() == HTTP_GET) ? "GET" : "POST";
|
|
message += "\nArguments: ";
|
|
message += WebServer.args();
|
|
message += "\n";
|
|
for (uint8_t i = 0; i < WebServer.args(); i++) {
|
|
message += " NAME:" + WebServer.argName(i) + "\n VALUE:" + WebServer.arg(i) + "\n";
|
|
}
|
|
WebServer.send(404, "text/plain", message);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface Setup Wizard
|
|
//********************************************************************************
|
|
void handle_setup() {
|
|
|
|
String reply = "";
|
|
addHeader(false, reply);
|
|
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
{
|
|
SaveSettings();
|
|
IPAddress ip = WiFi.localIP();
|
|
char host[20];
|
|
sprintf_P(host, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
|
reply += F("<BR>ESP is connected and using IP Address: ");
|
|
reply += host;
|
|
reply += F("<BR><BR>Connect your laptop / tablet / phone back to your main Wifi network and ");
|
|
reply += F("<a class=\"button-menu\" href='http://");
|
|
reply += host;
|
|
reply += F("/config'>Proceed to main config</a>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
wifiSetup = false;
|
|
WifiAPMode(false);
|
|
return;
|
|
}
|
|
|
|
static byte status = 0;
|
|
static int n = 0;
|
|
static byte refreshCount = 0;
|
|
String ssid = WebServer.arg("ssid");
|
|
String other = WebServer.arg("other");
|
|
String password = WebServer.arg("pass");
|
|
|
|
if (other.length() != 0)
|
|
{
|
|
ssid = other;
|
|
}
|
|
|
|
// if ssid config not set and params are both provided
|
|
if (status == 0 && ssid.length() != 0 && password.length() != 0 && strcasecmp(SecuritySettings.WifiSSID, "ssid") == 0)
|
|
{
|
|
strncpy(SecuritySettings.WifiKey, password.c_str(), sizeof(SecuritySettings.WifiKey));
|
|
strncpy(SecuritySettings.WifiSSID, ssid.c_str(), sizeof(SecuritySettings.WifiSSID));
|
|
wifiSetupConnect = true;
|
|
status = 1;
|
|
refreshCount = 0;
|
|
}
|
|
|
|
reply += F("<h1>Wifi Setup wizard</h1><BR>");
|
|
reply += F("<form name='frmselect' method='post'>");
|
|
|
|
if (status == 0) // first step, scan and show access points within reach...
|
|
{
|
|
if (n == 0)
|
|
n = WiFi.scanNetworks();
|
|
|
|
if (n == 0)
|
|
reply += F("No Access Points found");
|
|
else
|
|
{
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
reply += F("<input type='radio' name='ssid' value='");
|
|
reply += WiFi.SSID(i);
|
|
reply += F("'");
|
|
if (WiFi.SSID(i) == ssid)
|
|
reply += F(" checked ");
|
|
reply += F(">");
|
|
reply += WiFi.SSID(i);
|
|
reply += F("</input><br>");
|
|
}
|
|
}
|
|
|
|
reply += F("<input type='radio' name='ssid' id='other_ssid' value='other' >other SSID:</input>");
|
|
reply += F("<input type ='text' name='other' value='");
|
|
reply += other;
|
|
reply += F("'><br><br>");
|
|
reply += F("Password: <input type ='text' name='pass' value='");
|
|
reply += password;
|
|
reply += F("'><br>");
|
|
|
|
reply += F("<input type='submit' value='Connect'>");
|
|
}
|
|
|
|
if (status == 1) // connecting stage...
|
|
{
|
|
if (refreshCount > 0)
|
|
{
|
|
status = 0;
|
|
strncpy(SecuritySettings.WifiSSID, "ssid", sizeof(SecuritySettings.WifiSSID));
|
|
SecuritySettings.WifiKey[0] = 0;
|
|
reply += F("<a class=\"button-menu\" href=\"setup\">Back to Setup</a>");
|
|
}
|
|
else
|
|
{
|
|
int wait = 20;
|
|
if (refreshCount != 0)
|
|
wait = 3;
|
|
reply += F("Please wait for <h1 id=\"countdown\">20..</h1>");
|
|
reply += F("<script type=\"text/JavaScript\">");
|
|
reply += F("function timedRefresh(timeoutPeriod) {");
|
|
reply += F(" var timer = setInterval(function() {");
|
|
reply += F(" if (timeoutPeriod > 0) {");
|
|
reply += F(" timeoutPeriod -= 1;");
|
|
reply += F(" document.getElementById(\"countdown\").innerHTML = timeoutPeriod + \"..\" + \"<br />\";");
|
|
reply += F(" } else {");
|
|
reply += F(" clearInterval(timer);");
|
|
reply += F(" window.location.href = window.location.href;");
|
|
reply += F(" };");
|
|
reply += F(" }, 1000);");
|
|
reply += F("};");
|
|
reply += F("timedRefresh(");
|
|
reply += wait;
|
|
reply += F(");");
|
|
reply += F("</script>");
|
|
reply += F("seconds while trying to connect");
|
|
}
|
|
refreshCount++;
|
|
}
|
|
|
|
reply += F("</form>");
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
delay(10);
|
|
}
|
|
|
|
|
|
//********************************************************************************
|
|
// Web Interface rules page
|
|
//********************************************************************************
|
|
void handle_rules() {
|
|
if (!isLoggedIn()) return;
|
|
|
|
String reply = "";
|
|
uint8_t* data = new uint8_t[FLASH_EEPROM_SIZE];
|
|
|
|
addHeader(true, reply);
|
|
|
|
reply += F("<table><th>Rules<TR><TD>");
|
|
|
|
if (WebServer.args() == 1)
|
|
{
|
|
String rules = WebServer.arg("rules");
|
|
rules.toCharArray((char*)data, 4096);
|
|
uint32_t _sector = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
_sector += 10;
|
|
noInterrupts();
|
|
if (spi_flash_erase_sector(_sector) == SPI_FLASH_RESULT_OK)
|
|
if (spi_flash_write(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(data), FLASH_EEPROM_SIZE) == SPI_FLASH_RESULT_OK)
|
|
{
|
|
//Serial.println("flash save ok");
|
|
}
|
|
interrupts();
|
|
}
|
|
|
|
// load form data from flash
|
|
reply += F("<form method='post'>");
|
|
reply += F("<textarea name='rules' rows='15' cols='80' wrap='on'>");
|
|
uint32_t _sector = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
|
_sector += 10;
|
|
|
|
// load entire sector from flash into memory
|
|
noInterrupts();
|
|
spi_flash_read(_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast<uint32_t*>(data), FLASH_EEPROM_SIZE);
|
|
interrupts();
|
|
|
|
// check size of css file content
|
|
for (int x = 0; x < 4096; x++)
|
|
if (data[x] != 0)
|
|
reply += char(data[x]);
|
|
else
|
|
break;
|
|
|
|
reply += F("</textarea>");
|
|
|
|
reply += F("<TR><TD><input class=\"button-link\" type='submit' value='Submit'>");
|
|
reply += F("</table></form>");
|
|
delete [] data;
|
|
addFooter(reply);
|
|
WebServer.send(200, "text/html", reply);
|
|
}
|
|
|
|
//********************************************************************************
|
|
// URNEncode char string to string object
|
|
//********************************************************************************
|
|
String URLEncode(const char* msg)
|
|
{
|
|
const char *hex = "0123456789abcdef";
|
|
String encodedMsg = "";
|
|
|
|
while (*msg != '\0') {
|
|
if ( ('a' <= *msg && *msg <= 'z')
|
|
|| ('A' <= *msg && *msg <= 'Z')
|
|
|| ('0' <= *msg && *msg <= '9') ) {
|
|
encodedMsg += *msg;
|
|
} else {
|
|
encodedMsg += '%';
|
|
encodedMsg += hex[*msg >> 4];
|
|
encodedMsg += hex[*msg & 15];
|
|
}
|
|
msg++;
|
|
}
|
|
return encodedMsg;
|
|
}
|
|
|