#include "starpilot/ui/screenrecorder/screenrecorder.h" #include "starpilot/ui/qt/offroad/device_settings.h" namespace { std::string cacheParamsPath() { return Path::params_cache(); } void prepareKonikServerSwitch(bool use_konik) { Params params; Params params_cache(cacheParamsPath()); if (use_konik) { params.remove("KonikDongleId"); params_cache.remove("KonikDongleId"); } else { params.remove("DongleId"); params_cache.remove("DongleId"); } } } // namespace StarPilotDevicePanel::StarPilotDevicePanel(StarPilotSettingsWindow *parent, bool forceOpen) : StarPilotListWidget(parent), parent(parent) { forceOpenDescriptions = forceOpen; ScreenRecorder *screenRecorder = new ScreenRecorder(this); screenRecorder->setVisible(false); QStackedLayout *deviceLayout = new QStackedLayout(); addItem(deviceLayout); StarPilotListWidget *deviceList = new StarPilotListWidget(this); ScrollView *devicePanel = new ScrollView(deviceList, this); deviceLayout->addWidget(devicePanel); StarPilotListWidget *deviceManagementList = new StarPilotListWidget(this); StarPilotListWidget *screenList = new StarPilotListWidget(this); ScrollView *deviceManagementPanel = new ScrollView(deviceManagementList, this); ScrollView *screenPanel = new ScrollView(screenList, this); deviceLayout->addWidget(deviceManagementPanel); deviceLayout->addWidget(screenPanel); const std::vector> deviceToggles { {"DeviceManagement", tr("Device Settings"), tr("Settings that control how the device runs, powers off, and manages driving data."), "../../starpilot/assets/toggle_icons/icon_device.png"}, {"DeviceShutdown", tr("Device Shutdown Timer"), tr("Keep the device on for the set amount of time after a drive before it shuts down automatically."), ""}, {"DisableWideRoad", tr("Disable Wide Road Camera"), QString("%1

%2").arg(tr("WARNING: Only use this if the wide camera is malfunctioning or for development purposes. This may cause instability!")).arg(tr("Requires a reboot to take effect.")), ""}, {"NoLogging", tr("Disable Logging"), QString("%1

%2").arg(tr("WARNING: This will prevent your drives from being recorded and all data will be unobtainable!")).arg(tr("Prevent the device from saving driving data.")), ""}, {"NoUploads", tr("Disable Uploads"), QString("%1

%2").arg(tr("WARNING: This will prevent your drives from being uploaded to comma connect which will impact debugging and official support from comma!")).arg(tr("Prevent the device from uploading driving data.")), ""}, {"HigherBitrate", tr("High-Quality Recording"), tr("Save drive footage in higher video quality."), ""}, {"LowVoltageShutdown", tr("Low-Voltage Cutoff"), tr("While parked, if the battery voltage falls below the set level, the device shuts down to prevent excessive battery drain."), ""}, {"IncreaseThermalLimits", tr("Raise Temperature Limits"), QString("%1

%2").arg(tr("WARNING: Running at higher temperatures may damage your device!")).arg(tr("Allow the device to run at higher temperatures before throttling or shutting down. Use only if you understand the risks!")), ""}, {"UseKonikServer", tr("Use Konik Server"), tr("Upload driving data to \"stable.konik.ai\" instead of \"connect.comma.ai\"."), ""}, {"ScreenManagement", tr("Screen Settings"), tr("Settings that control screen brightness, screen recording, and timeout duration."), "../../starpilot/assets/toggle_icons/icon_light.png"}, {"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("The screen brightness while not driving."), ""}, {"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("The screen brightness while driving."), ""}, {"ScreenRecorder", tr("Screen Recorder"), tr("Add a button to the driving screen to record the display."), ""}, {"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("How long the screen stays on after being tapped while not driving."), ""}, {"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("How long the screen stays on after being tapped while driving."), ""}, {"StandbyMode", tr("Standby Mode"), tr("Turn the screen off while driving and automatically wake it up for alerts or engagement state changes."), ""} }; for (const auto &[param, title, desc, icon] : deviceToggles) { AbstractControl *deviceToggle; if (param == "DeviceManagement") { StarPilotManageControl *deviceManagementToggle = new StarPilotManageControl(param, title, desc, icon); QObject::connect(deviceManagementToggle, &StarPilotManageControl::manageButtonClicked, [deviceLayout, deviceManagementPanel]() { deviceLayout->setCurrentWidget(deviceManagementPanel); }); deviceToggle = deviceManagementToggle; } else if (param == "DeviceShutdown") { std::map shutdownLabels; for (int i = 0; i <= 33; ++i) { shutdownLabels[i] = i == 0 ? tr("5 mins") : i <= 3 ? QString::number(i * 15) + tr(" mins") : QString::number(i - 3) + (i == 4 ? tr(" hour") : tr(" hours")); } deviceToggle = new StarPilotParamValueControl(param, title, desc, icon, 0, 33, QString(), shutdownLabels); } else if (param == "NoUploads") { std::vector uploadsToggles{"DisableOnroadUploads"}; std::vector uploadsToggleNames{tr("Disable Onroad Only")}; deviceToggle = new StarPilotButtonToggleControl(param, title, desc, icon, uploadsToggles, uploadsToggleNames); } else if (param == "LowVoltageShutdown") { deviceToggle = new StarPilotParamValueControl(param, title, desc, icon, 11.8, 12.5, tr(" volts"), std::map(), 0.1); } else if (param == "ScreenManagement") { StarPilotManageControl *screenToggle = new StarPilotManageControl(param, title, desc, icon); QObject::connect(screenToggle, &StarPilotManageControl::manageButtonClicked, [deviceLayout, screenPanel]() { deviceLayout->setCurrentWidget(screenPanel); }); deviceToggle = screenToggle; } else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") { std::map brightnessLabels; const int minBrightness = 1; for (int i = minBrightness; i <= 101; ++i) { brightnessLabels[i] = i == 101 ? tr("Auto") : QString::number(i) + "%"; } deviceToggle = new StarPilotParamValueControl(param, title, desc, icon, minBrightness, 101, QString(), brightnessLabels, 1, true); } else if (param == "ScreenRecorder") { std::vector recorderButtonNames{tr("Start Recording"), tr("Stop Recording")}; StarPilotButtonControl *recorderToggle = new StarPilotButtonControl(param, title, desc, icon, recorderButtonNames, true); QObject::connect(recorderToggle, &StarPilotButtonControl::buttonClicked, [recorderToggle, screenRecorder](int id) { if (id == 0) { recorderToggle->setCheckedButton(1); recorderToggle->setVisibleButton(0, false); recorderToggle->setVisibleButton(1, true); screenRecorder->startRecording(); } else if (id == 1) { recorderToggle->clearCheckedButtons(); recorderToggle->setVisibleButton(0, true); recorderToggle->setVisibleButton(1, false); screenRecorder->stopRecording(); } }); recorderToggle->setVisibleButton(1, false); deviceToggle = recorderToggle; } else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") { deviceToggle = new StarPilotParamValueControl(param, title, desc, icon, 5, 60, tr(" seconds"), {}, 5); } else { deviceToggle = new ParamControl(param, title, desc, icon); } toggles[param] = deviceToggle; if (deviceManagementKeys.contains(param)) { deviceManagementList->addItem(deviceToggle); } else if (screenKeys.contains(param)) { screenList->addItem(deviceToggle); } else { deviceList->addItem(deviceToggle); parentKeys.insert(param); } if (StarPilotManageControl *frogPilotManageToggle = qobject_cast(deviceToggle)) { QObject::connect(frogPilotManageToggle, &StarPilotManageControl::manageButtonClicked, [this]() { emit openSubPanel(); openDescriptions(forceOpenDescriptions, toggles); }); } QObject::connect(deviceToggle, &AbstractControl::hideDescriptionEvent, [this]() { update(); }); QObject::connect(deviceToggle, &AbstractControl::showDescriptionEvent, [this]() { update(); }); } static_cast(toggles["IncreaseThermalLimits"])->setConfirmation(true, false); static_cast(toggles["NoLogging"])->setConfirmation(true, false); static_cast(toggles["NoUploads"])->setConfirmation(true, false); QSet brightnessKeys = {"ScreenBrightness", "ScreenBrightnessOnroad"}; for (const QString &key : brightnessKeys) { StarPilotParamValueControl *paramControl = static_cast(toggles[key]); QObject::connect(paramControl, &StarPilotParamValueControl::valueChanged, [key, this](int value) { if (!started && key == "ScreenBrightness") { Hardware::set_brightness(value); } else if (started && key == "ScreenBrightnessOnroad") { Hardware::set_brightness(value); } }); } QSet forceUpdateKeys = {"NoUploads"}; for (const QString &key : forceUpdateKeys) { QObject::connect(static_cast(toggles[key]), &StarPilotButtonToggleControl::buttonClicked, this, &StarPilotDevicePanel::updateToggles); QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, this, &StarPilotDevicePanel::updateToggles); } QSet rebootKeys = {"DisableWideRoad", "HigherBitrate", "UseKonikServer"}; for (const QString &key : rebootKeys) { QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, [key, this](bool state) { QString filePath; if (key == "HigherBitrate") { filePath = "/cache/use_HD"; } else if (key == "UseKonikServer") { filePath = "/cache/use_konik"; prepareKonikServerSwitch(state); } if (!filePath.isEmpty()) { QFile toggleFile(filePath); if (state) { if (!toggleFile.exists()) { toggleFile.open(QIODevice::WriteOnly); toggleFile.close(); } } else { if (toggleFile.exists()) { toggleFile.remove(); } } } if (StarPilotConfirmationDialog::toggleReboot(this)) { Hardware::reboot(); } }); } openDescriptions(forceOpenDescriptions, toggles); QObject::connect(parent, &StarPilotSettingsWindow::closeSubPanel, [deviceLayout, devicePanel, this] { openDescriptions(forceOpenDescriptions, toggles); deviceLayout->setCurrentWidget(devicePanel); }); QObject::connect(uiState(), &UIState::uiUpdate, this, &StarPilotDevicePanel::updateState); } void StarPilotDevicePanel::showEvent(QShowEvent *event) { updateToggles(); } void StarPilotDevicePanel::updateState(const UIState &s) { if (!isVisible()) { return; } started = s.scene.started; } void StarPilotDevicePanel::updateToggles() { const bool showAllToggles = parent->showAllTogglesEnabled(); for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { toggle->setVisible(showAllToggles); } } for (auto &[key, toggle] : toggles) { if (parentKeys.contains(key)) { continue; } bool setVisible = showAllToggles || parent->tuningLevel >= parent->starpilotToggleLevels[key].toDouble(); if (!showAllToggles && key == "HigherBitrate") { setVisible &= params.getBool("DeviceManagement") && params.getBool("NoUploads") && !params.getBool("DisableOnroadUploads"); } else if (key == "UseKonikServer" && QFile("/data/not_vetted").exists()) { static_cast(toggle)->forceOn(true); } toggle->setVisible(setVisible); if (setVisible) { if (deviceManagementKeys.contains(key)) { toggles["DeviceManagement"]->setVisible(true); } else if (screenKeys.contains(key)) { toggles["ScreenManagement"]->setVisible(true); } } } openDescriptions(forceOpenDescriptions, toggles); update(); }