/*
xdrv_01_webserver.ino - webserver for Tasmota
Copyright (C) 2021 Theo Arends and Adrian Scillato
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifdef USE_WEBSERVER
/*********************************************************************************************\
* Web server and WiFi Manager
*
* Enables configuration and reconfiguration of WiFi credentials using a Captive Portal
* Based on source by AlexT (https://github.com/tzapu)
\*********************************************************************************************/
#define XDRV_01 1
// Enable below demo feature only if defines USE_UNISHOX_COMPRESSION and USE_SCRIPT_WEB_DISPLAY are disabled
//#define USE_WEB_SSE
#ifndef WIFI_SOFT_AP_CHANNEL
#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI
#endif
#ifndef MAX_WIFI_NETWORKS_TO_SHOW
#define MAX_WIFI_NETWORKS_TO_SHOW 3 // Maximum number of Wifi Networks to show in the Wifi Configuration Menu BEFORE clicking on Show More Networks.
#endif
#ifndef RESTART_AFTER_INITIAL_WIFI_CONFIG
#define RESTART_AFTER_INITIAL_WIFI_CONFIG true // Restart Tasmota after initial Wifi Config of a blank device
#endif // If disabled, Tasmota will keep both the wifi AP and the wifi connection to the router
// but only until next restart.
#ifndef AFTER_INITIAL_WIFI_CONFIG_GO_TO_NEW_IP // If RESTART_AFTER_INITIAL_WIFI_CONFIG and AFTER_INITIAL_WIFI_CONFIG_GO_TO_NEW_IP are true,
#define AFTER_INITIAL_WIFI_CONFIG_GO_TO_NEW_IP true // the user will be redirected to the new IP of Tasmota (in the new Network).
#endif // If the first is true, but this is false, the device will restart but the user will see
// a window telling that the WiFi Configuration was Ok and that the window can be closed.
const uint16_t CHUNKED_BUFFER_SIZE = 500; // Chunk buffer size (needs to be well below stack space (4k for ESP8266, 8k for ESP32) but large enough to cache some small messages)
const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds
const uint16_t HTTP_RESTART_RECONNECT_TIME = 10000; // milliseconds - Allow time for restart and wifi reconnect
#ifdef ESP8266
const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 24000; // milliseconds - Allow time for uploading binary, unzip/write to final destination and wifi reconnect
#endif // ESP8266
#ifdef ESP32
const uint16_t HTTP_OTA_RESTART_RECONNECT_TIME = 10000; // milliseconds - Allow time for restart and wifi reconnect
#endif // ESP32
#include
#include
#ifdef USE_UNISHOX_COMPRESSION
#include "./html_compressed/HTTP_HEADER1_ES6.h"
#else
#include "./html_uncompressed/HTTP_HEADER1_ES6.h"
#endif
const char HTTP_SCRIPT_COUNTER[] PROGMEM =
"var cn=180;" // seconds
"function u(){"
"if(cn>=0){"
"eb('t').innerHTML='" D_RESTART_IN " '+cn+' " D_SECONDS "';"
"cn--;"
"setTimeout(u,1000);"
"}"
"}"
"wl(u);";
#ifdef USE_UNISHOX_COMPRESSION
#ifdef USE_SCRIPT_WEB_DISPLAY
#include "./html_compressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h"
#else
#include "./html_compressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h"
#endif
#include "./html_compressed/HTTP_SCRIPT_ROOT_PART2.h"
#else
#ifdef USE_SCRIPT_WEB_DISPLAY
#include "./html_uncompressed/HTTP_SCRIPT_ROOT_WEB_DISPLAY.h"
#else
#ifdef USE_WEB_SSE
#include "./html_uncompressed/HTTP_SCRIPT_ROOT_SSE_NO_WEB_DISPLAY.h"
#else
#include "./html_uncompressed/HTTP_SCRIPT_ROOT_NO_WEB_DISPLAY.h"
#endif // USE_WEB_SSE
#endif
#include "./html_uncompressed/HTTP_SCRIPT_ROOT_PART2.h"
#endif
const char HTTP_SCRIPT_WIFI[] PROGMEM =
"function c(l){"
"eb('s1').value=l.innerText||l.textContent;"
"eb('p1').focus();"
"}";
const char HTTP_SCRIPT_HIDE[] PROGMEM =
"function hidBtns(){"
"eb('butmo').style.display='none';"
"eb('butmod').style.display='none';"
"eb('but0').style.display='block';"
"eb('but1').style.display='block';"
"eb('but13').style.display='block';"
"eb('but0d').style.display='block';"
"eb('but13d').style.display='block';"
"}";
const char HTTP_SCRIPT_RELOAD_TIME[] PROGMEM =
"setTimeout(function(){location.href='.';},%d);";
#ifdef USE_UNISHOX_COMPRESSION
#include "./html_compressed/HTTP_SCRIPT_CONSOL.h"
#else
#include "./html_uncompressed/HTTP_SCRIPT_CONSOL.h"
#endif
const char HTTP_MODULE_TEMPLATE_REPLACE_INDEX[] PROGMEM =
"}2%d'>%s (%d)}3"; // }2 and }3 are used in below os.replace
const char HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX[] PROGMEM =
"}2%d'>%s}3"; // }2 and }3 are used in below os.replace
#ifdef USE_UNISHOX_COMPRESSION
#include "./html_compressed/HTTP_SCRIPT_MODULE_TEMPLATE.h"
#include "./html_compressed/HTTP_SCRIPT_TEMPLATE.h"
#else
#include "./html_uncompressed/HTTP_SCRIPT_MODULE_TEMPLATE.h"
#include "./html_uncompressed/HTTP_SCRIPT_TEMPLATE.h"
#endif
#if defined(ESP32) && CONFIG_IDF_TARGET_ESP32C3
const char HTTP_SCRIPT_TEMPLATE2[] PROGMEM =
"for(i=0;i<" STR(MAX_USER_PINS) ";i++){"
"sk(g[i],i);" // Set GPIO
"}";
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
const char HTTP_SCRIPT_TEMPLATE2[] PROGMEM =
"j=0;"
"for(i=0;i<" STR(MAX_USER_PINS) ";i++){" // Skip 22-32
"if(22==i){j=33;}"
"sk(g[i],j);" // Set GPIO
"j++;"
"}";
#elif defined(CONFIG_IDF_TARGET_ESP32)
const char HTTP_SCRIPT_TEMPLATE2[] PROGMEM =
"j=0;"
"for(i=0;i<" STR(MAX_USER_PINS) ";i++){" // Skip 28-31
"if(28==i){j=32;}"
"sk(g[i],j);" // Set GPIO
"j++;"
"}";
#else // ESP8266
const char HTTP_SCRIPT_TEMPLATE2[] PROGMEM =
"j=0;"
"for(i=0;i<" STR(MAX_USER_PINS) ";i++){" // Supports 13 GPIOs
"if(6==i){j=9;}"
"if(8==i){j=12;}"
"sk(g[i],j);" // Set GPIO
"j++;"
"}";
#endif
const char HTTP_SCRIPT_TEMPLATE3[] PROGMEM =
"\";"
"sk(g[13]," STR(ADC0_PIN) ");"; // Set ADC0
const char HTTP_SCRIPT_TEMPLATE4[] PROGMEM =
"g=o.shift();" // FLAG
"for(i=0;i<" STR(GPIO_FLAG_USED) ";i++){"
"p=(g>>i)&1;"
"eb('c'+i).checked=p;" // Set FLAG checkboxes
"}"
"if(" STR(USER_MODULE) "==c){"
"g=o.shift();"
"eb('g99').value=g;" // Set BASE for initial select
"}"
"}"
"function st(t){"
"c=t;" // Needed for initial BASE select
"var a='tp?t='+t;"
"ld(a,x1);" // ?t related to WebGetArg("t", stemp, sizeof(stemp));
"}"
"function sl(){"
"os=\""; // }2'0'>Sonoff Basic (1)}3...
const char HTTP_SCRIPT_TEMPLATE5[] PROGMEM =
"\";"
"sk(" STR(WEMOS_MODULE) ",99);" // 17 = WEMOS
"st(" STR(USER_MODULE) ");"
"}"
"wl(sl);";
const char HTTP_SCRIPT_INFO_BEGIN[] PROGMEM =
"function i(){"
"var s,o=\"";
const char HTTP_SCRIPT_INFO_END[] PROGMEM =
"\";" // "}1" and "}2" means do not use "}x" in Information text
"s=o.replace(/}1/g,\"