mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-04 19:43:49 +08:00
Expose parked shutdown settings in Galaxy
This commit is contained in:
@@ -22,6 +22,7 @@ from openpilot.starpilot.ui.conditional_feature import confirmation_question as
|
||||
from openpilot.starpilot.ui.appearance_owner import AppearanceOwner, LABELS as APPEARANCE_LABELS
|
||||
from openpilot.starpilot.ui.display_owner import AUTO_PREFIX as DISPLAY_AUTO_PREFIX, DisplayOwner, LABELS as DISPLAY_LABELS
|
||||
from openpilot.starpilot.ui.sounds_owner import SoundsOwner
|
||||
from openpilot.starpilot.ui.power_owner import PowerOwner, HOURS, VOLTS, POWER_KEYS, confirm_question as power_question
|
||||
from openpilot.starpilot.audio.alert_volume import SPECS as SOUND_SPECS
|
||||
from openpilot.starpilot.ui.presentation import Profile
|
||||
from openpilot.common.hardware import HARDWARE
|
||||
@@ -40,7 +41,7 @@ from openpilot.starpilot.ui.vasm_owner import ANNOTATION as VASM_ANNOTATION, RES
|
||||
|
||||
PAGES = frozenset(("hub", "localization", "developer", "vehicle", "slc", "lane", "lane_change", "profiles", "personalities",
|
||||
"traffic", "aggressive", "standard", "relaxed",
|
||||
"curve", "torque", "aol", "wheel", "appearance", "display", "data", "sounds", "pip", "sentry", "vasm",
|
||||
"curve", "torque", "aol", "wheel", "appearance", "display", "data", "power", "sounds", "pip", "sentry", "vasm",
|
||||
"conditional", "conditional/cem", "conditional/ccm",
|
||||
*(f"{name}/{category}" for name in ("traffic", "aggressive", "standard", "relaxed")
|
||||
for category in ("acceleration", "braking", "following"))))
|
||||
@@ -63,6 +64,14 @@ CAR_CONTROL_SECTIONS = (
|
||||
)
|
||||
|
||||
|
||||
def _power_request(request: FeatureSettingsRequest) -> FeatureSettingsRequest:
|
||||
if request.key == HOURS:
|
||||
return replace(request, value=f"{float(request.value):.0f} h")
|
||||
if request.key == VOLTS:
|
||||
return replace(request, value=f"{float(request.value):.1f} V")
|
||||
return request
|
||||
|
||||
|
||||
class SettingsUnavailable(Exception):
|
||||
pass
|
||||
|
||||
@@ -138,7 +147,7 @@ def _onroad_preference(page: str, key: str) -> bool:
|
||||
# Regular saved choices are consumed by their runtime owners. Only explicit
|
||||
# multi-key reset/repair operations need the offroad transaction boundary;
|
||||
# their owners independently recheck that requirement at commit time.
|
||||
return bool(page != "developer" and key and key != SETUP_ACTION and key not in (
|
||||
return bool(page not in ("developer", "power") and key and key != SETUP_ACTION and key not in (
|
||||
"IsRhdDetected", "AlphaLongitudinalEnabled", "reset_profiles", "curve_reset", "slc_adopt", "slc_reset",
|
||||
"conditional:reset", "conditional:manual_reset",
|
||||
) and not is_long_confirm_action(key) and not key.startswith("torque_repair:"))
|
||||
@@ -388,6 +397,8 @@ def _projection(row: FeatureRow) -> dict:
|
||||
|
||||
def _question(row: FeatureRow, request: FeatureSettingsRequest) -> str:
|
||||
key = request.key
|
||||
if key in POWER_KEYS:
|
||||
return power_question(_power_request(request))
|
||||
if key == "LaneCenteringStrength":
|
||||
raw = float(request.value)
|
||||
level = (1 + (raw - row.minimum) * 4 / (100 - row.minimum) if raw <= 100 else
|
||||
@@ -552,6 +563,15 @@ class SettingsGateway:
|
||||
return fresh.cp_raw == ctx.cp_raw
|
||||
return SoundsOwner(self.params, authorized)
|
||||
|
||||
def _power_owner(self, ctx: AuthorityContext, *, live: bool = False,
|
||||
session_valid: Callable[[], bool] = lambda: True) -> PowerOwner:
|
||||
def parked() -> bool:
|
||||
if not session_valid():
|
||||
return False
|
||||
fresh = self.context.sample() if live else ctx
|
||||
return fresh.parked and fresh.cp_raw == ctx.cp_raw
|
||||
return PowerOwner(self.params, parked)
|
||||
|
||||
def _display_owner(self, ctx: AuthorityContext, *, live: bool = False,
|
||||
session_valid: Callable[[], bool] = lambda: True) -> DisplayOwner:
|
||||
def parked() -> bool:
|
||||
@@ -607,6 +627,14 @@ class SettingsGateway:
|
||||
return self._appearance_owner(ctx).snapshot(Profile.COMPACT, include_large=True)
|
||||
if page == "sounds":
|
||||
return self._sounds_owner(ctx).snapshot()
|
||||
if page == "power":
|
||||
state = self._power_owner(ctx).snapshot()
|
||||
rows = tuple(replace(row, value=row.value.removesuffix(" h").removesuffix(" V"), choices=(),
|
||||
default_value=row.default_value.removesuffix(" h").removesuffix(" V"),
|
||||
minimum=1 if row.key == HOURS else 11.8, maximum=30 if row.key == HOURS else 12.5,
|
||||
step=1 if row.key == HOURS else 0.1, unit="h" if row.key == HOURS else "V")
|
||||
if row.key in (HOURS, VOLTS) else row for row in state.rows)
|
||||
return replace(state, rows=rows)
|
||||
if page == "display":
|
||||
return self._display_owner(ctx).snapshot(Profile.COMPACT if HARDWARE.get_device_type() == "mici" else Profile.LARGE)
|
||||
if page == "pip":
|
||||
@@ -820,6 +848,8 @@ class SettingsGateway:
|
||||
return self._appearance_owner(ctx, live=True, session_valid=session_valid).apply(intent.request)
|
||||
if intent.page == "sounds":
|
||||
return self._sounds_owner(ctx, live=True, session_valid=session_valid).apply(intent.request)
|
||||
if intent.page == "power":
|
||||
return self._power_owner(ctx, live=True, session_valid=session_valid).apply(_power_request(intent.request))
|
||||
if intent.page == "display":
|
||||
return self._display_owner(ctx, live=True, session_valid=session_valid).apply(intent.request)
|
||||
if intent.page == "sentry":
|
||||
|
||||
@@ -53,6 +53,44 @@ class SettingsGatewayTest(unittest.TestCase):
|
||||
def page(self, name):
|
||||
return self.gateway.page(name, self.session, self.generation)
|
||||
|
||||
def test_parked_power_sliders_preserve_defaults_and_save_policy(self):
|
||||
from openpilot.starpilot.power.offroad_preferences import KEY, read_saved, effective
|
||||
page = self.page('power')
|
||||
self.assertIsNone(self.params.get(KEY))
|
||||
self.assertEqual([(r['value'], r['minimum'], r['maximum'], r['step'], r['unit']) for r in page['rows'][1:]],
|
||||
[('30', 1, 30, 1, 'h'), ('11.8', 11.8, 12.5, 0.1, 'V')])
|
||||
for index, value in ((1, 6), (2, 12.1), (0, 'On')):
|
||||
page = self.page('power')
|
||||
intent = self.gateway.preview(page['view'], index, 0, self.session, self.generation, value=value)
|
||||
self.assertTrue(self.gateway.confirm(intent['intent'], self.session, self.generation))
|
||||
policy = effective(read_saved(self.params))
|
||||
self.assertEqual((policy.enabled, policy.delay_hours, policy.cutoff_tenths), (True, 6, 121))
|
||||
page = self.page('power')
|
||||
intent = self.gateway.preview(page['view'], 1, 0, self.session, self.generation, reset_default=True)
|
||||
self.assertTrue(self.gateway.confirm(intent['intent'], self.session, self.generation))
|
||||
self.assertEqual(read_saved(self.params).policy.delay_hours, 30)
|
||||
page = self.page('power')
|
||||
intent = self.gateway.preview(page['view'], 0, 0, self.session, self.generation, value='Stock')
|
||||
self.assertTrue(self.gateway.confirm(intent['intent'], self.session, self.generation))
|
||||
self.assertEqual((effective(read_saved(self.params)).delay_hours, effective(read_saved(self.params)).cutoff_tenths), (30, 118))
|
||||
|
||||
def test_parked_power_refuses_invalid_values_stale_source_and_onroad_save(self):
|
||||
from openpilot.starpilot.power.offroad_preferences import KEY, to_value, PowerPolicy
|
||||
for index, value in ((1, 0), (1, 31), (1, 1.5), (2, 11.7), (2, 12.6), (2, 12.15)):
|
||||
page = self.page('power')
|
||||
with self.assertRaises(SettingsChanged):
|
||||
self.gateway.preview(page['view'], index, 0, self.session, self.generation, value=value)
|
||||
page = self.page('power')
|
||||
intent = self.gateway.preview(page['view'], 1, 0, self.session, self.generation, value=6)
|
||||
self.params.put(KEY, to_value(PowerPolicy(True, 10, 120)), block=True)
|
||||
self.assertFalse(self.gateway.confirm(intent['intent'], self.session, self.generation))
|
||||
page = self.page('power')
|
||||
intent = self.gateway.preview(page['view'], 1, 0, self.session, self.generation, value=6)
|
||||
self.context.value = replace(self.context.value, parked=False)
|
||||
with self.assertRaises(SettingsChanged):
|
||||
self.gateway.confirm(intent['intent'], self.session, self.generation)
|
||||
self.assertTrue(all(not row['available'] for row in self.page('power')['rows']))
|
||||
|
||||
def test_aol_steering_pause_actual_angle_gateway_save_and_default(self):
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.ford.values import CAR as FORD
|
||||
|
||||
@@ -194,7 +194,7 @@ for (const id of ["wheel", "sounds", "localization"]) {
|
||||
}
|
||||
|
||||
const deviceData = SETTINGS_SECTIONS.find((item) => item.id === "device")
|
||||
assert.deepEqual(deviceData.pages, ["display", "data"])
|
||||
assert.deepEqual(deviceData.pages, ["power", "display", "data"])
|
||||
assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceData,
|
||||
state: { data: { page: "hub" } } }), true)
|
||||
assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceData,
|
||||
@@ -202,7 +202,7 @@ assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceDat
|
||||
|
||||
const dataLinks = SettingsPage.computed.visibleRows.call({ initialPage: "hub", activeSection: deviceData,
|
||||
state: { query: "", data: { page: "hub", rows: [] } } })
|
||||
assert.deepEqual(dataLinks.map(({ row }) => row.page), ["display", "data"])
|
||||
assert.deepEqual(dataLinks.map(({ row }) => row.page), ["power", "display", "data"])
|
||||
|
||||
assert.equal(SettingsPage.computed.atSectionRoot.call({initialPage:'hub',state:{data:null,parents:[]}}),true,
|
||||
'section tabs stay mounted while loading a new section')
|
||||
@@ -221,3 +221,9 @@ assert.deepEqual(SettingsPage.computed.visibleRows.call({initialPage: "hub", act
|
||||
["aggressive", "standard", "relaxed", "traffic"])
|
||||
assert.equal(SettingsPage.computed.activeSection.call({sections: SETTINGS_SECTIONS,
|
||||
state: {data: {page: "personalities"}}}).id, "longitudinal")
|
||||
|
||||
assert.ok(SETTINGS_SECTIONS.find(section => section.id === "device").pages.includes("power"))
|
||||
for (const row of [{ value: "30", minimum: 1, maximum: 30, step: 1, unit: "h" },
|
||||
{ value: "11.8", minimum: 11.8, maximum: 12.5, step: .1, unit: "V" }]) {
|
||||
assert.equal(settingControl(row), "slider")
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export const SETTINGS_SECTIONS = Object.freeze([
|
||||
{ id: "wheel", label: "Wheel Controls", icon: "bi-controller", pages: ["wheel"] },
|
||||
{ id: "visual", label: "Visual (Display & UI)", icon: "bi-eye", pages: ["appearance", "ui_layout", "favorites", "pip"] },
|
||||
{ id: "sounds", label: "Sounds & Alerts", icon: "bi-volume-up", pages: ["sounds"] },
|
||||
{ id: "device", label: "Device & Data", icon: "bi-hdd", pages: ["display", "data"] },
|
||||
{ id: "device", label: "Device & Data", icon: "bi-hdd", pages: ["power", "display", "data"] },
|
||||
{ id: "localization", label: "Localization", icon: "bi-globe", pages: ["localization"] },
|
||||
{ id: "developer", label: "Developer", icon: "bi-code-slash", pages: ["developer"] },
|
||||
])
|
||||
@@ -34,7 +34,7 @@ export const isPipCropRow = (row) => ["Visual camera crop editor", "Camera crop
|
||||
/^Vehicle (left|right) crop/.test(row.label || "")
|
||||
|
||||
const SECTION_LINKS = {
|
||||
device: [{ label: "Display", page: "display", value: "Adjust screen brightness and display timing" }, { label: "Data Uploads", page: "data", value: "Choose mobile-data uploads and usage-statistics sharing" }],
|
||||
device: [{ label: "Parked Power", page: "power", value: "Adjust shutdown hours and battery-voltage cutoff" }, { label: "Display", page: "display", value: "Adjust screen brightness and display timing" }, { label: "Data Uploads", page: "data", value: "Choose mobile-data uploads and usage-statistics sharing" }],
|
||||
visual: [{ label: "Driving Screen Widgets, Colors & Layout", page: "ui_layout" }, { label: "Quick Select", page: "favorites" }, { label: "Blind Spot Camera and Preview", page: "pip" }],
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "705cb122e92ebc1be7d7388bc9600fce1eb52bb917ee1926cf626fa7a089b373", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "bac7d61b5ba9533b8179581ef7b58b9b12726afd6de30981b7bf78b37b1a9e9b", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user