Files
roro9stack/src/apps/firmware_page.cpp
T
twislaandClaude Opus 5.5 4e439410cd Stop IRC by hand; remove mDNS
IrcService::disconnect() stops the session from any state: QUIT if
connected, then no more retries. /quit goes through it (before, it only
stopped a connected session; while waiting for Wi-Fi or retrying it did
nothing), and so does the new `irc stop` command. Stopped by hand,
opening the IRC App no longer reconnects; typing a line does.

mDNS is gone: it never crossed the dev box's routed network, and it
cost about 7.5 KB of RAM. Pushes go to the IP shown in Settings ->
Firmware, which drops its Name row. OTA Q54 records the change.

On the device, Debug Build: 105.6 KB free with Wi-Fi (was 98); with IRC
on TLS 54 KB free (was 46); after `irc stop`, back to 99 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-04 23:17:48 +02:00

102 lines
3.4 KiB
C++

#include "firmware_page.h"
#include <SD.h>
#include "ui/widgets.h"
#include "version.h"
namespace roro {
namespace {
constexpr const char* kUpdatesFolder = "/updates";
bool endsWith(const std::string& s, const std::string& suffix) {
return s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
}
} // namespace
void FirmwarePage::enter() {
confirm_.reset();
{
std::lock_guard<std::mutex> g(lock_);
files_.clear();
listed_ = false;
}
list_.setCount(kFixed);
storage_.runJob([this]() {
std::vector<std::string> found;
File dir = SD.open(kUpdatesFolder);
if (dir && dir.isDirectory())
for (File f = dir.openNextFile(); f; f = dir.openNextFile())
if (!f.isDirectory() && endsWith(f.name(), ".ota")) found.push_back(std::string(kUpdatesFolder) + "/" + f.name());
std::lock_guard<std::mutex> g(lock_);
files_ = found;
listed_ = true;
});
}
std::vector<std::string> FirmwarePage::files() {
std::lock_guard<std::mutex> g(lock_);
return files_;
}
bool FirmwarePage::onKey(const KeyEvent& e) {
auto found = files();
if (confirm_) {
confirm_->onKey(e);
int pick = list_.selected() - kFixed;
if (confirm_->result() == 1 && pick >= 0 && pick < static_cast<int>(found.size())) update_.installFromSd(found[pick]);
if (confirm_->result() != DialogModel::kPending) confirm_.reset();
return true;
}
list_.setCount(kFixed + static_cast<int>(found.size()));
switch (e.key) {
case Key::Up: list_.up(); break;
case Key::Down: list_.down(); break;
case Key::Back: return false;
case Key::Select:
if (list_.selected() >= kFixed) confirm_.reset(new DialogModel({"Cancel", "Install"}));
break;
default: break;
}
return true;
}
void FirmwarePage::draw(Canvas& c) {
auto found = files();
bool listed;
{
std::lock_guard<std::mutex> g(lock_);
listed = listed_;
}
list_.setCount(kFixed + static_cast<int>(found.size()));
std::string ip = wifi_.ip();
widgets::list(
c, list_, theme::kContent,
[&](int i) -> std::string {
switch (i) {
case kVersion: return "Version";
case kStatus: return "Status";
case kAddress: return "Push to";
case kSdHeader: return listed ? (found.empty() ? "No .ota files in /updates" : "On the SD card:") : "Looking on the SD card...";
default: return " " + found[i - kFixed].substr(std::string(kUpdatesFolder).size() + 1);
}
},
[&](int i) -> std::string {
switch (i) {
case kVersion: return versionString();
case kStatus: return update_.onProbation() ? "on probation" : "confirmed";
case kAddress: return ip.empty() ? "Wi-Fi not connected" : ip + ":" + std::to_string(UpdateService::kPort);
case kSdHeader: return "";
default: return "install >";
}
});
if (confirm_) {
int pick = list_.selected() - kFixed;
std::string name = pick >= 0 && pick < static_cast<int>(found.size()) ? found[pick] : "";
widgets::dialog(c, "Install update?", name + " is checked before anything is written. The device restarts after.", *confirm_);
}
}
} // namespace roro