Files
Crosspoint/src/activities/network/CalibreConnectActivity.cpp
T

249 lines
8.6 KiB
C++

#include "CalibreConnectActivity.h"
#include <ESPmDNS.h>
#include <GfxRenderer.h>
#include <I18n.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include "activities/network/SignalStrengthWidget.h"
#include "MappedInputManager.h"
#include "WifiSelectionActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
namespace {
constexpr const char* HOSTNAME = "crosspoint";
} // namespace
void CalibreConnectActivity::onEnter() {
Activity::onEnter();
requestUpdate();
state = CalibreConnectState::WIFI_SELECTION;
connectedIP.clear();
connectedSSID.clear();
lastHandleClientTime = 0;
lastProgressReceived = 0;
lastProgressTotal = 0;
currentUploadName.clear();
lastCompleteName.clear();
lastCompleteAt = 0;
lastProcessedCompleteAt = 0;
currentRssi = 0;
lastRssiUpdateTime = 0;
exitRequested = false;
if (WiFi.status() != WL_CONNECTED) {
startActivityForResult(std::make_unique<WifiSelectionActivity>(renderer, mappedInput),
[this](const ActivityResult& result) {
if (!result.isCancelled) {
const auto& wifi = std::get<WifiResult>(result.data);
connectedIP = wifi.ip;
connectedSSID = wifi.ssid;
}
onWifiSelectionComplete(!result.isCancelled);
});
} else {
connectedIP = WiFi.localIP().toString().c_str();
connectedSSID = WiFi.SSID().c_str();
startWebServer();
}
}
void CalibreConnectActivity::onExit() {
Activity::onExit();
stopWebServer();
MDNS.end();
delay(50);
WiFi.disconnect(false);
delay(30);
WiFi.mode(WIFI_OFF);
delay(30);
}
void CalibreConnectActivity::onWifiSelectionComplete(const bool connected) {
if (!connected) {
finish();
return;
}
startWebServer();
}
void CalibreConnectActivity::startWebServer() {
state = CalibreConnectState::SERVER_STARTING;
requestUpdate();
if (MDNS.begin(HOSTNAME)) {
// mDNS is optional for the Calibre plugin but still helpful for users.
LOG_DBG("CAL", "mDNS started: http://%s.local/", HOSTNAME);
}
webServer.reset(new CrossPointWebServer());
webServer->begin();
if (webServer->isRunning()) {
state = CalibreConnectState::SERVER_RUNNING;
requestUpdate();
} else {
state = CalibreConnectState::ERROR;
requestUpdate();
}
}
void CalibreConnectActivity::stopWebServer() {
if (webServer) {
webServer->stop();
webServer.reset();
}
}
void CalibreConnectActivity::loop() {
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
exitRequested = true;
}
if (webServer && webServer->isRunning()) {
const unsigned long timeSinceLastHandleClient = millis() - lastHandleClientTime;
if (lastHandleClientTime > 0 && timeSinceLastHandleClient > 100) {
LOG_DBG("CAL", "WARNING: %lu ms gap since last handleClient", timeSinceLastHandleClient);
}
esp_task_wdt_reset();
constexpr int MAX_ITERATIONS = 80;
for (int i = 0; i < MAX_ITERATIONS && webServer->isRunning(); i++) {
webServer->handleClient();
if ((i & 0x07) == 0x07) {
esp_task_wdt_reset();
}
if ((i & 0x0F) == 0x0F) {
yield();
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
exitRequested = true;
break;
}
}
}
lastHandleClientTime = millis();
if (millis() - lastRssiUpdateTime > 5000) {
lastRssiUpdateTime = millis();
currentRssi = WiFi.RSSI();
requestUpdate();
}
const auto status = webServer->getWsUploadStatus();
bool changed = false;
if (status.inProgress) {
if (status.received != lastProgressReceived || status.total != lastProgressTotal ||
status.filename != currentUploadName) {
lastProgressReceived = status.received;
lastProgressTotal = status.total;
currentUploadName = status.filename;
changed = true;
}
} else if (lastProgressReceived != 0 || lastProgressTotal != 0) {
lastProgressReceived = 0;
lastProgressTotal = 0;
currentUploadName.clear();
changed = true;
}
// Only update lastCompleteAt if the server has a NEW value (not one we already processed)
// This prevents restoring an old value after the 6s timeout clears it
if (status.lastCompleteAt != 0 && status.lastCompleteAt != lastProcessedCompleteAt) {
lastCompleteAt = status.lastCompleteAt;
lastCompleteName = status.lastCompleteName;
lastProcessedCompleteAt = status.lastCompleteAt; // Mark this value as processed
changed = true;
}
if (lastCompleteAt > 0 && (millis() - lastCompleteAt) >= 6000) {
lastCompleteAt = 0;
lastCompleteName.clear();
// Note: we DON'T reset lastProcessedCompleteAt here, so we won't re-process the old server value
changed = true;
}
if (changed) {
requestUpdate();
}
}
if (exitRequested) {
finish();
return;
}
}
void CalibreConnectActivity::render(RenderLock&&) {
const auto& metrics = UITheme::getInstance().getMetrics();
const Rect contentRect = UITheme::getContentRect(renderer, true, false);
renderer.clearScreen();
GUI.drawHeader(renderer, Rect{contentRect.x, metrics.topPadding, contentRect.width, metrics.headerHeight},
tr(STR_CALIBRE_WIRELESS));
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
const auto top = contentRect.y + (contentRect.height - height) / 2;
if (state == CalibreConnectState::SERVER_STARTING) {
renderer.drawCenteredText(UI_12_FONT_ID, top, tr(STR_CALIBRE_STARTING));
} else if (state == CalibreConnectState::ERROR) {
renderer.drawCenteredText(UI_12_FONT_ID, top, tr(STR_CONNECTION_FAILED), true, EpdFontFamily::BOLD);
} else if (state == CalibreConnectState::SERVER_RUNNING) {
GUI.drawSubHeader(
renderer,
Rect{contentRect.x, metrics.topPadding + metrics.headerHeight, contentRect.width, metrics.tabBarHeight},
connectedSSID.c_str(), (std::string(tr(STR_IP_ADDRESS_PREFIX)) + connectedIP).c_str());
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 * 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;
renderer.drawText(SMALL_FONT_ID, textX, y, tr(STR_CALIBRE_INSTRUCTION_1));
renderer.drawText(SMALL_FONT_ID, textX, y + height, tr(STR_CALIBRE_INSTRUCTION_2));
renderer.drawText(SMALL_FONT_ID, textX, y + height * 2, tr(STR_CALIBRE_INSTRUCTION_3));
renderer.drawText(SMALL_FONT_ID, textX, y + height * 3, tr(STR_CALIBRE_INSTRUCTION_4));
y += height * 3 + metrics.verticalSpacing * 4;
renderer.drawText(UI_12_FONT_ID, textX, y, tr(STR_CALIBRE_STATUS), true, EpdFontFamily::BOLD);
y += heightText12 + metrics.verticalSpacing * 2;
if (lastProgressTotal > 0 && lastProgressReceived <= lastProgressTotal) {
std::string label = tr(STR_CALIBRE_RECEIVING);
if (!currentUploadName.empty()) {
label += ": " + currentUploadName;
label = renderer.truncatedText(SMALL_FONT_ID, label.c_str(), textWidth, EpdFontFamily::REGULAR);
}
renderer.drawText(SMALL_FONT_ID, textX, y, label.c_str());
GUI.drawProgressBar(renderer,
Rect{contentRect.x + metrics.contentSidePadding, y + height + metrics.verticalSpacing,
textWidth, metrics.progressBarHeight},
lastProgressReceived, lastProgressTotal);
y += height + metrics.verticalSpacing * 2 + metrics.progressBarHeight;
}
if (lastCompleteAt > 0 && (millis() - lastCompleteAt) < 6000) {
std::string msg = std::string(tr(STR_CALIBRE_RECEIVED)) + lastCompleteName;
msg = renderer.truncatedText(SMALL_FONT_ID, msg.c_str(), textWidth, EpdFontFamily::REGULAR);
renderer.drawText(SMALL_FONT_ID, textX, y, msg.c_str());
}
const auto labels = mappedInput.mapLabels(tr(STR_EXIT), "", "", "");
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
}
renderer.displayBuffer();
}