""" Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos """ from collections.abc import Callable import pyray as rl from iqpilot.common.params import Params from iqpilot.selfdrive.ui.ui_state import ui_state from iqpilot.system.ui.lib.multilang import tr from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem, IQToggleAction, SafeIQToggleAction from iqpilot.system.ui.iqwidgets.widgets.list_view import OptionControl from iqpilot.system.ui.widgets import Widget from iqpilot.system.ui.widgets.network import NavButton from iqpilot.system.ui.widgets.scroller_tici import Scroller from iqdbc.car.volkswagen.values import CAR, VolkswagenFlags def _toggle_item(title: str, description: str, param: str) -> IQListItem: return IQListItem( title=lambda t=title: tr(t), description=lambda d=description: tr(d), action_item=IQToggleAction( initial_state=Params().get_bool(param), callback=lambda state, p=param: Params().put_bool(p, state), param=param, ), ) def _safe_toggle_item(title: str, description: str, param: str, default_on: bool = True) -> IQListItem: return IQListItem( title=lambda t=title: tr(t), description=lambda d=description: tr(d), action_item=SafeIQToggleAction( param=param, default_on=default_on, ), ) def _option_item(title: str, description: str, param: str, minimum: int, maximum: int, step: int = 1, label_callback=None, use_float_scaling: bool = False) -> IQListItem: return IQListItem( title=lambda t=title: tr(t), description=lambda d=description: tr(d), action_item=OptionControl( param=param, min_value=minimum, max_value=maximum, value_change_step=step, use_float_scaling=use_float_scaling, label_callback=label_callback, ), ) class IQDynamicLayout(Widget): def __init__(self, back_btn_callback: Callable): super().__init__() self._params = Params() self._back_button = NavButton(tr("Back")) self._back_button.set_click_callback(back_btn_callback) items = self._initialize_items() self._scroller = Scroller(items, line_separator=True, spacing=0) @staticmethod def _is_pq() -> bool: bundle = ui_state.params.get("CarPlatformBundle") if bundle: platform = bundle.get("platform") if platform: try: return bool(CAR[platform].config.flags & VolkswagenFlags.PQ) except (KeyError, AttributeError): return False elif ui_state.CP: return bool(ui_state.CP.flags & VolkswagenFlags.PQ) return False def _initialize_items(self): ms_to_mph = 2.23694 def speed_label(value: float | int) -> str: try: speed_ms = float(value) except (TypeError, ValueError): return "-- mph" return f"{int(round(speed_ms * ms_to_mph))} mph" def distance_label(value: float | int) -> str: try: distance_m = float(value) except (TypeError, ValueError): return "-- m" if self._params.get_bool("IsMetric"): return f"{distance_m:.1f} m" return f"{distance_m * 3.28084:.0f} ft" def seconds_label(value: float | int) -> str: try: seconds = float(value) except (TypeError, ValueError): return "--.-s" return f"{seconds:.1f}s" self._blend_radar_toggle = _toggle_item( "Blend IQ.Pilot + Stock ACC Radar", "VW PQ only. Keep the factory ACC radar engaged and use its acceleration as IQ.Dynamic's ACC " "(chill) command, while IQ.Pilot still owns blended/e2e control for low speed, stops, and traffic.", "IQDynamicBlendStockRadar", ) self._pq_only_items = [self._blend_radar_toggle] return [ self._blend_radar_toggle, _toggle_item( "IQ.Dynamic Curves", "Allow IQ.Dynamic to enter blended control for curves and strong vision slowdown cues.", "IQDynamicConditionalCurves", ), _toggle_item( "IQ.Dynamic Slower Lead", "Allow IQ.Dynamic to switch toward blended control when a slower lead vehicle is detected.", "IQDynamicConditionalSlowerLead", ), _toggle_item( "IQ.Dynamic Stopped Lead", "Allow IQ.Dynamic to react more aggressively when a lead vehicle is nearly stopped.", "IQDynamicConditionalStoppedLead", ), _toggle_item( "IQ.Dynamic Model Stops", "Allow IQ.Dynamic to switch toward blended control for stop-sign and stop-light style vision stops.", "IQDynamicConditionalModelStops", ), _toggle_item( "IQ.Dynamic SLC Fallback", "Allow IQ.Dynamic to request blended control when Speed Limit Controller has no usable target.", "IQDynamicConditionalSLCFallback", ), _option_item( "IQ.Dynamic Low Speed", "Below this speed, IQ.Dynamic prefers blended control when no lead is present.", "IQDynamicConditionalSpeed", 500, 3500, step=50, use_float_scaling=True, label_callback=speed_label, ), _toggle_item( "IQ Force Stops", "Bring the car to a complete stop for stop signs and stop lights, and feather the brake in the final meters of " "every stop so it settles gently instead of rocking on its suspension. Yields full braking authority when a lead " "is close. Override with the accelerator.", "IQForceStops", ), _option_item( "IQ.Dynamic Lead Speed", "Below this speed, IQ.Dynamic prefers blended control even with a tracked lead.", "IQDynamicConditionalLeadSpeed", 500, 4000, step=50, use_float_scaling=True, label_callback=speed_label, ), _option_item( "IQ.Dynamic Model Stop Time", "Sets the vision stop prediction time horizon used by IQ.Dynamic and IQ Force Stops. Shorter values react later. Longer values react earlier.", "IQDynamicModelStopTime", 100, 600, step=25, use_float_scaling=True, label_callback=seconds_label, ), _option_item( "IQ.Dynamic Min Force Stop Length", "When IQ Force Stops is enabled, keep at least this much stopping distance in the force-stop ramp. Set to 0 to disable the minimum.", "IQDynamicMinimumForceStopLength", 0, 5000, step=50, use_float_scaling=True, label_callback=distance_label, ), ] def _render(self, rect: rl.Rectangle): is_pq = self._is_pq() for item in self._pq_only_items: item.set_visible(is_pq) self._back_button.set_position(self._rect.x, self._rect.y + 20) self._back_button.render() content_rect = rl.Rectangle( rect.x, rect.y + self._back_button.rect.height + 40, rect.width, rect.height - self._back_button.rect.height - 40, ) self._scroller.render(content_rect) def show_event(self): self._scroller.show_event()