Recon
Dispatch and Track Recon Robots
Send a Recon robot on a patrol, to a point on its map, or back to its charging pad with the deploy and sendHome operations, then follow the run to its outcome through the robot's state and events.
On this page
Two operations move a robot: deploy sends it on a patrol or to a point, and sendHome returns it to its charging pad. Both hand the command to the robot and return; the robot then accepts or refuses it, and reports how it went. The examples use the robot_api helper from the overview.
You need an API key with admin access to the robot.
These calls move a physical machine. Make sure the area is safe for the robot to drive through, ask a person to confirm before anything automated or AI-driven sends a robot out, and keep a way to stop it at hand: the emergency stop on the live socket.
Check the robot first
Dispatch a robot that's connected and free: waiting (IDLE), docking or charging on its own (IDLE_DOCKING), or done with its last activity. Read its state (Monitor robots):
FREE = {"IDLE", "IDLE_DOCKING"}
DONE = {"COMPLETED", "FAILED", "CANCELED"}
state = robot_api("getFullState", {"deviceUuid": ROBOT_UUID, "force": False})["fullState"]
if state["connectionStatus"] != "GREEN":
raise SystemExit("The robot isn't connected")
if state.get("activity") not in FREE and state.get("event") not in DONE:
raise SystemExit(f"The robot is busy: {state.get('activity')}")The robot still has the final say: it can refuse a command this check lets through (When the robot refuses).
Run a patrol now
Deploy with navType PATROL and the patrol's UUID. The patrol must be enabled, and its route must be on the robot's map:
run = robot_api("deploy", {"deviceUuid": ROBOT_UUID, "navType": "PATROL", "patrolUuid": PATROL_UUID})
RUN_UUID = run["routeRunUuid"]The patrol runs with its own arrival actions. A patrol needs no schedule to be dispatched this way (Routes and patrols).
Send the robot to a point
Deploy with navType POSE and a complete pose (all three position and all four orientation values). To send it to a saved waypoint, use that waypoint's pose:
waypoint = robot_api("getRobotWaypoint", {"mapUuid": MAP_UUID, "waypointUuid": WAYPOINT_UUID})["waypoint"]
run = robot_api("deploy", {"deviceUuid": ROBOT_UUID, "navType": "POSE", "pose": waypoint["pose"]})
RUN_UUID = run["routeRunUuid"]When the robot arrives the activity completes, and it waits there until its next command or until it returns to its pad on its own (IDLE_DOCKING).
Send the robot home
robot_api("sendHome", {"deviceUuid": ROBOT_UUID})The robot drives to its charging pad and docks. sendHome interrupts a patrol rather than ending it: the patrol is SUSPENDED, and it can resume by itself (a RESUMED event) after the robot has docked. To end a run, send CMD_NAVIGATE_CANCEL on the live socket; there's no REST operation that cancels one.
What the response means
"error": false with a routeRunUuid means the command reached the robot, not that the robot took it:
- The robot accepts or refuses it. Follow the run (below) to learn which.
- An unreachable robot fails the call with a
5xxstatus. CheckconnectionStatusbefore you dispatch, and retry later rather than in a loop. - An incomplete request also fails with
500, not400: aPATROLdeploy withoutpatrolUuidor for a disabled patrol, or aPOSEdeploy without a complete pose. Validate before you send.
When the robot refuses
The robot weighs a new command against what it's doing. It refuses (a REJECTED event, with preemptedBy naming the activity in the way) when:
- its emergency stop is engaged;
- it's held for map maintenance;
- it's returning to its pad to recharge, or recharging after a low battery;
- an operator is driving it;
- for a patrol, it's on its way to a point or back to its pad.
A new patrol replaces a patrol that's running (the old run ends as CANCELED), and sendHome suspends one. A robot on its charging pad backs off the pad first (phase UNDOCKING).
Follow the run
The routeRunUuid from deploy is the run's activity ID: it appears as activityUuid in the robot's state and its events.
- While it runs, poll
getFullState:activity,phase,waypointIndexofwaypointTotal,estimatedTimeRemainingSecanddistanceRemaining, as long asactivityUuidis your run. - For the outcome, read the robot's events for that
activityUuiduntil one is final:COMPLETED,FAILED(withfailureReason),CANCELEDorREJECTED.
import time
FINAL = {"COMPLETED", "FAILED", "CANCELED", "REJECTED"}
def wait_for_run(robot_uuid, run_uuid, started_ms, timeout_s=3600):
deadline = time.time() + timeout_s
while time.time() < deadline:
now = int(time.time() * 1000)
for event in robot_events(robot_uuid, started_ms - 60_000, now + 60_000):
if event.get("activityUuid") == run_uuid and event.get("event") in FINAL:
return event
time.sleep(15)
raise TimeoutError(f"run {run_uuid} hasn't finished")
started = int(time.time() * 1000)
run = robot_api("deploy", {"deviceUuid": ROBOT_UUID, "navType": "PATROL", "patrolUuid": PATROL_UUID})
result = wait_for_run(ROBOT_UUID, run["routeRunUuid"], started)
print(result["event"], result.get("failureReason") or "", (result.get("preemptedBy") or {}).get("activity", ""))robot_events is the reader from Monitor robots. The window starts a minute early and ends a minute late to allow for the robot's clock. For updates as they happen, the live socket delivers the same events as BEHAVIOR_EVENT messages, with the run's ID in activity_uuid.
Keep your own record of runs from these events. The route run and waypoint run operations in the API Reference don't return runs started by deploy or by patrol schedules.