Files
StarPilot/openpilot/starpilot/favorites/layout_actions.py
T
firestar5683 80fbb4f6db Feature registration
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior.

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: Kacper Rączy <gfw.kra@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
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: haraschax <6804392+haraschax@users.noreply.github.com>
Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

95 lines
4.3 KiB
Python

"""Quick Select toggles for display switches retired into the onroad layout.
The legacy keys and labels stay, so slots saved before the retirement keep working.
Each toggle edits the active profile's layout, guarded by the exact saved bytes.
"""
import hashlib
import json
from collections.abc import Callable
from openpilot.starpilot.favorites.state import FavoriteAction
from openpilot.starpilot.saved_document import commit_exact
from openpilot.starpilot.saved_source import read_saved
from openpilot.starpilot.ui.onroad_customization import (
MAX_BYTES, PARAM_KEY, STOPPED_TIMER_WIDGET, decode_document, default_document, validate_document,
)
def _widget(key):
def get(document, profile):
return document["layouts"][profile][key]["enabled"]
def put(document, profile, value):
document["layouts"][profile][key]["enabled"] = value
return get, put
def _max_speed():
# The large card hides its MAX line; the small UI has a separate MAX widget.
def get(document, profile):
return document.get("showMaxSpeed", True) if profile == "large" else document["layouts"][profile]["max_speed"]["enabled"]
def put(document, profile, value):
if profile == "large":
document["showMaxSpeed"] = value
else:
document["layouts"][profile]["max_speed"]["enabled"] = value
return get, put
def _rainbow():
def get(document, profile):
return document["roadColors"][profile].get("pathMode") == "rainbow"
def put(document, profile, value):
document["roadColors"][profile]["pathMode"] = "rainbow" if value else "acceleration"
return get, put
# key: (label, accessor, inverted, profiles). Inverted keys read On when the widget is hidden, as before.
TOGGLES = {
"HideSpeed": ("Hide Current Speed", _widget("current_speed"), True, ("large",)),
"HideMaxSpeed": ("Hide MAX Speed", _max_speed(), True, ("large", "compact")),
"HideSteeringWheel": ("Hide Steering Wheel", _widget("steering_wheel"), True, ("large", "compact")),
"HideDMIcon": ("Hide Driver Monitoring Icon", _widget("driver_monitor"), True, ("large", "compact")),
"EnableTorqueBarWidget": ("Show Torque Bar", _widget("torque_bar"), False, ("large", "compact")),
"StoppedTimer": ("Stopped Timer", _widget(STOPPED_TIMER_WIDGET), False, ("compact",)),
"RainbowPath": ("Rainbow Road", _rainbow(), False, ("large", "compact")),
}
def layout_actions(params, profile: str, authorized: Callable[[], bool],
on_change: Callable[[], None] = lambda: None) -> dict[str, FavoriteAction]:
raw, readable = read_saved(params, PARAM_KEY, MAX_BYTES)
document = None
if readable:
try:
document = decode_document(raw) if raw is not None else default_document()
except (ValueError, TypeError, UnicodeError, RecursionError):
document = None
actions = {}
for key, (label, (get, put), inverted, profiles) in TOGGLES.items():
supported = profile in profiles
value = (get(document, profile) != inverted) if document is not None and supported else None
reason = ("" if value is not None else "Only on the small UI" if not supported and profile == "large" else
"Only on the large UI" if not supported else "Saved layout needs repair in Galaxy")
token = hashlib.sha256(repr((key, profile, raw)).encode()).hexdigest()
def invoke(get=get, put=put, inverted=inverted, expected=raw):
current, fresh = read_saved(params, PARAM_KEY, MAX_BYTES)
if not fresh or current != expected or not authorized():
return False
changed = decode_document(current) if current is not None else default_document()
put(changed, profile, not get(changed, profile))
encoded = json.dumps(validate_document(changed), separators=(",", ":"), allow_nan=False).encode()
result = commit_exact(params, key=PARAM_KEY, max_bytes=MAX_BYTES, raw=encoded, expected=current,
authorized=authorized, temp_prefix=".favorite-layout-")
if result.committed and result.verified:
on_change()
return True
return False
available = value is not None and authorized()
actions[key] = FavoriteAction(key, label, "toggle", "Unavailable" if value is None else "On" if value else "Off",
available, "" if available else reason or "Available while the driving screen is shown",
token, invoke if available else None, "Visual")
return actions