diff --git a/common/params_keys.h b/common/params_keys.h index 40a69ebd88..2b540b744c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -8,6 +8,7 @@ inline static std::unordered_map keys = { {"AdbEnabled", PERSISTENT}, {"AlwaysOnDM", PERSISTENT}, {"ApiCache_Device", PERSISTENT}, + {"ApiCache_FirehoseStats", PERSISTENT}, {"AssistNowToken", PERSISTENT}, {"AthenadPid", PERSISTENT}, {"AthenadUploadQueue", PERSISTENT}, @@ -36,7 +37,6 @@ inline static std::unordered_map keys = { {"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY}, {"ExperimentalMode", PERSISTENT}, {"ExperimentalModeConfirmed", PERSISTENT}, - {"FirehoseMode", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"ForcePowerDown", PERSISTENT}, {"GitBranch", PERSISTENT}, diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc index 7b48b0fd9a..77474d00c2 100644 --- a/selfdrive/ui/qt/offroad/firehose.cc +++ b/selfdrive/ui/qt/offroad/firehose.cc @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) { layout = new QVBoxLayout(this); @@ -28,7 +31,7 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) content_layout->setSpacing(20); // Top description - QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models with better Experimental Mode.")); + QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.")); description->setStyleSheet("font-size: 45px; padding-bottom: 20px;"); description->setWordWrap(true); content_layout->addWidget(description); @@ -40,41 +43,16 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) line->setStyleSheet("background-color: #444444; margin-top: 5px; margin-bottom: 5px;"); content_layout->addWidget(line); - enable_firehose = new ParamControl("FirehoseMode", tr("Enable Firehose Mode"), "", ""); + toggle_label = new QLabel(tr("Firehose Mode: ACTIVE")); + toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: white;"); + content_layout->addWidget(toggle_label); - content_layout->addWidget(enable_firehose); - - // Create progress bar container - progress_container = new QFrame(); - progress_container->hide(); - QHBoxLayout *progress_layout = new QHBoxLayout(progress_container); - progress_layout->setContentsMargins(10, 0, 10, 10); - progress_layout->setSpacing(20); - - progress_bar = new QProgressBar(); - progress_bar->setRange(0, 100); - progress_bar->setValue(0); - progress_bar->setTextVisible(false); - progress_bar->setStyleSheet(R"( - QProgressBar { - background-color: #444444; - border-radius: 10px; - height: 20px; - } - QProgressBar::chunk { - background-color: #3498db; - border-radius: 10px; - } - )"); - progress_bar->setFixedHeight(40); - - // Progress text - progress_text = new QLabel(tr("0%")); - progress_text->setStyleSheet("font-size: 40px; font-weight: bold; color: white;"); - - progress_layout->addWidget(progress_text); - - content_layout->addWidget(progress_container); + // Add contribution label + contribution_label = new QLabel("0 minutes"); + contribution_label->setStyleSheet("font-size: 52px; margin-top: 10px; margin-bottom: 10px;"); + contribution_label->setWordWrap(true); + contribution_label->hide(); + content_layout->addWidget(contribution_label); // Add a separator before detailed instructions QFrame *line2 = new QFrame(); @@ -85,22 +63,49 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) // Detailed instructions at the bottom detailed_instructions = new QLabel(tr( - "Follow these steps to get your device ready:
" - "\t1. Bring your device inside and connect to a good USB-C adapter
" - "\t2. Connect to Wi-Fi
" - "\t3. Enable the toggle
" - "\t4. Leave it connected for at least 30 minutes
" + "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.
" "
" - "The toggle turns off once you restart your device. Repeat at least once a week for maximum effectiveness." - "

FAQ
" - "Does it matter how or where I drive? Nope, just drive as you normally would.
" - "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.
" - "Do I need to be on Wi-Fi? Yes.
" - "Do I need to bring the device inside? No, you can enable once you're parked, however your uploads will be limited by your car's battery.
" + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.
" + "

" + "Frequently Asked Questions

" + "Does it matter how or where I drive? Nope, just drive as you normally would.

" + "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.

" + "Does it matter which software I run? Yes, only upstream openpilot (and particular forks) are able to be used for training." )); - detailed_instructions->setStyleSheet("font-size: 40px; padding: 20px; color: #E4E4E4;"); + detailed_instructions->setStyleSheet("font-size: 40px; color: #E4E4E4;"); detailed_instructions->setWordWrap(true); content_layout->addWidget(detailed_instructions); layout->addWidget(content, 1); + + // Set up the API request for firehose stats + const QString dongle_id = QString::fromStdString(Params().get("DongleId")); + firehose_stats = new RequestRepeater(this, CommaApi::BASE_URL + "/v1/devices/" + dongle_id + "/firehose_stats", + "ApiCache_FirehoseStats", 30, true); + QObject::connect(firehose_stats, &RequestRepeater::requestDone, [=](const QString &response, bool success) { + if (success) { + QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); + QJsonObject json = doc.object(); + int count = json["firehose"].toInt(); + contribution_label->setText(tr("%1 %2 of your driving are in the training dataset so far.").arg(count).arg(count == 1 ? "segment" : "segments")); + contribution_label->show(); + } + }); + + QObject::connect(uiState(), &UIState::uiUpdate, this, &FirehosePanel::refresh); +} + +void FirehosePanel::refresh() { + auto deviceState = (*uiState()->sm)["deviceState"].getDeviceState(); + auto networkType = deviceState.getNetworkType(); + bool networkMetered = deviceState.getNetworkMetered(); + + bool is_active = !networkMetered && (networkType != cereal::DeviceState::NetworkType::NONE); + if (is_active) { + toggle_label->setText(tr("ACTIVE")); + toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: #2ecc71;"); + } else { + toggle_label->setText(tr("INACTIVE: connect to unmetered network")); + toggle_label->setStyleSheet("font-size: 60px;"); + } } diff --git a/selfdrive/ui/qt/offroad/firehose.h b/selfdrive/ui/qt/offroad/firehose.h index 7f5899f9f0..9082cb0f99 100644 --- a/selfdrive/ui/qt/offroad/firehose.h +++ b/selfdrive/ui/qt/offroad/firehose.h @@ -2,10 +2,8 @@ #include #include -#include #include -#include "selfdrive/ui/qt/widgets/controls.h" -#include "common/params.h" +#include "selfdrive/ui/qt/request_repeater.h" // Forward declarations class SettingsWindow; @@ -17,12 +15,13 @@ public: private: QVBoxLayout *layout; - - ParamControl *enable_firehose; - QFrame *progress_container; - QProgressBar *progress_bar; - QLabel *progress_text; + QLabel *detailed_instructions; - - void updateFirehoseState(bool enabled); + QLabel *contribution_label; + QLabel *toggle_label; + + RequestRepeater *firehose_stats; + +private slots: + void refresh(); }; diff --git a/system/athena/athenad.py b/system/athena/athenad.py index 27440e84a6..b36bdb103d 100755 --- a/system/athena/athenad.py +++ b/system/athena/athenad.py @@ -508,10 +508,6 @@ def getSshAuthorizedKeys() -> str: def getGithubUsername() -> str: return Params().get("GithubUsername", encoding='utf8') or '' -@dispatcher.add_method -def getFirehoseMode() -> bool: - return Params().get_bool("FirehoseMode") or False - @dispatcher.add_method def getSimInfo(): return HARDWARE.get_sim_info()