\").replace(/}2/g,\" \");"
"eb('i').innerHTML=s;"
"}"
"";
const char HTTP_MSG_SLIDER1[] PROGMEM =
"" D_COLDLIGHT " " D_WARMLIGHT "
"
"
";
const char HTTP_MSG_SLIDER2[] PROGMEM =
"" D_DARKLIGHT " " D_BRIGHTLIGHT "
"
"
";
const char HTTP_MSG_RSTRT[] PROGMEM =
"" D_DEVICE_WILL_RESTART "
";
const char HTTP_BTN_MENU1[] PROGMEM =
#ifndef BE_MINIMAL
" "
" "
#endif
" "
" ";
const char HTTP_BTN_RSTRT[] PROGMEM =
" ";
const char HTTP_BTN_MENU_MODULE[] PROGMEM =
" ";
#if defined(USE_TIMERS) && defined(USE_TIMERS_WEB)
const char HTTP_BTN_MENU_TIMER[] PROGMEM =
" ";
#endif // USE_TIMERS and USE_TIMERS_WEB
const char HTTP_BTN_MENU_WIFI[] PROGMEM =
" ";
const char HTTP_BTN_MENU_MQTT[] PROGMEM =
" "
#ifdef USE_DOMOTICZ
" "
#endif // USE_DOMOTICZ
"";
const char HTTP_BTN_MENU4[] PROGMEM =
#ifdef USE_KNX
#ifdef USE_KNX_WEB_MENU
" "
#endif // USE_KNX_WEB_MENU
#endif // USE_KNX
" "
" "
" "
" "
" "
" ";
const char HTTP_BTN_MAIN[] PROGMEM =
" ";
const char HTTP_FORM_LOGIN[] PROGMEM =
"";
const char HTTP_BTN_CONF[] PROGMEM =
" ";
const char HTTP_FORM_MODULE[] PROGMEM =
" " D_MODULE_PARAMETERS "
// }2 =
String func = FPSTR(HTTP_SCRIPT_INFO_BEGIN);
func += F("");
func += F(D_PROGRAM_VERSION "}2"); func += my_version;
func += F("}1" D_BUILD_DATE_AND_TIME "}2"); func += GetBuildDateAndTime();
func += F("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/"); func += String(ESP.getSdkVersion());
func += F("}1" D_UPTIME "}2"); func += GetDateAndTime(DT_UPTIME);
snprintf_P(stopic, sizeof(stopic), PSTR(" at %X"), GetSettingsAddress());
func += F("}1" D_FLASH_WRITE_COUNT "}2"); func += String(Settings.save_flag); func += stopic;
func += F("}1" D_BOOT_COUNT "}2"); func += String(Settings.bootcount);
func += F("}1" D_RESTART_REASON "}2"); func += GetResetReason();
uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present;
if (SONOFF_IFAN02 == Settings.module) { maxfn = 1; }
for (byte i = 0; i < maxfn; i++) {
func += F("}1" D_FRIENDLY_NAME " "); func += i +1; func += F("}2"); func += Settings.friendlyname[i];
}
func += F("}1}2 "); // Empty line
func += F("}1" D_AP); func += String(Settings.sta_active +1);
func += F(" " D_SSID " (" D_RSSI ")}2"); func += Settings.sta_ssid[Settings.sta_active]; func += F(" ("); func += WifiGetRssiAsQuality(WiFi.RSSI()); func += F("%)");
func += F("}1" D_HOSTNAME "}2"); func += my_hostname;
if (static_cast(WiFi.localIP()) != 0) {
func += F("}1" D_IP_ADDRESS "}2"); func += WiFi.localIP().toString();
func += F("}1" D_GATEWAY "}2"); func += IPAddress(Settings.ip_address[1]).toString();
func += F("}1" D_SUBNET_MASK "}2"); func += IPAddress(Settings.ip_address[2]).toString();
func += F("}1" D_DNS_SERVER "}2"); func += IPAddress(Settings.ip_address[3]).toString();
func += F("}1" D_MAC_ADDRESS "}2"); func += WiFi.macAddress();
}
if (static_cast(WiFi.softAPIP()) != 0) {
func += F("}1" D_AP " " D_IP_ADDRESS "}2"); func += WiFi.softAPIP().toString();
func += F("}1" D_AP " " D_GATEWAY "}2"); func += WiFi.softAPIP().toString();
func += F("}1" D_AP " " D_MAC_ADDRESS "}2"); func += WiFi.softAPmacAddress();
}
func += F("}1}2 "); // Empty line
if (Settings.flag.mqtt_enabled) {
func += F("}1" D_MQTT_HOST "}2"); func += Settings.mqtt_host;
func += F("}1" D_MQTT_PORT "}2"); func += String(Settings.mqtt_port);
func += F("}1" D_MQTT_CLIENT " & " D_FALLBACK_TOPIC "}2"); func += mqtt_client;
func += F("}1" D_MQTT_USER "}2"); func += Settings.mqtt_user;
func += F("}1" D_MQTT_TOPIC "}2"); func += Settings.mqtt_topic;
func += F("}1" D_MQTT_GROUP_TOPIC "}2"); func += Settings.mqtt_grptopic;
GetTopic_P(stopic, CMND, mqtt_topic, "");
func += F("}1" D_MQTT_FULL_TOPIC "}2"); func += stopic;
} else {
func += F("}1" D_MQTT "}2" D_DISABLED);
}
func += F("}1}2 "); // Empty line
func += F("}1" D_EMULATION "}2");
#ifdef USE_EMULATION
if (EMUL_WEMO == Settings.flag2.emulation) {
func += F(D_BELKIN_WEMO);
}
else if (EMUL_HUE == Settings.flag2.emulation) {
func += F(D_HUE_BRIDGE);
}
else {
func += F(D_NONE);
}
#else
func += F(D_DISABLED);
#endif // USE_EMULATION
func += F("}1" D_MDNS_DISCOVERY "}2");
#ifdef USE_DISCOVERY
func += F(D_ENABLED);
func += F("}1" D_MDNS_ADVERTISE "}2");
#ifdef WEBSERVER_ADVERTISE
func += F(D_WEB_SERVER);
#else
func += F(D_DISABLED);
#endif // WEBSERVER_ADVERTISE
#else
func += F(D_DISABLED);
#endif // USE_DISCOVERY
func += F("}1}2 "); // Empty line
func += F("}1" D_ESP_CHIP_ID "}2"); func += String(ESP.getChipId());
func += F("}1" D_FLASH_CHIP_ID "}2"); func += String(ESP.getFlashChipId());
func += F("}1" D_FLASH_CHIP_SIZE "}2"); func += String(ESP.getFlashChipRealSize() / 1024); func += F("kB");
func += F("}1" D_PROGRAM_FLASH_SIZE "}2"); func += String(ESP.getFlashChipSize() / 1024); func += F("kB");
func += F("}1" D_PROGRAM_SIZE "}2"); func += String(ESP.getSketchSize() / 1024); func += F("kB");
func += F("}1" D_FREE_PROGRAM_SPACE "}2"); func += String(ESP.getFreeSketchSpace() / 1024); func += F("kB");
func += F("}1" D_FREE_MEMORY "}2"); func += String(freeMem / 1024); func += F("kB");
func += F("
");
func += FPSTR(HTTP_SCRIPT_INFO_END);
page.replace(F(""), func);
page.replace(F(""), F(""));
// page += F("");
page += FPSTR(HTTP_BTN_MAIN);
ShowPage(page);
}
#endif // Not BE_MINIMAL
void HandleUpgradeFirmware()
{
if (HttpUser()) { return; }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE);
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_FIRMWARE_UPGRADE));
page += FPSTR(HTTP_HEAD_STYLE);
page += FPSTR(HTTP_FORM_UPG);
page.replace(F("{o1"), Settings.ota_url);
page += FPSTR(HTTP_FORM_RST_UPG);
page.replace(F("{r1"), F(D_UPGRADE));
page += FPSTR(HTTP_BTN_MAIN);
ShowPage(page);
upload_error = 0;
upload_file_type = UPL_TASMOTA;
}
void HandleUpgradeFirmwareStart()
{
if (HttpUser()) { return; }
char svalue[100];
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
WifiConfigCounter();
char tmp[100];
WebGetArg("o", tmp, sizeof(tmp));
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_OTAURL " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_INFORMATION));
page += FPSTR(HTTP_HEAD_STYLE);
page += F("" D_UPGRADE_STARTED " ...
");
page += FPSTR(HTTP_MSG_RSTRT);
page += FPSTR(HTTP_BTN_MAIN);
ShowPage(page);
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_UPGRADE " 1"));
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
void HandleUploadDone()
{
if (HttpUser()) { return; }
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
char error[100];
WifiConfigCounter();
restart_flag = 0;
MqttRetryCounter(0);
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_INFORMATION));
page += FPSTR(HTTP_HEAD_STYLE);
page += F("" D_UPLOAD " " D_FAILED " ");
switch (upload_error) {
case 1: strncpy_P(error, PSTR(D_UPLOAD_ERR_1), sizeof(error)); break;
case 2: strncpy_P(error, PSTR(D_UPLOAD_ERR_2), sizeof(error)); break;
case 3: strncpy_P(error, PSTR(D_UPLOAD_ERR_3), sizeof(error)); break;
case 4: strncpy_P(error, PSTR(D_UPLOAD_ERR_4), sizeof(error)); break;
case 5: strncpy_P(error, PSTR(D_UPLOAD_ERR_5), sizeof(error)); break;
case 6: strncpy_P(error, PSTR(D_UPLOAD_ERR_6), sizeof(error)); break;
case 7: strncpy_P(error, PSTR(D_UPLOAD_ERR_7), sizeof(error)); break;
case 8: strncpy_P(error, PSTR(D_UPLOAD_ERR_8), sizeof(error)); break;
case 9: strncpy_P(error, PSTR(D_UPLOAD_ERR_9), sizeof(error)); break;
#ifdef USE_RF_FLASH
case 10: strncpy_P(error, PSTR(D_UPLOAD_ERR_10), sizeof(error)); break;
case 11: strncpy_P(error, PSTR(D_UPLOAD_ERR_11), sizeof(error)); break;
case 12: strncpy_P(error, PSTR(D_UPLOAD_ERR_12), sizeof(error)); break;
case 13: strncpy_P(error, PSTR(D_UPLOAD_ERR_13), sizeof(error)); break;
#endif
default:
snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), upload_error);
}
page += error;
snprintf_P(log_data, sizeof(log_data), PSTR(D_UPLOAD ": %s"), error);
AddLog(LOG_LEVEL_DEBUG);
stop_flash_rotate = Settings.flag.stop_flash_rotate;
} else {
page += F("green'>" D_SUCCESSFUL " ");
page += FPSTR(HTTP_MSG_RSTRT);
ShowWebSource(SRC_WEBGUI);
restart_flag = 2; // Always restart to re-enable disabled features during update
}
SettingsBufferFree();
page += F("
");
page += FPSTR(HTTP_BTN_MAIN);
ShowPage(page);
}
void HandleUploadLoop()
{
// Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update)
boolean _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level);
if (HTTP_USER == webserver_state) { return; }
if (upload_error) {
if (UPL_TASMOTA == upload_file_type) { Update.end(); }
return;
}
HTTPUpload& upload = WebServer->upload();
if (UPLOAD_FILE_START == upload.status) {
restart_flag = 60;
if (0 == upload.filename.c_str()[0]) {
upload_error = 1; // No file selected
return;
}
SettingsSave(1); // Free flash for upload
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str());
AddLog(LOG_LEVEL_INFO);
if (UPL_SETTINGS == upload_file_type) {
if (!SettingsBufferAlloc()) {
upload_error = 2; // Not enough space
return;
}
} else {
MqttRetryCounter(60);
#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
#ifdef USE_ARILUX_RF
AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine
#endif // USE_ARILUX_RF
if (Settings.flag.mqtt_enabled) MqttDisconnect();
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace)) { //start with max available size
// if (_serialoutput) Update.printError(Serial);
// if (Update.getError() == UPDATE_ERROR_BOOTSTRAP) {
// if (_serialoutput) Serial.println("Device still in UART update mode, perform powercycle");
// }
upload_error = 2; // Not enough space
return;
}
}
upload_progress_dot_count = 0;
} else if (!upload_error && (UPLOAD_FILE_WRITE == upload.status)) {
if (0 == upload.totalSize) {
if (UPL_SETTINGS == upload_file_type) {
config_block_count = 0;
}
else {
#ifdef USE_RF_FLASH
if ((SONOFF_BRIDGE == Settings.module) && (upload.buf[0] == ':')) { // Check if this is a RF bridge FW file
Update.end(); // End esp8266 update session
upload_file_type = UPL_EFM8BB1;
upload_error = SnfBrUpdateInit();
if (upload_error != 0) { return; }
} else
#endif // USE_RF_FLASH
{
if (upload.buf[0] != 0xE9) {
upload_error = 3; // Magic byte is not 0xE9
return;
}
uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4);
if(bin_flash_size > ESP.getFlashChipRealSize()) {
upload_error = 4; // Program flash size is larger than real flash size
return;
}
upload.buf[2] = 3; // Force DOUT - ESP8285
}
}
}
if (UPL_SETTINGS == upload_file_type) {
if (!upload_error) {
if (upload.currentSize > (sizeof(Settings) - (config_block_count * HTTP_UPLOAD_BUFLEN))) {
upload_error = 9; // File too large
return;
}
memcpy(settings_buffer + (config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize);
config_block_count++;
}
}
#ifdef USE_RF_FLASH
else if (UPL_EFM8BB1 == upload_file_type) {
if (efm8bb1_update != NULL) { // We have carry over data since last write, i. e. a start but not an end
ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize);
free(efm8bb1_update);
efm8bb1_update = NULL;
if (result != 0) {
upload_error = abs(result); // 2 = Not enough space, 8 = File invalid
return;
}
}
ssize_t result = rf_search_and_write(upload.buf, upload.currentSize);
if (result < 0) {
upload_error = abs(result);
return;
} else if (result > 0) {
if (result > upload.currentSize) {
// Offset is larger than the buffer supplied, this should not happen
upload_error = 9; // File too large - Failed to decode RF firmware
return;
}
// A remnant has been detected, allocate data for it plus a null termination byte
size_t remnant_sz = upload.currentSize - result;
efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1);
if (efm8bb1_update == NULL) {
upload_error = 2; // Not enough space - Unable to allocate memory to store new RF firmware
return;
}
memcpy(efm8bb1_update, upload.buf + result, remnant_sz);
// Add null termination at the end of of remnant buffer
efm8bb1_update[remnant_sz] = '\0';
}
}
#endif // USE_RF_FLASH
else { // firmware
if (!upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) {
upload_error = 5; // Upload buffer miscompare
return;
}
if (_serialoutput) {
Serial.printf(".");
upload_progress_dot_count++;
if (!(upload_progress_dot_count % 80)) { Serial.println(); }
}
}
} else if(!upload_error && (UPLOAD_FILE_END == upload.status)) {
if (_serialoutput && (upload_progress_dot_count % 80)) {
Serial.println();
}
if (UPL_SETTINGS == upload_file_type) {
if (config_xor_on_set) {
for (uint16_t i = 2; i < sizeof(Settings); i++) {
settings_buffer[i] ^= (config_xor_on_set +i);
}
}
bool valid_settings = false;
unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8];
if (buffer_version > 0x06000000) {
uint16_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2];
uint16_t buffer_crc = settings_buffer[15] << 8 | settings_buffer[14];
uint16_t crc = 0;
for (uint16_t i = 0; i < buffer_size; i++) {
if ((i < 14) || (i > 15)) { crc += settings_buffer[i]*(i+1); } // Skip crc
}
valid_settings = (buffer_crc == crc);
} else {
valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN);
}
if (valid_settings) {
SettingsDefaultSet2();
memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16);
Settings.version = buffer_version; // Restore version and auto upgrade after restart
SettingsBufferFree();
} else {
upload_error = 8; // File invalid
return;
}
}
#ifdef USE_RF_FLASH
else if (UPL_EFM8BB1 == upload_file_type) {
// RF FW flash done
upload_file_type = UPL_TASMOTA;
}
#endif // USE_RF_FLASH
else {
if (!Update.end(true)) { // true to set the size to the current progress
if (_serialoutput) { Update.printError(Serial); }
upload_error = 6; // Upload failed. Enable logging 3
return;
}
}
if (!upload_error) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize);
AddLog(LOG_LEVEL_INFO);
}
} else if (UPLOAD_FILE_ABORTED == upload.status) {
restart_flag = 0;
MqttRetryCounter(0);
upload_error = 7; // Upload aborted
if (UPL_TASMOTA == upload_file_type) { Update.end(); }
}
delay(0);
}
void HandlePreflightRequest()
{
WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*"));
WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST"));
WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("authorization"));
WebServer->send(200, FPSTR(HDR_CTYPE_HTML), "");
}
void HandleHttpCommand()
{
if (HttpUser()) { return; }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
uint8_t valid = 1;
if (Settings.web_password[0] != 0) {
char tmp1[100];
WebGetArg("user", tmp1, sizeof(tmp1));
char tmp2[100];
WebGetArg("password", tmp2, sizeof(tmp2));
if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = 0; }
}
String message = F("{\"" D_RSLT_WARNING "\":\"");
if (valid) {
byte curridx = web_log_index;
WebGetArg("cmnd", svalue, sizeof(svalue));
if (strlen(svalue)) {
ExecuteWebCommand(svalue, SRC_WEBCOMMAND);
}
if (web_log_index != curridx) {
byte counter = curridx;
message = F("{");
do {
char* tmp;
size_t len;
GetLog(counter, &tmp, &len);
if (len) {
// [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}]
char* JSON = (char*)memchr(tmp, '{', len);
if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O])
if (message.length() > 1) { message += F(","); }
size_t JSONlen = len - (JSON - tmp);
strlcpy(mqtt_data, JSON +1, JSONlen -2);
message += mqtt_data;
}
}
counter++;
if (!counter) counter++; // Skip 0 as it is not allowed
} while (counter != web_log_index);
message += F("}");
} else {
message += F(D_ENABLE_WEBLOG_FOR_RESPONSE "\"}");
}
} else {
message += F(D_NEED_USER_AND_PASSWORD "\"}");
}
SetHeader();
WebServer->send(200, FPSTR(HDR_CTYPE_JSON), message);
}
void HandleConsole()
{
if (HttpUser()) { return; }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE);
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_CONSOLE));
page += FPSTR(HTTP_HEAD_STYLE);
page.replace(F(""), FPSTR(HTTP_SCRIPT_CONSOL));
page.replace(F(""), F(""));
page += FPSTR(HTTP_FORM_CMND);
page += FPSTR(HTTP_BTN_MAIN);
ShowPage(page);
}
void HandleAjaxConsoleRefresh()
{
if (HttpUser()) { return; }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
byte cflg = 1;
byte counter = 0; // Initial start, should never be 0 again
WebGetArg("c1", svalue, sizeof(svalue));
if (strlen(svalue)) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_COMMAND "%s"), svalue);
AddLog(LOG_LEVEL_INFO);
ExecuteWebCommand(svalue, SRC_WEBCONSOLE);
}
WebGetArg("c2", svalue, sizeof(svalue));
if (strlen(svalue)) { counter = atoi(svalue); }
byte last_reset_web_log_flag = reset_web_log_flag;
String message = F("}9"); // Cannot load mqtt_data here as <> will be encoded by replacements below
if (!reset_web_log_flag) {
counter = 0;
reset_web_log_flag = 1;
}
if (counter != web_log_index) {
if (!counter) {
counter = web_log_index;
cflg = 0;
}
do {
char* tmp;
size_t len;
GetLog(counter, &tmp, &len);
if (len) {
if (cflg) {
message += F("\n");
} else {
cflg = 1;
}
strlcpy(mqtt_data, tmp, len);
message += mqtt_data;
}
counter++;
if (!counter) { counter++; } // Skip 0 as it is not allowed
} while (counter != web_log_index);
// XML encoding to fix blank console log in concert with javascript decodeURIComponent
message.replace(F("%"), F("%25")); // Needs to be done first as otherwise the % in %26 will also be converted
message.replace(F("&"), F("%26"));
message.replace(F("<"), F("%3C"));
message.replace(F(">"), F("%3E"));
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d %d "), web_log_index, last_reset_web_log_flag);
message.replace(F("}9"), mqtt_data); // Save to load here
message += F(" ");
WebServer->send(200, FPSTR(HDR_CTYPE_XML), message);
}
void HandleRestart()
{
if (HttpUser()) { return; }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART);
String page = FPSTR(HTTP_HEAD);
page.replace(F("{v}"), FPSTR(S_RESTART));
page += FPSTR(HTTP_HEAD_STYLE);
page += FPSTR(HTTP_MSG_RSTRT);
if (HTTP_MANAGER == webserver_state) {
webserver_state = HTTP_ADMIN;
} else {
page += FPSTR(HTTP_BTN_MAIN);
}
ShowPage(page);
ShowWebSource(SRC_WEBGUI);
restart_flag = 2;
}
/********************************************************************************************/
void HandleNotFound()
{
// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP "Not fount (%s)"), WebServer->uri().c_str());
// AddLog(LOG_LEVEL_DEBUG);
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page.
#ifdef USE_EMULATION
String path = WebServer->uri();
if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
HandleHueApi(&path);
} else
#endif // USE_EMULATION
{
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"),
WebServer->uri().c_str(), (WebServer->method() == HTTP_GET) ? "GET" : "POST", WebServer->args());
for (uint8_t i = 0; i < WebServer->args(); i++) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s %s: %s\n"), mqtt_data, WebServer->argName(i).c_str(), WebServer->arg(i).c_str());
}
SetHeader();
WebServer->send(404, FPSTR(HDR_CTYPE_PLAIN), mqtt_data);
}
}
/* Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
boolean CaptivePortal()
{
if ((HTTP_MANAGER == webserver_state) && !ValidIpAddress(WebServer->hostHeader())) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED));
WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true);
WebServer->send(302, FPSTR(HDR_CTYPE_PLAIN), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
WebServer->client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}
/** Is this an IP? */
boolean ValidIpAddress(String str)
{
for (uint16_t i = 0; i < str.length(); i++) {
int c = str.charAt(i);
if (c != '.' && (c < '0' || c > '9')) { return false; }
}
return true;
}
/*********************************************************************************************/
String UrlEncode(const String& text)
{
const char hex[] = "0123456789ABCDEF";
String encoded = "";
int len = text.length();
int i = 0;
while (i < len) {
char decodedChar = text.charAt(i++);
/*
if (('a' <= decodedChar && decodedChar <= 'z') ||
('A' <= decodedChar && decodedChar <= 'Z') ||
('0' <= decodedChar && decodedChar <= '9') ||
('=' == decodedChar)) {
encoded += decodedChar;
} else {
encoded += '%';
encoded += hex[decodedChar >> 4];
encoded += hex[decodedChar & 0xF];
}
*/
if (' ' == decodedChar) {
encoded += '%';
encoded += hex[decodedChar >> 4];
encoded += hex[decodedChar & 0xF];
} else {
encoded += decodedChar;
}
}
return encoded;
}
int WebSend(char *buffer)
{
// http://192.168.178.86:80/cm?user=admin&password=joker&cmnd=POWER1 ON
// http://192.168.178.86:80/cm?cmnd=POWER1 ON
// [192.168.178.86:80,admin:joker] POWER1 ON
char *host;
char *port;
char *user;
char *password;
char *command;
uint16_t nport = 80;
int status = 1; // Wrong parameters
host = strtok_r(buffer, "]", &command); // buffer = [192.168.178.86:80,admin:joker] POWER1 ON
if (host && command) {
host = LTrim(host);
host++; // Skip [
host = strtok_r(host, ",", &user); // host = 192.168.178.86:80,admin:joker > 192.168.178.86:80
host = strtok_r(host, ":", &port); // host = 192.168.178.86:80 > 192.168.178.86
if (user) {
user = strtok_r(user, ":", &password); // user = admin:joker > admin
}
//snprintf_P(log_data, sizeof(log_data), PSTR("DBG: Buffer |%X|, Host |%X|, Port |%X|, User |%X|, Password |%X|, Command |%X|"), buffer, host, port, user, password, command);
//AddLog(LOG_LEVEL_DEBUG);
if (port) { nport = atoi(port); }
String nuri = "";
if (user && password) {
nuri += F("user=");
nuri += user;
nuri += F("&password=");
nuri += password;
nuri += F("&");
}
nuri += F("cmnd=");
nuri += LTrim(command);
String uri = UrlEncode(nuri);
IPAddress host_ip;
if (WiFi.hostByName(host, host_ip)) {
WiFiClient client;
bool connected = false;
byte retry = 2;
while ((retry > 0) && !connected) {
--retry;
connected = client.connect(host_ip, nport);
if (connected) break;
}
if (connected) {
String url = F("GET /cm?");
url += uri;
url += F(" HTTP/1.1\r\n Host: ");
url += IPAddress(host_ip).toString();
if (port) {
url += F(" \r\n Port: ");
url += port;
}
url += F(" \r\n Connection: close\r\n\r\n");
//snprintf_P(log_data, sizeof(log_data), PSTR("DBG: Url |%s|"), url.c_str());
//AddLog(LOG_LEVEL_DEBUG);
client.print(url.c_str());
client.flush();
client.stop();
status = 0; // No error - Done
} else {
status = 2; // Connection failed
}
} else {
status = 3; // Host not found
}
}
return status;
}
/*********************************************************************************************/
enum WebCommands { CMND_WEBSERVER, CMND_WEBPASSWORD, CMND_WEBLOG, CMND_WEBSEND, CMND_EMULATION };
const char kWebCommands[] PROGMEM = D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBSEND "|" D_CMND_EMULATION ;
const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND ;
bool WebCommand()
{
char command[CMDSZ];
bool serviced = true;
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kWebCommands);
if (-1 == command_code) {
serviced = false; // Unknown command
}
if (CMND_WEBSERVER == command_code) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.webserver = XdrvMailbox.payload; }
if (Settings.webserver) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"),
(2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str());
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(0));
}
}
else if (CMND_WEBPASSWORD == command_code) {
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) {
strlcpy(Settings.web_password, (SC_CLEAR == Shortcut(XdrvMailbox.data)) ? "" : (SC_DEFAULT == Shortcut(XdrvMailbox.data)) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password));
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.web_password);
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_ASTERIX, command);
}
}
else if (CMND_WEBLOG == command_code) {
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { Settings.weblog_level = XdrvMailbox.payload; }
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.weblog_level);
}
else if (CMND_WEBSEND == command_code) {
if (XdrvMailbox.data_len > 0) {
uint8_t result = WebSend(XdrvMailbox.data);
char stemp1[20];
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus));
}
}
#ifdef USE_EMULATION
else if (CMND_EMULATION == command_code) {
if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) {
Settings.flag2.emulation = XdrvMailbox.payload;
restart_flag = 2;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.emulation);
}
#endif // USE_EMULATION
else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
#define XDRV_02
boolean Xdrv02(byte function)
{
boolean result = false;
switch (function) {
case FUNC_LOOP:
PollDnsWebserver();
#ifdef USE_EMULATION
if (Settings.flag2.emulation) PollUdp();
#endif // USE_EMULATION
break;
case FUNC_COMMAND:
result = WebCommand();
break;
}
return result;
}
#endif // USE_WEBSERVER