Newer
Older
smart-home-server / devices / sh_core_esp8266 / src / WebHandlers.cpp
#include "sh_core_esp8266.h"
#include <Updater.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("&amp;");
    else if (c == '<') out += F("&lt;");
    else if (c == '>') out += F("&gt;");
    else if (c == '\"') out += F("&quot;");
    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)");
  }

  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);
}