Triggers
Triggers in Commands V3
A Trigger is a BooleanSupplier with bind helpers: button.onTrue(cmd), sensor.whileTrue(cmd), and friends. A Trigger fires a Command.
Every binding belongs to a scope (global, opmode, or command). Exiting that scope removes the binding automatically, so a binding's lifetime always matches the thing it belongs to: the whole program, a single match phase, or one running command.
A Trigger fires a Command. What matters is who owns the binding's lifetime, and therefore when it goes away.
The basic shape
A Trigger exposes onTrue / onFalse / whileTrue / whileFalse for binding a command to an edge or a hold. Predicate-based triggers wrap any BooleanSupplier in new Trigger(...), so a sensor reading binds the same way a button does.
The Trigger class and the controllers (like CommandNiDsXboxController) live under org.wpilib.command3.button. The interesting part isn't what you write at the binding site; it's where you write it, because that decides how long the binding lives.
Why bindings are scoped
Different phases of a match want different bindings: the A button might raise the arm in teleop and do nothing in auto. Scoping ties a binding's lifetime to the thing it belongs to. A binding made for a mode lives exactly as long as that mode is active; a binding made for a running command lives exactly as long as that command runs. When the mode changes or the command ends, its bindings go away with it.
The payoff is that you never track or clear bindings by hand, and a binding can't linger into a phase it was never meant for. You declare each binding in the scope it belongs to and let the framework add and remove it at the right moments. The next sections cover the three scopes and when to reach for each.
The three trigger scopes
Every binding belongs to a scope. The scope decides when the binding goes away.
Bindings made in the Robot constructor
Created before any OpMode is selected, so they live as long as the robot program runs. Good fit for bindings that are genuinely always on: the template's brake-while-disabled (RobotModeTriggers.disabled()), an emergency stop, a low-battery alert. Not much else qualifies.
Bindings made in an OpMode constructor
Live while the opmode is active; cleaned up when it exits. Teleop's driver-controller bindings, auto's sensor-trigger bindings, test mode's diagnostic toggles — each opmode declares its own and the framework swaps the active set when the mode changes.
Bindings made inside a Command body
Live only while the command is scheduled. Useful for "while this routine is running, also fire X on sensor Y". The extra binding disappears when the routine finishes or is cancelled.
OpMode: a sibling to Mechanism
"The mode the robot is in" is a first-class concept: the OpMode. Where a Mechanism owns hardware, an OpMode owns the policy for a phase of the match — which bindings are live, which default commands are in force, what auto routine to run. Teleop, auto, and a utility/calibration mode each become their own class: you extends PeriodicOpMode and tag it @Teleop, @Autonomous, or @Utility (the framework discovers it by that annotation and lists it on the driver station). The bindings go in the constructor, and because the Triggers are constructed there, they're scoped to the OpMode and torn down automatically on a mode switch.
Selecting auto on the driver station constructsthe auto OpMode (building its bindings) and tears down the teleop OpMode along with its bindings. When the driver picks Teleop again, a fresh TeleopOpMode is constructed and only its bindings come back. Each mode's bindings come and go with the mode; nothing to clean up by hand.
Command-scoped bindings
The third scope is the most situational but worth knowing about. Bindings registered from inside a command body live as long as that command is scheduled. The instant the command finishes or is cancelled, the binding goes away.
Outside of climb()the operator's B button does whatever the opmode-level binding (or nothing) says it should. The abort binding exists only for the duration of the climb: it's created when the command starts and dropped the moment it ends, with no cleanup code of your own.
All three binding scopes are shipped API