diff --git a/common/params.cc b/common/params.cc index e7b6b104d..174d21d2e 100644 --- a/common/params.cc +++ b/common/params.cc @@ -206,6 +206,7 @@ std::unordered_map keys = { {"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"CarParamsSPCache", CLEAR_ON_MANAGER_START}, {"CarParamsSPPersistent", PERSISTENT}, + {"CarPlatformBundle", PERSISTENT}, {"EnableGithubRunner", PERSISTENT | BACKUP}, {"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION}, {"OffroadMode", CLEAR_ON_MANAGER_START}, diff --git a/opendbc_repo b/opendbc_repo index 4fa4acd98..cd086f3e5 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 4fa4acd981b681d01a0677319d5c8a7b5b1cb141 +Subproject commit cd086f3e5e0edd00bfafcd51c736b9e72b01d763 diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 90195e09c..d7827ce45 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import json import os import time import threading @@ -104,7 +105,9 @@ class Car: with car.CarParams.from_bytes(cached_params_raw) as _cached_params: cached_params = _cached_params - self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params) + fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None) + + self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params, fixed_fingerprint) interfaces.setup_car_interface_sp(self.CI.CP, self.CI.CP_SP, self.params) self.RI = get_radar_interface(self.CI.CP, self.CI.CP_SP) self.CP = self.CI.CP diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index e80ecf786..297545012 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -35,8 +35,12 @@ sunnypilot_panel_qt_src = [ "sunnypilot/qt/offroad/settings/sunnypilot/mads_settings.cc", ] +vehicle_panel_qt_src = [ + "sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc", +] + sp_widgets_src = widgets_src -sp_qt_src = qt_src + sunnypilot_panel_qt_src +sp_qt_src = qt_src + sunnypilot_panel_qt_src + vehicle_panel_qt_src sp_qt_util = qt_util Export('sp_widgets_src', 'sp_qt_src', "sp_qt_util") diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc new file mode 100644 index 000000000..c9e82fd82 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h" + +#include +#include +#include +#include + +#include "selfdrive/ui/sunnypilot/qt/util.h" + +QVariant PlatformSelector::getPlatformBundle(const QString &key) { + QString platform_bundle = QString::fromStdString(params.get("CarPlatformBundle")); + if (!platform_bundle.isEmpty()) { + QJsonDocument json = QJsonDocument::fromJson(platform_bundle.toUtf8()); + if (!json.isNull() && json.isObject()) { + return json.object().value(key).toVariant(); + } + } + return {}; +} + +PlatformSelector::PlatformSelector() : ButtonControl(tr("Vehicle"), "", "") { + platforms = loadPlatformList(); + + QObject::connect(this, &ButtonControl::clicked, [=]() { + if (text() == tr("SEARCH")) { + QString query = InputDialog::getText(tr("Search your vehicle"), this, tr("Enter model year (e.g., 2021) and model name (Toyota Corolla):"), false); + if (query.length() > 0) { + setText(tr("SEARCHING")); + setEnabled(false); + searchPlatforms(query); + refresh(offroad); + } + } else { + params.remove("CarPlatformBundle"); + refresh(offroad); + } + }); + + refresh(offroad); +} + +void PlatformSelector::refresh(bool _offroad) { + QString name = getPlatformBundle("name").toString(); + setValue(name); + setText(name.isEmpty() ? tr("SEARCH") : tr("REMOVE")); + setEnabled(true); + + offroad = _offroad; +} + +void PlatformSelector::setPlatform(const QString &platform) { + QVariantMap platform_data = platforms[platform]; + + const QString offroad_msg = offroad ? tr("This setting will take effect immediately.") : + tr("This setting will take effect once the device enters offroad state."); + const QString msg = QString("%1

%2") + .arg(platform, offroad_msg); + + QString content("

" + tr("Vehicle Selector") + "


" + "

" + msg + "

"); + + if (ConfirmationDialog(content, tr("Confirm"), tr("Cancel"), true, this).exec()) { + QJsonObject json_bundle; + json_bundle["platform"] = platform_data["platform"].toString(); + json_bundle["name"] = platform; + json_bundle["make"] = platform_data["make"].toString(); + json_bundle["brand"] = platform_data["brand"].toString(); + json_bundle["model"] = platform_data["model"].toString(); + json_bundle["package"] = platform_data["package"].toString(); + + QVariantList yearList = platform_data["year"].toList(); + QJsonArray yearArray; + for (const QVariant &year : yearList) { + yearArray.append(year.toString()); + } + json_bundle["year"] = yearArray; + + QString json_bundle_str = QString::fromUtf8(QJsonDocument(json_bundle).toJson(QJsonDocument::Compact)); + + params.put("CarPlatformBundle", json_bundle_str.toStdString()); + } +} + +void PlatformSelector::searchPlatforms(const QString &query) { + if (query.isEmpty()) { + return; + } + + QSet matched_cars; + + QString normalized_query = query.simplified().toLower(); + QStringList tokens = normalized_query.split(" ", QString::SkipEmptyParts); + + int search_year = -1; + QStringList search_terms; + + for (const QString &token : tokens) { + bool ok; + int year = token.toInt(&ok); + if (ok && year >= 1900 && year <= 2100) { + search_year = year; + } else { + search_terms << token; + } + } + + for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) { + QString platform_name = it.key(); + QVariantMap platform_data = it.value(); + + if (search_year != -1) { + QVariantList year_list = platform_data["year"].toList(); + bool year_match = false; + for (const QVariant &year_var : year_list) { + int year = year_var.toString().toInt(); + if (year == search_year) { + year_match = true; + break; + } + } + if (!year_match) continue; + } + + QString normalized_make = platform_data["make"].toString().normalized(QString::NormalizationForm_KD).toLower(); + QString normalized_model = platform_data["model"].toString().normalized(QString::NormalizationForm_KD).toLower(); + normalized_make.remove(QRegExp("[^a-zA-Z0-9\\s]")); + normalized_model.remove(QRegExp("[^a-zA-Z0-9\\s]")); + + bool all_terms_match = true; + for (const QString &term : search_terms) { + QString normalized_term = term.normalized(QString::NormalizationForm_KD).toLower(); + normalized_term.remove(QRegExp("[^a-zA-Z0-9\\s]")); + + bool term_matched = false; + + if (normalized_make.contains(normalized_term, Qt::CaseInsensitive)) { + term_matched = true; + } + + if (!term_matched) { + if (term.contains(QRegExp("[a-z]\\d|\\d[a-z]", Qt::CaseInsensitive))) { + QString clean_model = normalized_model; + QString clean_term = normalized_term; + clean_model.remove(" "); + clean_term.remove(" "); + if (clean_model.contains(clean_term, Qt::CaseInsensitive)) { + term_matched = true; + } + } else { + if (normalized_model.contains(normalized_term, Qt::CaseInsensitive)) { + term_matched = true; + } + } + } + + if (!term_matched) { + all_terms_match = false; + break; + } + } + + if (all_terms_match) { + matched_cars.insert(platform_name); + } + } + + QStringList results = matched_cars.toList(); + results.sort(); + + if (results.isEmpty()) { + ConfirmationDialog::alert(tr("No vehicles found for query: %1").arg(query), this); + return; + } + + QString selected_platform = MultiOptionDialog::getSelection(tr("Select a vehicle"), results, "", this); + + if (!selected_platform.isEmpty()) { + setPlatform(selected_platform); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h new file mode 100644 index 000000000..8a6183200 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" + +class PlatformSelector : public ButtonControl { + Q_OBJECT + +public: + PlatformSelector(); + QVariant getPlatformBundle(const QString &key); + +public slots: + void refresh(bool _offroad); + +private: + void searchPlatforms(const QString &query); + void setPlatform(const QString &platform = ""); + QMap platforms; + + Params params; + bool offroad; +}; diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc index b0e0abae9..cc3e6df18 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.cc @@ -7,5 +7,34 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h" -VehiclePanel::VehiclePanel(QWidget *parent) : QWidget(parent) { +#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h" + +VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) { + main_layout = new QStackedLayout(this); + ListWidget *list = new ListWidget(this); + + vehicleScreen = new QWidget(this); + QVBoxLayout *vlayout = new QVBoxLayout(vehicleScreen); + vlayout->setContentsMargins(50, 20, 50, 20); + + platformSelector = new PlatformSelector(); + list->addItem(platformSelector); + + ScrollViewSP *scroller = new ScrollViewSP(list, this); + vlayout->addWidget(scroller); + + QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel); + + main_layout->addWidget(vehicleScreen); + main_layout->setCurrentWidget(vehicleScreen); +} + +void VehiclePanel::showEvent(QShowEvent *event) { + updatePanel(offroad); +} + +void VehiclePanel::updatePanel(bool _offroad) { + platformSelector->refresh(_offroad); + + offroad = _offroad; } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h index 840fe1e4b..f6265da7a 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h @@ -9,9 +9,21 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" -class VehiclePanel : public QWidget { +#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h" + +class VehiclePanel : public QFrame { Q_OBJECT public: explicit VehiclePanel(QWidget *parent = nullptr); + void showEvent(QShowEvent *event) override; + +public slots: + void updatePanel(bool _offroad); + +private: + QStackedLayout* main_layout = nullptr; + QWidget* vehicleScreen = nullptr; + PlatformSelector *platformSelector = nullptr; + bool offroad; }; diff --git a/selfdrive/ui/sunnypilot/qt/util.cc b/selfdrive/ui/sunnypilot/qt/util.cc index a2bd7bde2..c729eaafd 100644 --- a/selfdrive/ui/sunnypilot/qt/util.cc +++ b/selfdrive/ui/sunnypilot/qt/util.cc @@ -12,8 +12,12 @@ #include #include +#include +#include +#include #include #include +#include #include "system/hardware/hw.h" @@ -33,3 +37,44 @@ QString getUserAgent(bool sunnylink) { std::optional getSunnylinkDongleId() { return getParamIgnoringDefault("SunnylinkDongleId", "UnregisteredDevice"); } + +QMap loadPlatformList() { + QMap _platforms; + + std::string json_data = util::read_file("../../sunnypilot/selfdrive/car/car_list.json"); + + if (json_data.empty()) { + return _platforms; + } + + QJsonParseError json_error{}; + QJsonDocument doc = QJsonDocument::fromJson(QString::fromStdString(json_data).toUtf8(), &json_error); + if (doc.isNull()) { + return _platforms; + } + + if (doc.isObject()) { + QJsonObject obj = doc.object(); + for (const QString &key : obj.keys()) { + QJsonObject attributes = obj.value(key).toObject(); + QVariantMap platform_data; + + QJsonArray yearArray = attributes.value("year").toArray(); + QVariantList yearList; + for (const QJsonValue &year : yearArray) { + yearList.append(year.toString()); + } + + platform_data["year"] = yearList; + platform_data["make"] = attributes.value("make").toString(); + platform_data["brand"] = attributes.value("brand").toString(); + platform_data["model"] = attributes.value("model").toString(); + platform_data["platform"] = attributes.value("platform").toString(); + platform_data["package"] = attributes.value("package").toString(); + + _platforms[key] = platform_data; + } + } + + return _platforms; +} diff --git a/selfdrive/ui/sunnypilot/qt/util.h b/selfdrive/ui/sunnypilot/qt/util.h index a6c58eb06..0a581579e 100644 --- a/selfdrive/ui/sunnypilot/qt/util.h +++ b/selfdrive/ui/sunnypilot/qt/util.h @@ -10,9 +10,11 @@ #include #include +#include #include #include QString getUserAgent(bool sunnylink = false); std::optional getSunnylinkDongleId(); std::optional getParamIgnoringDefault(const std::string ¶m_name, const std::string &default_value); +QMap loadPlatformList(); diff --git a/selfdrive/ui/tests/test_ui/run.py b/selfdrive/ui/tests/test_ui/run.py index 7c3a095cf..8eb5eb4fd 100644 --- a/selfdrive/ui/tests/test_ui/run.py +++ b/selfdrive/ui/tests/test_ui/run.py @@ -1,5 +1,6 @@ from collections import namedtuple import capnp +import json import pathlib import shutil import sys @@ -220,6 +221,13 @@ def setup_settings_trips(click, pm: PubMaster, scroll=None): time.sleep(UI_DELAY) def setup_settings_vehicle(click, pm: PubMaster, scroll=None): + Params().put("CarPlatformBundle", json.dumps( + { + "platform": "HONDA_CIVIC_2022", + "name": "Honda Civic 2022-24" + } + )) + setup_settings_device(click, pm) scroll(-400, 278, 862) click(278, 862) diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index 50ea0539d..4f9a1bf0b 100644 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -674,6 +674,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.إلغاء + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + تأكيد + + + Cancel + إلغاء + + + REMOVE + إزالة + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index de38c1368..fc200009d 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.Aktivieren + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + Bestätigen + + + Cancel + Abbrechen + + + REMOVE + LÖSCHEN + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_es.ts b/selfdrive/ui/translations/main_es.ts index 93ebf64b4..a5aba6531 100644 --- a/selfdrive/ui/translations/main_es.ts +++ b/selfdrive/ui/translations/main_es.ts @@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.Cancelar + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + Confirmar + + + Cancel + Cancelar + + + REMOVE + ELIMINAR + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index 00f8cfc02..26290da1c 100644 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.Annuler + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + Confirmer + + + Cancel + Annuler + + + REMOVE + SUPPRIMER + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 2792a5eff..a40a9f161 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.を有効化 + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + 確認 + + + Cancel + キャンセル + + + REMOVE + 削除 + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index c7849a290..17ac40644 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.활성화 + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + 확인 + + + Cancel + 취소 + + + REMOVE + 삭제 + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 7355c1091..b3ffdf2c4 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.Ativar + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + Confirmar + + + Cancel + Cancelar + + + REMOVE + REMOVER + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index 977c4a9e4..b62c8c109 100644 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.ยกเลิก + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + ยืนยัน + + + Cancel + ยกเลิก + + + REMOVE + ลบ + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 6e73e46d3..bc91ec18b 100644 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -668,6 +668,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed. + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + Onayla + + + Cancel + + + + REMOVE + KALDIR + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 2bd7e9ecc..0098f0142 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.启用 + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + 确认 + + + Cancel + 取消 + + + REMOVE + 删除 + + PrimeAdWidget diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 5cc18511a..5cb3b9cf9 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.啟用 + + PlatformSelector + + Vehicle + + + + SEARCH + + + + Search your vehicle + + + + SEARCHING + + + + No vehicles found for query: %1 + + + + Select a vehicle + + + + Enter model year (e.g., 2021) and model name (Toyota Corolla): + + + + Vehicle Selector + + + + This setting will take effect once the device enters offroad state. + + + + This setting will take effect immediately. + + + + Confirm + 確認 + + + Cancel + 取消 + + + REMOVE + 移除 + + PrimeAdWidget diff --git a/sunnypilot/selfdrive/car/car_list.json b/sunnypilot/selfdrive/car/car_list.json new file mode 120000 index 000000000..a4ae7b75f --- /dev/null +++ b/sunnypilot/selfdrive/car/car_list.json @@ -0,0 +1 @@ +../../../opendbc_repo/opendbc/sunnypilot/car/car_list.json \ No newline at end of file