Compare commits

..

21 Commits

Author SHA1 Message Date
royjr
0491242b4a Merge branch 'master' into visuals-hide-camera 2026-03-15 15:14:12 -04:00
Jason Wen
37ac33fbcc gitignore: add CLAUDE.md and SKILL.md 2026-03-13 19:19:37 -04:00
James Vecellio-Grant
0376660023 ci: modify models repo title (#1764) 2026-03-13 13:19:45 -04:00
Jason Wen
2e82908c07 pandad: always prioritize internal panda (#1759)
* pandad: filter out external panda

* fix

* internal panda

* move it even higher

* this

* should be this still

* anoter

* more

* 1 more time

* bruh

* try this out

* revert

* gotta do this after

* filter
2026-03-10 20:30:25 -04:00
Jason Wen
b71914e006 [TIZI/TICI] ui: branch switcher is always available (#1762) 2026-03-07 01:48:35 -05:00
Jason Wen
a9d5c9e23a ui: add new timer options for Onroad Brightness Delay (#1760)
* ui: add new timer options for OnOnroad Brightness Delay

* migrate

* in future pr

* Revert "in future pr"

This reverts commit ca9940f809.

* consolidate

* update

* gate

* fix
2026-03-07 01:36:24 -05:00
Jason Wen
c01719bb99 ui: gate Onroad Brightness Delay on readiness (#1761)
ui: gate Onroad Brightness Timer on readiness
2026-03-06 23:38:38 -05:00
royjr
1b89608ccc Merge branch 'master' into visuals-hide-camera 2026-03-02 02:42:15 -05:00
royjr
53a24655d2 move to visuals 2026-02-13 22:18:08 -05:00
royjr
c9f92a8c76 no mici toggle 2026-02-13 22:11:36 -05:00
royjr
10b1d673c9 Apply description suggestion from @sunnyhaibin
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2026-02-13 22:10:02 -05:00
Jason Wen
7080167daf Merge branch 'master' into visuals-hide-camera 2026-02-13 17:03:34 -05:00
Jason Wen
c7a1c70504 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into visuals-hide-camera
# Conflicts:
#	selfdrive/ui/sunnypilot/ui_state.py
2026-02-13 16:55:01 -05:00
royjr
c6a6caf6ff Merge branch 'master' into visuals-hide-camera 2026-02-05 00:52:57 -05:00
royjr
8d49a44f52 Merge branch 'master' into visuals-hide-camera 2025-12-31 15:09:29 -05:00
royjr
3434ca9d3e bool 2025-12-31 14:19:32 -05:00
royjr
e4f8a5edd1 Update params_keys.h 2025-12-29 14:45:47 -05:00
royjr
1f4f9bd4bd big ui 2025-12-29 02:00:42 -05:00
royjr
455e730c4c simpler 2025-12-29 01:53:12 -05:00
royjr
b243d4e356 fix param 2025-12-29 01:12:00 -05:00
royjr
de0550d47b init 2025-12-29 01:06:47 -05:00
20 changed files with 121 additions and 31 deletions

View File

@@ -34,10 +34,10 @@ jobs:
echo "tinygrad_ref=$ref" >> $GITHUB_OUTPUT
echo "tinygrad_ref is $ref"
- name: Checkout docs repo (sunnypilot-docs, gh-pages)
- name: Checkout docs repo (sunnypilot-models, gh-pages)
uses: actions/checkout@v4
with:
repository: sunnypilot/sunnypilot-docs
repository: sunnypilot/sunnypilot-models
ref: gh-pages
path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}
@@ -202,7 +202,7 @@ jobs:
- name: Checkout docs repo
uses: actions/checkout@v4
with:
repository: sunnypilot/sunnypilot-docs
repository: sunnypilot/sunnypilot-models
ref: gh-pages
path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}

View File

@@ -119,7 +119,7 @@ jobs:
- name: Checkout docs repo
uses: actions/checkout@v4
with:
repository: sunnypilot/sunnypilot-docs
repository: sunnypilot/sunnypilot-models
ref: gh-pages
path: docs
ssh-key: ${{ secrets.CI_SUNNYPILOT_DOCS_PRIVATE_KEY }}

2
.gitignore vendored
View File

@@ -101,6 +101,8 @@ Pipfile
.context/
PLAN.md
TASK.md
CLAUDE.md
SKILL.md
### JetBrains ###
!.idea/customTargets.xml

View File

@@ -54,6 +54,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"GsmRoaming", {PERSISTENT | BACKUP, BOOL}},
{"HardwareSerial", {PERSISTENT, STRING}},
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
{"HideCamera", {PERSISTENT | BACKUP, BOOL, "0"}},
{"InstallDate", {PERSISTENT, TIME}},
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsEngaged", {PERSISTENT, BOOL}},
@@ -172,6 +173,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
{"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
{"OnroadScreenOffTimerMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
{"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}},

View File

@@ -68,21 +68,20 @@ def flash_panda(panda_serial: str) -> Panda:
return panda
def check_panda_support(panda_serials: list[str]) -> bool:
unsupported = []
def check_panda_support(panda_serials: list[str]) -> list[str]:
spi_serials = set(Panda.spi_list())
for serial in panda_serials:
if serial in spi_serials:
return [serial]
for serial in panda_serials:
panda = Panda(serial)
hw_type = panda.get_type()
is_internal = panda.is_internal()
panda.close()
if hw_type in Panda.SUPPORTED_DEVICES:
return True
if is_internal:
return [serial]
unsupported.append((serial, hw_type))
for serial, hw_type in unsupported:
cloudlog.warning(f"Panda {serial} is not supported (hw_type: {hw_type}), skipping...")
return False
return []
def main() -> None:
@@ -137,8 +136,9 @@ def main() -> None:
# custom flasher for xnor's Rivian Longitudinal Upgrade Kit
flash_rivian_long(panda_serials)
# skip flashing and health check if no supported panda is detected
if not check_panda_support(panda_serials):
# find the internal supported panda (e.g. skip external Black Panda)
panda_serials = check_panda_support(panda_serials)
if len(panda_serials) == 0:
continue
# Flash the first panda

View File

@@ -69,7 +69,6 @@ class SoftwareLayout(Widget):
# Branch switcher
self._branch_btn = button_item(lambda: tr("Target Branch"), lambda: tr("SELECT"), callback=self._on_select_branch)
self._branch_btn.set_visible(not ui_state.params.get_bool("IsTestedBranch"))
self._branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "")
self._branch_dialog: MultiOptionDialog | None = None

View File

@@ -231,7 +231,7 @@ class AlertRenderer(Widget, SpeedLimitAlertRenderer):
self._alpha_filter.update(0 if alert is None else 1)
if gui_app.sunnypilot_ui():
ui_state.onroad_brightness_handle_alerts(ui_state.started, alert)
ui_state.onroad_brightness_handle_alerts(ui_state, alert)
if alert is None:
# If still animating out, keep the previous alert

View File

@@ -212,6 +212,8 @@ class AugmentedRoadView(CameraView):
# Render the base camera view
super()._render(self._content_rect)
if ui_state.hide_camera:
rl.draw_rectangle_rec(self._content_rect, rl.BLACK)
# Draw all UI overlays
self._model_renderer.render(self._content_rect)

View File

@@ -118,7 +118,7 @@ class AlertRenderer(Widget):
alert = self.get_alert(ui_state.sm)
if gui_app.sunnypilot_ui():
ui_state.onroad_brightness_handle_alerts(ui_state.started, alert)
ui_state.onroad_brightness_handle_alerts(ui_state, alert)
if not alert:
return

View File

@@ -91,6 +91,8 @@ class AugmentedRoadView(CameraView, AugmentedRoadViewSP):
# Render the base camera view
super()._render(rect)
if ui_state.hide_camera:
rl.draw_rectangle_rec(self._content_rect, rl.BLACK)
# Draw all UI overlays
self.model_renderer.render(self._content_rect)

View File

@@ -12,8 +12,7 @@ from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.widgets.scroller_tici import Scroller
from openpilot.system.ui.sunnypilot.widgets.list_view import option_item_sp, ToggleActionSP
ONROAD_BRIGHTNESS_TIMER_VALUES = {0: 15, 1: 30, **{i: (i - 1) * 60 for i in range(2, 12)}}
from openpilot.sunnypilot.system.params_migration import ONROAD_BRIGHTNESS_TIMER_VALUES
class OnroadBrightness(IntEnum):
@@ -46,7 +45,7 @@ class DisplayLayout(Widget):
title=lambda: tr("Onroad Brightness Delay"),
description="",
min_value=0,
max_value=11,
max_value=15,
value_change_step=1,
value_map=ONROAD_BRIGHTNESS_TIMER_VALUES,
label_callback=lambda value: f"{value} s" if value < 60 else f"{int(value/60)} m",
@@ -92,7 +91,11 @@ class DisplayLayout(Widget):
if isinstance(_item.action_item, ToggleActionSP) and _item.action_item.toggle.param_key is not None:
_item.action_item.set_state(self._params.get_bool(_item.action_item.toggle.param_key))
elif isinstance(_item.action_item, OptionControlSP) and _item.action_item.param_key is not None:
_item.action_item.set_value(self._params.get(_item.action_item.param_key, return_default=True))
raw_value = self._params.get(_item.action_item.param_key, return_default=True)
if _item.action_item.value_map:
reverse_map = {v: k for k, v in _item.action_item.value_map.items()}
raw_value = reverse_map.get(raw_value, _item.action_item.current_value)
_item.action_item.set_value(raw_value)
brightness_val = self._params.get("OnroadScreenOffBrightness", return_default=True)
self._onroad_brightness_timer.action_item.set_enabled(brightness_val not in (OnroadBrightness.AUTO, OnroadBrightness.AUTO_DARK))

View File

@@ -93,6 +93,11 @@ class VisualsLayout(Widget):
"This displays what the car is currently doing, not what the planner is requesting."),
None,
),
"HideCamera": (
lambda: tr("Hide Camera"),
tr("Hide the camera live view from the driving screen."),
None,
),
}
self._toggles = {}
for param, (title, desc, callback) in self._toggle_defs.items():

View File

@@ -49,8 +49,11 @@ class UIStateSP:
else:
self.sunnylink_state.stop()
def onroad_brightness_handle_alerts(self, started: bool, alert):
has_alert = started and self.onroad_brightness != OnroadBrightness.AUTO and alert is not None
def onroad_brightness_handle_alerts(self, _ui_state, alert):
if _ui_state.sm.recv_frame["carState"] < _ui_state.started_frame:
return
has_alert = _ui_state.started and self.onroad_brightness != OnroadBrightness.AUTO and alert is not None
self.update_onroad_brightness(has_alert)
if has_alert:
@@ -130,6 +133,7 @@ class UIStateSP:
self.chevron_metrics = self.params.get("ChevronInfo")
self.custom_interactive_timeout = self.params.get("InteractivityTimeout", return_default=True)
self.developer_ui = self.params.get("DevUIInfo")
self.hide_camera = self.params.get_bool("HideCamera")
self.hide_v_ego_ui = self.params.get_bool("HideVEgoUI")
self.onroad_brightness = int(float(self.params.get("OnroadScreenOffBrightness", return_default=True)))
self.onroad_brightness_timer_param = self.params.get("OnroadScreenOffTimer", return_default=True)

View File

@@ -116,7 +116,7 @@ class ModelCache:
class ModelFetcher:
"""Handles fetching and caching of model data from remote source"""
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-docs/refs/heads/gh-pages/docs/driving_models_v15.json"
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v15.json"
def __init__(self, params: Params):
self.params = params

View File

@@ -84,7 +84,11 @@ def flash_rivian_long(panda_serials: list[str]) -> None:
cloudlog.info("Not a Rivian, skipping longitudinal upgrade...")
return
# only check USB connected pandas, internal panda uses SPI and is never an external panda
usb_serials = set(Panda.usb_list())
for serial in panda_serials:
if serial not in usb_serials:
continue
panda = Panda(serial)
# only flash external black pandas (HW_TYPE_BLACK = 0x03)
if panda.get_type() == b'\x03' and not panda.is_internal():

View File

@@ -370,6 +370,10 @@
"title": "Has Accepted sunnypilot Terms",
"description": ""
},
"HideCamera": {
"title": "Hide Camera",
"description": "Hide the camera live view from the driving screen."
},
"HideVEgoUI": {
"title": "[TIZI/TICI only] Speedometer: Hide from Onroad Screen",
"description": "When enabled, the speedometer on the onroad screen is not displayed."
@@ -941,6 +945,22 @@
"title": "Onroad Brightness Delay",
"description": "",
"options": [
{
"value": 3,
"label": "3s"
},
{
"value": 5,
"label": "5s"
},
{
"value": 7,
"label": "7s"
},
{
"value": 10,
"label": "10s"
},
{
"value": 15,
"label": "15s"
@@ -991,6 +1011,10 @@
}
]
},
"OnroadScreenOffTimerMigrated": {
"title": "Onroad Brightness Delay Migration Version",
"description": "This param is to track whether OnroadScreenOffTimer needs to be migrated."
},
"OnroadUploads": {
"title": "Onroad Uploads",
"description": ""
@@ -1290,7 +1314,7 @@
"min": 0.1,
"max": 5.0,
"step": 0.1,
"unit": "m/s²"
"unit": "m/s\u00b2"
},
"ToyotaEnforceStockLongitudinal": {
"title": "Toyota: Enforce Factory Longitudinal Control",

View File

@@ -10,6 +10,7 @@ import os
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.sunnypilot.system.params_migration import ONROAD_BRIGHTNESS_TIMER_VALUES
METADATA_PATH = os.path.join(os.path.dirname(__file__), "../params_metadata.json")
TORQUE_VERSIONS_JSON = os.path.join(BASEDIR, "sunnypilot", "selfdrive", "controls", "lib", "latcontrol_torque_versions.json")
@@ -56,6 +57,9 @@ def main():
# update onroad screen brightness params
update_onroad_brightness_param()
# update onroad screen brightness timer params
update_onroad_brightness_timer_param()
# update torque versions param
update_torque_versions_param()
@@ -81,6 +85,24 @@ def update_onroad_brightness_param():
print(f"Failed to update OnroadScreenOffBrightness versions in params_metadata.json: {e}")
def update_onroad_brightness_timer_param():
try:
with open(METADATA_PATH) as f:
params_metadata = json.load(f)
if "OnroadScreenOffTimer" in params_metadata:
options = []
for _index, seconds in sorted(ONROAD_BRIGHTNESS_TIMER_VALUES.items()):
label = f"{seconds}s" if seconds < 60 else f"{seconds // 60}m"
options.append({"value": seconds, "label": label})
params_metadata["OnroadScreenOffTimer"]["options"] = options
with open(METADATA_PATH, 'w') as f:
json.dump(params_metadata, f, indent=2)
f.write('\n')
print(f"Updated OnroadScreenOffTimer options in params_metadata.json with {len(options)} options.")
except Exception as e:
print(f"Failed to update OnroadScreenOffTimer options in params_metadata.json: {e}")
def update_torque_versions_param():
with open(TORQUE_VERSIONS_JSON) as f:
current_versions = json.load(f)

View File

@@ -7,13 +7,18 @@ See the LICENSE.md file in the root directory for more details.
from openpilot.common.swaglog import cloudlog
ONROAD_BRIGHTNESS_MIGRATION_VERSION: str = "1.0"
ONROAD_BRIGHTNESS_TIMER_MIGRATION_VERSION: str = "1.0"
# index → seconds mapping for OnroadScreenOffTimer (SSoT)
ONROAD_BRIGHTNESS_TIMER_VALUES = {0: 3, 1: 5, 2: 7, 3: 10, 4: 15, 5: 30, **{i: (i - 5) * 60 for i in range(6, 16)}}
VALID_TIMER_VALUES = set(ONROAD_BRIGHTNESS_TIMER_VALUES.values())
def run_migration(_params):
# migrate OnroadScreenOffBrightness
if _params.get("OnroadScreenOffBrightnessMigrated") != ONROAD_BRIGHTNESS_MIGRATION_VERSION:
try:
val = _params.get("OnroadScreenOffBrightness")
val = _params.get("OnroadScreenOffBrightness", return_default=True)
if val >= 2: # old: 5%, new: Screen Off
new_val = val + 1
_params.put("OnroadScreenOffBrightness", new_val)
@@ -25,3 +30,18 @@ def run_migration(_params):
cloudlog.info(log_str + f" Setting OnroadScreenOffBrightnessMigrated to {ONROAD_BRIGHTNESS_MIGRATION_VERSION}")
except Exception as e:
cloudlog.exception(f"Error migrating OnroadScreenOffBrightness: {e}")
# migrate OnroadScreenOffTimer
if _params.get("OnroadScreenOffTimerMigrated") != ONROAD_BRIGHTNESS_TIMER_MIGRATION_VERSION:
try:
val = _params.get("OnroadScreenOffTimer", return_default=True)
if val not in VALID_TIMER_VALUES:
_params.put("OnroadScreenOffTimer", 15)
log_str = f"Successfully migrated OnroadScreenOffTimer from {val} to 15 (default)."
else:
log_str = "Migration not required for OnroadScreenOffTimer."
_params.put("OnroadScreenOffTimerMigrated", ONROAD_BRIGHTNESS_TIMER_MIGRATION_VERSION)
cloudlog.info(log_str + f" Setting OnroadScreenOffTimerMigrated to {ONROAD_BRIGHTNESS_TIMER_MIGRATION_VERSION}")
except Exception as e:
cloudlog.exception(f"Error migrating OnroadScreenOffTimer: {e}")

View File

@@ -51,7 +51,8 @@ def manager_init() -> None:
if params.get_bool("RecordFrontLock"):
params.put_bool("RecordFront", True)
run_migration(params)
if not PC:
run_migration(params)
# set unset params to their default value
for k in params.all_keys():