diff --git a/common/params_keys.h b/common/params_keys.h index 2b8ba205ad..0df6956716 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -172,6 +172,7 @@ inline static std::unordered_map 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"}}, diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index bda349aec2..a8300de321 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -134,6 +134,9 @@ def main() -> None: cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}") + # 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): continue @@ -142,9 +145,6 @@ def main() -> None: panda_serial = panda_serials[0] panda = flash_panda(panda_serial) - # flash Rivian longitudinal upgrade panda - flash_rivian_long(panda) - # Ensure internal panda is present if expected if HARDWARE.has_internal_panda() and not panda.is_internal(): cloudlog.error("Internal panda is missing, trying again") diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index f7424b974d..8086b1f635 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -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 diff --git a/selfdrive/ui/mici/onroad/alert_renderer.py b/selfdrive/ui/mici/onroad/alert_renderer.py index 68b2aef7a5..a9d4f3a48b 100644 --- a/selfdrive/ui/mici/onroad/alert_renderer.py +++ b/selfdrive/ui/mici/onroad/alert_renderer.py @@ -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 diff --git a/selfdrive/ui/onroad/alert_renderer.py b/selfdrive/ui/onroad/alert_renderer.py index 2c21b4006e..6e79d23253 100644 --- a/selfdrive/ui/onroad/alert_renderer.py +++ b/selfdrive/ui/onroad/alert_renderer.py @@ -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 diff --git a/selfdrive/ui/sunnypilot/layouts/settings/display.py b/selfdrive/ui/sunnypilot/layouts/settings/display.py index d44118d8f4..acd7b52dcc 100644 --- a/selfdrive/ui/sunnypilot/layouts/settings/display.py +++ b/selfdrive/ui/sunnypilot/layouts/settings/display.py @@ -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)) diff --git a/selfdrive/ui/sunnypilot/onroad/augmented_road_view.py b/selfdrive/ui/sunnypilot/onroad/augmented_road_view.py index 64d7a3d946..c7dedee540 100644 --- a/selfdrive/ui/sunnypilot/onroad/augmented_road_view.py +++ b/selfdrive/ui/sunnypilot/onroad/augmented_road_view.py @@ -23,7 +23,7 @@ class AugmentedRoadViewSP: def update_fade_out_bottom_overlay(self, _content_rect): # Fade out bottom of overlays for looks (only when engaged) fade_alpha = self._fade_alpha_filter.update(ui_state.status != UIStatus.DISENGAGED) - if ui_state.torque_bar and ui_state.sm['controlsState'].lateralControlState.which() != 'angleState' and fade_alpha > 1e-2: + if ui_state.torque_bar and fade_alpha > 1e-2: # Scale the fade texture to the content rect rl.draw_texture_pro(self._fade_texture, rl.Rectangle(0, 0, self._fade_texture.width, self._fade_texture.height), diff --git a/selfdrive/ui/sunnypilot/onroad/hud_renderer.py b/selfdrive/ui/sunnypilot/onroad/hud_renderer.py index 1063809c32..f8e4257733 100644 --- a/selfdrive/ui/sunnypilot/onroad/hud_renderer.py +++ b/selfdrive/ui/sunnypilot/onroad/hud_renderer.py @@ -131,10 +131,11 @@ class HudRendererSP(HudRenderer): def _render(self, rect: rl.Rectangle) -> None: super()._render(rect) - torque_rect = rect - if ui_state.developer_ui in (DeveloperUiState.BOTTOM, DeveloperUiState.BOTH): - torque_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height - get_bottom_dev_ui_offset()) - self._torque_bar.render(torque_rect) + if ui_state.torque_bar: + torque_rect = rect + if ui_state.developer_ui in (DeveloperUiState.BOTTOM, DeveloperUiState.BOTH): + torque_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height - get_bottom_dev_ui_offset()) + self._torque_bar.render(torque_rect) self.developer_ui.render(rect) self.road_name_renderer.render(rect) diff --git a/selfdrive/ui/sunnypilot/ui_state.py b/selfdrive/ui/sunnypilot/ui_state.py index 050df6d26d..7766c353ad 100644 --- a/selfdrive/ui/sunnypilot/ui_state.py +++ b/selfdrive/ui/sunnypilot/ui_state.py @@ -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: diff --git a/sunnypilot/selfdrive/pandad/rivian_long_flasher.py b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py index 24191a73a2..70ddb44358 100755 --- a/sunnypilot/selfdrive/pandad/rivian_long_flasher.py +++ b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py @@ -72,9 +72,10 @@ def _flash_panda(panda: Panda) -> None: _flash_static(panda._handle, code) panda.reconnect() + cloudlog.info(f"Successfully flashed xnor's Rivian Longitudinal Upgrade Kit: {panda.get_usb_serial()}") -def flash_rivian_long(panda: Panda) -> None: +def flash_rivian_long(panda_serials: list[str]) -> None: if not os.path.isfile(FW_PATH): cloudlog.error(f"Rivian longitudinal upgrade firmware not found at {FW_PATH}") return @@ -83,13 +84,18 @@ def flash_rivian_long(panda: Panda) -> None: cloudlog.info("Not a Rivian, skipping longitudinal upgrade...") return - # only flash external black pandas (HW_TYPE_BLACK = 0x03) - if panda.get_type() == b'\x03' and not panda.is_internal(): - try: - _flash_panda(panda) - except Exception: - cloudlog.exception(f"Failed to flash F4 panda {panda.get_usb_serial()}") + for serial in panda_serials: + panda = Panda(serial) + # only flash external black pandas (HW_TYPE_BLACK = 0x03) + if panda.get_type() == b'\x03' and not panda.is_internal(): + try: + _flash_panda(panda) + except Exception: + cloudlog.exception(f"Failed to flash xnor's Rivian Longitudinal Upgrade Kit: {serial}") + panda.close() + + return if __name__ == '__main__': - flash_rivian_long(Panda()) + flash_rivian_long(Panda.list()) diff --git a/sunnypilot/sunnylink/params_metadata.json b/sunnypilot/sunnylink/params_metadata.json index 79c2b5caf9..4d66bf8762 100644 --- a/sunnypilot/sunnylink/params_metadata.json +++ b/sunnypilot/sunnylink/params_metadata.json @@ -941,6 +941,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 +1007,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 +1310,7 @@ "min": 0.1, "max": 5.0, "step": 0.1, - "unit": "m/s²" + "unit": "m/s\u00b2" }, "ToyotaEnforceStockLongitudinal": { "title": "Toyota: Enforce Factory Longitudinal Control", diff --git a/sunnypilot/sunnylink/tools/update_params_metadata.py b/sunnypilot/sunnylink/tools/update_params_metadata.py index 013544005b..ea3765420d 100755 --- a/sunnypilot/sunnylink/tools/update_params_metadata.py +++ b/sunnypilot/sunnylink/tools/update_params_metadata.py @@ -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) diff --git a/sunnypilot/system/params_migration.py b/sunnypilot/system/params_migration.py index 6a82f1866d..5e524de06e 100644 --- a/sunnypilot/system/params_migration.py +++ b/sunnypilot/system/params_migration.py @@ -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}") diff --git a/system/manager/manager.py b/system/manager/manager.py index 8c219909e4..3ec554f904 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -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():