Public Access
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
102 lines
3.4 KiB
C++
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
|