Vehicle Selector (#619)

* QFrame

* symlink for OP

* symlink dat

* search with user input

* single list is fine

* bump opendbc

* set actual fingerprint

* more precision search

* different prompt in onroad/offroad

* confirm/cancel

* set while initializing

* tldr

* show demo car

* old qt

* need that param too

* use toList

* Need to be str

* Encoding

* generate `car.CarParams.brand` in json

* start cleanup

* generate no car docs platforms

* add brand to parser

* include all dashcamOnly platforms

* pass all fields to a platform in json

* introduce `getPlatformBundle`

* extract platform from CarPlatformBundle directly

* Fix name

* do not include dashcamOnly yet

* slight cleanup

* bump opendbc

* it's a json now

* split set platform

* move loadPlatformList to sp util

* add year list to bundle

* bros

* missed header

* invoke platform selector on show event
This commit is contained in:
Jason Wen
2025-02-01 00:55:09 -05:00
committed by GitHub
parent 767880ffaf
commit 977b164dd3
23 changed files with 931 additions and 5 deletions
+1
View File
@@ -206,6 +206,7 @@ std::unordered_map<std::string, uint32_t> 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},
+4 -1
View File
@@ -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
+5 -1
View File
@@ -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")
@@ -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 <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMap>
#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("<b>%1</b><br><br>%2")
.arg(platform, offroad_msg);
QString content("<body><h2 style=\"text-align: center;\">" + tr("Vehicle Selector") + "</h2><br>"
"<p style=\"text-align: center; margin: 0 128px; font-size: 50px;\">" + msg + "</p></body>");
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<QString> 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);
}
}
@@ -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<QString, QVariantMap> platforms;
Params params;
bool offroad;
};
@@ -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;
}
@@ -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;
};
+45
View File
@@ -12,8 +12,12 @@
#include <vector>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLayoutItem>
#include <QPainterPath>
#include <QVariant>
#include "system/hardware/hw.h"
@@ -33,3 +37,44 @@ QString getUserAgent(bool sunnylink) {
std::optional<QString> getSunnylinkDongleId() {
return getParamIgnoringDefault("SunnylinkDongleId", "UnregisteredDevice");
}
QMap<QString, QVariantMap> loadPlatformList() {
QMap<QString, QVariantMap> _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;
}
+2
View File
@@ -10,9 +10,11 @@
#include <optional>
#include <vector>
#include <QMap>
#include <QPainter>
#include <QWidget>
QString getUserAgent(bool sunnylink = false);
std::optional<QString> getSunnylinkDongleId();
std::optional<QString> getParamIgnoringDefault(const std::string &param_name, const std::string &default_value);
QMap<QString, QVariantMap> loadPlatformList();
+8
View File
@@ -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)
+55
View File
@@ -674,6 +674,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation type="unfinished">إلغاء</translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">تأكيد</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">إلغاء</translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">إزالة</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Aktivieren</translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Bestätigen</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Abbrechen</translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">LÖSCHEN</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Cancelar</translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmar</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">ELIMINAR</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Annuler</translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmer</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Annuler</translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">SUPPRIMER</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -670,6 +670,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation>Ativar</translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Confirmar</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">REMOVER</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -668,6 +668,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished">Onayla</translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished">KALDIR</translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+55
View File
@@ -669,6 +669,61 @@ Pause Steering: ALC will be paused after the brake pedal is manually pressed.</s
<translation></translation>
</message>
</context>
<context>
<name>PlatformSelector</name>
<message>
<source>Vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCH</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search your vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>SEARCHING</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No vehicles found for query: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select a vehicle</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter model year (e.g., 2021) and model name (Toyota Corolla):</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Vehicle Selector</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect once the device enters offroad state.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This setting will take effect immediately.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>REMOVE</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PrimeAdWidget</name>
<message>
+1
View File
@@ -0,0 +1 @@
../../../opendbc_repo/opendbc/sunnypilot/car/car_list.json