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>
107 lines
5.2 KiB
Python
107 lines
5.2 KiB
Python
"""Parked, source-bound edits to the optional offroad power policy."""
|
|
|
|
from collections.abc import Callable
|
|
from dataclasses import replace
|
|
|
|
from openpilot.starpilot.power.offroad_preferences import KEY, PowerPolicy, SavedPower, decode, read_saved, to_value
|
|
from openpilot.starpilot.ui.feature_settings_state import FeatureRow, FeatureSettingsRequest, FeatureSettingsState, row_change
|
|
|
|
|
|
ENABLED = "power:enabled"
|
|
HOURS = "power:hours"
|
|
VOLTS = "power:volts"
|
|
POWER_KEYS = frozenset((ENABLED, HOURS, VOLTS))
|
|
HOUR_CHOICES = tuple(f"{hour} h" for hour in range(1, 31))
|
|
VOLT_CHOICES = tuple(f"{tenths / 10:.1f} V" for tenths in range(118, 126))
|
|
|
|
|
|
def power_row_change(row: FeatureRow, direction: int = 1) -> FeatureSettingsRequest | None:
|
|
if row.key in (HOURS, VOLTS) and row.value in row.choices:
|
|
next_index = row.choices.index(row.value) + direction
|
|
if next_index < 0 or next_index >= len(row.choices):
|
|
return None # a boundary press must never wrap 30 hours to 1 hour
|
|
return row_change(row, direction)
|
|
|
|
|
|
def confirm_question(request: FeatureSettingsRequest) -> str:
|
|
if request.key == ENABLED:
|
|
if request.value == "On":
|
|
policy = decode(request.expected) if request.expected is not None else PowerPolicy()
|
|
if policy is None:
|
|
return "Review saved parked-power limits before enabling."
|
|
return (f"Use a {policy.delay_hours}-hour maximum and {policy.cutoff_tenths / 10:.1f}-V cutoff while parked? " +
|
|
"Other protections can shut down earlier.")
|
|
return "Return to stock parked-power limits? Older saved choices remain untouched."
|
|
if request.key == HOURS:
|
|
return f"Save a maximum parked time of {request.value}? Other protections can shut down earlier."
|
|
return f"Save a low-voltage cutoff of {request.value}? The device may shut down while parked."
|
|
|
|
|
|
class PowerOwner:
|
|
def __init__(self, params, parked: Callable[[], bool]):
|
|
self.params = params
|
|
self.parked = parked
|
|
|
|
def snapshot(self) -> FeatureSettingsState:
|
|
saved = read_saved(self.params)
|
|
parked = self.parked()
|
|
policy = saved.policy
|
|
reason = ("Saved policy cannot be read" if not saved.readable else
|
|
"Invalid saved policy; choose Stock to repair" if not saved.valid else
|
|
"Stock limits apply until a custom policy is saved" if saved.raw is None else "")
|
|
inactive = "Saved; stock power policy applies" if saved.valid and not policy.enabled else ""
|
|
rows = (FeatureRow(ENABLED, "Parked Power", "Invalid" if not saved.valid else "On" if policy.enabled else "Stock",
|
|
source=saved.raw, choices=("Stock", "On"), available=parked and saved.readable,
|
|
repair_value="Stock" if not saved.valid else "", reason=reason),
|
|
FeatureRow(HOURS, "Maximum Parked Time", f"{policy.delay_hours} h" if saved.valid else "Invalid",
|
|
source=saved.raw, choices=HOUR_CHOICES, available=parked and saved.readable and saved.valid,
|
|
reason=inactive or "Other protections may act sooner"),
|
|
FeatureRow(VOLTS, "Low-Voltage Cutoff", f"{policy.cutoff_tenths / 10:.1f} V" if saved.valid else "Invalid",
|
|
source=saved.raw, choices=VOLT_CHOICES, available=parked and saved.readable and saved.valid,
|
|
reason=inactive or "Uses filtered car voltage"))
|
|
initial = PowerPolicy()
|
|
defaults = {ENABLED: 'Stock', HOURS: f'{initial.delay_hours} h', VOLTS: f'{initial.cutoff_tenths / 10:.1f} V'}
|
|
rows = tuple(replace(row, default_value=defaults[row.key]) if row.key in defaults else row for row in rows)
|
|
return FeatureSettingsState(page="power", title="Parked Power",
|
|
subtitle="Saved limits for automatic offroad shutdown.", rows=rows, parked=parked)
|
|
|
|
@staticmethod
|
|
def _desired(saved: SavedPower, request: FeatureSettingsRequest) -> PowerPolicy | None:
|
|
policy = saved.policy
|
|
if request.key == ENABLED:
|
|
if request.value not in ("Stock", "On"):
|
|
return None
|
|
if not saved.valid and request.value != "Stock":
|
|
return None
|
|
return replace(policy, enabled=request.value == "On")
|
|
if not saved.valid:
|
|
return None
|
|
if request.key == HOURS and request.value in HOUR_CHOICES:
|
|
return replace(policy, delay_hours=int(request.value[:-2]))
|
|
if request.key == VOLTS and request.value in VOLT_CHOICES:
|
|
return replace(policy, cutoff_tenths=int(round(float(request.value[:-2]) * 10)))
|
|
return None
|
|
|
|
def apply(self, request: FeatureSettingsRequest) -> bool:
|
|
if request.key not in POWER_KEYS or not request.confirmation:
|
|
return False
|
|
desired: PowerPolicy | None = None
|
|
for _ in range(2):
|
|
if not self.parked():
|
|
return False
|
|
saved = read_saved(self.params)
|
|
if not saved.readable or saved.raw != request.expected:
|
|
return False
|
|
desired = self._desired(saved, request)
|
|
if desired is None:
|
|
return False
|
|
assert desired is not None
|
|
if not self.parked():
|
|
return False
|
|
try:
|
|
self.params.put(KEY, to_value(desired), block=True)
|
|
except (OSError, KeyError, TypeError, ValueError):
|
|
return False
|
|
observed = read_saved(self.params)
|
|
return observed.readable and observed.valid and observed.policy == desired
|