From 2ccc5fa9dbba52443d71db90757f554a319693cb Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Mon, 2 Nov 2026 08:11:23 -0600 Subject: [PATCH] Keep Tesla screen lateral off until fresh engagement --- .../opendbc/safety/modes/tesla_screen_aol.h | 2 +- .../safety/tests/test_tesla_screen_button.py | 28 ++++++++++++- .../starpilot/car/tesla/screen_intent.py | 2 +- .../car/tesla/tests/test_screen_intent.py | 41 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/opendbc_repo/opendbc/safety/modes/tesla_screen_aol.h b/opendbc_repo/opendbc/safety/modes/tesla_screen_aol.h index 57357e4717..3484a1497e 100644 --- a/opendbc_repo/opendbc/safety/modes/tesla_screen_aol.h +++ b/opendbc_repo/opendbc/safety/modes/tesla_screen_aol.h @@ -110,7 +110,7 @@ static void tesla_screen_rx(const CANPacket_t *msg, bool autopark) { const unsigned int state = (msg->data[1] >> 4U) & 7U; const bool cruise = ((state == 2U) || (state == 3U) || (state == 4U) || (state == 6U) || (state == 7U)) && !tesla_screen_autopark; const bool main_available = ((state == 1U) || cruise) && !tesla_screen_autopark; - if ((cruise && !tesla_screen_cruise) || (main_available && !tesla_screen_main)) { + if (cruise && !tesla_screen_cruise) { tesla_screen_override = 0; } if (tesla_screen_main && !main_available && !brake_pressed) { diff --git a/opendbc_repo/opendbc/safety/tests/test_tesla_screen_button.py b/opendbc_repo/opendbc/safety/tests/test_tesla_screen_button.py index ef8f88a17b..9b12e2054f 100644 --- a/opendbc_repo/opendbc/safety/tests/test_tesla_screen_button.py +++ b/opendbc_repo/opendbc/safety/tests/test_tesla_screen_button.py @@ -75,7 +75,7 @@ class TestTeslaScreenButton(unittest.TestCase): self.touch(3) self.assertEqual(self.permission(), 0) - def test_cruise_baseline_screen_override_and_main_cycle(self): + def test_cruise_baseline_screen_override_and_real_engagement(self): self.feed(state=1) self.assertEqual(self.permission(), 1) self.touch(0) @@ -88,8 +88,34 @@ class TestTeslaScreenButton(unittest.TestCase): self.assertEqual(self.permission(), 0) self.feed(state=0) self.feed(state=1) + self.assertEqual(self.permission(), 0, 'availability is not a new driver engagement') + self.feed(state=2) self.assertEqual(self.permission(), 1) + def test_availability_cycles_cannot_rearm_a_cleared_permission(self): + for word in (512, 1536): + for fault in ({'door': True}, {'eps': 3}, {'hands': 3}, {'gear': 0}): + with self.subTest(word=word, fault=fault): + self.start(word) + self.arm() + self.feed(state=1) + self.feed(state=1, **fault) + self.assertEqual(self.permission(), 0) + for state in (0, 1, 0, 1): + self.feed(state=state) + self.assertEqual(self.permission(), 0) + self.arm() + + def test_brake_disengagement_cannot_be_undone_by_availability(self): + self.start(1536) + self.arm() + self.feed(state=1, brake=True) + self.assertEqual(self.permission(), 0) + for state in (0, 1, 0, 1): + self.feed(state=state) + self.assertEqual(self.permission(), 0) + self.arm() + def test_brake_word_and_physical_faults_withdraw(self): for word in (512, 1536): self.start(word) diff --git a/openpilot/starpilot/car/tesla/screen_intent.py b/openpilot/starpilot/car/tesla/screen_intent.py index 9ce9a1d9f2..521cc3b3f7 100644 --- a/openpilot/starpilot/car/tesla/screen_intent.py +++ b/openpilot/starpilot/car/tesla/screen_intent.py @@ -22,7 +22,7 @@ class TeslaScreenCardIntent(AolCardIntent): engaged = bool(CS.cruiseState.enabled) available = bool(CS.cruiseState.available) if (engaged and not self.previous_enabled) or (self.previous_available and not available and not CS.brakePressed): - self.screen_override = None + self.screen_override = None if engaged and not self.previous_enabled else False self.brake_blocked = False self.pause_lateral = False cancel = any(event.type == ButtonType.cancel and event.pressed for event in CS.buttonEvents) diff --git a/openpilot/starpilot/car/tesla/tests/test_screen_intent.py b/openpilot/starpilot/car/tesla/tests/test_screen_intent.py index cabfbb3529..bfdfb944b4 100644 --- a/openpilot/starpilot/car/tesla/tests/test_screen_intent.py +++ b/openpilot/starpilot/car/tesla/tests/test_screen_intent.py @@ -140,3 +140,44 @@ def test_screen_existing_intent_survives_known_gear_pause_without_new_arm(gear): cs.gearShifter = 'drive' owner.update(cs.as_reader(), now_ns=8, fault_active=False) assert not owner.allowed_latch + + +@pytest.mark.parametrize('withdrawal', ['screen_off', 'can_timeout', 'native_rejection', 'cancel', 'brake']) +def test_availability_cycle_cannot_undo_an_explicit_or_fault_off(withdrawal): + owner = intent(brake=withdrawal == 'brake') + owner.update(state(available=False).as_reader(), now_ns=10) + owner.update(state(available=False, press=True).as_reader(), now_ns=20) + owner.update(state(available=True, press=False).as_reader(), now_ns=30) + assert owner.allowed_latch + cs = state() + kwargs = {} + if withdrawal == 'screen_off': + cs = state(press=True) + elif withdrawal == 'can_timeout': + cs.canTimeout = True + elif withdrawal == 'native_rejection': + kwargs['native_rejection_ns'] = 40 + elif withdrawal == 'cancel': + cs.buttonEvents = [structs.CarState.ButtonEvent(type=ButtonType.cancel, pressed=True)] + else: + cs.brakePressed = True + owner.update(cs.as_reader(), now_ns=50, **kwargs) + assert not owner.allowed_latch + owner.update(state(press=False).as_reader(), now_ns=60) + for index, available in enumerate((False, True, False, True)): + owner.update(state(available=available).as_reader(), now_ns=70 + index) + assert not owner.allowed_latch + owner.update(state(press=True).as_reader(), now_ns=80) + assert owner.allowed_latch + + +def test_availability_off_still_allows_a_new_ordinary_cruise_engagement(): + owner = intent() + owner.update(state().as_reader(), now_ns=10) + owner.update(state(press=True).as_reader(), now_ns=20) + assert not owner.allowed_latch + owner.update(state(available=False, press=False).as_reader(), now_ns=30) + owner.update(state(available=True).as_reader(), now_ns=40) + assert not owner.allowed_latch + owner.update(state(enabled=True).as_reader(), now_ns=50) + assert owner.allowed_latch and owner.screen_override is None