from cereal import log from openpilot.common.params import Params, UnknownKeyName from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import DialogResult from openpilot.selfdrive.ui.ui_state import ui_state PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants # Description constants DESCRIPTIONS = { "OpenpilotEnabledToggle": ( "Use the openpilot system for adaptive cruise control and lane keep driver assistance. " + "Your attention is required at all times to use this feature." ), "DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.", "LongitudinalPersonality": ( "Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " + "In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " + "your steering wheel distance button." ), "IsLdwEnabled": ( "Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " + "without a turn signal activated while driving over 31 mph (50 km/h)." ), "AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.", 'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.", "IsMetric": "Display speed in km/h instead of mph.", "RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.", } class TogglesLayout(Widget): def __init__(self): super().__init__() self._params = Params() self._is_release = self._params.get_bool("IsReleaseBranch") # param, title, desc, icon, needs_restart self._toggle_defs = { "OpenpilotEnabledToggle": ( "Enable openpilot", DESCRIPTIONS["OpenpilotEnabledToggle"], "chffr_wheel.png", True, ), "ExperimentalMode": ( "Experimental Mode", "", "experimental_white.png", False, ), "DisengageOnAccelerator": ( "Disengage on Accelerator Pedal", DESCRIPTIONS["DisengageOnAccelerator"], "disengage_on_accelerator.png", False, ), "IsLdwEnabled": ( "Enable Lane Departure Warnings", DESCRIPTIONS["IsLdwEnabled"], "warning.png", False, ), "AlwaysOnDM": ( "Always-On Driver Monitoring", DESCRIPTIONS["AlwaysOnDM"], "monitoring.png", False, ), "RecordFront": ( "Record and Upload Driver Camera", DESCRIPTIONS["RecordFront"], "monitoring.png", True, ), "RecordAudio": ( "Record and Upload Microphone Audio", DESCRIPTIONS["RecordAudio"], "microphone.png", True, ), "IsMetric": ( "Use Metric System", DESCRIPTIONS["IsMetric"], "metric.png", False, ), } self._long_personality_setting = multiple_button_item( "Driving Personality", DESCRIPTIONS["LongitudinalPersonality"], buttons=["Aggressive", "Standard", "Relaxed"], button_width=255, callback=self._set_longitudinal_personality, selected_index=self._params.get("LongitudinalPersonality", return_default=True), icon="speed_limit.png" ) self._toggles = {} self._locked_toggles = set() for param, (title, desc, icon, needs_restart) in self._toggle_defs.items(): toggle = toggle_item( title, desc, self._params.get_bool(param), callback=lambda state, p=param: self._toggle_callback(state, p), icon=icon, ) try: locked = self._params.get_bool(param + "Lock") except UnknownKeyName: locked = False toggle.action_item.set_enabled(not locked) if needs_restart and not locked: toggle.set_description(toggle.description + " Changing this setting will restart openpilot if the car is powered on.") # track for engaged state updates if locked: self._locked_toggles.add(param) self._toggles[param] = toggle # insert longitudinal personality after NDOG toggle if param == "DisengageOnAccelerator": self._toggles["LongitudinalPersonality"] = self._long_personality_setting self._update_experimental_mode_icon() self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0) ui_state.add_engaged_transition_callback(self._update_toggles) def _update_state(self): if ui_state.sm.updated["selfdriveState"]: personality = PERSONALITY_TO_INT[ui_state.sm["selfdriveState"].personality] if personality != ui_state.personality and ui_state.started: self._long_personality_setting.action_item.set_selected_button(personality) ui_state.personality = personality def show_event(self): self._update_toggles() def _update_toggles(self): ui_state.update_params() e2e_description = ( "openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " + "Experimental features are listed below:
" + "

End-to-End Longitudinal Control


" + "Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " + "Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " + "mistakes should be expected.
" + "

New Driving Visualization


" + "The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. " + "The Experimental mode logo will also be shown in the top right corner." ) if ui_state.CP is not None: if ui_state.has_longitudinal_control: self._toggles["ExperimentalMode"].action_item.set_enabled(True) self._toggles["ExperimentalMode"].set_description(e2e_description) self._long_personality_setting.action_item.set_enabled(True) else: # no long for now self._toggles["ExperimentalMode"].action_item.set_enabled(False) self._toggles["ExperimentalMode"].action_item.set_state(False) self._long_personality_setting.action_item.set_enabled(False) self._params.remove("ExperimentalMode") unavailable = "Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control." long_desc = unavailable + " openpilot longitudinal control may come in a future update." if ui_state.CP.alphaLongitudinalAvailable: if self._is_release: long_desc = unavailable + " " + ("An alpha version of openpilot longitudinal control can be tested, along with " + "Experimental mode, on non-release branches.") else: long_desc = "Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode." self._toggles["ExperimentalMode"].set_description("" + long_desc + "

" + e2e_description) else: self._toggles["ExperimentalMode"].set_description(e2e_description) self._update_experimental_mode_icon() # TODO: make a param control list item so we don't need to manage internal state as much here # refresh toggles from params to mirror external changes for param in self._toggle_defs: self._toggles[param].action_item.set_state(self._params.get_bool(param)) # these toggles need restart, block while engaged for toggle_def in self._toggle_defs: if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles: self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged) def _render(self, rect): self._scroller.render(rect) def _update_experimental_mode_icon(self): icon = "experimental.png" if self._toggles["ExperimentalMode"].action_item.get_state() else "experimental_white.png" self._toggles["ExperimentalMode"].set_icon(icon) def _handle_experimental_mode_toggle(self, state: bool): confirmed = self._params.get_bool("ExperimentalModeConfirmed") if state and not confirmed: def confirm_callback(result: int): if result == DialogResult.CONFIRM: self._params.put_bool("ExperimentalMode", True) self._params.put_bool("ExperimentalModeConfirmed", True) else: self._toggles["ExperimentalMode"].action_item.set_state(False) self._update_experimental_mode_icon() # show confirmation dialog content = (f"

{self._toggles['ExperimentalMode'].title}


" + f"

{self._toggles['ExperimentalMode'].description}

") dlg = ConfirmDialog(content, "Enable", rich=True) gui_app.set_modal_overlay(dlg, callback=confirm_callback) else: self._update_experimental_mode_icon() self._params.put_bool("ExperimentalMode", state) def _toggle_callback(self, state: bool, param: str): if param == "ExperimentalMode": self._handle_experimental_mode_toggle(state) return self._params.put_bool(param, state) if self._toggle_defs[param][3]: self._params.put_bool("OnroadCycleRequested", True) def _set_longitudinal_personality(self, button_index: int): self._params.put("LongitudinalPersonality", button_index)