Files
StarPilot/openpilot/starpilot/ui/appearance_preferences.py
T
firestar5683 a82935f326 User interfaces and Android Auto
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: Shane Smiskol <shane@smiskol.com>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: pharmacomaniac <blittle65@gmail.com>

Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

200 lines
6.6 KiB
Python

"""Strict saved visibility choices shared by settings and onroad presentation."""
from dataclasses import dataclass
from enum import IntEnum
import math
import os
import stat
from typing import Any
from openpilot.common.constants import CV
from openpilot.starpilot.saved_source import read_saved
class CameraViewChoice(IntEnum):
AUTO = 0
DRIVER = 1
STANDARD = 2
WIDE = 3
NONE = 4
CAMERA_LABELS = ("Auto", "Driver", "Standard", "Wide", "None")
class LeadInfoMode(IntEnum):
OFF = 0
DISTANCE = 1
SPEED = 2
LEAD_INFO_LABELS = ("Off", "Distance", "Speed")
def format_lead_info(lead, mode: int, metric: bool | None) -> str:
"""The lead's distance or speed for the text above the lead marker; empty when there is nothing to show."""
if lead is None or not lead.present or mode not in (LeadInfoMode.DISTANCE, LeadInfoMode.SPEED) or metric is None:
return ""
try:
value = float(lead.dRel if mode == LeadInfoMode.DISTANCE else lead.vLead)
except (AttributeError, TypeError, ValueError, OverflowError):
return ""
if not math.isfinite(value):
return ""
value = max(value, 0.0)
if mode == LeadInfoMode.DISTANCE:
return f"{round(value if metric else value * 3.28084)} {'m' if metric else 'ft'}"
return f"{round(value * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))} {'km/h' if metric else 'mph'}"
@dataclass(frozen=True)
class SavedCameraView:
raw: bytes | None
value: CameraViewChoice | None
readable: bool
def read_camera_view(params: Any) -> SavedCameraView:
try:
raw, readable = read_saved(params, "CameraView", 8)
except (AttributeError, OSError, TypeError, ValueError):
return SavedCameraView(None, None, False)
if not readable:
return SavedCameraView(raw, None, False)
if raw is None:
return SavedCameraView(None, CameraViewChoice.STANDARD, True)
try:
if raw not in (b"0", b"1", b"2", b"3", b"4"):
raise ValueError("Invalid camera choice")
return SavedCameraView(raw, CameraViewChoice(int(raw)), True)
except (TypeError, ValueError):
return SavedCameraView(raw, None, True)
DEFAULTS = {
"DeveloperSidebar": False,
"HideSpeed": False,
"HideMaxSpeed": False,
"HideSteeringWheel": False,
"HideDMIcon": False,
"HidePersonalityAlerts": False,
"ShowBrakeStatus": False,
"PedalsOnUI": False,
"DriverCamera": False,
"StoppedTimer": False,
"StockConfidenceBallWidget": False,
"EnableTorqueBarWidget": True,
"RainbowPath": False,
"HideLeadMarker": True,
"LeadInfo": False,
"BlindSpotMetrics": True,
"SignalMetrics": False,
"ShowSpeedLimits": True,
"UseVienna": False,
}
MAX_RAW_BYTES = 16
# Superseded by the onroad customization document; read only to retire them.
RETIRED_DISPLAY_KEYS = ("HideSpeed", "HideMaxSpeed", "HideSteeringWheel", "HideDMIcon", "EnableTorqueBarWidget",
"StoppedTimer", "RainbowPath")
@dataclass(frozen=True)
class SavedVisibility:
raw: bytes | None
value: bool | None
readable: bool = True
@dataclass(frozen=True)
class SavedLeadInfo:
flag: SavedVisibility
mode_raw: bytes | None
mode: LeadInfoMode | None
readable: bool
def read_lead_info(params: Any) -> SavedLeadInfo:
flag = read_visibility(params, "LeadInfo")
try:
raw, readable = read_saved(params, "LeadInfoMode", 16)
except (AttributeError, OSError, TypeError, ValueError):
raw, readable = None, False
if not flag.readable or not readable:
return SavedLeadInfo(flag, raw, None, False)
if flag.value is None or raw not in (None, b"0", b"1", b"2"):
return SavedLeadInfo(flag, raw, None, True)
if not flag.value:
mode = LeadInfoMode.OFF
elif raw in (None, b"0", b"2"):
mode = LeadInfoMode.SPEED
else:
mode = LeadInfoMode.DISTANCE
return SavedLeadInfo(flag, raw, mode, True)
def read_visibility(params: Any, key: str) -> SavedVisibility:
if key not in DEFAULTS:
raise KeyError(key)
try:
path = params.get_param_path(key)
descriptor = os.open(path, os.O_RDONLY | os.O_NONBLOCK | os.O_NOFOLLOW)
except FileNotFoundError:
return SavedVisibility(None, DEFAULTS[key])
except (AttributeError, OSError, TypeError, ValueError):
return SavedVisibility(None, None, False)
try:
if not stat.S_ISREG(os.fstat(descriptor).st_mode):
return SavedVisibility(None, None, False)
raw = os.read(descriptor, MAX_RAW_BYTES + 1)
except OSError:
return SavedVisibility(None, None, False)
finally:
os.close(descriptor)
if len(raw) > MAX_RAW_BYTES:
return SavedVisibility(raw, None, False)
return SavedVisibility(raw, False if raw == b"0" else True if raw == b"1" else None)
@dataclass(frozen=True)
class OnroadAppearance:
show_developer_sidebar: bool = False
show_blindspot_border: bool = True
show_signal_border: bool = False
show_speed_limit_sign: bool = True
use_vienna_sign: bool = False
show_stock_confidence_ball: bool = False
show_lead_indicator: bool = False
lead_info_mode: LeadInfoMode = LeadInfoMode.OFF
lead_info_metric: bool | None = False
camera_view: CameraViewChoice = CameraViewChoice.STANDARD
driver_camera_on_reverse: bool = False
hide_personality_alerts: bool = False
wheel_pedal_feedback: bool = False
def onroad_appearance(params: Any) -> OnroadAppearance:
"""Bad saved bytes keep the visible baseline; no view writes during refresh."""
values = {key: read_visibility(params, key).value for key in DEFAULTS}
saved_lead_info = read_lead_info(params)
camera_view = read_camera_view(params).value
try:
metric_raw, metric_readable = read_saved(params, "IsMetric", 8)
except (AttributeError, OSError, TypeError, ValueError):
metric_raw, metric_readable = None, False
metric = False if metric_readable and metric_raw in (None, b"0") else True if metric_readable and metric_raw == b"1" else None
return OnroadAppearance(
show_developer_sidebar=values["DeveloperSidebar"] is True,
hide_personality_alerts=values["HidePersonalityAlerts"] is True,
wheel_pedal_feedback=values["ShowBrakeStatus"] is True or values["PedalsOnUI"] is True,
show_blindspot_border=values["BlindSpotMetrics"] is True,
show_signal_border=values["SignalMetrics"] is True,
show_speed_limit_sign=values["ShowSpeedLimits"] is True,
use_vienna_sign=values["UseVienna"] is True,
show_stock_confidence_ball=values["StockConfidenceBallWidget"] is True,
show_lead_indicator=values["HideLeadMarker"] is False,
lead_info_mode=saved_lead_info.mode if values["HideLeadMarker"] is False and saved_lead_info.mode is not None else LeadInfoMode.OFF,
lead_info_metric=metric,
camera_view=camera_view if camera_view is not None else CameraViewChoice.STANDARD,
driver_camera_on_reverse=values["DriverCamera"] is True,
)