From f38a98fc0932abc9c6889528ae558132e2b96fc5 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 21 May 2025 20:45:47 -0700 Subject: [PATCH] Metered Wi-Fi toggle (#35293) * draft * here too * fixes * fix * ugh more fix, wifiManager is craze * more * be optimistic and let refresh happen naturally, the immediate refresh causes some paths/active connect to temporarily be unavailable selfdrive/ui/qt/network/wifi_manager.cc: DBus call error: "Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the re ply timeout expired, or the network connection was broken." selfdrive/ui/qt/network/wifi_manager.cc: DBus call error: "Object path cannot be empty" * nm is slow -- it takes 2s to commit to disk, and dbus errors are raised if you try again while previous is running (this is an ubuntu 24.04 bug) * nice experience * rm * minor * clean up * self-explanatory * rename * rm this too * revert * draft * Revert "draft" This reverts commit 15283d977880fc60b8f9732772256e8337d6ac8e. * Reapply "draft" This reverts commit 8629921b0086ca71b88746d77ec7b8d3af3cd289. * rm colors * trinary, bit more code * choose default when disabled * only if enabling, wait for disable as normal * remove original binary toggle * clean up * collapse * clean up wifimanager * update comment * lite is a word --- pyproject.toml | 2 +- selfdrive/ui/qt/network/networking.cc | 46 ++++++++++++++++++++++--- selfdrive/ui/qt/network/networking.h | 3 +- selfdrive/ui/qt/network/wifi_manager.cc | 39 +++++++++++++++++++++ selfdrive/ui/qt/network/wifi_manager.h | 7 ++++ selfdrive/ui/qt/widgets/controls.h | 4 +++ 6 files changed, 94 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 464b605f86..b9a9fcab55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -169,7 +169,7 @@ testpaths = [ [tool.codespell] quiet-level = 3 # if you've got a short variable name that's getting flagged, add it here -ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl" +ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite" builtin = "clear,rare,informal,code,names,en-GB_to_en-US" skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*" diff --git a/selfdrive/ui/qt/network/networking.cc b/selfdrive/ui/qt/network/networking.cc index 93f7ff3f21..bb914b6449 100644 --- a/selfdrive/ui/qt/network/networking.cc +++ b/selfdrive/ui/qt/network/networking.cc @@ -173,14 +173,36 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid }); list->addItem(editApnButton); - // Metered toggle + // Cellular metered toggle (prime lite or none) const bool metered = params.getBool("GsmMetered"); - meteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered connection"), "", metered); - QObject::connect(meteredToggle, &SshToggle::toggleFlipped, [=](bool state) { + cellularMeteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered cellular connection"), "", metered); + QObject::connect(cellularMeteredToggle, &SshToggle::toggleFlipped, [=](bool state) { params.putBool("GsmMetered", state); wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state); }); - list->addItem(meteredToggle); + list->addItem(cellularMeteredToggle); + + // Wi-Fi metered toggle + std::vector metered_button_texts{tr("default"), tr("metered"), tr("unmetered")}; + wifiMeteredToggle = new MultiButtonControl(tr("Wi-Fi Network Metered"), tr("Prevent large data uploads when on a metered Wi-FI connection"), "", metered_button_texts); + QObject::connect(wifiMeteredToggle, &MultiButtonControl::buttonClicked, [=](int id) { + wifiMeteredToggle->setEnabled(false); + MeteredType metered = MeteredType::UNKNOWN; + if (id == NM_METERED_YES) { + metered = MeteredType::YES; + } else if (id == NM_METERED_NO) { + metered = MeteredType::NO; + } + auto pending_call = wifi->setCurrentNetworkMetered(metered); + if (pending_call) { + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(*pending_call); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=]() { + refresh(); + watcher->deleteLater(); + }); + } + }); + list->addItem(wifiMeteredToggle); // Hidden Network hiddenNetworkButton = new ButtonControl(tr("Hidden Network"), tr("CONNECT")); @@ -211,18 +233,32 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid void AdvancedNetworking::setGsmVisible(bool visible) { roamingToggle->setVisible(visible); editApnButton->setVisible(visible); - meteredToggle->setVisible(visible); + cellularMeteredToggle->setVisible(visible); } void AdvancedNetworking::refresh() { ipLabel->setText(wifi->ipv4_address); tetheringToggle->setEnabled(true); + + if (wifi->isTetheringEnabled() || wifi->ipv4_address == "") { + wifiMeteredToggle->setEnabled(false); + wifiMeteredToggle->setCheckedButton(0); + } else if (wifi->ipv4_address != "") { + MeteredType metered = wifi->currentNetworkMetered(); + wifiMeteredToggle->setEnabled(true); + wifiMeteredToggle->setCheckedButton(static_cast(metered)); + } + update(); } void AdvancedNetworking::toggleTethering(bool enabled) { wifi->setTetheringEnabled(enabled); tetheringToggle->setEnabled(false); + if (enabled) { + wifiMeteredToggle->setEnabled(false); + wifiMeteredToggle->setCheckedButton(0); + } } // WifiUI functions diff --git a/selfdrive/ui/qt/network/networking.h b/selfdrive/ui/qt/network/networking.h index 4fd604039b..0bdf64e459 100644 --- a/selfdrive/ui/qt/network/networking.h +++ b/selfdrive/ui/qt/network/networking.h @@ -65,7 +65,8 @@ private: ToggleControl* roamingToggle; ButtonControl* editApnButton; ButtonControl* hiddenNetworkButton; - ToggleControl* meteredToggle; + ToggleControl* cellularMeteredToggle; + MultiButtonControl* wifiMeteredToggle; WifiManager* wifi = nullptr; Params params; diff --git a/selfdrive/ui/qt/network/wifi_manager.cc b/selfdrive/ui/qt/network/wifi_manager.cc index 3d4bb9e7d1..d4ad8974b0 100644 --- a/selfdrive/ui/qt/network/wifi_manager.cc +++ b/selfdrive/ui/qt/network/wifi_manager.cc @@ -353,6 +353,7 @@ void WifiManager::activateModemConnection(const QDBusObjectPath &path) { } // function matches tici/hardware.py +// FIXME: it can mistakenly show CELL when connected to WIFI NetworkType WifiManager::currentNetworkType() { auto primary_conn = call(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "PrimaryConnection"); auto primary_type = call(primary_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); @@ -372,6 +373,44 @@ NetworkType WifiManager::currentNetworkType() { return NetworkType::NONE; } +MeteredType WifiManager::currentNetworkMetered() { + MeteredType metered = MeteredType::UNKNOWN; + for (const auto &active_conn : getActiveConnections()) { + QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); + if (type == "802-11-wireless") { + QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); + if (!conn.path().isEmpty()) { + Connection settings = getConnectionSettings(conn); + int metered_prop = settings.value("connection").value("metered").toInt(); + if (metered_prop == NM_METERED_YES) { + metered = MeteredType::YES; + } else if (metered_prop == NM_METERED_NO) { + metered = MeteredType::NO; + } + } + break; + } + } + return metered; +} + +std::optional WifiManager::setCurrentNetworkMetered(MeteredType metered) { + for (const auto &active_conn : getActiveConnections()) { + QString type = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type"); + if (type == "802-11-wireless") { + if (!isTetheringEnabled()) { + QDBusObjectPath conn = call(active_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Connection"); + if (!conn.path().isEmpty()) { + Connection settings = getConnectionSettings(conn); + settings["connection"]["metered"] = static_cast(metered); + return asyncCall(conn.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Update", QVariant::fromValue(settings)); + } + } + } + } + return std::nullopt; +} + void WifiManager::updateGsmSettings(bool roaming, QString apn, bool metered) { if (!lteConnectionPath.path().isEmpty()) { bool changes = false; diff --git a/selfdrive/ui/qt/network/wifi_manager.h b/selfdrive/ui/qt/network/wifi_manager.h index e5f79c5149..cab932a388 100644 --- a/selfdrive/ui/qt/network/wifi_manager.h +++ b/selfdrive/ui/qt/network/wifi_manager.h @@ -22,6 +22,11 @@ enum class NetworkType { CELL, ETHERNET }; +enum class MeteredType { + UNKNOWN, + YES, + NO +}; typedef QMap Connection; typedef QVector IpConfig; @@ -53,6 +58,8 @@ public: bool isKnownConnection(const QString &ssid); std::optional activateWifiConnection(const QString &ssid); NetworkType currentNetworkType(); + MeteredType currentNetworkMetered(); + std::optional setCurrentNetworkMetered(MeteredType metered); void updateGsmSettings(bool roaming, QString apn, bool metered); void connect(const Network &ssid, const bool is_hidden = false, const QString &password = {}, const QString &username = {}); diff --git a/selfdrive/ui/qt/widgets/controls.h b/selfdrive/ui/qt/widgets/controls.h index ca93110e5b..3342de5324 100644 --- a/selfdrive/ui/qt/widgets/controls.h +++ b/selfdrive/ui/qt/widgets/controls.h @@ -204,6 +204,9 @@ public: QPushButton:checked:enabled { background-color: #33Ab4C; } + QPushButton:checked:disabled { + background-color: #9933Ab4C; + } QPushButton:disabled { color: #33E4E4E4; } @@ -214,6 +217,7 @@ public: for (int i = 0; i < button_texts.size(); i++) { QPushButton *button = new QPushButton(button_texts[i], this); button->setCheckable(true); + button->setChecked(i == 0); button->setStyleSheet(style); button->setMinimumWidth(minimum_button_width); hlayout->addWidget(button);