360cli.exe is the local automation and control surface for the Windows x360
Controller app. Use it to run a standalone controller host for
controlleremu.xex, or to send status-aware button commands to an already
running GUI or CLI host.
It is useful for normal command-line use, scripts, and local tools that need to check whether the controller path is ready before pressing buttons.
-
Copy
360cli.exeto a folder on your Windows PC. -
Edit
controlleremu.inibefore copying it to the Xbox 360. -
Use the PC's local IPv4 address in
controlleremu.ini. -
Keep the default port
3000unless you change it in both places. -
Copy the edited
controlleremu.iniandcontrolleremu.xexto:Hdd:\Plugins\
The release INI includes documentation addresses, Android 192.0.2.2 and
Windows 192.0.2.1, as defaults. Replace them with your real device IPs.
Run 360cli.exe without a control command to start the local Windows host. This
opens the controller server that controlleremu.xex connects to over the local
network.
.\360cli.exe
Allow Windows Firewall access for private networks when Windows asks. Leave the window open while you use the controller.
Useful host-mode options:
.\360cli.exe --help
.\360cli.exe --show-keys
.\360cli.exe --port 3000
.\360cli.exe --slot 0
.\360cli.exe --no-xinput
.\360cli.exe --no-keyboard
.\360cli.exe --management-port 0
While host mode is running, it also publishes a local-only management API and
writes the same discovery registry used by status, tap, hold, combo,
and sequence. Command-mode tools can control either the GUI backend or this
standalone CLI host with the same calls.
--once-packet prints one neutral packet and exits without starting the
controller host or local management API.
Run 360cli.exe status, tap, hold, combo, or sequence to command an
already running GUI or CLI host. Command mode does not replace the host; it
finds the local management API and injects short manual inputs.
.\360cli.exe status
.\360cli.exe status --json
.\360cli.exe tap A --ms 120
.\360cli.exe hold GUIDE --ms 700
.\360cli.exe combo LB+RB+START --ms 150
.\360cli.exe sequence --ms 120 A wait:500 B wait:250 START
If auto-discovery is not enough, pass the API URL directly:
.\360cli.exe status --management-url http://127.0.0.1:51234 --json
Scripts and local tools should prefer JSON because it is stable to parse and
includes readiness fields such as ok, running, error, console_clients,
selected_slot, selected_slot_number, rol, actions, and manual_active.
-
Check status:
.\360cli.exe status --json -
Continue only when
okistrue,runningistrue, andconsole_clientsis greater than0. Iferroris not empty, stop and report it. -
Send input with JSON output:
.\360cli.exe tap A --ms 120 --json .\360cli.exe hold B --ms 500 --json .\360cli.exe combo LB+RB+START --ms 150 --json .\360cli.exe sequence --ms 100 --json A wait:500 B wait:250 START -
Re-check status if the next action depends on connection, slot, or active input state:
.\360cli.exe status --json
.\360cli.exe status [--json]
.\360cli.exe tap BUTTON [--ms N] [--force] [--json]
.\360cli.exe hold BUTTON --ms N [--force] [--json]
.\360cli.exe combo BUTTON+BUTTON+... [--ms N] [--force] [--json]
.\360cli.exe sequence [--ms N] [--force] [--json] A wait:500 B wait:250 START
tap presses one button or alias briefly. hold keeps one button active for
the requested duration. combo presses multiple buttons together. sequence
runs left-to-right steps; button steps use --ms, and delay steps use
wait:N or wait=N milliseconds.
Examples:
.\360cli.exe tap START --ms 100
.\360cli.exe combo LT+RT --ms 250
.\360cli.exe sequence --ms 90 DPAD_UP wait:200 DPAD_DOWN wait:200 A
Press 20 buttons with delays:
.\360cli.exe sequence --ms 100 A wait:150 B wait:150 X wait:150 Y wait:150 LB wait:150 RB wait:150 LT wait:150 RT wait:150 BACK wait:150 START wait:150 GUIDE wait:150 LS wait:150 RS wait:150 DPAD_UP wait:150 DPAD_DOWN wait:150 DPAD_LEFT wait:150 DPAD_RIGHT wait:150 L_UP wait:150 L_DOWN wait:150 L_LEFT
Same idea using aliases:
.\360cli.exe sequence --ms 100 A wait:150 B wait:150 X wait:150 Y wait:150 LEFT_BUMPER wait:150 RIGHT_BUMPER wait:150 LEFT_TRIGGER wait:150 RIGHT_TRIGGER wait:150 SELECT wait:150 MENU wait:150 GUIDE wait:150 L3 wait:150 R3 wait:150 UP wait:150 DOWN wait:150 LEFT wait:150 RIGHT wait:150 LEFT_STICK_UP wait:150 LEFT_STICK_DOWN wait:150 LEFT_STICK_LEFT
Canonical names:
A B X Y
GUIDE START BACK
LB RB LT RT
LS RS
DPAD_UP DPAD_DOWN DPAD_LEFT DPAD_RIGHT
L_UP L_DOWN L_LEFT L_RIGHT
R_UP R_DOWN R_LEFT R_RIGHT
Accepted aliases:
UP DOWN LEFT RIGHT
DPADUP DPADDOWN DPADLEFT DPADRIGHT
D_UP D_DOWN D_LEFT D_RIGHT
L3 R3
SELECT MENU
LEFT_BUMPER RIGHT_BUMPER
LEFT_TRIGGER RIGHT_TRIGGER
LEFT_STICK RIGHT_STICK
LEFT_STICK_UP LEFT_STICK_DOWN LEFT_STICK_LEFT LEFT_STICK_RIGHT
RIGHT_STICK_UP RIGHT_STICK_DOWN RIGHT_STICK_LEFT RIGHT_STICK_RIGHT
LS_UP LS_DOWN LS_LEFT LS_RIGHT
RS_UP RS_DOWN RS_LEFT RS_RIGHT
Names are case-insensitive. Spaces and hyphens are normalized to underscores,
so left stick up, left-stick-up, and LEFT_STICK_UP are equivalent.
By default, input commands refuse to send unless the local API is reachable, the controller server is running, and at least one Xbox console client is connected. This keeps automation from silently pressing buttons into nowhere.
Use --force only when you intentionally want to send anyway, such as starting
the stopped local server through the API or testing the local manual-input path
without a console connected.
Do not use --force for normal automation. If status says there is no running
host, no console client, or a backend error, the better action is usually to
fix the connection first.
--slot uses zero-based host slots 0 through 3, matching the packet format.
Status output also reports player-facing slot numbers 1 through 4 as
selected_slot_number and in the rol object.
The selected Ring of Light slot is active only while the host is running and an Xbox console WebSocket client is connected. It is inactive when the host is stopped or disconnected.
| Key | Xbox input |
|---|---|
A, B, X, Y |
Face buttons |
G |
Guide |
Enter |
Start |
Backspace |
Back |
1, 2, 3, 4 |
LT, LB, RB, RT |
[, ] |
Left stick click, right stick click |
| Arrow keys | D-pad |
Numpad 8, 5, 4, 6 |
Left stick |
I, K, J, L |
Right stick |
Esc |
Quit |
Run .\360cli.exe --show-keys to print the active bindings.
The CLI also reads supported XInput controllers. By default, keyboard and
XInput are both enabled. Use --no-keyboard or --no-xinput if you want only
one input source.
The default controller chord LB + RB + Start sends Guide.
No running app or host:
.\360cli.exe status --json
If discovery reports that the local API was not found, start either
x360-controller-windows.exe or .\360cli.exe first. If the API is running on
a known port, pass --management-url.
No console client:
Status must show console_clients greater than 0 before normal input
commands work. Check that the Xbox 360 and Windows PC are on the same local
network, controlleremu.ini uses the PC IPv4 address, the port matches, and the
console was rebooted after changing controlleremu.ini.
Duplicate console connections:
The host keeps one active console client. If duplicate console WebSocket connections appear, stale duplicates are closed and status collapses back to one active console client.
Port, rate, or slot controls are greyed out:
These runtime settings are locked while the controller server is active. Stop the server, change the setting, then start it again.
Port already in use:
Close other apps using port 3000, or start the host with another port and
make the same change in controlleremu.ini.
Button holds:
Current manual hold commands keep the action active for the requested
duration through the local host. Older v1 builds documented repeated press and
release behavior; update to the current CLI if holds behave that way.
Windows source is in windows/. From the repository root, run:
scripts\build-cli-exe.bat
The script builds dist\360cli.exe. It requires Python, PyInstaller, and the
standard Windows Python runtime support used by the source.