diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index b453bae4..23253403 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -44,6 +44,12 @@ STR_CONNECT_WIFI_HINT: "Connect your device to this WiFi network" STR_OPEN_URL_HINT: "Open this URL in your browser" STR_OR_HTTP_PREFIX: "or http://" STR_SCAN_QR_HINT: "or scan QR code with your phone:" +STR_RSSI: "RSSI" +STR_NO_SIGNAL: "No signal" +STR_SIGNAL_QUALITY_POOR: "Poor" +STR_SIGNAL_QUALITY_WEAK: "Weak" +STR_SIGNAL_QUALITY_GOOD: "Good" +STR_SIGNAL_QUALITY_EXCELLENT: "Excellent" STR_CALIBRE_WIRELESS: "Calibre Wireless" STR_CALIBRE_WEB_URL: "Calibre Web URL" STR_NETWORK_LEGEND: "* = Encrypted | + = Saved" diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 609bc425..0bf0d8b2 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -44,6 +44,12 @@ STR_CONNECT_WIFI_HINT: "Connectez un appareil à ce WiFi" STR_OPEN_URL_HINT: "Ouvrez cette URL dans un navigateur" STR_OR_HTTP_PREFIX: "ou http://" STR_SCAN_QR_HINT: "ou scannez le QR code :" +STR_RSSI: "RSSI" +STR_NO_SIGNAL: "Pas de signal" +STR_SIGNAL_QUALITY_POOR: "Faible" +STR_SIGNAL_QUALITY_WEAK: "Très faible" +STR_SIGNAL_QUALITY_GOOD: "Bon" +STR_SIGNAL_QUALITY_EXCELLENT: "Excellent" STR_CALIBRE_WIRELESS: "Connexion Calibre sans fil" STR_CALIBRE_WEB_URL: "URL Web Calibre" STR_NETWORK_LEGEND: "* = Sécurisé | + = Sauvegardé" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index 1de48bd6..8f69dd64 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -44,6 +44,12 @@ STR_CONNECT_WIFI_HINT: "Gerät mit diesem WLAN verbinden" STR_OPEN_URL_HINT: "Diese URL im Browser öffnen" STR_OR_HTTP_PREFIX: "oder http://" STR_SCAN_QR_HINT: "oder QR-Code mit dem Handy scannen:" +STR_RSSI: "RSSI" +STR_NO_SIGNAL: "Kein Signal" +STR_SIGNAL_QUALITY_POOR: "Schlecht" +STR_SIGNAL_QUALITY_WEAK: "Schwach" +STR_SIGNAL_QUALITY_GOOD: "Gut" +STR_SIGNAL_QUALITY_EXCELLENT: "Ausgezeichnet" STR_CALIBRE_WIRELESS: "Calibre Wireless" STR_CALIBRE_WEB_URL: "Calibre-Web-URL" STR_NETWORK_LEGEND: "* = Verschlüsselt | + = Gespeichert" diff --git a/lib/I18n/translations/italian.yaml b/lib/I18n/translations/italian.yaml index 8c805347..10c58894 100644 --- a/lib/I18n/translations/italian.yaml +++ b/lib/I18n/translations/italian.yaml @@ -44,6 +44,12 @@ STR_CONNECT_WIFI_HINT: "Connettere il dispositivo a questa rete WiFi" STR_OPEN_URL_HINT: "Aprire questo URL nel browser" STR_OR_HTTP_PREFIX: "o http://" STR_SCAN_QR_HINT: "oppure scansionare il codice QR col telefono:" +STR_RSSI: "RSSI" +STR_NO_SIGNAL: "Nessun segnale" +STR_SIGNAL_QUALITY_POOR: "Scarso" +STR_SIGNAL_QUALITY_WEAK: "Debole" +STR_SIGNAL_QUALITY_GOOD: "Buono" +STR_SIGNAL_QUALITY_EXCELLENT: "Eccellente" STR_CALIBRE_WIRELESS: "Calibre wireless" STR_CALIBRE_WEB_URL: "URL Web Calibre" STR_NETWORK_LEGEND: "* = Protetta | + = Salvata" diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index 76098cb4..e4054460 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -44,6 +44,12 @@ STR_CONNECT_WIFI_HINT: "Подключите устройство к этой с STR_OPEN_URL_HINT: "Откройте этот адрес в браузере" STR_OR_HTTP_PREFIX: "или http://" STR_SCAN_QR_HINT: "или отсканируйте QR-код:" +STR_RSSI: "RSSI" +STR_NO_SIGNAL: "Нет сигнала" +STR_SIGNAL_QUALITY_POOR: "Плохо" +STR_SIGNAL_QUALITY_WEAK: "Слабо" +STR_SIGNAL_QUALITY_GOOD: "Хорошо" +STR_SIGNAL_QUALITY_EXCELLENT: "Отлично" STR_CALIBRE_WIRELESS: "Calibre по Wi-Fi" STR_CALIBRE_WEB_URL: "Web-адрес Calibre" STR_NETWORK_LEGEND: "* = Защищена | + = Сохранена" diff --git a/scripts/build_html.py b/scripts/build_html.py index 32847760..47000bfe 100644 --- a/scripts/build_html.py +++ b/scripts/build_html.py @@ -1,10 +1,85 @@ +import configparser import os import re import gzip +import subprocess +import sys SRC_DIR = "src" +CROSSPOINT_NAME = "CrossPoint Reader" +PLACEHOLDERS = { + "%%CROSSPOINT%%": CROSSPOINT_NAME, +} + + +def warn(msg: str) -> None: + print(f"WARNING [build_html.py]: {msg}", file=sys.stderr) + + +def get_base_version(project_dir: str) -> str: + ini_path = os.path.join(project_dir, "platformio.ini") + if not os.path.isfile(ini_path): + warn(f"platformio.ini not found at {ini_path}; using 0.0.0") + return "0.0.0" + config = configparser.ConfigParser() + config.read(ini_path) + if not config.has_option("crosspoint", "version"): + warn("No [crosspoint] section or version in platformio.ini; using 0.0.0") + return "0.0.0" + return config.get("crosspoint", "version") + + +def get_git_branch(project_dir: str) -> str: + try: + branch = subprocess.check_output( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + text=True, + stderr=subprocess.PIPE, + cwd=project_dir, + ).strip() + if branch == "HEAD": + branch = subprocess.check_output( + ["git", "rev-parse", "--short", "HEAD"], + text=True, + stderr=subprocess.PIPE, + cwd=project_dir, + ).strip() + return "".join(c for c in branch if c not in '"\\') + except FileNotFoundError: + warn('git not found on PATH; branch suffix will be "unknown"') + return "unknown" + except subprocess.CalledProcessError as e: + warn( + f'git command failed (exit {e.returncode}): {e.stderr.strip()}; branch suffix will be "unknown"' + ) + return "unknown" + except Exception as e: + warn( + f'Unexpected error reading git branch: {e}; branch suffix will be "unknown"' + ) + return "unknown" + + +def get_version_string(project_dir: str) -> str: + env_version = os.environ.get("CROSSPOINT_VERSION") + if env_version: + return env_version.strip('"') + base_version = get_base_version(project_dir) + pioenv = os.environ.get("PIOENV", "default") + if pioenv == "default": + branch = get_git_branch(project_dir) + return f"{base_version}-dev+{branch}" + return base_version + + +def replace_placeholders(html: str, replacements: dict) -> str: + for placeholder, replacement in replacements.items(): + html = html.replace(placeholder, replacement) + return html + + def strip_js_comments(js: str) -> str: """Remove JS comments while preserving string literals and URLs.""" result = [] @@ -159,6 +234,24 @@ def sanitize_identifier(name: str) -> str: return sanitized +def get_project_dir() -> str: + try: + Import("env") # type: ignore[name-defined] + return env["PROJECT_DIR"] + except NameError: + if "__file__" in globals(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + elif sys.argv and sys.argv[0]: + script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + else: + script_dir = os.getcwd() + return os.path.dirname(script_dir) + + +project_dir = get_project_dir() +version_string = get_version_string(project_dir) +PLACEHOLDERS["%%VERSION%%"] = version_string + for root, _, files in os.walk(SRC_DIR): for file in files: if file.endswith(".html") or file.endswith(".js"): @@ -166,8 +259,9 @@ for root, _, files in os.walk(SRC_DIR): with open(file_path, "r", encoding="utf-8") as f: content = f.read() - # Only minify HTML files; JS files are typically pre-minified (e.g., jszip.min.js) + # Replace build-time placeholders only in HTML files if file.endswith(".html"): + content = replace_placeholders(content, PLACEHOLDERS) processed = minify_html(content) else: processed = content diff --git a/src/SystemStatus.h b/src/SystemStatus.h index d800c788..238ee460 100644 --- a/src/SystemStatus.h +++ b/src/SystemStatus.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -72,9 +73,11 @@ struct SystemStatus { } static void fillSdStatus(SystemStatus& s) { + uint32_t t0 = millis(); s.sdTotalBytes = Storage.sdTotalBytes(); s.sdUsedBytes = Storage.sdUsedBytes(); s.sdFreeBytes = Storage.sdFreeBytes(); + LOG_DBG("SYSINFO", "Filled SD status in %u ms", millis() - t0); } static SystemStatus collect() { diff --git a/src/activities/network/CalibreConnectActivity.cpp b/src/activities/network/CalibreConnectActivity.cpp index 4d9f307d..f5cf2bb9 100644 --- a/src/activities/network/CalibreConnectActivity.cpp +++ b/src/activities/network/CalibreConnectActivity.cpp @@ -8,6 +8,7 @@ #include "MappedInputManager.h" #include "WifiSelectionActivity.h" +#include "activities/network/SignalStrengthWidget.h" #include "components/UITheme.h" #include "fontIds.h" @@ -29,6 +30,8 @@ void CalibreConnectActivity::onEnter() { lastCompleteName.clear(); lastCompleteAt = 0; lastProcessedCompleteAt = 0; + currentRssi = 0; + lastRssiUpdateTime = 0; exitRequested = false; if (WiFi.status() != WL_CONNECTED) { @@ -126,6 +129,12 @@ void CalibreConnectActivity::loop() { } lastHandleClientTime = millis(); + if (millis() - lastRssiUpdateTime > 5000) { + lastRssiUpdateTime = millis(); + currentRssi = WiFi.RSSI(); + requestUpdate(); + } + const auto status = webServer->getWsUploadStatus(); bool changed = false; if (status.inProgress) { @@ -190,8 +199,15 @@ void CalibreConnectActivity::render(RenderLock&&) { const int textX = contentRect.x + metrics.contentSidePadding; const int textWidth = contentRect.width - metrics.contentSidePadding * 2; - int y = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 4; + int y = metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing * 2; const auto heightText12 = renderer.getTextHeight(UI_12_FONT_ID); + const auto lineHeightSmall = renderer.getLineHeight(SMALL_FONT_ID); + + const int signalHeight = 22; + drawWifiSignalStrength(renderer, textX, y, textWidth, signalHeight, currentRssi); + renderer.drawText(SMALL_FONT_ID, textX, y + signalHeight + 2, rssiLabel(currentRssi).c_str()); + y += signalHeight + lineHeightSmall + metrics.verticalSpacing * 3; + renderer.drawText(UI_12_FONT_ID, textX, y, tr(STR_CALIBRE_SETUP), true, EpdFontFamily::BOLD); y += heightText12 + metrics.verticalSpacing * 2; diff --git a/src/activities/network/CalibreConnectActivity.h b/src/activities/network/CalibreConnectActivity.h index a8afd4af..0687f3df 100644 --- a/src/activities/network/CalibreConnectActivity.h +++ b/src/activities/network/CalibreConnectActivity.h @@ -26,6 +26,8 @@ class CalibreConnectActivity final : public Activity { std::string lastCompleteName; unsigned long lastCompleteAt = 0; unsigned long lastProcessedCompleteAt = 0; // Track which server value we've already processed + int currentRssi = 0; + unsigned long lastRssiUpdateTime = 0; bool exitRequested = false; void renderServerRunning() const; diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index 4f3b4f34..03dec966 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -14,6 +14,7 @@ #include "NetworkModeSelectionActivity.h" #include "WifiSelectionActivity.h" #include "activities/network/CalibreConnectActivity.h" +#include "activities/network/SignalStrengthWidget.h" #include "components/UITheme.h" #include "fontIds.h" #include "util/QrUtils.h" @@ -244,6 +245,10 @@ void CrossPointWebServerActivity::startWebServer() { if (webServer->isRunning()) { state = WebServerActivityState::SERVER_RUNNING; + if (!isApMode) { + currentRssi = WiFi.RSSI(); + lastRssiUpdateTime = millis(); + } LOG_DBG("WEBACT", "Web server started successfully"); // Force an immediate render since we're transitioning from a subactivity @@ -293,6 +298,12 @@ void CrossPointWebServerActivity::loop() { LOG_DBG("WEBACT", "Warning: Weak WiFi signal: %d dBm", rssi); } } + + if (millis() - lastRssiUpdateTime > 5000) { // Refresh signal indicator every 5 seconds + lastRssiUpdateTime = millis(); + currentRssi = WiFi.RSSI(); + requestUpdate(); + } } // Handle web server requests - maximize throughput with watchdog safety @@ -366,6 +377,8 @@ void CrossPointWebServerActivity::render(RenderLock&&) { } } +namespace {} // namespace + void CrossPointWebServerActivity::renderServerRunning() const { const auto& metrics = UITheme::getInstance().getMetrics(); const Rect contentRect = UITheme::getContentRect(renderer, true, false); @@ -412,6 +425,12 @@ void CrossPointWebServerActivity::renderServerRunning() const { hostnameUrl.c_str()); renderer.drawText(SMALL_FONT_ID, metrics.contentSidePadding + QR_CODE_WIDTH + metrics.verticalSpacing, startY + 100, ipUrl.c_str()); + + const int signalHeight = 22; + const int signalWidth = contentRect.width - metrics.contentSidePadding * 2; + const int signalY = startY + 120; + drawWifiSignalStrength(renderer, contentRect.x + metrics.contentSidePadding, signalY, signalWidth, signalHeight, 0); + renderer.drawCenteredText(SMALL_FONT_ID, signalY + signalHeight + 2, tr(STR_HOTSPOT_MODE)); } else { startY += metrics.verticalSpacing * 2; @@ -435,8 +454,19 @@ void CrossPointWebServerActivity::renderServerRunning() const { // Also show hostname URL std::string hostnameUrl = std::string(tr(STR_OR_HTTP_PREFIX)) + AP_HOSTNAME + ".local/"; renderer.drawCenteredText(SMALL_FONT_ID, startY, hostnameUrl.c_str(), true); + + // AP mode: no external RSSI metric available, but keep UI spacing consistent. } - const auto labels = mappedInput.mapLabels(tr(STR_EXIT), "", "", ""); + if (!isApMode) { + const int signalHeight = 22; + const int signalWidth = contentRect.width - metrics.contentSidePadding * 2; + const int signalY = startY + height10 + metrics.verticalSpacing * 2; + drawWifiSignalStrength(renderer, contentRect.x + metrics.contentSidePadding, signalY, signalWidth, signalHeight, + currentRssi); + renderer.drawCenteredText(SMALL_FONT_ID, signalY + signalHeight + 2, rssiLabel(currentRssi).c_str(), true); + } + + const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } diff --git a/src/activities/network/CrossPointWebServerActivity.h b/src/activities/network/CrossPointWebServerActivity.h index 5069f199..ed8c11f9 100644 --- a/src/activities/network/CrossPointWebServerActivity.h +++ b/src/activities/network/CrossPointWebServerActivity.h @@ -40,6 +40,8 @@ class CrossPointWebServerActivity final : public Activity { // Server status std::string connectedIP; std::string connectedSSID; // For STA mode: network name, For AP mode: AP name + int currentRssi = 0; + unsigned long lastRssiUpdateTime = 0; // Performance monitoring unsigned long lastHandleClientTime = 0; diff --git a/src/activities/network/SignalStrengthWidget.h b/src/activities/network/SignalStrengthWidget.h new file mode 100644 index 00000000..0773bf49 --- /dev/null +++ b/src/activities/network/SignalStrengthWidget.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include +#include + +inline StrId getRssiQualityStrId(int rssi) { + if (rssi <= -90) { + return StrId::STR_SIGNAL_QUALITY_POOR; + } + if (rssi <= -75) { + return StrId::STR_SIGNAL_QUALITY_WEAK; + } + if (rssi <= -60) { + return StrId::STR_SIGNAL_QUALITY_GOOD; + } + return StrId::STR_SIGNAL_QUALITY_EXCELLENT; +} + +inline void drawWifiSignalStrength(const GfxRenderer& renderer, int x, int y, int width, int height, int rssi) { + const int barCount = 4; + const int gap = 6; + const int barWidth = std::max(1, std::min(10, (width - (barCount - 1) * gap) / barCount)); + const int totalWidth = barCount * barWidth + (barCount - 1) * gap; + const int startX = x + (width - totalWidth) / 2; + const int maxBarHeight = std::max(1, height - 8); + const int bars = rssi == 0 ? 0 : (rssi <= -90 ? 1 : (rssi <= -75 ? 2 : (rssi <= -60 ? 3 : 4))); + const int baseY = y + height - 2; + for (int i = 0; i < barCount; ++i) { + const int barHeight = std::max(0, ((i + 1) * maxBarHeight) / barCount); + if (barHeight <= 0 || barWidth <= 0) { + continue; + } + const int barX = startX + i * (barWidth + gap); + const int barY = baseY - barHeight; + renderer.drawRect(barX, barY, barWidth, barHeight, true); + if (i < bars && barWidth > 2 && barHeight > 2) { + renderer.fillRect(barX + 1, barY + 1, barWidth - 2, barHeight - 2); + } + } +} + +inline std::string rssiLabel(int rssi) { + if (rssi == 0) { + return std::string(tr(STR_NO_SIGNAL)); + } + const char* quality = I18N.get(getRssiQualityStrId(rssi)); + return std::string(tr(STR_RSSI)) + ": " + std::to_string(rssi) + " dBm (" + quality + ")"; +} diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index 04822a38..366e1ba0 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -17,6 +17,7 @@ #include "html/FilesPageHtml.generated.h" #include "html/HomePageHtml.generated.h" #include "html/SettingsPageHtml.generated.h" +#include "html/WelcomePageHtml.generated.h" #include "html/js/jszip_minJs.generated.h" namespace { @@ -162,11 +163,13 @@ void CrossPointWebServer::begin() { // Setup routes LOG_DBG("WEB", "Setting up routes..."); - server->on("/", HTTP_GET, [this] { handleRoot(); }); - server->on("/files", HTTP_GET, [this] { handleFileList(); }); + server->on("/", HTTP_GET, [this] { handleWelcomePage(); }); + server->on("/systeminfo", HTTP_GET, [this] { handleSystemInfoPage(); }); + server->on("/files", HTTP_GET, [this] { handleRoot(); }); server->on("/js/jszip.min.js", HTTP_GET, [this] { handleJszip(); }); server->on("/api/status", HTTP_GET, [this] { handleStatus(); }); + server->on("/api/status/fast", HTTP_GET, [this] { handleStatusFast(); }); server->on("/api/files", HTTP_GET, [this] { handleFileListData(); }); server->on("/download", HTTP_GET, [this] { handleDownload(); }); @@ -353,8 +356,24 @@ static void sendHtmlContent(WebServer* server, const char* data, size_t len) { } void CrossPointWebServer::handleRoot() const { + int32_t t0 = millis(); + sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml)); + int32_t t1 = millis(); + LOG_DBG("WEB", "Served file manager page in %d ms", t1 - t0); +} + +void CrossPointWebServer::handleWelcomePage() const { + int32_t t0 = millis(); + sendHtmlContent(server.get(), WelcomePageHtml, sizeof(WelcomePageHtml)); + int32_t t1 = millis(); + LOG_DBG("WEB", "Served welcome page in %d ms", t1 - t0); +} + +void CrossPointWebServer::handleSystemInfoPage() const { + int32_t t0 = millis(); sendHtmlContent(server.get(), HomePageHtml, sizeof(HomePageHtml)); - LOG_DBG("WEB", "Served root page"); + int32_t t1 = millis(); + LOG_DBG("WEB", "Served system info page in %d ms", t1 - t0); } void CrossPointWebServer::handleJszip() const { @@ -370,10 +389,14 @@ void CrossPointWebServer::handleNotFound() const { } void CrossPointWebServer::handleStatus() const { - const bool fastOnly = server->hasArg("phase") && server->arg("phase") == "fast"; - + const bool fastOnly = server->hasArg("phase") && server->arg("phase").equalsIgnoreCase("fast"); + LOG_DBG("SYSINFO", "handleStatus request received (fastOnly=%d)", fastOnly); + int32_t t0 = millis(); SystemStatus status = SystemStatus::collectFast(); + int32_t t1 = millis(); + LOG_DBG("SYSINFO", "Collected fast status in %d ms (fastOnly=%d)", t1 - t0, fastOnly); if (!fastOnly) { + LOG_DBG("SYSINFO", "handleStatus will collect SD stats"); SystemStatus::fillSdStatus(status); } @@ -404,6 +427,41 @@ void CrossPointWebServer::handleStatus() const { server->send(200, "application/json", json); } +void CrossPointWebServer::handleStatusFast() const { + LOG_DBG("SYSINFO", "handleStatusFast request received"); + int32_t t0 = millis(); + SystemStatus status = SystemStatus::collectFast(); + int32_t t1 = millis(); + LOG_DBG("SYSINFO", "Collected fast-only status in %d ms", t1 - t0); + + JsonDocument doc; + doc["version"] = status.version; + doc["chipVersion"] = status.chipVersion; + doc["cpuMHz"] = status.cpuFreqMHz; + doc["ip"] = status.ip; + doc["mode"] = status.wifiMode; + doc["rssi"] = status.rssi; + doc["macAddress"] = status.macAddress; + doc["freeHeap"] = status.freeHeapBytes; + doc["minFreeHeap"] = status.minFreeHeapBytes; + doc["maxAllocHeap"] = status.maxAllocHeapBytes; + doc["flashTotal"] = status.flashBytes; + doc["flashAppUsed"] = status.flashAppUsedBytes; + doc["flashAppFree"] = status.flashAppFreeBytes; + doc["batteryPercent"] = status.batteryPercent; + doc["charging"] = status.charging; + doc["uptime"] = status.uptimeSeconds; + doc["sdReady"] = false; + // Still include SD stats in response, but client should ignore them when sdReady=false + doc["sdTotal"] = status.sdTotalBytes; + doc["sdUsed"] = status.sdUsedBytes; + doc["sdFree"] = status.sdFreeBytes; + + String json; + serializeJson(doc, json); + server->send(200, "application/json", json); +} + void CrossPointWebServer::scanFiles(const char* path, const std::function& callback) const { FsFile root = Storage.open(path); if (!root) { diff --git a/src/network/CrossPointWebServer.h b/src/network/CrossPointWebServer.h index 9a8444ec..14a71ad8 100644 --- a/src/network/CrossPointWebServer.h +++ b/src/network/CrossPointWebServer.h @@ -89,9 +89,12 @@ class CrossPointWebServer { // Request handlers void handleRoot() const; + void handleWelcomePage() const; + void handleSystemInfoPage() const; void handleJszip() const; void handleNotFound() const; void handleStatus() const; + void handleStatusFast() const; void handleFileList() const; void handleFileListData() const; void handleDownload() const; diff --git a/src/network/html/FilesPage.html b/src/network/html/FilesPage.html index 54830b0f..96795d17 100644 --- a/src/network/html/FilesPage.html +++ b/src/network/html/FilesPage.html @@ -1742,9 +1742,9 @@