Raindrops on Roses & Whiskers on Kittens

This commit is contained in:
firestar5683
2026-06-14 23:50:41 -05:00
parent 0262b934c4
commit 3ecd8be1f1
22 changed files with 1332 additions and 33 deletions
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
from collections.abc import Iterable
from typing import Any
from openpilot.common.params import ParamKeyType, Params
FAVORITE_SLOTS_PARAM = "StarPilotFavoriteSlots"
FAVORITE_SLOT_COUNT = 3
def default_favorite_slots() -> list[dict[str, Any]]:
return [
{"enabled": False, "show_onroad": False, "key": None, "label": ""}
for _ in range(FAVORITE_SLOT_COUNT)
]
def _load_raw_slots(raw_slots: Any) -> list[Any]:
if raw_slots in (None, "", b""):
return default_favorite_slots()
if isinstance(raw_slots, bytes):
raw_slots = raw_slots.decode("utf-8", errors="replace")
if isinstance(raw_slots, str):
try:
raw_slots = json.loads(raw_slots)
except json.JSONDecodeError:
return default_favorite_slots()
if isinstance(raw_slots, dict):
raw_slots = raw_slots.get("slots", [])
return raw_slots if isinstance(raw_slots, list) else default_favorite_slots()
def _get_key_type(params: Params, key: str):
for getter_name in ("get_type", "get_key_type"):
getter = getattr(params, getter_name, None)
if getter is None:
continue
try:
return getter(key)
except Exception:
return None
return None
def is_bool_param(params: Params, key: str | None, eligible_keys: Iterable[str] | None = None) -> bool:
if not key:
return False
if eligible_keys is not None and key not in set(eligible_keys):
return False
return _get_key_type(params, key) == ParamKeyType.BOOL
def normalize_favorite_slots(raw_slots: Any, params: Params | None = None,
eligible_keys: Iterable[str] | None = None) -> list[dict[str, Any]]:
slots = default_favorite_slots()
eligible = set(eligible_keys) if eligible_keys is not None else None
for idx, raw_slot in enumerate(_load_raw_slots(raw_slots)[:FAVORITE_SLOT_COUNT]):
if not isinstance(raw_slot, dict):
continue
key = raw_slot.get("key")
if key is not None:
key = str(key).strip() or None
if key and ((eligible is not None and key not in eligible) or (params is not None and not is_bool_param(params, key))):
key = None
label = str(raw_slot.get("label") or "").strip()
if len(label) > 32:
label = label[:32].rstrip()
slots[idx] = {
"enabled": bool(raw_slot.get("enabled", False)),
"show_onroad": bool(raw_slot.get("show_onroad", False)),
"key": key,
"label": label if key else "",
}
return slots
def load_favorite_slots(params: Params | None = None, eligible_keys: Iterable[str] | None = None) -> list[dict[str, Any]]:
params = params or Params(return_defaults=True)
try:
raw_slots = params.get(FAVORITE_SLOTS_PARAM)
except Exception:
raw_slots = None
return normalize_favorite_slots(raw_slots, params=params, eligible_keys=eligible_keys)
def save_favorite_slots(slots: list[dict[str, Any]], params: Params | None = None) -> list[dict[str, Any]]:
params = params or Params(return_defaults=True)
normalized = normalize_favorite_slots(slots, params=params)
params.put(FAVORITE_SLOTS_PARAM, normalized)
return normalized
def request_starpilot_toggle_refresh(params_memory: Params | None = None) -> None:
params_memory = params_memory or Params(memory=True)
params_memory.put_bool("StarPilotTogglesUpdated", True)
def toggle_favorite_slot(slot_index: int, params: Params | None = None, params_memory: Params | None = None) -> bool:
if slot_index < 0 or slot_index >= FAVORITE_SLOT_COUNT:
return False
params = params or Params(return_defaults=True)
slots = load_favorite_slots(params)
slot = slots[slot_index]
key = slot.get("key")
if not slot.get("enabled") or not is_bool_param(params, key):
return False
next_value = not params.get_bool(key)
put_bool = getattr(params, "put_bool_nonblocking", None) or getattr(params, "put_bool", None)
if put_bool is None:
return False
put_bool(key, next_value)
request_starpilot_toggle_refresh(params_memory)
return True
+21
View File
@@ -137,6 +137,9 @@ BUTTON_FUNCTIONS = {
"BOOKMARK": 8,
"AOL_TOGGLE": 9,
"SLC_ADOPT": 10,
"FAVORITE_1": 11,
"FAVORITE_2": 12,
"FAVORITE_3": 13,
}
CANCEL_BUTTON_MIGRATION_KEY = "CancelButtonControlsMigrated"
@@ -482,6 +485,11 @@ class StarPilotVariables:
# runtime behavior to defaults after the driver has configured them.
return self.get_value(key, cast=float, condition=condition, respect_tuning_level=False)
@staticmethod
def set_favorite_button_flags(toggle, suffix, button_control):
for slot_number in range(1, 4):
setattr(toggle, f"favorite_{slot_number}_via_{suffix}", button_control == BUTTON_FUNCTIONS[f"FAVORITE_{slot_number}"])
def _sync_stock_param(self, key, stock_key, live_value):
try:
live_value = float(live_value)
@@ -876,6 +884,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_distance = distance_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_distance = toggle.openpilot_longitudinal and distance_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_distance = distance_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "distance", distance_button_control)
distance_button_control_long = self.get_button_function("LongDistanceButtonControl")
toggle.experimental_mode_via_distance_long = toggle.openpilot_longitudinal and distance_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -887,6 +896,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_distance_long = distance_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_distance_long = toggle.openpilot_longitudinal and distance_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_distance_long = distance_button_control_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "distance_long", distance_button_control_long)
distance_button_control_very_long = self.get_button_function("VeryLongDistanceButtonControl")
toggle.experimental_mode_via_distance_very_long = toggle.openpilot_longitudinal and distance_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -898,6 +908,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_distance_very_long = distance_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_distance_very_long = toggle.openpilot_longitudinal and distance_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_distance_very_long = distance_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "distance_very_long", distance_button_control_very_long)
cancel_button_control = self.get_button_function("CancelButtonControl", condition=toggle.remap_cancel_to_distance)
toggle.experimental_mode_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -909,6 +920,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_cancel = cancel_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel = cancel_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "cancel", cancel_button_control)
cancel_button_control_long = self.get_button_function("LongCancelButtonControl", condition=toggle.remap_cancel_to_distance)
toggle.experimental_mode_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -920,6 +932,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_cancel_long = cancel_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel_long = cancel_button_control_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "cancel_long", cancel_button_control_long)
cancel_button_control_very_long = self.get_button_function("VeryLongCancelButtonControl", condition=toggle.remap_cancel_to_distance)
toggle.experimental_mode_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -931,6 +944,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_cancel_very_long = cancel_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel_very_long = cancel_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "cancel_very_long", cancel_button_control_very_long)
toggle.frogsgomoo_tweak = self.get_value("FrogsGoMoosTweak", condition=toggle.openpilot_longitudinal and toggle.car_make == "toyota")
toggle.stoppingDecelRate = 0.01 if toggle.frogsgomoo_tweak else toggle.stoppingDecelRate
@@ -986,6 +1000,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_lkas = lkas_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_lkas = toggle.openpilot_longitudinal and lkas_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_lkas = lkas_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "lkas", lkas_button_control)
has_canfd_media_buttons = toggle.car_make == "hyundai" and bool(CP.flags & HyundaiFlags.CANFD)
mode_button_control = self.get_button_function("ModeButtonControl", condition=has_canfd_media_buttons)
@@ -998,6 +1013,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_mode = mode_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_mode = mode_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "mode", mode_button_control)
mode_button_control_long = self.get_button_function("LongModeButtonControl", condition=has_canfd_media_buttons)
toggle.experimental_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -1009,6 +1025,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "mode_long", mode_button_control_long)
mode_button_control_very_long = self.get_button_function("VeryLongModeButtonControl", condition=has_canfd_media_buttons)
toggle.experimental_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -1020,6 +1037,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "mode_very_long", mode_button_control_very_long)
star_button_control = self.get_button_function("StarButtonControl", condition=has_canfd_media_buttons)
toggle.experimental_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -1031,6 +1049,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_star = star_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_star = star_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "star", star_button_control)
star_button_control_long = self.get_button_function("LongStarButtonControl", condition=has_canfd_media_buttons)
toggle.experimental_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -1042,6 +1061,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "star_long", star_button_control_long)
star_button_control_very_long = self.get_button_function("VeryLongStarButtonControl", condition=has_canfd_media_buttons)
toggle.experimental_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -1053,6 +1073,7 @@ class StarPilotVariables:
toggle.switchback_mode_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
self.set_favorite_button_flags(toggle, "star_very_long", star_button_control_very_long)
toggle.has_canfd_media_buttons = has_canfd_media_buttons
toggle.lock_doors_timer = self.get_value("LockDoorsTimer", cast=float, condition=(toggle.car_make == "toyota"))
@@ -0,0 +1,80 @@
from openpilot.common.params import ParamKeyType
from openpilot.starpilot.common.favorite_slots import (
FAVORITE_SLOTS_PARAM,
default_favorite_slots,
load_favorite_slots,
toggle_favorite_slot,
)
class FakeParams:
def __init__(self):
self.store = {}
self.types = {
FAVORITE_SLOTS_PARAM: ParamKeyType.JSON,
"RedneckCruise": ParamKeyType.BOOL,
"NotBool": ParamKeyType.INT,
}
def get(self, key):
return self.store.get(key)
def get_bool(self, key):
return bool(self.store.get(key, False))
def put(self, key, value):
self.store[key] = value
def put_bool(self, key, value):
self.store[key] = bool(value)
def put_bool_nonblocking(self, key, value):
self.put_bool(key, value)
def get_type(self, key):
return self.types.get(key, ParamKeyType.STRING)
def test_load_favorite_slots_defaults_on_empty_payload():
params = FakeParams()
assert load_favorite_slots(params) == default_favorite_slots()
def test_load_favorite_slots_filters_non_bool_keys():
params = FakeParams()
params.put(FAVORITE_SLOTS_PARAM, [
{"enabled": True, "show_onroad": True, "key": "NotBool", "label": "Bad"},
{"enabled": True, "show_onroad": False, "key": "RedneckCruise", "label": "Redneck Cruise"},
])
slots = load_favorite_slots(params)
assert slots[0]["key"] is None
assert slots[0]["enabled"] is True
assert slots[1]["key"] == "RedneckCruise"
assert slots[1]["show_onroad"] is False
def test_toggle_favorite_slot_ignores_disabled_slot():
params = FakeParams()
params.put("RedneckCruise", False)
params.put(FAVORITE_SLOTS_PARAM, [
{"enabled": False, "show_onroad": True, "key": "RedneckCruise", "label": "Redneck Cruise"},
])
assert toggle_favorite_slot(0, params, FakeParams()) is False
assert params.get_bool("RedneckCruise") is False
def test_toggle_favorite_slot_flips_bool_and_requests_refresh():
params = FakeParams()
memory = FakeParams()
params.put("RedneckCruise", False)
params.put(FAVORITE_SLOTS_PARAM, [
{"enabled": True, "show_onroad": False, "key": "RedneckCruise", "label": "Redneck Cruise"},
])
assert toggle_favorite_slot(0, params, memory) is True
assert params.get_bool("RedneckCruise") is True
assert memory.get_bool("StarPilotTogglesUpdated") is True
@@ -105,6 +105,16 @@ def test_button_function_ignores_tuning_level_gate():
assert variables.get_button_function("LKASButtonControl") == spv.BUTTON_FUNCTIONS["AOL_TOGGLE"]
def test_favorite_button_flags_map_to_three_slots():
toggle = SimpleNamespace()
spv.StarPilotVariables.set_favorite_button_flags(toggle, "lkas", spv.BUTTON_FUNCTIONS["FAVORITE_2"])
assert toggle.favorite_1_via_lkas is False
assert toggle.favorite_2_via_lkas is True
assert toggle.favorite_3_via_lkas is False
def test_set_speed_limit_available_on_openpilot_longitudinal():
assert spv.set_speed_limit_available(openpilot_longitudinal=True, has_cc_long=False, pcm_cruise_speed=True) is True