mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
a82935f326
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>
176 lines
10 KiB
Python
176 lines
10 KiB
Python
"""Parked, source-bound edits for supported onroad visibility controls."""
|
|
|
|
from collections.abc import Callable
|
|
from dataclasses import replace
|
|
|
|
from openpilot.starpilot.saved_document import commit_exact
|
|
from openpilot.starpilot.ui.appearance_preferences import (CAMERA_LABELS, DEFAULTS, LEAD_INFO_LABELS, MAX_RAW_BYTES,
|
|
RETIRED_DISPLAY_KEYS, read_camera_view, read_lead_info, read_visibility)
|
|
from openpilot.starpilot.ui.feature_settings_state import FeatureRow, FeatureSettingsRequest, FeatureSettingsState
|
|
from openpilot.starpilot.ui.presentation import Profile
|
|
|
|
|
|
# Widget visibility, the stopped timer and Rainbow Road live in the onroad customization document.
|
|
LABELS = {
|
|
"DeveloperSidebar": "Developer Sidebar",
|
|
"HidePersonalityAlerts": "Hide Personality Alerts",
|
|
"ShowBrakeStatus": "Wheel brake and acceleration colors",
|
|
"DriverCamera": "Driver Camera on Reverse",
|
|
"StockConfidenceBallWidget": "Use StarPilot Widgets",
|
|
"HideLeadMarker": "Lead Indicator",
|
|
"LeadInfo": "Lead Info",
|
|
"SignalMetrics": "C4 amber signal border",
|
|
"BlindSpotMetrics": "C4 red blind-spot border",
|
|
}
|
|
|
|
|
|
class AppearanceOwner:
|
|
def __init__(self, params, parked: Callable[[], bool]):
|
|
self.params = params
|
|
self.parked = parked
|
|
|
|
def snapshot(self, profile: Profile, *, include_large: bool = False) -> FeatureSettingsState:
|
|
parked = self.parked()
|
|
compact_only = ("StockConfidenceBallWidget", "DriverCamera", "HideLeadMarker", "LeadInfo")
|
|
keys = tuple(key for key in DEFAULTS if key not in compact_only and key in LABELS) if profile == Profile.LARGE else \
|
|
("DriverCamera", "StockConfidenceBallWidget", "HidePersonalityAlerts", "ShowBrakeStatus",
|
|
"HideLeadMarker", "LeadInfo", "SignalMetrics", "BlindSpotMetrics")
|
|
if profile == Profile.COMPACT and include_large:
|
|
keys = ("DeveloperSidebar", *keys)
|
|
rows = []
|
|
if profile == Profile.COMPACT:
|
|
saved_camera = read_camera_view(self.params)
|
|
camera_valid = saved_camera.value is not None and saved_camera.readable
|
|
rows.append(FeatureRow("CameraView", "Camera View",
|
|
CAMERA_LABELS[saved_camera.value] if camera_valid else "Invalid saved choice",
|
|
source=saved_camera.raw, choices=CAMERA_LABELS, available=parked and saved_camera.readable,
|
|
reason="" if camera_valid else "Saved choice cannot be read" if not saved_camera.readable else "Choose Standard to repair",
|
|
repair_value="Standard" if saved_camera.readable and not camera_valid else ""))
|
|
for key in keys:
|
|
if key == "ShowBrakeStatus":
|
|
saved, pedals = read_visibility(self.params, key), read_visibility(self.params, "PedalsOnUI")
|
|
readable = saved.readable and pedals.readable
|
|
valid = readable and saved.value is not None and pedals.value is not None
|
|
value = ("On" if saved.value or pedals.value else "Off") if valid else "Invalid saved choice"
|
|
rows.append(FeatureRow(key, LABELS[key], value, source=saved.raw, choices=("Off", "On"),
|
|
available=parked and readable, dependencies=(("PedalsOnUI", pedals.raw),),
|
|
reason="" if valid else "Saved choice cannot be read" if not readable else "Choose Off to repair",
|
|
repair_value="Off" if readable and not valid else ""))
|
|
continue
|
|
if key == "LeadInfo":
|
|
saved = read_lead_info(self.params)
|
|
marker = read_visibility(self.params, "HideLeadMarker")
|
|
valid = saved.mode is not None and saved.readable
|
|
marker_on = marker.readable and marker.value is False
|
|
rows.append(FeatureRow(key, LABELS[key], LEAD_INFO_LABELS[saved.mode] if valid else "Invalid saved choice",
|
|
source=saved.flag.raw, related_source=saved.mode_raw,
|
|
choices=LEAD_INFO_LABELS if valid else (), available=parked and saved.readable and marker_on,
|
|
reason="Turn on Lead Indicator first" if not marker_on else
|
|
"Saved choice cannot be read" if not saved.readable else
|
|
"Choose Off to repair" if not valid else "",
|
|
dependencies=(("HideLeadMarker", marker.raw),),
|
|
repair_value="Off" if saved.readable and not valid else ""))
|
|
continue
|
|
saved = read_visibility(self.params, key)
|
|
valid = saved.value is not None and saved.readable
|
|
displayed = not saved.value if key in ("HideLeadMarker", "StockConfidenceBallWidget") else saved.value
|
|
value = ("On" if displayed else "Off") if valid else "Invalid saved choice"
|
|
reason = ("" if valid else "Saved choice cannot be read" if not saved.readable else "Choose default to repair")
|
|
default_displayed = not DEFAULTS[key] if key in ("HideLeadMarker", "StockConfidenceBallWidget") else DEFAULTS[key]
|
|
repair = ("On" if default_displayed else "Off") if saved.readable and not valid else ""
|
|
rows.append(FeatureRow(key, LABELS[key], value, source=saved.raw, choices=("Off", "On"),
|
|
available=parked and saved.readable, reason=reason, repair_value=repair))
|
|
defaults = {key: ('On' if (not value if key in ('HideLeadMarker', 'StockConfidenceBallWidget') else value) else 'Off')
|
|
for key, value in DEFAULTS.items()}
|
|
defaults.update(CameraView='Standard', LeadInfo='Off')
|
|
rows = tuple(replace(row, default_value=defaults[row.key]) if row.key in defaults else row for row in rows)
|
|
return FeatureSettingsState(page="appearance", title="Onroad HUD" if profile == Profile.LARGE else "Visuals",
|
|
subtitle="Display preferences; border highlights apply to C4 only.",
|
|
rows=tuple(rows), parked=parked)
|
|
|
|
def apply(self, request: FeatureSettingsRequest) -> bool:
|
|
if request.key == "ShowBrakeStatus":
|
|
return self._save_wheel_feedback(request)
|
|
if request.key == "LeadInfo":
|
|
return self._save_lead_info(request)
|
|
if request.key == "CameraView" and request.value in CAMERA_LABELS:
|
|
raw = str(CAMERA_LABELS.index(request.value)).encode()
|
|
max_bytes = 8
|
|
elif request.key in DEFAULTS and request.key not in RETIRED_DISPLAY_KEYS and request.value in ("Off", "On"):
|
|
desired = (request.value != "On") if request.key in ("HideLeadMarker", "StockConfidenceBallWidget") else request.value == "On"
|
|
raw = b"1" if desired else b"0"
|
|
max_bytes = MAX_RAW_BYTES
|
|
else:
|
|
return False
|
|
result = commit_exact(self.params, key=request.key, max_bytes=max_bytes, raw=raw, expected=request.expected,
|
|
authorized=self.parked, temp_prefix=".appearance-")
|
|
return result.committed and result.verified
|
|
|
|
def _save_wheel_feedback(self, request: FeatureSettingsRequest) -> bool:
|
|
saved = {key: read_visibility(self.params, key) for key in ("ShowBrakeStatus", "PedalsOnUI")}
|
|
if (request.value not in ("Off", "On") or any(not value.readable for value in saved.values()) or
|
|
request.expected != saved["ShowBrakeStatus"].raw or
|
|
request.dependencies != (("PedalsOnUI", saved["PedalsOnUI"].raw),) or
|
|
any(value.value is None for value in saved.values()) and request.value != "Off"):
|
|
return False
|
|
expected = {key: value.raw for key, value in saved.items()}
|
|
|
|
def authorized():
|
|
current = {key: read_visibility(self.params, key) for key in expected}
|
|
return self.parked() and all(value.readable and value.raw == expected[key] for key, value in current.items())
|
|
|
|
# Enable the canonical flag before clearing its old alias; disable the alias first.
|
|
desired = {"ShowBrakeStatus": b"1" if request.value == "On" else b"0", "PedalsOnUI": b"0"}
|
|
order = ("ShowBrakeStatus", "PedalsOnUI") if request.value == "On" else ("PedalsOnUI", "ShowBrakeStatus")
|
|
for key in order:
|
|
if expected[key] == desired[key]:
|
|
continue
|
|
result = commit_exact(self.params, key=key, max_bytes=MAX_RAW_BYTES, raw=desired[key],
|
|
expected=expected[key], authorized=authorized, temp_prefix=".wheel-feedback-")
|
|
if not result.committed or not result.verified:
|
|
return False
|
|
expected[key] = desired[key]
|
|
return authorized()
|
|
|
|
def _save_lead_info(self, request: FeatureSettingsRequest) -> bool:
|
|
if request.value not in LEAD_INFO_LABELS:
|
|
return False
|
|
saved = read_lead_info(self.params)
|
|
marker = read_visibility(self.params, "HideLeadMarker")
|
|
if (not saved.readable or saved.flag.raw != request.expected or saved.mode_raw != request.related_source or
|
|
not marker.readable or marker.value is not False or
|
|
request.dependencies != (("HideLeadMarker", marker.raw),) or
|
|
saved.mode is None and request.value != "Off"):
|
|
return False
|
|
|
|
desired_mode = str(LEAD_INFO_LABELS.index(request.value)).encode()
|
|
desired_flag = b"0" if request.value == "Off" else b"1"
|
|
flag_raw, mode_raw = saved.flag.raw, saved.mode_raw
|
|
|
|
def authorized(expected_flag: bytes | None, expected_mode: bytes | None) -> bool:
|
|
current = read_lead_info(self.params)
|
|
current_marker = read_visibility(self.params, "HideLeadMarker")
|
|
return (self.parked() and current.readable and current.flag.raw == expected_flag and
|
|
current.mode_raw == expected_mode and current_marker.readable and current_marker.value is False and
|
|
current_marker.raw == marker.raw)
|
|
|
|
def write(key: str, raw: bytes, expected: bytes | None, expected_flag: bytes | None,
|
|
expected_mode: bytes | None) -> bool:
|
|
result = commit_exact(self.params, key=key, max_bytes=MAX_RAW_BYTES, raw=raw, expected=expected,
|
|
authorized=lambda: authorized(expected_flag, expected_mode), temp_prefix=".lead-info-")
|
|
return result.committed and result.verified
|
|
|
|
if desired_flag == b"0" and flag_raw != b"0":
|
|
if not write("LeadInfo", b"0", flag_raw, flag_raw, mode_raw):
|
|
return False
|
|
flag_raw = b"0"
|
|
if mode_raw != desired_mode:
|
|
if not write("LeadInfoMode", desired_mode, mode_raw, flag_raw, mode_raw):
|
|
return False
|
|
mode_raw = desired_mode
|
|
if desired_flag == b"1" and flag_raw != b"1":
|
|
if not write("LeadInfo", b"1", flag_raw, flag_raw, mode_raw):
|
|
return False
|
|
flag_raw = b"1"
|
|
return authorized(flag_raw, mode_raw)
|