""" Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/ """ import unicodedata import pyray as rl from iqpilot.selfdrive.car.vehicle_catalog import load_catalog from iqpilot.selfdrive.ui.mici.widgets.stock_button import BigButton, BigParamControl from iqpilot.selfdrive.ui.mici.layouts.settings.iq_widgets import MappedParamToggle from iqpilot.selfdrive.ui.ui_state import ui_state from iqpilot.system.ui.lib.application import gui_app, FontWeight, MousePos from iqpilot.system.ui.lib.text_measure import measure_text_cached from iqpilot.system.ui.widgets import Widget from iqpilot.system.ui.widgets.scroller import NavScroller from iqpilot.system.ui.lib.multilang import tr def _ascii_safe(text: str) -> str: return unicodedata.normalize("NFD", text).encode("ascii", "ignore").decode("ascii") def _load_platforms() -> dict: try: return load_catalog() except Exception: return {} class _PickerRow(Widget): HEIGHT = 92 def __init__(self, label: str, on_tap): super().__init__() self._label = label self._on_tap = on_tap self.set_rect(rl.Rectangle(0, 0, gui_app.width, self.HEIGHT)) self._font = gui_app.font(FontWeight.MEDIUM) def _handle_mouse_release(self, mouse_pos: MousePos): super()._handle_mouse_release(mouse_pos) self._on_tap(self._label) def _render(self, _): color = rl.Color(255, 255, 255, 255) if self.is_pressed else rl.Color(255, 255, 255, 200) ts = measure_text_cached(self._font, self._label, 46) rl.draw_text_ex(self._font, self._label, rl.Vector2(self._rect.x + 44, self._rect.y + (self.HEIGHT - ts.y) / 2), 46, 0, color) class _VerticalPicker(NavScroller): def __init__(self, options: list[str], on_pick): super().__init__(horizontal=False, snap_items=False, pad_start=20, pad_end=20) self._on_pick = on_pick rows = [_PickerRow(o, self._pick) for o in options] for row in rows: row.set_touch_valid_callback(lambda: self._scroller.scroll_panel.is_touch_valid()) self._scroller.add_widgets(rows) def _pick(self, option: str): gui_app.pop_widget() self._on_pick(option) class VehicleLayoutMici(NavScroller): def __init__(self): super().__init__() self._platforms = _load_platforms() self._vehicle_btn = BigButton(tr("vehicle")) self._vehicle_btn.set_click_callback(self._on_vehicle_clicked) self._toyota_long = BigParamControl(tr("enforce factory long."), "IQToyotaFactoryLong", toggle_callback=self._on_toyota_long) self._hyundai_tuning = MappedParamToggle(tr("hyundai long. tuning"), "IQHyundaiLongTune", [tr("off"), tr("dynamic"), tr("predictive")], [0, 1, 2]) self._subaru_snag = BigParamControl(tr("creep from standstill (beta)"), "IQSubaruCreepAssist") self._subaru_manual = BigParamControl(tr("stop and go manual brake"), "IQSubaruCreepAssistManualBrake") self._vw_pq_hca = BigParamControl(tr("PQ HCA status 7 mode"), "pqhca5or7Toggle") self._vw_lateral = BigParamControl(tr("lateral when cruise faulted"), "AllowLateralWhenLongUnavailable") self._vw_mqb_acc_resume = BigParamControl(tr("MQB ACC resume"), "iqMqbAccResume") self._vw_mqb_steering_lockout = BigParamControl(tr("MQB steering lockout"), "iqMqbSteeringLockout") self._tesla_vtb = BigParamControl(tr("virtual torque blending"), "IQTeslaTorqueBlend") self._tesla_fsd_visualization = BigParamControl(tr("FSD visuals"), "IQTeslaFsdVisualization") self._brand_widgets = { "toyota": [self._toyota_long], "hyundai": [self._hyundai_tuning], "subaru": [self._subaru_snag, self._subaru_manual], "volkswagen": [self._vw_pq_hca, self._vw_lateral, self._vw_mqb_acc_resume, self._vw_mqb_steering_lockout], "tesla": [self._tesla_vtb, self._tesla_fsd_visualization], } self._all_brand_widgets = [w for ws in self._brand_widgets.values() for w in ws] self._scroller.add_widgets([self._vehicle_btn] + self._all_brand_widgets) def _get_current_brand(self) -> str: bundle = ui_state.params.get("CarPlatformBundle") if bundle: return bundle.get("brand", "") if ui_state.CP: return getattr(ui_state.CP, "brand", "") return "" def _vw_flags(self): try: from iqdbc.car.volkswagen.values import CAR bundle = ui_state.params.get("CarPlatformBundle") if bundle and (platform := bundle.get("platform")): return CAR[platform].config.flags if ui_state.CP: return ui_state.CP.flags except Exception: pass return 0 def _uses_vw_hca_status_toggle(self) -> bool: from iqdbc.car.volkswagen.values import VolkswagenFlags return bool(self._vw_flags() & (VolkswagenFlags.PQ | VolkswagenFlags.MLB)) def _is_vw_mqb(self) -> bool: from iqdbc.car.volkswagen.values import VolkswagenFlags flags = self._vw_flags() return not bool(flags & (VolkswagenFlags.PQ | VolkswagenFlags.MLB | VolkswagenFlags.MEB | VolkswagenFlags.MEB_GEN2 | VolkswagenFlags.MQB_EVO)) def _supports_vw_lateral_when_faulted(self) -> bool: from iqdbc.car.volkswagen.values import VolkswagenFlags # PQ, MEB, MQB_EVO and base MQB all implement cruiseFaultLateralMode in carstate.py. # MLB does not. return not bool(self._vw_flags() & VolkswagenFlags.MLB) def _pretty_name(self, platform: str) -> str: for name, v in self._platforms.items(): if v.get("platform") == platform: make = v.get("make", "") if make and name.lower().startswith(make.lower() + " "): return name[len(make) + 1:] return name return _ascii_safe(platform).replace("_", " ").title() def _vehicle_status(self) -> str: bundle = ui_state.params.get("CarPlatformBundle") if bundle: name = _ascii_safe(bundle.get("name", "?")) make = bundle.get("make", "") if make and name.lower().startswith(make.lower() + " "): name = name[len(make) + 1:] return name[:1].upper() + name[1:] if ui_state.CP and ui_state.CP.carFingerprint not in ("", "MOCK"): return self._pretty_name(ui_state.CP.carFingerprint) return "tap to select" def _on_vehicle_clicked(self): if ui_state.params.get("CarPlatformBundle"): ui_state.params.remove("CarPlatformBundle") self._refresh() else: self._open_make_picker() def _open_make_picker(self): makes = sorted({v.get("make", "") for v in self._platforms.values() if v.get("make")}) label_to_make = {_ascii_safe(m): m for m in makes} gui_app.push_widget(_VerticalPicker(list(label_to_make.keys()), lambda lbl: self._open_model_picker(label_to_make.get(lbl, "")))) def _open_model_picker(self, make: str): if not make: return prefix = make + " " label_to_key: dict = {} for key in sorted(p for p, v in self._platforms.items() if v.get("make") == make): label = key[len(prefix):] if key.lower().startswith(prefix.lower()) else key label_to_key[_ascii_safe(label)] = key gui_app.push_widget(_VerticalPicker(list(label_to_key.keys()), lambda lbl: self._select_platform(label_to_key.get(lbl, "")))) def _select_platform(self, key: str): if key and (data := self._platforms.get(key)): ui_state.params.put("CarPlatformBundle", {**data, "name": key}) gui_app.pop_widgets_to(self) self._refresh() def _on_toyota_long(self, checked: bool): if checked and ui_state.params.get_bool("AlphaLongitudinalEnabled"): ui_state.params.put_bool("AlphaLongitudinalEnabled", False) ui_state.params.put_bool("OnroadCycleRequested", True) def _refresh(self): self._vehicle_btn.set_value(self._vehicle_status()) brand = self._get_current_brand() offroad = ui_state.is_offroad() uses_hca_status_toggle = self._uses_vw_hca_status_toggle() is_mqb = self._is_vw_mqb() supports_lateral_when_faulted = self._supports_vw_lateral_when_faulted() visible = set(self._brand_widgets.get(brand, [])) for w in self._all_brand_widgets: show = w in visible if w is self._vw_pq_hca: show = show and uses_hca_status_toggle elif w in (self._vw_mqb_acc_resume, self._vw_mqb_steering_lockout): show = show and is_mqb elif w is self._vw_lateral: show = show and supports_lateral_when_faulted w.set_visible(show) if show: w.refresh() for w in (self._toyota_long, self._subaru_snag, self._subaru_manual, self._tesla_vtb, self._tesla_fsd_visualization): w.set_enabled(offroad) def _update_state(self): super()._update_state() self._vehicle_btn.set_value(self._vehicle_status()) def show_event(self): super().show_event() self._refresh()