IQ.Pilot Release Commit @ f2a861c

This commit is contained in:
IQ.Lvbs CI [bot]
2026-09-02 15:07:09 -05:00
parent b42569dbca
commit e8748fd704
5497 changed files with 316070 additions and 179848 deletions

View File

@@ -1,34 +1,25 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
Steering Assistance Behavior (SAB): brand preference resolution, the guidance
state machine and the per-frame event/behaviour engine, together in one module.
"""
from dataclasses import dataclass
from typing import Optional
from openpilot.common.params import Params, UnknownKeyName
from iqpilot.common.params import Params, UnknownKeyName
from iqdbc.car import structs
from openpilot.common.realtime import DT_CTRL
from iqpilot.common.realtime import DT_CTRL
from iqdbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.selfdrive.selfdrived.events import ET
from iqpilot.selfdrive.selfdrived.events import ET
from iqdbc.car.hyundai.values import HyundaiFlags, HyundaiFlagsIQ, HyundaiSafetyFlagsIQ
from openpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from cereal import log, custom
from iqpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from iqpilot.cereal import log, custom
State = custom.AlwaysOnLateral.AlwaysOnLateralState
# ===== preferences =====
class DriverInterventionMode:
"""What a brake press does to steering guidance (AolSteeringMode param values)."""
CONTINUE = 0
SUSPEND = 1
CANCEL = 2
# Per-brand quirks. A brand absent from a set behaves normally.
_FORCED_BRAKE_CANCEL = frozenset({"rivian"})
BRANDS_WITHOUT_MAIN_CRUISE_TOGGLE = ("rivian", "tesla")
_HYUNDAI_MAIN_CRUISE_FLAG_BRANDS = frozenset({"hyundai"})
@@ -56,6 +47,10 @@ def read_joint_engagement_pref(params: Params):
return params.get_bool("AolUnifiedEngagementMode")
def read_lateral_override_pause_pref(params: Params):
return params.get_bool("AolPauseOnSteeringOverride")
def resolve_brake_intervention_mode(CP: structs.CarParams, CP_IQ: structs.IQCarParams, params: Params):
if uses_forced_brake_cancel(CP, CP_IQ):
return DriverInterventionMode.CANCEL
@@ -76,14 +71,12 @@ def apply_aol_brand_overrides(CP: structs.CarParams, CP_IQ: structs.IQCarParams,
CP_IQ.iqSafetyFlags |= HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
if uses_forced_brake_cancel(CP, CP_IQ):
# the brand can only cancel on brake; pin the params so the UI reflects reality
params.put("AolSteeringMode", DriverInterventionMode.CANCEL)
params.put_bool("AolUnifiedEngagementMode", True)
if CP.brand in BRANDS_WITHOUT_MAIN_CRUISE_TOGGLE:
params.remove("AolMainCruiseAllowed")
# ===== state_machine =====
EventName = log.OnroadEvent.EventName
EventNameIQ = custom.IQOnroadEvent.EventName
@@ -131,12 +124,22 @@ class GuidancePulse:
class GuidanceStateMachine:
def __init__(self, sab):
self.sab = sab
self.selfdrive = sab.selfdrive
self._sm_core = sab.selfdrive.state_machine
self._events = sab.selfdrive.events
self._events_iq = sab.selfdrive.events_iq
self.state = State.disabled
@property
def _parks_on_override(self) -> bool:
return bool(self.sab.pause_on_lateral_override)
def _hands_on_landing(self, pulse: GuidancePulse) -> State:
if not pulse.hands_on_wheel:
return State.enabled
return State.paused if self._parks_on_override else State.overriding
def _queue_alert_if_solo(self, alert_type: str):
if not self.selfdrive.enabled:
self._sm_core.current_alert_types.append(alert_type)
@@ -180,7 +183,7 @@ class GuidanceStateMachine:
self._queue_alert_if_solo(GUIDANCE_GATE_BLOCK_SIGNAL)
return State.paused if pulse.pit_stop_ready else State.disabled
self._queue_alert_if_solo(GUIDANCE_AVAILABLE_SIGNAL)
return State.overriding if pulse.hands_on_wheel else State.enabled
return self._hands_on_landing(pulse)
def _handle_enabled(self, pulse: GuidancePulse) -> State:
forced_state = self._run_global_cutoffs(pulse)
@@ -190,6 +193,8 @@ class GuidanceStateMachine:
self._start_grace_period()
return State.softDisabling
if pulse.hands_on_wheel:
if self._parks_on_override:
return State.paused
self._queue_alert_if_solo(GUIDANCE_DRIVER_OVERRIDE_SIGNAL)
return State.overriding
return State.enabled
@@ -215,7 +220,7 @@ class GuidanceStateMachine:
self._queue_alert_if_solo(GUIDANCE_GATE_BLOCK_SIGNAL)
return State.paused
self._queue_alert_if_solo(GUIDANCE_AVAILABLE_SIGNAL)
return State.overriding if pulse.hands_on_wheel else State.enabled
return self._hands_on_landing(pulse)
def _handle_overriding(self, pulse: GuidancePulse) -> State:
forced_state = self._run_global_cutoffs(pulse)
@@ -225,6 +230,8 @@ class GuidanceStateMachine:
self._start_grace_period()
return State.softDisabling
if pulse.hands_on_wheel:
if self._parks_on_override:
return State.paused
self._sm_core.current_alert_types.append(GUIDANCE_DRIVER_OVERRIDE_SIGNAL)
return State.overriding
return State.enabled
@@ -246,8 +253,6 @@ class GuidanceStateMachine:
self._queue_alert_if_solo(GUIDANCE_ACTIVE_ALERT)
return enabled, active
# ===== behavior =====
_E = log.OnroadEvent.EventName
_Q = custom.IQOnroadEvent.EventName
_BTN = structs.CarState.ButtonEvent.Type
@@ -257,10 +262,6 @@ _CRUISE_SET_TAPS = frozenset((_BTN.accelCruise, _BTN.resumeCruise, _BTN.decelCru
_LATERAL_TOGGLE_BUTTONS = (_BTN.lkas, _BTN.lfaButton)
_HYUNDAI_LDA_MASK = HyundaiFlags.CANFD
# While a lateral-only session rides through a pause, these stock blockers are
# swapped for their silent IQ twins. Order here is not load-bearing (each row
# guards a distinct event), so it is grouped standstill-first for readability.
# (silent replacement, stock trigger, only-when-stopped, extra predicate)
_QUIET_SWAPS = (
(_Q.seatbeltUnbuckledSilent, _E.seatbeltNotLatched, True, None),
(_Q.doorAjarSilent, _E.doorOpen, True, None),
@@ -271,7 +272,6 @@ _QUIET_SWAPS = (
lambda cs: cs.vEgo < 2.5 or cs.gearShifter == _GEAR.reverse),
)
# Longitudinal-only chatter that must not gate a lateral-only session.
_DROP_ON_ENTRY = (_E.speedTooLow, _E.belowEngageSpeed, _E.preEnableStandstill,
_E.manualRestart, _E.cruiseDisabled)
_DROP_ON_EXIT = (_E.wrongCruiseMode, _E.pedalPressed, _E.buttonCancel, _E.pcmDisable)
@@ -285,6 +285,7 @@ class SteeringAssistanceBehavior:
self.events, self.events_iq = sd.events, sd.events_iq
self.enabled = self.active = self.available = False
self.pause_on_lateral_override = False
sd.enabled_prev = False
self.state_machine = GuidanceStateMachine(self)
@@ -301,6 +302,7 @@ class SteeringAssistanceBehavior:
def _reload_preferences(self, full: bool = False):
self.main_enabled_toggle = read_main_cruise_pref(self.params)
self.unified_engagement_mode = read_joint_engagement_pref(self.params)
self.pause_on_lateral_override = read_lateral_override_pause_pref(self.params)
if full:
self.enabled_toggle = read_aol_enabled_pref(self.params)
self.steering_mode_on_brake = resolve_brake_intervention_mode(self.CP, self.CP_IQ, self.params)
@@ -308,7 +310,6 @@ class SteeringAssistanceBehavior:
def read_params(self):
self._reload_preferences()
# -- event plumbing (thin wrappers over the stock/IQ event queues) -----------
def _has(self, ev):
return self.events.has(ev)
@@ -333,17 +334,21 @@ class SteeringAssistanceBehavior:
def _iq_has(self, ev):
return self.events_iq.has(ev)
# -- predicates --------------------------------------------------------------
def _brake_without_gas(self, cs):
prev_gas = self.selfdrive.CS_prev.gasPressed
gas_rising_edge = cs.gasPressed and not prev_gas
override_via_gas = gas_rising_edge and self.disengage_on_accelerator
return self._has(_E.pedalPressed) and not override_via_gas
def _lateral_overridden(self):
return self.events.contains(ET.OVERRIDE_LATERAL) or self.events_iq.contains(ET.OVERRIDE_LATERAL)
def _may_silently_resume(self, cs):
suspend_on_brake = self.steering_mode_on_brake == DriverInterventionMode.SUSPEND
if suspend_on_brake and self._brake_without_gas(cs):
return False
if self.pause_on_lateral_override and self._lateral_overridden():
return False
return not self._emitted_any(GEARS_ALLOW_PAUSED_SILENT)
@property
@@ -366,7 +371,6 @@ class SteeringAssistanceBehavior:
return True
return bool(getattr(cs, 'cruiseFaultLateralMode', False))
# -- event surgery -----------------------------------------------------------
def _swap_event(self, stock: int, silent: int):
self._drop(stock)
self._emit(silent)
@@ -382,7 +386,6 @@ class SteeringAssistanceBehavior:
elif self._has(_E.wrongCarMode):
self._swap_event(_E.wrongCarMode, _Q.carModeMismatchNotice)
# -- joystick/debug hook -----------------------------------------------------
def _consume_joystick_aol_request(self, cs) -> str | None:
if not self.params.get_bool("JoystickDebugMode"):
return None
@@ -411,7 +414,6 @@ class SteeringAssistanceBehavior:
parked_or_reverse = getattr(cs, "gearShifter", _GEAR.unknown) in (_GEAR.park, _GEAR.reverse)
return None if parked_or_reverse else verb
# -- pipeline stages ---------------------------------------------------------
def _phase_joystick(self, cs):
verb = self._consume_joystick_aol_request(cs)
if verb is not None:

View File

@@ -4,28 +4,34 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
from types import SimpleNamespace
from cereal import custom
from iqpilot.cereal import custom
from iqdbc.car import structs
from iqdbc.car.hyundai.values import HyundaiFlags, HyundaiFlagsIQ
from openpilot.iqpilot.sab.behavior import SteeringAssistanceBehavior
from openpilot.iqpilot.selfdrive.selfdrived.events import IQEvents
from openpilot.selfdrive.selfdrived.events import Events
from iqpilot.sab.behavior import SteeringAssistanceBehavior
from iqpilot.selfdrive.selfdrived.iq_events import IQEvents
from iqpilot.selfdrive.selfdrived.events import Events
from iqpilot.cereal import log
ButtonType = structs.CarState.ButtonEvent.Type
EventName = log.OnroadEvent.EventName
EventNameIQ = custom.IQOnroadEvent.EventName
GuidanceState = custom.AlwaysOnLateral.AlwaysOnLateralState
class MockParams:
def __init__(self, main_cruise_allowed: bool = False, aol_enabled: bool = True):
def __init__(self, main_cruise_allowed: bool = False, aol_enabled: bool = True,
pause_on_steering_override: bool = False):
self.main_cruise_allowed = main_cruise_allowed
self.aol_enabled = aol_enabled
self.pause_on_steering_override = pause_on_steering_override
def get_bool(self, key: str) -> bool:
return {
"AolEnabled": self.aol_enabled,
"AolMainCruiseAllowed": self.main_cruise_allowed,
"AolUnifiedEngagementMode": False,
"AolPauseOnSteeringOverride": self.pause_on_steering_override,
"JoystickDebugMode": False,
}.get(key, False)
@@ -39,7 +45,8 @@ class MockParams:
def make_selfdrive(cp_flags: int, brand: str = "hyundai", main_cruise_allowed: bool = False,
aol_enabled: bool = True, cp_iq_flags: int = 0):
aol_enabled: bool = True, cp_iq_flags: int = 0,
pause_on_steering_override: bool = False):
cp = SimpleNamespace(
brand=brand,
flags=cp_flags,
@@ -51,7 +58,7 @@ def make_selfdrive(cp_flags: int, brand: str = "hyundai", main_cruise_allowed: b
return SimpleNamespace(
CP=cp,
CP_IQ=cp_iq,
params=MockParams(main_cruise_allowed, aol_enabled),
params=MockParams(main_cruise_allowed, aol_enabled, pause_on_steering_override),
state_machine=SimpleNamespace(soft_disable_timer=0, current_alert_types=[]),
events=Events(),
events_iq=IQEvents(),
@@ -164,6 +171,53 @@ def test_main_cruise_rising_edge_does_not_engage_when_toggle_is_off():
assert guidance.state_machine.state == custom.AlwaysOnLateral.AlwaysOnLateralState.disabled
def run_cycle(guidance, selfdrive, car_state, steering_pressed: bool):
selfdrive.events.clear()
selfdrive.events_iq.clear()
if steering_pressed:
selfdrive.events.add(EventName.steerOverride)
guidance.update(car_state)
selfdrive.CS_prev = car_state
def make_engaged_guidance(pause_on_steering_override: bool):
selfdrive = make_selfdrive(0, brand="volkswagen", pause_on_steering_override=pause_on_steering_override)
selfdrive.CS_prev = make_vw_car_state(cruise_available=True)
guidance = SteeringAssistanceBehavior(selfdrive)
guidance.enabled = True
guidance.state_machine.state = GuidanceState.enabled
return guidance, selfdrive
def test_steering_override_parks_guidance_when_enabled():
guidance, selfdrive = make_engaged_guidance(True)
run_cycle(guidance, selfdrive, make_vw_car_state(cruise_available=True), steering_pressed=True)
assert guidance.state_machine.state == GuidanceState.paused
assert guidance.enabled
assert not guidance.active
def test_guidance_resumes_once_steering_is_released():
guidance, selfdrive = make_engaged_guidance(True)
run_cycle(guidance, selfdrive, make_vw_car_state(cruise_available=True), steering_pressed=True)
run_cycle(guidance, selfdrive, make_vw_car_state(cruise_available=True), steering_pressed=False)
assert guidance.state_machine.state == GuidanceState.enabled
assert guidance.active
def test_steering_override_keeps_torque_when_option_is_off():
guidance, selfdrive = make_engaged_guidance(False)
run_cycle(guidance, selfdrive, make_vw_car_state(cruise_available=True), steering_pressed=True)
assert guidance.state_machine.state == GuidanceState.overriding
assert guidance.active
def test_main_cruise_rising_edge_engages_when_toggle_is_on():
selfdrive = make_selfdrive(0, brand="volkswagen", main_cruise_allowed=True, aol_enabled=True)
selfdrive.CS_prev = make_vw_car_state(cruise_available=False)

View File

@@ -1,17 +1,13 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
Table-driven checks for GuidanceStateMachine: every transition is one row of
(start state, signals present, expected state), and the side effects (queued
alert types, soft-disable timer arming) are asserted separately.
"""
import pytest
from cereal import custom
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.selfdrived.events import ET
from openpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from openpilot.iqpilot.sab.behavior import (GuidanceStateMachine, PAUSE_WITH_IQ_EVENTS,
from iqpilot.cereal import custom
from iqpilot.common.realtime import DT_CTRL
from iqpilot.selfdrive.selfdrived.events import ET
from iqpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from iqpilot.sab.behavior import (GuidanceStateMachine, PAUSE_WITH_IQ_EVENTS,
PAUSE_WITH_STOCK_EVENTS)
State = custom.AlwaysOnLateral.AlwaysOnLateralState
@@ -19,20 +15,17 @@ EventNameIQ = custom.IQOnroadEvent.EventName
SOFT_DISABLE_FRAMES = int(SOFT_DISABLE_TIME / DT_CTRL)
# signal aliases used in the table rows
ENABLE = ET.ENABLE
NO_ENTRY = ET.NO_ENTRY
SOFT = ET.SOFT_DISABLE
USER = ET.USER_DISABLE
IMMEDIATE = ET.IMMEDIATE_DISABLE
OVERRIDE = ET.OVERRIDE_LATERAL
SILENT = "silent-disable" # silentLkasDisable present in the IQ event bag
PAUSE_OK = "pause-eligible" # a gear/door/belt event from the pause lists is present
SILENT = "silent-disable"
PAUSE_OK = "pause-eligible"
class SignalBag:
"""Stands in for both event buckets; the machine only probes membership."""
def __init__(self, types=(), names=()):
self._types = set(types)
self._names = set(names)
@@ -48,8 +41,6 @@ class SignalBag:
class Host:
"""Minimal stand-in for the sab/selfdrive plumbing the machine touches."""
class _SSM:
def __init__(self):
self.current_alert_types = []
@@ -70,27 +61,24 @@ class Host:
class Sab:
def __init__(self, host):
def __init__(self, host, pause_on_lateral_override=False):
self.selfdrive = host
self.pause_on_lateral_override = pause_on_lateral_override
def machine_at(state, signals, selfdrive_enabled=False):
def machine_at(state, signals, selfdrive_enabled=False, pause_on_lateral_override=False):
host = Host(signals, selfdrive_enabled)
m = GuidanceStateMachine(Sab(host))
m = GuidanceStateMachine(Sab(host, pause_on_lateral_override))
m.state = state
return m, host
# (id, start state, signals, expected state)
TRANSITIONS = [
# from disabled
("idle stays idle", State.disabled, (), State.disabled),
("engage", State.disabled, (ENABLE,), State.enabled),
("engage while overriding", State.disabled, (ENABLE, OVERRIDE), State.overriding),
("blocked entry", State.disabled, (ENABLE, NO_ENTRY), State.disabled),
("blocked entry parks when pause-eligible", State.disabled, (ENABLE, NO_ENTRY, PAUSE_OK), State.paused),
# from enabled
("cruise steady", State.enabled, (), State.enabled),
("driver off switch", State.enabled, (USER,), State.disabled),
("driver off switch, silent -> pause", State.enabled, (USER, SILENT), State.paused),
@@ -99,13 +87,9 @@ TRANSITIONS = [
("hands on wheel", State.enabled, (OVERRIDE,), State.overriding),
("user beats soft", State.enabled, (USER, SOFT), State.disabled),
("hard beats soft", State.enabled, (IMMEDIATE, SOFT), State.disabled),
# from softDisabling (timer still armed -> stays; see timer tests for expiry)
("condition cleared", State.softDisabling, (), State.enabled),
("user during grace", State.softDisabling, (USER,), State.disabled),
("hard during grace", State.softDisabling, (IMMEDIATE,), State.disabled),
# from paused
("stays parked", State.paused, (), State.paused),
("blocked resume", State.paused, (ENABLE, NO_ENTRY), State.paused),
("resume", State.paused, (ENABLE,), State.enabled),
@@ -113,8 +97,6 @@ TRANSITIONS = [
("user kill while parked", State.paused, (USER,), State.disabled),
("silent user kill re-parks", State.paused, (USER, SILENT), State.paused),
("hard fault while parked", State.paused, (IMMEDIATE,), State.disabled),
# from overriding
("override released", State.overriding, (), State.enabled),
("override held", State.overriding, (OVERRIDE,), State.overriding),
("override to grace", State.overriding, (SOFT,), State.softDisabling),
@@ -122,7 +104,6 @@ TRANSITIONS = [
("override hard fault", State.overriding, (IMMEDIATE,), State.disabled),
]
@pytest.mark.parametrize("label,start,signals,expected", TRANSITIONS, ids=[t[0] for t in TRANSITIONS])
def test_transition(label, start, signals, expected):
m, _ = machine_at(start, signals)
@@ -133,7 +114,7 @@ def test_transition(label, start, signals, expected):
@pytest.mark.parametrize("start,signals,expected_enabled,expected_active", [
(State.disabled, (), False, False),
(State.disabled, (ENABLE,), True, True),
(State.disabled, (ENABLE, NO_ENTRY, PAUSE_OK), True, False), # paused: guidance armed, torque off
(State.disabled, (ENABLE, NO_ENTRY, PAUSE_OK), True, False),
(State.enabled, (), True, True),
(State.enabled, (SOFT,), True, True),
(State.enabled, (USER,), False, False),
@@ -144,7 +125,6 @@ def test_update_outputs(start, signals, expected_enabled, expected_active):
enabled, active = m.update()
assert (enabled, active) == (expected_enabled, expected_active)
class TestSoftDisableTimer:
def test_grace_period_arms_timer_when_solo(self):
m, host = machine_at(State.enabled, (SOFT,))
@@ -171,7 +151,6 @@ class TestSoftDisableTimer:
m.update()
assert m.state == State.softDisabling
class TestAlertQueueing:
def test_alerts_only_queued_when_solo(self):
m, host = machine_at(State.disabled, (ENABLE,), selfdrive_enabled=True)
@@ -182,7 +161,7 @@ class TestAlertQueueing:
m, host = machine_at(State.disabled, (ENABLE,))
m.update()
assert ET.ENABLE in host.state_machine.current_alert_types
assert ET.WARNING in host.state_machine.current_alert_types # active -> warning channel open
assert ET.WARNING in host.state_machine.current_alert_types
def test_no_entry_alert_queued(self):
m, host = machine_at(State.disabled, (ENABLE, NO_ENTRY))
@@ -190,7 +169,6 @@ class TestAlertQueueing:
assert ET.NO_ENTRY in host.state_machine.current_alert_types
def test_user_disable_alert_always_queued(self):
# user disable bypasses the solo gate — the driver asked, the driver hears back
m, host = machine_at(State.enabled, (USER,), selfdrive_enabled=True)
m.update()
assert ET.USER_DISABLE in host.state_machine.current_alert_types
@@ -200,7 +178,6 @@ class TestAlertQueueing:
m.update()
assert ET.OVERRIDE_LATERAL in host.state_machine.current_alert_types
class TestPauseEligibility:
@pytest.mark.parametrize("event_name", PAUSE_WITH_IQ_EVENTS)
def test_each_iq_pause_event_parks(self, event_name):
@@ -219,3 +196,39 @@ class TestPauseEligibility:
m.state = State.disabled
m.update()
assert m.state == State.paused
OVERRIDE_PAUSE_TRANSITIONS = [
("hands on wheel parks", State.enabled, (OVERRIDE,), State.paused),
("override held stays parked", State.overriding, (OVERRIDE,), State.paused),
("engage while overriding parks", State.disabled, (ENABLE, OVERRIDE), State.paused),
("parked resume waits for release", State.paused, (ENABLE, OVERRIDE), State.paused),
("release resumes", State.paused, (ENABLE,), State.enabled),
("hands off keeps steering", State.enabled, (), State.enabled),
("user kill still kills", State.enabled, (OVERRIDE, USER), State.disabled),
("hard fault still faults", State.enabled, (OVERRIDE, IMMEDIATE), State.disabled),
("grace beats override", State.enabled, (OVERRIDE, SOFT), State.softDisabling),
]
@pytest.mark.parametrize("label,start,signals,expected", OVERRIDE_PAUSE_TRANSITIONS,
ids=[t[0] for t in OVERRIDE_PAUSE_TRANSITIONS])
def test_override_pause_transition(label, start, signals, expected):
m, _ = machine_at(start, signals, pause_on_lateral_override=True)
m.update()
assert m.state == expected
class TestOverridePauseOutputs:
def test_torque_stops_while_overriding(self):
m, _ = machine_at(State.enabled, (OVERRIDE,), pause_on_lateral_override=True)
enabled, active = m.update()
assert (enabled, active) == (True, False)
def test_torque_returns_on_release(self):
m, _ = machine_at(State.paused, (ENABLE,), pause_on_lateral_override=True)
enabled, active = m.update()
assert (enabled, active) == (True, True)
def test_override_alert_not_queued_while_parked(self):
m, host = machine_at(State.enabled, (OVERRIDE,), pause_on_lateral_override=True)
m.update()
assert ET.OVERRIDE_LATERAL not in host.state_machine.current_alert_types