#include "sh_core_esp8266.h"
#include <Updater.h>
#include "styles.h"
static String json_escape(const String &s) {
String out;
out.reserve(s.length() + 8);
for (uint16_t i = 0; i < s.length(); i++) {
char c = s[i];
switch (c) {
case '\"': out += F("\\\""); break;
case '\\': out += F("\\\\"); break;
case '\b': out += F("\\b"); break;
case '\f': out += F("\\f"); break;
case '\n': out += F("\\n"); break;
case '\r': out += F("\\r"); break;
case '\t': out += F("\\t"); break;
default:
if ((uint8_t)c < 0x20) {
out += F("\\u00");
const char hex[] = "0123456789ABCDEF";
out += hex[((uint8_t)c >> 4) & 0x0F];
out += hex[(uint8_t)c & 0x0F];
} else {
out += c;
}
}
}
return out;
}
static int rssi_to_percent(int rssi) {
// Типичные значения: -100 (очень плохо) .. -50 (очень хорошо)
if (rssi <= -100) return 0;
if (rssi >= -50) return 100;
return 2 * (rssi + 100); // -100->0, -50->100
}
static String html_escape(const String &s) {
String out;
out.reserve(s.length() + 10);
for (uint16_t i = 0; i < s.length(); i++) {
char c = s[i];
if (c == '&') out += F("&");
else if (c == '<') out += F("<");
else if (c == '>') out += F(">");
else if (c == '\"') out += F(""");
else out += c;
}
return out;
}
static String wifi_scan_json() {
// Для сканирования STA должен быть включён (AP+STA ок)
WiFi.mode(WIFI_AP_STA);
int n = WiFi.scanNetworks(/*async=*/false, /*hidden=*/true);
String json;
json.reserve(256);
if (n <= 0) {
json = F("{\"status\":\"ok\",\"count\":0,\"networks\":[]}");
WiFi.scanDelete();
return json;
}
json += F("{\"status\":\"ok\",\"count\":");
json += String(n);
json += F(",\"networks\":[");
for (int i = 0; i < n; i++) {
if (i) json += ',';
String ssid = WiFi.SSID(i);
int rssi = WiFi.RSSI(i);
int p = rssi_to_percent(rssi);
bool enc = (WiFi.encryptionType(i) != ENC_TYPE_NONE);
// (опционально) bssid/channel/hidden — если нужно, можно добавить
json += F("{\"ssid\":\"");
json += json_escape(ssid);
json += F("\",\"rssi\":");
json += String(rssi);
json += F(",\"signal\":");
json += String(p);
json += F(",\"secured\":");
json += (enc ? F("true") : F("false"));
json += F("}");
}
json += F("]}");
WiFi.scanDelete();
return json;
}
static String render_wifi_setup_page() {
String html = wifi_setup_page;
if (savedSSID.length() > 0) {
html.replace("{{LAST_SSID}}", savedSSID);
} else {
html.replace("{{LAST_SSID}}", "(Empty)");
}
html.replace("{{STYLES}}", get_css_styles());
return html;
}
// -------------------- handlers --------------------
static void handleRoot() {
auto mode = WiFi.getMode();
if (deviceMode == DEVICE_MODE_SETUP || mode == WIFI_AP || mode == WIFI_AP_STA) {
server.send(200, "text/html; charset=utf-8", render_wifi_setup_page());
return;
}
String msg = "ESP SmartHome Device\nID: " + getUniqueID() + "\nVersion: ";
msg += FW_VERSION;
server.send(200, "text/plain; charset=utf-8", msg);
}
static void handleSaveWiFi() {
String ssid = server.arg("ssid");
String pass = server.arg("pass");
savedSSID = ssid;
savedPASS = pass;
saveWiFiConfig(savedSSID, savedPASS);
server.send(200, "text/plain; charset=utf-8", "Saved! Rebooting...");
delay(800);
ESP.restart();
}
static void handleUpdateGet() {
server.send(200, "text/html; charset=utf-8", update_page);
}
static void handleUpdateUpload() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("OTA start: %s\n", upload.filename.c_str());
size_t sketch_space = (ESP.getFreeSketchSpace() - 0x2000) & 0xFFFFF000;
if (!Update.begin(sketch_space)) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (!Update.end(true)) {
Update.printError(Serial);
}
}
}
static void handleUpdatePost() {
if (Update.hasError()) server.send(200, "text/plain; charset=utf-8", "Update Failed!");
else server.send(200, "text/plain; charset=utf-8", "Update OK! Rebooting...");
delay(800);
ESP.restart();
}
static void handleWIFICredsStayConfirmed() {
server.send(200, "text/plain", "OK, rebooting...");
delay(200);
ESP.restart();
}
static void handleSetupGetUi() {
if (deviceMode != DEVICE_MODE_SETUP) {
server.send(403, "text/plain; charset=utf-8", "Setup mode is not active");
return;
}
server.send(200, "text/html; charset=utf-8", render_wifi_setup_page());
}
static void handleSetupPostUi() {
if (deviceMode != DEVICE_MODE_SETUP) {
server.send(403, "text/plain; charset=utf-8", "Setup mode is not active");
return;
}
String ssid = server.arg("ssid");
String pass = server.arg("pass");
ssid.trim();
pass.trim();
if (ssid.length() == 0) {
server.send(400, "text/plain; charset=utf-8", "SSID is required");
return;
}
savedSSID = ssid;
savedPASS = pass;
saveWiFiConfig(savedSSID, savedPASS);
server.send(200, "text/plain; charset=utf-8", "Wi-Fi configured. Rebooting...");
delay(600);
ESP.restart();
}
static void handle_wifi_scan() {
String body = wifi_scan_json();
server.send(200, F("application/json; charset=utf-8"), body);
}
// -------------------- routes register --------------------
void registerWebUiRoutes() {
server.on("/", HTTP_GET, handleRoot);
server.on("/save_wifi", HTTP_POST, handleSaveWiFi);
server.on("/update", HTTP_GET, handleUpdateGet);
server.on("/update", HTTP_POST, handleUpdatePost, handleUpdateUpload);
server.on("/wifi_confirm", HTTP_POST, handleWIFICredsStayConfirmed);
server.on("/setup", HTTP_GET, handleSetupGetUi);
server.on("/setup", HTTP_POST, handleSetupPostUi);
server.on("/wifi_scan", HTTP_GET, handle_wifi_scan);
}