Merge remote-tracking branch 'upstream/master' into hyundai-match-dash-speed

pull/25235/head
Shane Smiskol 3 years ago
commit b0003faca7
  1. 2
      cereal
  2. 19
      common/params.cc
  3. 2
      common/params.h
  4. 5
      common/params_pyx.pyx
  5. 8
      common/tests/test_params.py
  6. 110
      docs/CARS.md
  7. 2
      panda
  8. 8
      selfdrive/boardd/boardd.cc
  9. 2
      selfdrive/car/chrysler/values.py
  10. 2
      selfdrive/car/gm/values.py
  11. 2
      selfdrive/car/hyundai/values.py
  12. 7
      selfdrive/car/subaru/carstate.py
  13. 2
      selfdrive/car/tests/routes.py
  14. 2
      selfdrive/car/tests/test_models.py
  15. 2
      selfdrive/car/torque_data/override.yaml
  16. 14
      selfdrive/car/volkswagen/values.py
  17. 4
      selfdrive/sensord/tests/test_sensord.py
  18. 2
      selfdrive/test/process_replay/ref_commit
  19. 2
      selfdrive/ui/.gitignore
  20. 3
      selfdrive/ui/SConscript
  21. 81
      selfdrive/ui/qt/offroad/settings.cc
  22. 13
      selfdrive/ui/qt/offroad/settings.h
  23. 156
      selfdrive/ui/qt/offroad/software_settings.cc
  24. 8
      selfdrive/ui/qt/widgets/controls.cc
  25. 14
      selfdrive/ui/qt/widgets/controls.h
  26. 2
      selfdrive/ui/qt/widgets/offroad_alerts.cc
  27. 8
      selfdrive/ui/qt/widgets/ssh_keys.cc
  28. 2
      selfdrive/ui/qt/widgets/ssh_keys.h
  29. 2
      selfdrive/ui/tests/cycle_offroad_alerts.py
  30. 91
      selfdrive/ui/translations/main_ja.ts
  31. 91
      selfdrive/ui/translations/main_ko.ts
  32. 91
      selfdrive/ui/translations/main_pt-BR.ts
  33. 91
      selfdrive/ui/translations/main_zh-CHS.ts
  34. 91
      selfdrive/ui/translations/main_zh-CHT.ts
  35. 310
      selfdrive/updated.py
  36. 61
      system/camerad/cameras/camera_qcom2.cc
  37. 4
      system/camerad/cameras/camera_qcom2.h
  38. 6
      system/camerad/cameras/sensor2_i2c.h

@ -1 +1 @@
Subproject commit bd2f7fa56706bcec3c9906bd57c2ec46f0666ac5 Subproject commit 513dfc7ee001243cd68a57a9d92fe3170fc49c7d

@ -3,6 +3,7 @@
#include <dirent.h> #include <dirent.h>
#include <sys/file.h> #include <sys/file.h>
#include <algorithm>
#include <csignal> #include <csignal>
#include <unordered_map> #include <unordered_map>
@ -154,18 +155,24 @@ std::unordered_map<std::string, uint32_t> keys = {
{"PrimeType", PERSISTENT}, {"PrimeType", PERSISTENT},
{"RecordFront", PERSISTENT}, {"RecordFront", PERSISTENT},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet {"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"ReleaseNotes", PERSISTENT},
{"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"ShouldDoUpdate", CLEAR_ON_MANAGER_START}, {"ShouldDoUpdate", CLEAR_ON_MANAGER_START},
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"SshEnabled", PERSISTENT}, {"SshEnabled", PERSISTENT},
{"SubscriberInfo", PERSISTENT}, {"SubscriberInfo", PERSISTENT},
{"SwitchToBranch", CLEAR_ON_MANAGER_START},
{"TermsVersion", PERSISTENT}, {"TermsVersion", PERSISTENT},
{"Timezone", PERSISTENT}, {"Timezone", PERSISTENT},
{"TrainingVersion", PERSISTENT}, {"TrainingVersion", PERSISTENT},
{"UpdateAvailable", CLEAR_ON_MANAGER_START}, {"UpdateAvailable", CLEAR_ON_MANAGER_START},
{"UpdateFailedCount", CLEAR_ON_MANAGER_START}, {"UpdateFailedCount", CLEAR_ON_MANAGER_START},
{"UpdaterState", CLEAR_ON_MANAGER_START},
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
{"UpdaterAvailableBranches", CLEAR_ON_MANAGER_START},
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
{"Version", PERSISTENT}, {"Version", PERSISTENT},
{"VisionRadarToggle", PERSISTENT}, {"VisionRadarToggle", PERSISTENT},
{"WideCameraOnly", PERSISTENT}, {"WideCameraOnly", PERSISTENT},
@ -197,6 +204,14 @@ Params::Params(const std::string &path) {
params_path = path.empty() ? default_param_path : ensure_params_path(prefix, path); params_path = path.empty() ? default_param_path : ensure_params_path(prefix, path);
} }
std::vector<std::string> Params::allKeys() const {
std::vector<std::string> ret;
for (auto &p : keys) {
ret.push_back(p.first);
}
return ret;
}
bool Params::checkKey(const std::string &key) { bool Params::checkKey(const std::string &key) {
return keys.find(key) != keys.end(); return keys.find(key) != keys.end();
} }

@ -2,6 +2,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
enum ParamKeyType { enum ParamKeyType {
PERSISTENT = 0x02, PERSISTENT = 0x02,
@ -15,6 +16,7 @@ enum ParamKeyType {
class Params { class Params {
public: public:
Params(const std::string &path = {}); Params(const std::string &path = {});
std::vector<std::string> allKeys() const;
bool checkKey(const std::string &key); bool checkKey(const std::string &key);
ParamKeyType getKeyType(const std::string &key); ParamKeyType getKeyType(const std::string &key);
inline std::string getParamPath(const std::string &key = {}) { inline std::string getParamPath(const std::string &key = {}) {

@ -2,6 +2,7 @@
# cython: language_level = 3 # cython: language_level = 3
from libcpp cimport bool from libcpp cimport bool
from libcpp.string cimport string from libcpp.string cimport string
from libcpp.vector cimport vector
import threading import threading
cdef extern from "common/params.h": cdef extern from "common/params.h":
@ -22,6 +23,7 @@ cdef extern from "common/params.h":
bool checkKey(string) nogil bool checkKey(string) nogil
string getParamPath(string) nogil string getParamPath(string) nogil
void clearAll(ParamKeyType) void clearAll(ParamKeyType)
vector[string] allKeys()
def ensure_bytes(v): def ensure_bytes(v):
@ -99,6 +101,9 @@ cdef class Params:
cdef string key_bytes = ensure_bytes(key) cdef string key_bytes = ensure_bytes(key)
return self.p.getParamPath(key_bytes).decode("utf-8") return self.p.getParamPath(key_bytes).decode("utf-8")
def all_keys(self):
return self.p.allKeys()
def put_nonblocking(key, val, d=""): def put_nonblocking(key, val, d=""):
threading.Thread(target=lambda: Params(d).put(key, val)).start() threading.Thread(target=lambda: Params(d).put(key, val)).start()

@ -98,6 +98,14 @@ class TestParams(unittest.TestCase):
assert q.get("CarParams") is None assert q.get("CarParams") is None
assert q.get("CarParams", True) == b"1" assert q.get("CarParams", True) == b"1"
def test_params_all_keys(self):
keys = Params().all_keys()
# sanity checks
assert len(keys) > 20
assert len(keys) == len(set(keys))
assert b"CarParams" in keys
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -11,27 +11,27 @@ A supported vehicle is one that just works when you install a comma three. All s
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| |Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec| |Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Nidec|
|Acura|RDX 2019-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| |Acura|RDX 2019-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|
|Audi|A3 2014-19|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|Q2 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|Q3 2020-21|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|Q3 2020-21|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|RS3 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Audi|S3 2015-17|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| |Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
|Genesis|G70 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F| |Genesis|G70 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai F|
|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Genesis|G90 2017-18|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Genesis|G90 2017-18|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Honda|Accord 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| |Honda|Accord 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|
|Honda|Accord Hybrid 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A| |Honda|Accord Hybrid 2018-22|All|openpilot|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Honda Bosch A|
@ -80,8 +80,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| |Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Kia|EV6 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P| |Kia|EV6 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|
|Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B| |Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai B|
@ -122,9 +122,9 @@ A supported vehicle is one that just works when you install a comma three. All s
|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| |Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|
|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| |Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A| |Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Nissan A|
|Ram|1500 2019-22|Adaptive Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram| |Ram|1500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Ram|
|SEAT|Ateca 2018|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|SEAT|Leon 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
@ -135,13 +135,13 @@ A supported vehicle is one that just works when you install a comma three. All s
|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B| |Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru B|
|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Škoda|Kamiq 2021[<sup>5</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Kamiq 2021[<sup>5</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Karoq 2019-21[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Karoq 2019-21[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Kodiaq 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Kodiaq 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Octavia 2015, 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Octavia RS 2016|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Scala 2020|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Škoda|Superb 2015-18|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Škoda|Superb 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Avalon 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Avalon 2016|Toyota Safety Sense P|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
@ -182,37 +182,37 @@ A supported vehicle is one that just works when you install a comma three. All s
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Toyota|Sienna 2018-20|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Sienna 2018-20|All|Stock[<sup>3</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Volkswagen|Arteon 2018-22[<sup>7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Arteon 2018-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Arteon eHybrid 2020-22[<sup>7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Arteon eHybrid 2020-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Arteon R 2020-22[<sup>7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Arteon R 2020-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Atlas 2018-23[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Atlas 2018-23[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Atlas Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Atlas Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|California 2021[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|California 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Caravelle 2020[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Caravelle 2020[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|CC 2018-22[<sup>7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|CC 2018-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|e-Golf 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf 2015-20[<sup>8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf 2015-20[<sup>8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf Alltrack 2015-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf GTD 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf GTE 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf GTI 2015-21|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf R 2015-19[<sup>8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf R 2015-19[<sup>8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Golf SportsVan 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
|Volkswagen|Jetta 2018-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Jetta 2018-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Jetta GLI 2021-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Jetta GLI 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Passat 2015-22[<sup>6,7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Passat 2015-22[<sup>6,7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Passat Alltrack 2015-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Passat Alltrack 2015-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Passat GTE 2015-22[<sup>7,8</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Passat GTE 2015-22[<sup>7,8</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Polo 2020-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Polo 2020-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Polo GTI 2020-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Polo GTI 2020-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|T-Cross 2021[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|T-Cross 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|T-Roc 2021[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|T-Roc 2021[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Taos 2022[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Taos 2022[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Teramont 2018-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Teramont 2018-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Teramont Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Teramont Cross Sport 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Teramont X 2021-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Teramont X 2021-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Tiguan 2019-22[<sup>7</sup>](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Tiguan 2019-22[<sup>7</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Volkswagen|Touran 2017|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW| |Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|VW|
<a id="footnotes"></a> <a id="footnotes"></a>
<sup>1</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br /> <sup>1</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />

@ -1 +1 @@
Subproject commit 19983f13b37518298a3c282d5069c090b68f6864 Subproject commit 046fd58e8d64c58ed80769fcbec5ac2417a04c71

@ -346,14 +346,14 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
auto ps = pss[i]; auto ps = pss[i];
ps.setUptime(health.uptime_pkt); ps.setUptime(health.uptime_pkt);
ps.setBlockedCnt(health.blocked_msg_cnt_pkt); ps.setSafetyTxBlocked(health.safety_tx_blocked_pkt);
ps.setSafetyRxInvalid(health.safety_rx_invalid_pkt);
ps.setIgnitionLine(health.ignition_line_pkt); ps.setIgnitionLine(health.ignition_line_pkt);
ps.setIgnitionCan(health.ignition_can_pkt); ps.setIgnitionCan(health.ignition_can_pkt);
ps.setControlsAllowed(health.controls_allowed_pkt); ps.setControlsAllowed(health.controls_allowed_pkt);
ps.setGasInterceptorDetected(health.gas_interceptor_detected_pkt); ps.setGasInterceptorDetected(health.gas_interceptor_detected_pkt);
ps.setCanRxErrs(health.can_rx_errs_pkt); ps.setTxBufferOverflow(health.tx_buffer_overflow_pkt);
ps.setCanSendErrs(health.can_send_errs_pkt); ps.setRxBufferOverflow(health.rx_buffer_overflow_pkt);
ps.setCanFwdErrs(health.can_fwd_errs_pkt);
ps.setGmlanSendErrs(health.gmlan_send_errs_pkt); ps.setGmlanSendErrs(health.gmlan_send_errs_pkt);
ps.setPandaType(panda->hw_type); ps.setPandaType(panda->hw_type);
ps.setSafetyModel(cereal::CarParams::SafetyModel(health.safety_mode_pkt)); ps.setSafetyModel(cereal::CarParams::SafetyModel(health.safety_mode_pkt));

@ -52,7 +52,7 @@ RAM_CARS = RAM_DT | RAM_HD
@dataclass @dataclass
class ChryslerCarInfo(CarInfo): class ChryslerCarInfo(CarInfo):
package: str = "Adaptive Cruise Control" package: str = "Adaptive Cruise Control (ACC)"
harness: Enum = Harness.fca harness: Enum = Harness.fca
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {

@ -71,7 +71,7 @@ class Footnote(Enum):
@dataclass @dataclass
class GMCarInfo(CarInfo): class GMCarInfo(CarInfo):
package: str = "Adaptive Cruise Control" package: str = "Adaptive Cruise Control (ACC)"
harness: Enum = Harness.obd_ii harness: Enum = Harness.obd_ii
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II]) footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II])

@ -29,6 +29,8 @@ class CarControllerParams:
self.STEER_DRIVER_ALLOWANCE = 250 self.STEER_DRIVER_ALLOWANCE = 250
self.STEER_DRIVER_MULTIPLIER = 2 self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_THRESHOLD = 250 self.STEER_THRESHOLD = 250
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
# To determine the limit for your car, find the maximum value that the stock LKAS will request. # To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list. # If the max stock LKAS request is <384, add your car to this list.

@ -32,9 +32,8 @@ class CarState(CarStateBase):
cp_wheels.vl["Wheel_Speeds"]["RR"], cp_wheels.vl["Wheel_Speeds"]["RR"],
) )
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
# Kalman filter, even though Subaru raw wheel speed is heaviliy filtered by default
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw < 0.01 ret.standstill = ret.vEgoRaw == 0
# continuous blinker signals for assisted lane change # continuous blinker signals for assisted lane change
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["Dashlights"]["LEFT_BLINKER"], ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["Dashlights"]["LEFT_BLINKER"],
@ -50,7 +49,7 @@ class CarState(CarStateBase):
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80 steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
@ -313,4 +312,4 @@ class CarState(CarStateBase):
checks += CarState.get_global_es_distance_signals()[1] checks += CarState.get_global_es_distance_signals()[1]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 1) return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 1)
return None return None

@ -194,7 +194,7 @@ routes = [
CarTestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER), CarTestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER),
CarTestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA), CarTestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA),
CarTestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020), CarTestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020),
CarTestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3), CarTestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=10),
CarTestRoute("c56e69bbc74b8fad|2022-08-18--09-43-51", SUBARU.LEGACY, segment=3), CarTestRoute("c56e69bbc74b8fad|2022-08-18--09-43-51", SUBARU.LEGACY, segment=3),
# Pre-global, dashcam # Pre-global, dashcam
CarTestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL), CarTestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL),

@ -234,7 +234,7 @@ class TestCarModelBase(unittest.TestCase):
checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev() checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev()
checks['cruiseState'] += CS.cruiseState.enabled and not CS.cruiseState.available checks['cruiseState'] += CS.cruiseState.enabled and not CS.cruiseState.available
if self.CP.carName not in ("hyundai", "volkswagen", "subaru", "gm", "body"): if self.CP.carName not in ("hyundai", "volkswagen", "gm", "body"):
# TODO: fix standstill mismatches for other makes # TODO: fix standstill mismatches for other makes
checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving() checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving()

@ -21,7 +21,7 @@ FORD FOCUS 4TH GEN: [.nan, 1.5, .nan]
COMMA BODY: [.nan, 1000, .nan] COMMA BODY: [.nan, 1000, .nan]
# Totally new cars # Totally new cars
KIA EV6 2022: [3.5, 2.5, 0.0] KIA EV6 2022: [3.5, 3.0, 0.0]
RAM 1500 5TH GEN: [2.0, 2.0, 0.0] RAM 1500 5TH GEN: [2.0, 2.0, 0.0]
RAM HD 5TH GEN: [1.4, 1.4, 0.0] RAM HD 5TH GEN: [1.4, 1.4, 0.0]
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]

@ -164,7 +164,7 @@ class Footnote(Enum):
@dataclass @dataclass
class VWCarInfo(CarInfo): class VWCarInfo(CarInfo):
package: str = "Driver Assistance" package: str = "Adaptive Cruise Control (ACC) & Lane Assist"
harness: Enum = Harness.vw harness: Enum = Harness.vw
@ -216,13 +216,13 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
], ],
CAR.TROC_MK1: VWCarInfo("Volkswagen T-Roc 2021", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), CAR.TROC_MK1: VWCarInfo("Volkswagen T-Roc 2021", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533),
CAR.AUDI_A3_MK3: [ CAR.AUDI_A3_MK3: [
VWCarInfo("Audi A3 2014-19", "ACC + Lane Assist"), VWCarInfo("Audi A3 2014-19"),
VWCarInfo("Audi A3 Sportback e-tron 2017-18", "ACC + Lane Assist"), VWCarInfo("Audi A3 Sportback e-tron 2017-18"),
VWCarInfo("Audi RS3 2018", "ACC + Lane Assist"), VWCarInfo("Audi RS3 2018"),
VWCarInfo("Audi S3 2015-17", "ACC + Lane Assist"), VWCarInfo("Audi S3 2015-17"),
], ],
CAR.AUDI_Q2_MK1: VWCarInfo("Audi Q2 2018", "ACC + Lane Assist"), CAR.AUDI_Q2_MK1: VWCarInfo("Audi Q2 2018"),
CAR.AUDI_Q3_MK2: VWCarInfo("Audi Q3 2020-21", "ACC + Lane Assist"), CAR.AUDI_Q3_MK2: VWCarInfo("Audi Q3 2020-21"),
CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"), CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"),
CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"), CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"),
CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.KAMIQ]), CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.KAMIQ]),

@ -121,6 +121,10 @@ class TestSensord(unittest.TestCase):
cls.events = read_sensor_events(5) cls.events = read_sensor_events(5)
managed_processes["sensord"].stop() managed_processes["sensord"].stop()
def tearDown(self):
# interrupt check might leave sensord running
managed_processes["sensord"].stop()
def test_sensors_present(self): def test_sensors_present(self):
# verify correct sensors configuration # verify correct sensors configuration

@ -1 +1 @@
3ad478bf44f50815d05acc5b12ff2f01a6cb42ff ef5395e5f36550d2b485216eee5406bf6062e9c9

@ -1,6 +1,8 @@
moc_* moc_*
*.moc *.moc
translations/main_test_en.*
_mui _mui
watch3 watch3
installer/installers/* installer/installers/*

@ -56,7 +56,8 @@ qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs)
# build main UI # build main UI
qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc", qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
"qt/offroad/onboarding.cc", "qt/offroad/driverview.cc"] "qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc",
"qt/offroad/driverview.cc"]
qt_env.Program("_ui", qt_src + [asset_obj], LIBS=qt_libs) qt_env.Program("_ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('test'): if GetOption('test'):
qt_src.remove("main.cc") # replaced by test_runner qt_src.remove("main.cc") # replaced by test_runner

@ -159,7 +159,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(dcamBtn); addItem(dcamBtn);
auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), ""); auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), "");
connect(resetCalibBtn, &ButtonControl::showDescription, this, &DevicePanel::updateCalibDescription); connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription);
connect(resetCalibBtn, &ButtonControl::clicked, [&]() { connect(resetCalibBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), this)) { if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), this)) {
params.remove("CalibrationParams"); params.remove("CalibrationParams");
@ -282,85 +282,6 @@ void DevicePanel::poweroff() {
} }
} }
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
gitBranchLbl = new LabelControl(tr("Git Branch"));
gitCommitLbl = new LabelControl(tr("Git Commit"));
osVersionLbl = new LabelControl(tr("OS Version"));
versionLbl = new LabelControl(tr("Version"), "", QString::fromStdString(params.get("ReleaseNotes")).trimmed());
lastUpdateLbl = new LabelControl(tr("Last Update Check"), "", tr("The last time openpilot successfully checked for an update. The updater only runs while the car is off."));
updateBtn = new ButtonControl(tr("Check for Update"), "");
connect(updateBtn, &ButtonControl::clicked, [=]() {
if (params.getBool("IsOffroad")) {
fs_watch->addPath(QString::fromStdString(params.getParamPath("LastUpdateTime")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateFailedCount")));
updateBtn->setText(tr("CHECKING"));
updateBtn->setEnabled(false);
}
std::system("pkill -1 -f selfdrive.updated");
});
connect(uiState(), &UIState::offroadTransition, updateBtn, &QPushButton::setEnabled);
branchSwitcherBtn = new ButtonControl(tr("Switch Branch"), tr("ENTER"), tr("The new branch will be pulled the next time the updater runs."));
connect(branchSwitcherBtn, &ButtonControl::clicked, [=]() {
QString branch = InputDialog::getText(tr("Enter branch name"), this, tr("The new branch will be pulled the next time the updater runs."),
false, -1, QString::fromStdString(params.get("SwitchToBranch")));
if (branch.isEmpty()) {
params.remove("SwitchToBranch");
} else {
params.put("SwitchToBranch", branch.toStdString());
}
std::system("pkill -1 -f selfdrive.updated");
});
connect(uiState(), &UIState::offroadTransition, branchSwitcherBtn, &QPushButton::setEnabled);
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
connect(uninstallBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), this)) {
params.putBool("DoUninstall", true);
}
});
connect(uiState(), &UIState::offroadTransition, uninstallBtn, &QPushButton::setEnabled);
QWidget *widgets[] = {versionLbl, lastUpdateLbl, updateBtn, branchSwitcherBtn, gitBranchLbl, gitCommitLbl, osVersionLbl, uninstallBtn};
for (QWidget* w : widgets) {
if (w == branchSwitcherBtn && params.getBool("IsTestedBranch")) {
continue;
}
addItem(w);
}
fs_watch = new QFileSystemWatcher(this);
QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) {
if (path.contains("UpdateFailedCount") && std::atoi(params.get("UpdateFailedCount").c_str()) > 0) {
lastUpdateLbl->setText(tr("failed to fetch update"));
updateBtn->setText(tr("CHECK"));
updateBtn->setEnabled(true);
} else if (path.contains("LastUpdateTime")) {
updateLabels();
}
});
}
void SoftwarePanel::showEvent(QShowEvent *event) {
updateLabels();
}
void SoftwarePanel::updateLabels() {
QString lastUpdate = "";
auto tm = params.get("LastUpdateTime");
if (!tm.empty()) {
lastUpdate = timeAgo(QDateTime::fromString(QString::fromStdString(tm + "Z"), Qt::ISODate));
}
versionLbl->setText(getBrandVersion());
lastUpdateLbl->setText(lastUpdate);
updateBtn->setText(tr("CHECK"));
updateBtn->setEnabled(true);
gitBranchLbl->setText(QString::fromStdString(params.get("GitBranch")));
gitCommitLbl->setText(QString::fromStdString(params.get("GitCommit")).left(10));
osVersionLbl->setText(QString::fromStdString(Hardware::get_os_version()).trimmed());
}
void SettingsWindow::showEvent(QShowEvent *event) { void SettingsWindow::showEvent(QShowEvent *event) {
panel_widget->setCurrentIndex(0); panel_widget->setCurrentIndex(0);
nav_btns->buttons()[0]->setChecked(true); nav_btns->buttons()[0]->setChecked(true);

@ -71,14 +71,15 @@ public:
private: private:
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
void updateLabels(); void updateLabels();
void checkForUpdates();
LabelControl *gitBranchLbl; bool is_onroad = false;
LabelControl *gitCommitLbl;
LabelControl *osVersionLbl; QLabel *onroadLbl;
LabelControl *versionLbl; LabelControl *versionLbl;
LabelControl *lastUpdateLbl; ButtonControl *installBtn;
ButtonControl *updateBtn; ButtonControl *downloadBtn;
ButtonControl *branchSwitcherBtn; ButtonControl *targetBranchBtn;
Params params; Params params;
QFileSystemWatcher *fs_watch; QFileSystemWatcher *fs_watch;

@ -0,0 +1,156 @@
#include "selfdrive/ui/qt/offroad/settings.h"
#include <cassert>
#include <cmath>
#include <string>
#include <QDebug>
#include <QLabel>
#include "common/params.h"
#include "common/util.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "system/hardware/hw.h"
void SoftwarePanel::checkForUpdates() {
std::system("pkill -SIGUSR1 -f selfdrive.updated");
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl);
// current version
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
downloadBtn->setEnabled(false);
if (downloadBtn->text() == tr("CHECK")) {
checkForUpdates();
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
}
});
addItem(downloadBtn);
// install update btn
installBtn = new ButtonControl(tr("Install Update"), tr("INSTALL"));
connect(installBtn, &ButtonControl::clicked, [=]() {
installBtn->setEnabled(false);
params.putBool("DoShutdown", true);
});
addItem(installBtn);
// branch selecting
targetBranchBtn = new ButtonControl(tr("Target Branch"), tr("SELECT"));
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch");
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
for (QString b : {current.c_str(), "devel-staging", "devel", "master-ci", "master"}) {
auto i = branches.indexOf(b);
if (i >= 0) {
branches.removeAt(i);
branches.insert(0, b);
}
}
QString cur = QString::fromStdString(params.get("UpdaterTargetBranch"));
QString selection = MultiOptionDialog::getSelection(tr("Select a branch"), branches, cur, this);
if (!selection.isEmpty()) {
params.put("UpdaterTargetBranch", selection.toStdString());
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
checkForUpdates();
}
});
if (!params.getBool("IsTestedBranch")) {
addItem(targetBranchBtn);
}
// uninstall button
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
connect(uninstallBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), this)) {
params.putBool("DoUninstall", true);
}
});
addItem(uninstallBtn);
fs_watch = new QFileSystemWatcher(this);
QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) {
updateLabels();
});
connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
is_onroad = !offroad;
updateLabels();
});
updateLabels();
}
void SoftwarePanel::showEvent(QShowEvent *event) {
// nice for testing on PC
installBtn->setEnabled(true);
updateLabels();
}
void SoftwarePanel::updateLabels() {
// add these back in case the files got removed
fs_watch->addPath(QString::fromStdString(params.getParamPath("LastUpdateTime")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateFailedCount")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdaterState")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateAvailable")));
if (!isVisible()) {
return;
}
// updater only runs offroad
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
bool failed = std::atoi(params.get("UpdateFailedCount").c_str()) > 0;
if (updater_state != "idle") {
downloadBtn->setEnabled(false);
downloadBtn->setValue(updater_state);
} else {
if (failed) {
downloadBtn->setText("CHECK");
downloadBtn->setValue("failed to check for update");
} else if (params.getBool("UpdaterFetchAvailable")) {
downloadBtn->setText("DOWNLOAD");
downloadBtn->setValue("update available");
} else {
QString lastUpdate = "never";
auto tm = params.get("LastUpdateTime");
if (!tm.empty()) {
lastUpdate = timeAgo(QDateTime::fromString(QString::fromStdString(tm + "Z"), Qt::ISODate));
}
downloadBtn->setText("CHECK");
downloadBtn->setValue("up to date, last checked " + lastUpdate);
}
downloadBtn->setEnabled(true);
}
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
// current + new versions
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")).left(40));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")).left(35));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
update();
}

@ -42,6 +42,12 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left"); title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left");
hlayout->addWidget(title_label); hlayout->addWidget(title_label);
// value next to control button
value = new QLabel();
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
value->setStyleSheet("color: #aaaaaa");
hlayout->addWidget(value);
main_layout->addLayout(hlayout); main_layout->addLayout(hlayout);
// description // description
@ -54,7 +60,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
connect(title_label, &QPushButton::clicked, [=]() { connect(title_label, &QPushButton::clicked, [=]() {
if (!description->isVisible()) { if (!description->isVisible()) {
emit showDescription(); emit showDescriptionEvent();
} }
if (!description->text().isEmpty()) { if (!description->text().isEmpty()) {

@ -45,8 +45,17 @@ public:
title_label->setText(title); title_label->setText(title);
} }
void setValue(const QString &val) {
value->setText(val);
}
public slots:
void showDescription() {
description->setVisible(true);
};
signals: signals:
void showDescription(); void showDescriptionEvent();
protected: protected:
AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr); AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
@ -54,6 +63,9 @@ protected:
QHBoxLayout *hlayout; QHBoxLayout *hlayout;
QPushButton *title_label; QPushButton *title_label;
private:
QLabel *value;
QLabel *description = nullptr; QLabel *description = nullptr;
}; };

@ -112,7 +112,7 @@ UpdateAlert::UpdateAlert(QWidget *parent) : AbstractAlert(true, parent) {
bool UpdateAlert::refresh() { bool UpdateAlert::refresh() {
bool updateAvailable = params.getBool("UpdateAvailable"); bool updateAvailable = params.getBool("UpdateAvailable");
if (updateAvailable) { if (updateAvailable) {
releaseNotes->setText(params.get("ReleaseNotes").c_str()); releaseNotes->setText(params.get("UpdaterNewReleaseNotes").c_str());
} }
return updateAvailable; return updateAvailable;
} }

@ -5,10 +5,6 @@
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.")) { SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.")) {
username_label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
username_label.setStyleSheet("color: #aaaaaa");
hlayout->insertWidget(1, &username_label);
QObject::connect(this, &ButtonControl::clicked, [=]() { QObject::connect(this, &ButtonControl::clicked, [=]() {
if (text() == tr("ADD")) { if (text() == tr("ADD")) {
QString username = InputDialog::getText(tr("Enter your GitHub username"), this); QString username = InputDialog::getText(tr("Enter your GitHub username"), this);
@ -30,10 +26,10 @@ SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This g
void SshControl::refresh() { void SshControl::refresh() {
QString param = QString::fromStdString(params.get("GithubSshKeys")); QString param = QString::fromStdString(params.get("GithubSshKeys"));
if (param.length()) { if (param.length()) {
username_label.setText(QString::fromStdString(params.get("GithubUsername"))); setValue(QString::fromStdString(params.get("GithubUsername")));
setText(tr("REMOVE")); setText(tr("REMOVE"));
} else { } else {
username_label.setText(""); setValue("");
setText(tr("ADD")); setText(tr("ADD"));
} }
setEnabled(true); setEnabled(true);

@ -27,8 +27,6 @@ public:
private: private:
Params params; Params params;
QLabel username_label;
void refresh(); void refresh();
void getUserKeys(const QString &username); void getUserKeys(const QString &username);
}; };

@ -20,7 +20,7 @@ if __name__ == "__main__":
params.put_bool("UpdateAvailable", True) params.put_bool("UpdateAvailable", True)
r = open(os.path.join(BASEDIR, "RELEASES.md")).read() r = open(os.path.join(BASEDIR, "RELEASES.md")).read()
r = r[:r.find('\n\n')] # Slice latest release notes r = r[:r.find('\n\n')] # Slice latest release notes
params.put("ReleaseNotes", r + "\n") params.put("UpdaterNewReleaseNotes", r + "\n")
time.sleep(t) time.sleep(t)
params.put_bool("UpdateAvailable", False) params.put_bool("UpdateAvailable", False)

@ -193,7 +193,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+4"/> <location line="+3"/>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation> https://connect.comma.ai</translation>
</message> </message>
<message> <message>
<location line="+57"/> <location line="+58"/>
<source>No home <source>No home
location set</source> location set</source>
<translation> <translation>
@ -432,7 +432,7 @@ location set</source>
</translation> </translation>
</message> </message>
<message> <message>
<location line="+113"/> <location line="+120"/>
<source>no recent destinations</source> <source>no recent destinations</source>
<translation></translation> <translation></translation>
</message> </message>
@ -718,7 +718,7 @@ location set</source>
<context> <context>
<name>SettingsWindow</name> <name>SettingsWindow</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="+101"/> <location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
@ -983,68 +983,47 @@ location set</source>
<context> <context>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-130"/> <location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Git Branch</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Git </translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+5"/>
<source>Git Commit</source> <source>Current Version</source>
<translation>Git </translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation>OS </translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+4"/>
<source>Version</source> <source>Download</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+12"/>
<source>Last Update Check</source> <source>Install Update</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source> <source>INSTALL</source>
<translation>openpilotが最後にアップデートの確認に成功してからの時間です</translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message> </message>
<message> <message>
<location line="+7"/> <location line="+8"/>
<source>Switch Branch</source> <source>Target Branch</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>ENTER</source> <source>SELECT</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+13"/>
<location line="+2"/> <source>Select a branch</source>
<source>The new branch will be pulled the next time the updater runs.</source> <translation type="unfinished"></translation>
<translation>updater </translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation></translation>
</message> </message>
<message> <message>
<location line="+11"/> <location line="+12"/>
<source>UNINSTALL</source> <source>UNINSTALL</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1059,13 +1038,8 @@ location set</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+17"/> <location line="-47"/>
<source>failed to fetch update</source> <location line="+3"/>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<source>CHECK</source> <source>CHECK</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1083,7 +1057,7 @@ location set</source>
<translation>警告: これはGitHub SSH GitHub GitHub </translation> <translation>警告: これはGitHub SSH GitHub GitHub </translation>
</message> </message>
<message> <message>
<location line="+6"/> <location line="+2"/>
<location line="+24"/> <location line="+24"/>
<source>ADD</source> <source>ADD</source>
<translation></translation> <translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context> <context>
<name>TogglesPanel</name> <name>TogglesPanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-324"/> <location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source> <source>Enable openpilot</source>
<translation>openpilot </translation> <translation>openpilot </translation>
</message> </message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name> <name>WifiUI</name>
<message> <message>
<location filename="../qt/offroad/networking.cc" line="+113"/> <location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source> <source>Scanning for networks...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>
<message> <message>
<location line="+26"/> <location line="+80"/>
<source>CONNECTING...</source> <source>CONNECTING...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>

@ -193,7 +193,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+4"/> <location line="+3"/>
<source>Select a language</source> <source>Select a language</source>
<translation> </translation> <translation> </translation>
</message> </message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation> https://connect.comma.ai</translation>
</message> </message>
<message> <message>
<location line="+57"/> <location line="+58"/>
<source>No home <source>No home
location set</source> location set</source>
<translation> <translation>
@ -432,7 +432,7 @@ location set</source>
</translation> </translation>
</message> </message>
<message> <message>
<location line="+113"/> <location line="+120"/>
<source>no recent destinations</source> <source>no recent destinations</source>
<translation> </translation> <translation> </translation>
</message> </message>
@ -718,7 +718,7 @@ location set</source>
<context> <context>
<name>SettingsWindow</name> <name>SettingsWindow</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="+101"/> <location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
@ -983,68 +983,47 @@ location set</source>
<context> <context>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-130"/> <location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Git Branch</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Git </translation> <translation> .</translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git </translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+5"/>
<source>OS Version</source> <source>Current Version</source>
<translation>OS </translation> <translation> </translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+4"/>
<source>Version</source> <source>Download</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+12"/>
<source>Last Update Check</source> <source>Install Update</source>
<translation> </translation> <translation> </translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source> <source>INSTALL</source>
<translation> openpilot이 . .</translation> <translation></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation> </translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation> </translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+8"/>
<source>ENTER</source> <source>Target Branch</source>
<translation></translation> <translation> </translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<location line="+2"/> <source>SELECT</source>
<source>The new branch will be pulled the next time the updater runs.</source> <translation></translation>
<translation> .</translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+13"/>
<source>Enter branch name</source> <source>Select a branch</source>
<translation> </translation> <translation> </translation>
</message> </message>
<message> <message>
<location line="+11"/> <location line="+12"/>
<source>UNINSTALL</source> <source>UNINSTALL</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1059,13 +1038,8 @@ location set</source>
<translation>?</translation> <translation>?</translation>
</message> </message>
<message> <message>
<location line="+17"/> <location line="-47"/>
<source>failed to fetch update</source> <location line="+3"/>
<translation> </translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<source>CHECK</source> <source>CHECK</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1083,7 +1057,7 @@ location set</source>
<translation>경고: 허용으로 GitHub SSH . GitHub ID . comma에서는 GitHub ID를 .</translation> <translation>경고: 허용으로 GitHub SSH . GitHub ID . comma에서는 GitHub ID를 .</translation>
</message> </message>
<message> <message>
<location line="+6"/> <location line="+2"/>
<location line="+24"/> <location line="+24"/>
<source>ADD</source> <source>ADD</source>
<translation></translation> <translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context> <context>
<name>TogglesPanel</name> <name>TogglesPanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-324"/> <location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source> <source>Enable openpilot</source>
<translation>openpilot </translation> <translation>openpilot </translation>
</message> </message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name> <name>WifiUI</name>
<message> <message>
<location filename="../qt/offroad/networking.cc" line="+113"/> <location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source> <source>Scanning for networks...</source>
<translation> ...</translation> <translation> ...</translation>
</message> </message>
<message> <message>
<location line="+26"/> <location line="+80"/>
<source>CONNECTING...</source> <source>CONNECTING...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>

@ -193,7 +193,7 @@
<translation>ALTERAR</translation> <translation>ALTERAR</translation>
</message> </message>
<message> <message>
<location line="+4"/> <location line="+3"/>
<source>Select a language</source> <source>Select a language</source>
<translation>Selecione o Idioma</translation> <translation>Selecione o Idioma</translation>
</message> </message>
@ -419,7 +419,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
uma assinatura prime Inscreva-se agora: https://connect.comma.ai</translation> uma assinatura prime Inscreva-se agora: https://connect.comma.ai</translation>
</message> </message>
<message> <message>
<location line="+57"/> <location line="+58"/>
<source>No home <source>No home
location set</source> location set</source>
<translation>Sem local <translation>Sem local
@ -433,7 +433,7 @@ location set</source>
trabalho definido</translation> trabalho definido</translation>
</message> </message>
<message> <message>
<location line="+113"/> <location line="+120"/>
<source>no recent destinations</source> <source>no recent destinations</source>
<translation>sem destinos recentes</translation> <translation>sem destinos recentes</translation>
</message> </message>
@ -722,7 +722,7 @@ trabalho definido</translation>
<context> <context>
<name>SettingsWindow</name> <name>SettingsWindow</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="+101"/> <location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
@ -987,68 +987,47 @@ trabalho definido</translation>
<context> <context>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-130"/> <location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Git Branch</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Git Branch</translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Último Commit</translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+5"/>
<source>OS Version</source> <source>Current Version</source>
<translation>Versão do Sistema</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+4"/>
<source>Version</source> <source>Download</source>
<translation>Versão</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+12"/>
<source>Last Update Check</source> <source>Install Update</source>
<translation>Verificação da última atualização</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source> <source>INSTALL</source>
<translation>A última vez que o openpilot verificou com sucesso uma atualização. O atualizador funciona com o carro desligado.</translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation>Verifique atualizações</translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation>VERIFICANDO</translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation>Alterar Branch</translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+8"/>
<source>ENTER</source> <source>Target Branch</source>
<translation>INSERIR</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<location line="+2"/> <source>SELECT</source>
<source>The new branch will be pulled the next time the updater runs.</source> <translation type="unfinished"></translation>
<translation>A nova branch será aplicada ao verificar atualizações.</translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+13"/>
<source>Enter branch name</source> <source>Select a branch</source>
<translation>Inserir o nome da branch</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+11"/> <location line="+12"/>
<source>UNINSTALL</source> <source>UNINSTALL</source>
<translation>DESINSTALAR</translation> <translation>DESINSTALAR</translation>
</message> </message>
@ -1063,13 +1042,8 @@ trabalho definido</translation>
<translation>Tem certeza que quer desinstalar?</translation> <translation>Tem certeza que quer desinstalar?</translation>
</message> </message>
<message> <message>
<location line="+17"/> <location line="-47"/>
<source>failed to fetch update</source> <location line="+3"/>
<translation>falha ao buscar atualização</translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<source>CHECK</source> <source>CHECK</source>
<translation>VERIFICAR</translation> <translation>VERIFICAR</translation>
</message> </message>
@ -1087,7 +1061,7 @@ trabalho definido</translation>
<translation>Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub.</translation> <translation>Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub.</translation>
</message> </message>
<message> <message>
<location line="+6"/> <location line="+2"/>
<location line="+24"/> <location line="+24"/>
<source>ADD</source> <source>ADD</source>
<translation>ADICIONAR</translation> <translation>ADICIONAR</translation>
@ -1157,7 +1131,7 @@ trabalho definido</translation>
<context> <context>
<name>TogglesPanel</name> <name>TogglesPanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-324"/> <location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source> <source>Enable openpilot</source>
<translation>Ativar openpilot</translation> <translation>Ativar openpilot</translation>
</message> </message>
@ -1294,12 +1268,11 @@ trabalho definido</translation>
<name>WifiUI</name> <name>WifiUI</name>
<message> <message>
<location filename="../qt/offroad/networking.cc" line="+113"/> <location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source> <source>Scanning for networks...</source>
<translation>Procurando redes...</translation> <translation>Procurando redes...</translation>
</message> </message>
<message> <message>
<location line="+26"/> <location line="+80"/>
<source>CONNECTING...</source> <source>CONNECTING...</source>
<translation>CONECTANDO...</translation> <translation>CONECTANDO...</translation>
</message> </message>

@ -193,7 +193,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+4"/> <location line="+3"/>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation> https://connect.comma.ai</translation>
</message> </message>
<message> <message>
<location line="+57"/> <location line="+58"/>
<source>No home <source>No home
location set</source> location set</source>
<translation></translation> <translation></translation>
@ -430,7 +430,7 @@ location set</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+113"/> <location line="+120"/>
<source>no recent destinations</source> <source>no recent destinations</source>
<translation></translation> <translation></translation>
</message> </message>
@ -716,7 +716,7 @@ location set</source>
<context> <context>
<name>SettingsWindow</name> <name>SettingsWindow</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="+101"/> <location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
@ -981,68 +981,47 @@ location set</source>
<context> <context>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-130"/> <location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Git Branch</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Git Branch</translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git Commit</translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+5"/>
<source>OS Version</source> <source>Current Version</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+4"/>
<source>Version</source> <source>Download</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+12"/>
<source>Last Update Check</source> <source>Install Update</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source> <source>INSTALL</source>
<translation></translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+8"/>
<source>ENTER</source> <source>Target Branch</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<location line="+2"/> <source>SELECT</source>
<source>The new branch will be pulled the next time the updater runs.</source> <translation type="unfinished"></translation>
<translation></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+13"/>
<source>Enter branch name</source> <source>Select a branch</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+11"/> <location line="+12"/>
<source>UNINSTALL</source> <source>UNINSTALL</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1057,13 +1036,8 @@ location set</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+17"/> <location line="-47"/>
<source>failed to fetch update</source> <location line="+3"/>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<source>CHECK</source> <source>CHECK</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1081,7 +1055,7 @@ location set</source>
<translation>SSH访问权限给您GitHub设置中的所有公钥GitHub用户名comma员工永远不会要求您添加他们的GitHub用户名</translation> <translation>SSH访问权限给您GitHub设置中的所有公钥GitHub用户名comma员工永远不会要求您添加他们的GitHub用户名</translation>
</message> </message>
<message> <message>
<location line="+6"/> <location line="+2"/>
<location line="+24"/> <location line="+24"/>
<source>ADD</source> <source>ADD</source>
<translation></translation> <translation></translation>
@ -1151,7 +1125,7 @@ location set</source>
<context> <context>
<name>TogglesPanel</name> <name>TogglesPanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-324"/> <location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source> <source>Enable openpilot</source>
<translation>openpilot</translation> <translation>openpilot</translation>
</message> </message>
@ -1288,12 +1262,11 @@ location set</source>
<name>WifiUI</name> <name>WifiUI</name>
<message> <message>
<location filename="../qt/offroad/networking.cc" line="+113"/> <location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source> <source>Scanning for networks...</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+26"/> <location line="+80"/>
<source>CONNECTING...</source> <source>CONNECTING...</source>
<translation></translation> <translation></translation>
</message> </message>

@ -193,7 +193,7 @@
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+4"/> <location line="+3"/>
<source>Select a language</source> <source>Select a language</source>
<translation></translation> <translation></translation>
</message> </message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation> https://connect.comma.ai</translation>
</message> </message>
<message> <message>
<location line="+57"/> <location line="+58"/>
<source>No home <source>No home
location set</source> location set</source>
<translation> <translation>
@ -432,7 +432,7 @@ location set</source>
</translation> </translation>
</message> </message>
<message> <message>
<location line="+113"/> <location line="+120"/>
<source>no recent destinations</source> <source>no recent destinations</source>
<translation></translation> <translation></translation>
</message> </message>
@ -718,7 +718,7 @@ location set</source>
<context> <context>
<name>SettingsWindow</name> <name>SettingsWindow</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="+101"/> <location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
@ -983,68 +983,47 @@ location set</source>
<context> <context>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-130"/> <location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Git Branch</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Git </translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git </translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+5"/>
<source>OS Version</source> <source>Current Version</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+4"/>
<source>Version</source> <source>Download</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+1"/> <location line="+12"/>
<source>Last Update Check</source> <source>Install Update</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source> <source>INSTALL</source>
<translation></translation> <translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+8"/>
<source>ENTER</source> <source>Target Branch</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<location line="+2"/> <source>SELECT</source>
<source>The new branch will be pulled the next time the updater runs.</source> <translation type="unfinished"></translation>
<translation></translation>
</message> </message>
<message> <message>
<location line="+0"/> <location line="+13"/>
<source>Enter branch name</source> <source>Select a branch</source>
<translation></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+11"/> <location line="+12"/>
<source>UNINSTALL</source> <source>UNINSTALL</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1059,13 +1038,8 @@ location set</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location line="+17"/> <location line="-47"/>
<source>failed to fetch update</source> <location line="+3"/>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<source>CHECK</source> <source>CHECK</source>
<translation></translation> <translation></translation>
</message> </message>
@ -1083,7 +1057,7 @@ location set</source>
<translation> GitHub SSH GitHub comma GitHub </translation> <translation> GitHub SSH GitHub comma GitHub </translation>
</message> </message>
<message> <message>
<location line="+6"/> <location line="+2"/>
<location line="+24"/> <location line="+24"/>
<source>ADD</source> <source>ADD</source>
<translation></translation> <translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context> <context>
<name>TogglesPanel</name> <name>TogglesPanel</name>
<message> <message>
<location filename="../qt/offroad/settings.cc" line="-324"/> <location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source> <source>Enable openpilot</source>
<translation> openpilot</translation> <translation> openpilot</translation>
</message> </message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name> <name>WifiUI</name>
<message> <message>
<location filename="../qt/offroad/networking.cc" line="+113"/> <location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source> <source>Scanning for networks...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>
<message> <message>
<location line="+26"/> <location line="+80"/>
<source>CONNECTING...</source> <source>CONNECTING...</source>
<translation>...</translation> <translation>...</translation>
</message> </message>

@ -23,6 +23,7 @@
# disable this service. # disable this service.
import os import os
import re
import datetime import datetime
import subprocess import subprocess
import psutil import psutil
@ -31,8 +32,9 @@ import signal
import fcntl import fcntl
import time import time
import threading import threading
from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import List, Tuple, Optional from typing import List, Union, Optional
from markdown_it import MarkdownIt from markdown_it import MarkdownIt
from common.basedir import BASEDIR from common.basedir import BASEDIR
@ -54,22 +56,27 @@ DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days
DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days
class WaitTimeHelper: class WaitTimeHelper:
def __init__(self, proc): def __init__(self):
self.proc = proc
self.ready_event = threading.Event() self.ready_event = threading.Event()
self.only_check_for_update = False
signal.signal(signal.SIGHUP, self.update_now) signal.signal(signal.SIGHUP, self.update_now)
signal.signal(signal.SIGUSR1, self.check_now)
def update_now(self, signum: int, frame) -> None: def update_now(self, signum: int, frame) -> None:
cloudlog.info("caught SIGHUP, running update check immediately") cloudlog.info("caught SIGHUP, attempting to downloading update")
self.only_check_for_update = False
self.ready_event.set()
def check_now(self, signum: int, frame) -> None:
cloudlog.info("caught SIGUSR1, checking for updates")
self.only_check_for_update = True
self.ready_event.set() self.ready_event.set()
def sleep(self, t: float) -> None: def sleep(self, t: float) -> None:
self.ready_event.wait(timeout=t) self.ready_event.wait(timeout=t)
def run(cmd: List[str], cwd: Optional[str] = None, low_priority: bool = False): def run(cmd: List[str], cwd: Optional[str] = None) -> str:
if low_priority:
cmd = ["nice", "-n", "19"] + cmd
return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8') return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8')
@ -82,59 +89,19 @@ def set_consistent_flag(consistent: bool) -> None:
consistent_file.unlink(missing_ok=True) consistent_file.unlink(missing_ok=True)
os.sync() os.sync()
def parse_release_notes(basedir: str) -> bytes:
def set_params(new_version: bool, failed_count: int, exception: Optional[str]) -> None: try:
params = Params() with open(os.path.join(basedir, "RELEASES.md"), "rb") as f:
r = f.read().split(b'\n\n', 1)[0] # Slice latest release notes
params.put("UpdateFailedCount", str(failed_count))
last_update = datetime.datetime.utcnow()
if failed_count == 0:
t = last_update.isoformat()
params.put("LastUpdateTime", t.encode('utf8'))
else:
try:
t = params.get("LastUpdateTime", encoding='utf8')
last_update = datetime.datetime.fromisoformat(t)
except (TypeError, ValueError):
pass
if exception is None:
params.remove("LastUpdateException")
else:
params.put("LastUpdateException", exception)
# Write out release notes for new versions
if new_version:
try: try:
with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f: return bytes(MarkdownIt().render(r.decode("utf-8")), encoding="utf-8")
r = f.read().split(b'\n\n', 1)[0] # Slice latest release notes
try:
params.put("ReleaseNotes", MarkdownIt().render(r.decode("utf-8")))
except Exception:
params.put("ReleaseNotes", r + b"\n")
except Exception: except Exception:
params.put("ReleaseNotes", "") return r + b"\n"
params.put_bool("UpdateAvailable", True) except FileNotFoundError:
pass
# Handle user prompt except Exception:
for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded", "Offroad_ConnectivityNeededPrompt"): cloudlog.exception("failed to parse release notes")
set_offroad_alert(alert, False) return b""
now = datetime.datetime.utcnow()
dt = now - last_update
if failed_count > 15 and exception is not None:
if is_tested_branch():
extra_text = "Ensure the software is correctly installed"
else:
extra_text = exception
set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1:
set_offroad_alert("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
def setup_git_options(cwd: str) -> None: def setup_git_options(cwd: str) -> None:
# We sync FS object atimes (which NEOS doesn't use) and mtimes, but ctimes # We sync FS object atimes (which NEOS doesn't use) and mtimes, but ctimes
@ -212,7 +179,7 @@ def init_overlay() -> None:
run(["sudo"] + mount_cmd) run(["sudo"] + mount_cmd)
run(["sudo", "chmod", "755", os.path.join(OVERLAY_METADATA, "work")]) run(["sudo", "chmod", "755", os.path.join(OVERLAY_METADATA, "work")])
git_diff = run(["git", "diff"], OVERLAY_MERGED, low_priority=True) git_diff = run(["git", "diff"], OVERLAY_MERGED)
params.put("GitDiff", git_diff) params.put("GitDiff", git_diff)
cloudlog.info(f"git diff output:\n{git_diff}") cloudlog.info(f"git diff output:\n{git_diff}")
@ -269,65 +236,161 @@ def handle_agnos_update() -> None:
set_offroad_alert("Offroad_NeosUpdate", False) set_offroad_alert("Offroad_NeosUpdate", False)
def check_git_fetch_result(fetch_txt: str) -> bool:
err_msg = "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n"
return len(fetch_txt) > 0 and (fetch_txt != err_msg)
class Updater:
def __init__(self):
self.params = Params()
self.branches = defaultdict(lambda: None)
@property
def target_branch(self) -> str:
b: Union[str, None] = self.params.get("UpdaterTargetBranch", encoding='utf-8')
if b is None:
b = self.get_branch(BASEDIR)
self.params.put("UpdaterTargetBranch", b)
return b
@property
def update_ready(self) -> bool:
consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent"))
if consistent_file.is_file():
hash_mismatch = self.get_commit_hash(BASEDIR) != self.branches[self.target_branch]
branch_mismatch = self.get_branch(BASEDIR) != self.target_branch
on_target_branch = self.get_branch(FINALIZED) == self.target_branch
return ((hash_mismatch or branch_mismatch) and on_target_branch)
return False
@property
def update_available(self) -> bool:
if os.path.isdir(OVERLAY_MERGED):
hash_mismatch = self.get_commit_hash(OVERLAY_MERGED) != self.branches[self.target_branch]
branch_mismatch = self.get_branch(OVERLAY_MERGED) != self.target_branch
return hash_mismatch or branch_mismatch
return False
def get_branch(self, path: str) -> str:
return run(["git", "rev-parse", "--abbrev-ref", "HEAD"], path).rstrip()
def get_commit_hash(self, path: str = OVERLAY_MERGED) -> str:
return run(["git", "rev-parse", "HEAD"], path).rstrip()
def set_params(self, failed_count: int, exception: Optional[str]) -> None:
self.params.put("UpdateFailedCount", str(failed_count))
self.params.put_bool("UpdaterFetchAvailable", self.update_available)
self.params.put("UpdaterAvailableBranches", ','.join(self.branches.keys()))
last_update = datetime.datetime.utcnow()
if failed_count == 0:
t = last_update.isoformat()
self.params.put("LastUpdateTime", t.encode('utf8'))
else:
try:
t = self.params.get("LastUpdateTime", encoding='utf8')
last_update = datetime.datetime.fromisoformat(t)
except (TypeError, ValueError):
pass
def check_for_update() -> Tuple[bool, bool]: if exception is None:
setup_git_options(OVERLAY_MERGED) self.params.remove("LastUpdateException")
try: else:
git_fetch_output = run(["git", "fetch", "--dry-run"], OVERLAY_MERGED, low_priority=True) self.params.put("LastUpdateException", exception)
return True, check_git_fetch_result(git_fetch_output)
except subprocess.CalledProcessError:
return False, False
def fetch_update() -> bool: # Write out current and new version info
cloudlog.info("attempting git fetch inside staging overlay") def get_description(basedir: str) -> str:
version = ""
branch = ""
commit = ""
try:
branch = self.get_branch(basedir)
commit = self.get_commit_hash(basedir)
with open(os.path.join(basedir, "common", "version.h")) as f:
version = f.read().split('"')[1]
except Exception:
pass
return f"{version} / {branch} / {commit[:7]}"
self.params.put("UpdaterCurrentDescription", get_description(BASEDIR))
self.params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR))
self.params.put("UpdaterNewDescription", get_description(FINALIZED))
self.params.put("UpdaterNewReleaseNotes", parse_release_notes(FINALIZED))
self.params.put_bool("UpdateAvailable", self.update_ready)
# Handle user prompt
for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded", "Offroad_ConnectivityNeededPrompt"):
set_offroad_alert(alert, False)
now = datetime.datetime.utcnow()
dt = now - last_update
if failed_count > 15 and exception is not None:
if is_tested_branch():
extra_text = "Ensure the software is correctly installed. Uninstall and re-install if this error persists."
else:
extra_text = exception
set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1:
set_offroad_alert("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
def check_for_update(self) -> None:
cloudlog.info("checking for updates")
excluded_branches = ('release2', 'release2-staging', 'dashcam', 'dashcam-staging')
setup_git_options(OVERLAY_MERGED)
output = run(["git", "ls-remote", "--heads"], OVERLAY_MERGED)
self.branches = defaultdict(lambda: None)
for line in output.split('\n'):
ls_remotes_re = r'(?P<commit_sha>\b[0-9a-f]{5,40}\b)(\s+)(refs\/heads\/)(?P<branch_name>.*$)'
x = re.fullmatch(ls_remotes_re, line.strip())
if x is not None and x.group('branch_name') not in excluded_branches:
self.branches[x.group('branch_name')] = x.group('commit_sha')
cur_branch = self.get_branch(OVERLAY_MERGED)
cur_commit = self.get_commit_hash(OVERLAY_MERGED)
new_branch = self.target_branch
new_commit = self.branches[new_branch]
if (cur_branch, cur_commit) != (new_branch, new_commit):
cloudlog.info(f"update available, {cur_branch} ({cur_commit[:7]}) -> {new_branch} ({new_commit[:7]})")
else:
cloudlog.info(f"up to date on {cur_branch} ({cur_commit[:7]})")
setup_git_options(OVERLAY_MERGED) def fetch_update(self) -> None:
cloudlog.info("attempting git fetch inside staging overlay")
git_fetch_output = run(["git", "fetch"], OVERLAY_MERGED, low_priority=True) self.params.put("UpdaterState", "downloading...")
cloudlog.info("git fetch success: %s", git_fetch_output)
cur_hash = run(["git", "rev-parse", "HEAD"], OVERLAY_MERGED).rstrip() # TODO: cleanly interrupt this and invalidate old update
upstream_hash = run(["git", "rev-parse", "@{u}"], OVERLAY_MERGED).rstrip() set_consistent_flag(False)
new_version: bool = cur_hash != upstream_hash self.params.put_bool("UpdateAvailable", False)
git_fetch_result = check_git_fetch_result(git_fetch_output)
new_branch = Params().get("SwitchToBranch", encoding='utf8') setup_git_options(OVERLAY_MERGED)
if new_branch is not None:
new_version = True
cloudlog.info(f"comparing {cur_hash} to {upstream_hash}") branch = self.target_branch
if new_version or git_fetch_result: git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED)
cloudlog.info("Running update") cloudlog.info("git fetch success: %s", git_fetch_output)
if new_version: cloudlog.info("git reset in progress")
cloudlog.info("git reset in progress") cmds = [
cmds = [ ["git", "checkout", "--force", "--no-recurse-submodules", branch],
["git", "reset", "--hard", "@{u}"], ["git", "reset", "--hard", f"origin/{branch}"],
["git", "clean", "-xdf"], ["git", "clean", "-xdf"],
["git", "submodule", "init"], ["git", "submodule", "init"],
["git", "submodule", "update"], ["git", "submodule", "update"],
] ]
if new_branch is not None: r = [run(cmd, OVERLAY_MERGED) for cmd in cmds]
cloudlog.info(f"switching to branch {repr(new_branch)}") cloudlog.info("git reset success: %s", '\n'.join(r))
cmds.insert(0, ["git", "checkout", "-f", new_branch])
r = [run(cmd, OVERLAY_MERGED, low_priority=True) for cmd in cmds]
cloudlog.info("git reset success: %s", '\n'.join(r))
if AGNOS: # TODO: show agnos download progress
handle_agnos_update() if AGNOS:
handle_agnos_update()
# Create the finalized, ready-to-swap update # Create the finalized, ready-to-swap update
self.params.put("UpdaterState", "finalizing update...")
finalize_update() finalize_update()
cloudlog.info("openpilot update successful!") cloudlog.info("finalize success!")
else:
cloudlog.info("nothing new from git at this time")
return new_version
def main() -> None: def main() -> None:
@ -359,8 +422,12 @@ def main() -> None:
overlay_init = Path(os.path.join(BASEDIR, ".overlay_init")) overlay_init = Path(os.path.join(BASEDIR, ".overlay_init"))
overlay_init.unlink(missing_ok=True) overlay_init.unlink(missing_ok=True)
updater = Updater()
update_failed_count = 0 # TODO: Load from param? update_failed_count = 0 # TODO: Load from param?
wait_helper = WaitTimeHelper(proc)
# no fetch on the first time
wait_helper = WaitTimeHelper()
wait_helper.only_check_for_update = True
# Run the update loop # Run the update loop
while True: while True:
@ -368,21 +435,24 @@ def main() -> None:
# Attempt an update # Attempt an update
exception = None exception = None
new_version = False
update_failed_count += 1
try: try:
# TODO: reuse overlay from previous updated instance if it looks clean
init_overlay() init_overlay()
# TODO: still needed? skip this and just fetch? # ensure we have some params written soon after startup
# Lightweight internt check updater.set_params(update_failed_count, exception)
internet_ok, update_available = check_for_update() update_failed_count += 1
if internet_ok and not update_available:
update_failed_count = 0 # check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
# Fetch update # download update
if internet_ok: if wait_helper.only_check_for_update:
new_version = fetch_update() cloudlog.info("skipping fetch this cycle")
update_failed_count = 0 else:
updater.fetch_update()
update_failed_count = 0
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
cloudlog.event( cloudlog.event(
"update process failed", "update process failed",
@ -398,12 +468,14 @@ def main() -> None:
overlay_init.unlink(missing_ok=True) overlay_init.unlink(missing_ok=True)
try: try:
set_params(new_version, update_failed_count, exception) params.put("UpdaterState", "idle")
updater.set_params(update_failed_count, exception)
except Exception: except Exception:
cloudlog.exception("uncaught updated exception while setting params, shouldn't happen") cloudlog.exception("uncaught updated exception while setting params, shouldn't happen")
# infrequent attempts if we successfully updated recently # infrequent attempts if we successfully updated recently
wait_helper.sleep(5*60 if update_failed_count > 0 else 90*60) wait_helper.only_check_for_update = False
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
if __name__ == "__main__": if __name__ == "__main__":

@ -61,39 +61,48 @@ const float DC_GAIN_OX03C10 = 7.32;
const float DC_GAIN_ON_GREY_AR0231= 0.2; const float DC_GAIN_ON_GREY_AR0231= 0.2;
const float DC_GAIN_OFF_GREY_AR0231 = 0.3; const float DC_GAIN_OFF_GREY_AR0231 = 0.3;
const float DC_GAIN_ON_GREY_OX03C10= 0.3; const float DC_GAIN_ON_GREY_OX03C10= 0.25;
const float DC_GAIN_OFF_GREY_OX03C10 = 0.375; const float DC_GAIN_OFF_GREY_OX03C10 = 0.35;
const int DC_GAIN_MIN_WEIGHT = 0; const int DC_GAIN_MIN_WEIGHT_AR0231 = 0;
const int DC_GAIN_MAX_WEIGHT_AR0231 = 1; const int DC_GAIN_MAX_WEIGHT_AR0231 = 1;
const int DC_GAIN_MIN_WEIGHT_OX03C10 = 16;
const int DC_GAIN_MAX_WEIGHT_OX03C10 = 32; const int DC_GAIN_MAX_WEIGHT_OX03C10 = 32;
const float TARGET_GREY_FACTOR_AR0231 = 1.0;
const float TARGET_GREY_FACTOR_OX03C10 = 0.02;
const float sensor_analog_gains_AR0231[] = { const float sensor_analog_gains_AR0231[] = {
1.0/8.0, 2.0/8.0, 2.0/7.0, 3.0/7.0, // 0, 1, 2, 3 1.0/8.0, 2.0/8.0, 2.0/7.0, 3.0/7.0, // 0, 1, 2, 3
3.0/6.0, 4.0/6.0, 4.0/5.0, 5.0/5.0, // 4, 5, 6, 7 3.0/6.0, 4.0/6.0, 4.0/5.0, 5.0/5.0, // 4, 5, 6, 7
5.0/4.0, 6.0/4.0, 6.0/3.0, 7.0/3.0, // 8, 9, 10, 11 5.0/4.0, 6.0/4.0, 6.0/3.0, 7.0/3.0, // 8, 9, 10, 11
7.0/2.0, 8.0/2.0, 8.0/1.0}; // 12, 13, 14, 15 = bypass 7.0/2.0, 8.0/2.0, 8.0/1.0}; // 12, 13, 14, 15 = bypass
// similar gain curve to AR
const float sensor_analog_gains_OX03C10[] = { const float sensor_analog_gains_OX03C10[] = {
1.0, 1.25, 1.3125, 1.5625, 1.0, 1.125, 1.25, 1.3125, 1.5625,
1.6875, 2.0, 2.25, 2.625, 1.6875, 2.0, 2.25, 2.625, 3.125,
3.125, 3.625, 4.5, 5.0, 3.625, 4.0, 4.5, 5.0, 5.5,
7.25, 8.5, 12.0, 15.5}; 6.0, 6.5, 7.0, 7.5, 8.0,
8.5, 9.0, 9.5, 10.0, 10.5,
11.0, 11.5, 12.0, 12.5, 13.0,
13.5, 14.0, 14.5, 15.0, 15.5};
const uint32_t ox03c10_analog_gains_reg[] = { const uint32_t ox03c10_analog_gains_reg[] = {
0x100, 0x140, 0x150, 0x190, 0x100, 0x120, 0x140, 0x150, 0x190,
0x1B0, 0x200, 0x240, 0x2A0, 0x1B0, 0x200, 0x240, 0x2A0, 0x320,
0x320, 0x3A0, 0x480, 0x500, 0x3A0, 0x400, 0x480, 0x500, 0x580,
0x740, 0x880, 0xC00, 0xF80}; 0x600, 0x680, 0x700, 0x780, 0x800,
0x880, 0x900, 0x980, 0xA00, 0xA80,
0xB00, 0xB80, 0xC00, 0xC80, 0xD00,
0xD80, 0xE00, 0xE80, 0xF00, 0xF80};
const int ANALOG_GAIN_MIN_IDX_AR0231 = 0x1; // 0.25x const int ANALOG_GAIN_MIN_IDX_AR0231 = 0x1; // 0.25x
const int ANALOG_GAIN_REC_IDX_AR0231 = 0x6; // 0.8x const int ANALOG_GAIN_REC_IDX_AR0231 = 0x6; // 0.8x
const int ANALOG_GAIN_MAX_IDX_AR0231 = 0xD; // 4.0x const int ANALOG_GAIN_MAX_IDX_AR0231 = 0xD; // 4.0x
const int ANALOG_GAIN_MIN_IDX_OX03C10 = 0x0; const int ANALOG_GAIN_MIN_IDX_OX03C10 = 0x0;
const int ANALOG_GAIN_REC_IDX_OX03C10 = 0x5; // 2x const int ANALOG_GAIN_REC_IDX_OX03C10 = 0x6; // 2x
const int ANALOG_GAIN_MAX_IDX_OX03C10 = 0xF; const int ANALOG_GAIN_MAX_IDX_OX03C10 = 0x22;
const int EXPOSURE_TIME_MIN_AR0231 = 2; // with HDR, fastest ss const int EXPOSURE_TIME_MIN_AR0231 = 2; // with HDR, fastest ss
const int EXPOSURE_TIME_MAX_AR0231 = 0x0855; // with HDR, slowest ss, 40ms const int EXPOSURE_TIME_MAX_AR0231 = 0x0855; // with HDR, slowest ss, 40ms
@ -517,6 +526,7 @@ void CameraState::enqueue_req_multi(int start, int n, bool dp) {
void CameraState::camera_set_parameters() { void CameraState::camera_set_parameters() {
if (camera_id == CAMERA_ID_AR0231) { if (camera_id == CAMERA_ID_AR0231) {
dc_gain_factor = DC_GAIN_AR0231; dc_gain_factor = DC_GAIN_AR0231;
dc_gain_min_weight = DC_GAIN_MIN_WEIGHT_AR0231;
dc_gain_max_weight = DC_GAIN_MAX_WEIGHT_AR0231; dc_gain_max_weight = DC_GAIN_MAX_WEIGHT_AR0231;
dc_gain_on_grey = DC_GAIN_ON_GREY_AR0231; dc_gain_on_grey = DC_GAIN_ON_GREY_AR0231;
dc_gain_off_grey = DC_GAIN_OFF_GREY_AR0231; dc_gain_off_grey = DC_GAIN_OFF_GREY_AR0231;
@ -529,8 +539,10 @@ void CameraState::camera_set_parameters() {
sensor_analog_gains[i] = sensor_analog_gains_AR0231[i]; sensor_analog_gains[i] = sensor_analog_gains_AR0231[i];
} }
min_ev = exposure_time_min * sensor_analog_gains[analog_gain_min_idx]; min_ev = exposure_time_min * sensor_analog_gains[analog_gain_min_idx];
target_grey_factor = TARGET_GREY_FACTOR_AR0231;
} else if (camera_id == CAMERA_ID_OX03C10) { } else if (camera_id == CAMERA_ID_OX03C10) {
dc_gain_factor = DC_GAIN_OX03C10; dc_gain_factor = DC_GAIN_OX03C10;
dc_gain_min_weight = DC_GAIN_MIN_WEIGHT_OX03C10;
dc_gain_max_weight = DC_GAIN_MAX_WEIGHT_OX03C10; dc_gain_max_weight = DC_GAIN_MAX_WEIGHT_OX03C10;
dc_gain_on_grey = DC_GAIN_ON_GREY_OX03C10; dc_gain_on_grey = DC_GAIN_ON_GREY_OX03C10;
dc_gain_off_grey = DC_GAIN_OFF_GREY_OX03C10; dc_gain_off_grey = DC_GAIN_OFF_GREY_OX03C10;
@ -543,6 +555,7 @@ void CameraState::camera_set_parameters() {
sensor_analog_gains[i] = sensor_analog_gains_OX03C10[i]; sensor_analog_gains[i] = sensor_analog_gains_OX03C10[i];
} }
min_ev = (exposure_time_min + VS_TIME_MIN_OX03C10) * sensor_analog_gains[analog_gain_min_idx]; min_ev = (exposure_time_min + VS_TIME_MIN_OX03C10) * sensor_analog_gains[analog_gain_min_idx];
target_grey_factor = TARGET_GREY_FACTOR_OX03C10;
} else { } else {
assert(false); assert(false);
} }
@ -551,7 +564,7 @@ void CameraState::camera_set_parameters() {
target_grey_fraction = 0.3; target_grey_fraction = 0.3;
dc_gain_enabled = false; dc_gain_enabled = false;
dc_gain_weight = DC_GAIN_MIN_WEIGHT; dc_gain_weight = dc_gain_min_weight;
gain_idx = analog_gain_rec_idx; gain_idx = analog_gain_rec_idx;
exposure_time = 5; exposure_time = 5;
cur_ev[0] = cur_ev[1] = cur_ev[2] = (1 + dc_gain_weight * (dc_gain_factor-1) / dc_gain_max_weight) * sensor_analog_gains[gain_idx] * exposure_time; cur_ev[0] = cur_ev[1] = cur_ev[2] = (1 + dc_gain_weight * (dc_gain_factor-1) / dc_gain_max_weight) * sensor_analog_gains[gain_idx] * exposure_time;
@ -1037,7 +1050,7 @@ void CameraState::set_camera_exposure(float grey_frac) {
const float cur_ev_ = cur_ev[buf.cur_frame_data.frame_id % 3]; const float cur_ev_ = cur_ev[buf.cur_frame_data.frame_id % 3];
// Scale target grey between 0.1 and 0.4 depending on lighting conditions // Scale target grey between 0.1 and 0.4 depending on lighting conditions
float new_target_grey = std::clamp(0.4 - 0.3 * log2(1.0 + cur_ev_) / log2(6000.0), 0.1, 0.4); float new_target_grey = std::clamp(0.4 - 0.3 * log2(1.0 + target_grey_factor*cur_ev_) / log2(6000.0), 0.1, 0.4);
float target_grey = (1.0 - k_grey) * target_grey_fraction + k_grey * new_target_grey; float target_grey = (1.0 - k_grey) * target_grey_fraction + k_grey * new_target_grey;
float desired_ev = std::clamp(cur_ev_ * target_grey / grey_frac, min_ev, max_ev); float desired_ev = std::clamp(cur_ev_ * target_grey / grey_frac, min_ev, max_ev);
@ -1053,14 +1066,14 @@ void CameraState::set_camera_exposure(float grey_frac) {
bool enable_dc_gain = dc_gain_enabled; bool enable_dc_gain = dc_gain_enabled;
if (!enable_dc_gain && target_grey < dc_gain_on_grey) { if (!enable_dc_gain && target_grey < dc_gain_on_grey) {
enable_dc_gain = true; enable_dc_gain = true;
dc_gain_weight = DC_GAIN_MIN_WEIGHT; dc_gain_weight = dc_gain_min_weight;
} else if (enable_dc_gain && target_grey > dc_gain_off_grey) { } else if (enable_dc_gain && target_grey > dc_gain_off_grey) {
enable_dc_gain = false; enable_dc_gain = false;
dc_gain_weight = dc_gain_max_weight; dc_gain_weight = dc_gain_max_weight;
} }
if (enable_dc_gain && dc_gain_weight < dc_gain_max_weight) {dc_gain_weight += 1;} if (enable_dc_gain && dc_gain_weight < dc_gain_max_weight) {dc_gain_weight += 1;}
if (!enable_dc_gain && dc_gain_weight > DC_GAIN_MIN_WEIGHT) {dc_gain_weight -= 1;} if (!enable_dc_gain && dc_gain_weight > dc_gain_min_weight) {dc_gain_weight -= 1;}
std::string gain_bytes, time_bytes; std::string gain_bytes, time_bytes;
if (env_ctrl_exp_from_params) { if (env_ctrl_exp_from_params) {
@ -1145,10 +1158,12 @@ void CameraState::set_camera_exposure(float grey_frac) {
// t_HCG + t_LCG + t_VS on LPD, t_SPD on SPD // t_HCG + t_LCG + t_VS on LPD, t_SPD on SPD
uint32_t hcg_time = std::max((dc_gain_weight * exposure_time / dc_gain_max_weight), 0); uint32_t hcg_time = std::max((dc_gain_weight * exposure_time / dc_gain_max_weight), 0);
uint32_t lcg_time = std::max(((dc_gain_max_weight - dc_gain_weight) * exposure_time / dc_gain_max_weight), 0); uint32_t lcg_time = std::max(((dc_gain_max_weight - dc_gain_weight) * exposure_time / dc_gain_max_weight), 0);
uint32_t spd_time = std::max(hcg_time / 16, (uint32_t)exposure_time_min); // uint32_t spd_time = std::max(hcg_time / 16, (uint32_t)exposure_time_min);
uint32_t vs_time = std::min(std::max(hcg_time / 64, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10); uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 128, VS_TIME_MIN_OX03C10), VS_TIME_MAX_OX03C10);
uint32_t spd_time = vs_time;
uint32_t real_gain = ox03c10_analog_gains_reg[new_g]; uint32_t real_gain = ox03c10_analog_gains_reg[new_g];
uint32_t min_gain = ox03c10_analog_gains_reg[0];
struct i2c_random_wr_payload exp_reg_array[] = { struct i2c_random_wr_payload exp_reg_array[] = {
{0x3501, hcg_time>>8}, {0x3502, hcg_time&0xFF}, {0x3501, hcg_time>>8}, {0x3502, hcg_time&0xFF},
@ -1157,9 +1172,9 @@ void CameraState::set_camera_exposure(float grey_frac) {
{0x35c1, vs_time>>8}, {0x35c2, vs_time&0xFF}, {0x35c1, vs_time>>8}, {0x35c2, vs_time&0xFF},
{0x3508, real_gain>>8}, {0x3509, real_gain&0xFF}, {0x3508, real_gain>>8}, {0x3509, real_gain&0xFF},
{0x3588, real_gain>>8}, {0x3589, real_gain&0xFF}, {0x3588, min_gain>>8}, {0x3589, min_gain&0xFF},
{0x3548, real_gain>>8}, {0x3549, real_gain&0xFF}, {0x3548, min_gain>>8}, {0x3549, min_gain&0xFF},
{0x35c8, real_gain>>8}, {0x35c9, real_gain&0xFF}, {0x35c8, min_gain>>8}, {0x35c9, min_gain&0xFF},
}; };
sensors_i2c(exp_reg_array, sizeof(exp_reg_array)/sizeof(struct i2c_random_wr_payload), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, false); sensors_i2c(exp_reg_array, sizeof(exp_reg_array)/sizeof(struct i2c_random_wr_payload), CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG, false);
} }

@ -31,11 +31,12 @@ public:
int exposure_time_max; int exposure_time_max;
float dc_gain_factor; float dc_gain_factor;
int dc_gain_min_weight;
int dc_gain_max_weight; int dc_gain_max_weight;
float dc_gain_on_grey; float dc_gain_on_grey;
float dc_gain_off_grey; float dc_gain_off_grey;
float sensor_analog_gains[16]; float sensor_analog_gains[35];
int analog_gain_min_idx; int analog_gain_min_idx;
int analog_gain_max_idx; int analog_gain_max_idx;
int analog_gain_rec_idx; int analog_gain_rec_idx;
@ -45,6 +46,7 @@ public:
float measured_grey_fraction; float measured_grey_fraction;
float target_grey_fraction; float target_grey_fraction;
float target_grey_factor;
unique_fd sensor_fd; unique_fd sensor_fd;
unique_fd csiphy_fd; unique_fd csiphy_fd;

@ -129,13 +129,13 @@ struct i2c_random_wr_payload init_array_ox03c10[] = {
{0x350a, 0x04}, {0x350b, 0x00}, {0x350c, 0x00}, // hcg digital gain {0x350a, 0x04}, {0x350b, 0x00}, {0x350c, 0x00}, // hcg digital gain
{0x3586, 0x40}, {0x3587, 0x00}, // lcg fine exposure {0x3586, 0x40}, {0x3587, 0x00}, // lcg fine exposure
{0x358a, 0x04}, {0x358b, 0x00}, {0x358c, 0x00}, // lcg digital gain {0x358a, 0x01}, {0x358b, 0x00}, {0x358c, 0x00}, // lcg digital gain
{0x3546, 0x20}, {0x3547, 0x00}, // spd fine exposure {0x3546, 0x20}, {0x3547, 0x00}, // spd fine exposure
{0x354a, 0x04}, {0x354b, 0x00}, {0x354c, 0x00}, // spd digital gain {0x354a, 0x01}, {0x354b, 0x00}, {0x354c, 0x00}, // spd digital gain
{0x35c6, 0xb0}, {0x35c7, 0x00}, // vs fine exposure {0x35c6, 0xb0}, {0x35c7, 0x00}, // vs fine exposure
{0x35ca, 0x04}, {0x35cb, 0x00}, {0x35cc, 0x00}, // vs digital gain {0x35ca, 0x01}, {0x35cb, 0x00}, {0x35cc, 0x00}, // vs digital gain
// also RSVD // also RSVD
{0x3600, 0x8f}, {0x3605, 0x16}, {0x3609, 0xf0}, {0x360a, 0x01}, {0x3600, 0x8f}, {0x3605, 0x16}, {0x3609, 0xf0}, {0x360a, 0x01},

Loading…
Cancel
Save