Recon
Monitor Recon Robots
Watch a Recon fleet from your own tools: connection and battery, what each robot is doing and how far along it is, and the history of every activity it ran, from the robot state and event operations.
Two kinds of reads cover a robot fleet: the state operations say what each robot is doing now, and the events operation says what it did. The examples use the robot_api helper from the overview.
You need an API key with read-only access to the robots' locations.
Fleet state
getMinimalStateList returns one entry per robot:
for robot in robot_api("getMinimalStateList", {})["minimalStates"]:
progress = ""
if robot.get("activity") == "ROUTE_RUN" and robot.get("waypointTotal"):
progress = f"stop {robot['waypointIndex'] + 1} of {robot['waypointTotal']}"
print(f"{robot['name']}: {robot['connectionStatus']}, {robot.get('batteryPercent')}%, {robot.get('activity')} {progress}")| Field | Meaning |
|---|---|
uuid, name, locationUuid | The robot, as the other robot operations and the console know it |
connectionStatus, healthStatus | GREEN, YELLOW, ORANGE or RED, best to worst, as for every device |
batteryPercent | Battery level, 0 to 100 |
activity, activityUuid | What the robot is doing (below) and that activity's ID. For a dispatch, activityUuid is the routeRunUuid that deploy returned |
event, phase, phaseEvent | The latest step of the activity: event is its lifecycle (below), phase the part in progress, such as NAVIGATING, DWELLING, UNDOCKING or CHARGING |
routeUuid, waypointUuid, waypointIndex, waypointTotal | During a patrol: the route, the current stop (a stop ID from the route), its position counted from 0, and the number of stops |
goalPose, pose, mapId | Where the robot is heading, where it is, and the map those poses are on |
estimatedTimeRemainingSec, distanceRemaining | Time (seconds) and distance (meters) left to the current goal |
failureReason | Why the latest activity failed, when it did |
firmwareVersion, hwVariation | Firmware and hardware model |
For one robot, getFullState returns the same fields plus the robot's capabilities. Include force (false is fine); the call is refused without it:
state = robot_api("getFullState", {"deviceUuid": ROBOT_UUID, "force": False})["fullState"]
capability = state.get("robotPlatformCapability") or {}
print(state["activity"], state.get("phase"), capability.get("supportedActions"))Use activity and phase to tell whether a robot is docked, charging or out working; the mode and charging fields aren't reported.
Activities
activity | The robot is |
|---|---|
IDLE | Waiting for a command |
ROUTE_RUN | Running a patrol or route. phase is DWELLING while it waits at a stop |
NAVIGATE_TO_POSE | Going to a point it was sent to |
NAVIGATE_TO_HOME | Returning to its charging pad because it was sent home |
IDLE_DOCKING | Returning to its charging pad on its own, after its work is done or it has sat idle |
LOW_BATTERY_DOCKING | Returning to its charging pad to recharge. It refuses new work until the battery recovers |
MANUAL_CMD_VEL, MANUAL_ACTION, MANUAL_POSTURE | Being driven live by an operator |
EMERGENCY | Stopped by an emergency stop, until someone releases it |
EXPLORE_AND_MAP, EXPLORE_AREA, MAINTENANCE_HOLD | Mapping its space, or held for map maintenance |
New values can be added. Treat one you don't recognize as busy.
Event history
findRobotEvents returns what a robot did between startMs (inclusive) and endMs (exclusive). The response is newline-delimited JSON (application/x-ndjson), one event per line, newest first:
import json
import time
def robot_events(robot_uuid, start_ms, end_ms):
response = requests.post(
f"{API}/findRobotEvents",
headers=HEADERS,
json={"deviceUuid": robot_uuid, "startMs": start_ms, "endMs": end_ms},
timeout=60,
)
response.raise_for_status()
events = []
for line in response.text.splitlines():
if not line.strip():
continue
event = json.loads(line)
if "_error" in event:
raise RuntimeError(f"findRobotEvents stopped early: {event.get('_message')}")
events.append(event)
return events
now = int(time.time() * 1000)
for event in robot_events(ROBOT_UUID, now - 24 * 3600 * 1000, now):
print(event["timestampMs"], event.get("activity"), event.get("event"), event.get("phase"), event.get("failureReason"))- There's no paging. Ask for a window you can handle, a day for example, and walk back window by window for longer periods.
- A last line with
_errormeans the stream stopped before the end, though the status is200. Retry that window. - Events can arrive late. A robot that was offline uploads what it recorded when it reconnects, with the original
timestampMs. When you collect events on a schedule, overlap the windows and skip events whoseuuidyou've already stored.
| Field | Meaning |
|---|---|
uuid | The event's ID; use it to skip duplicates |
timestampMs | When it happened, by the robot's clock |
activity, activityUuid | The activity the event belongs to |
event | STARTED, PROGRESS, SUSPENDED, RESUMED, COMPLETED, FAILED, CANCELED or REJECTED |
phase, phaseEvent | For PROGRESS, the phase that STARTED or COMPLETED |
routeUuid, waypointUuid, waypointIndex, waypointTotal | The route and the stop, during a patrol |
preemptedBy | For SUSPENDED or REJECTED, the activity that took priority |
failureReason | For FAILED, why |
originator | Who or what started the activity: type is USER, API_TOKEN or RULE, for example |
Each activity follows the same lifecycle: STARTED, then PROGRESS events as it moves through its phases (arriving at a stop is a PROGRESS event whose phaseEvent is COMPLETED), possibly SUSPENDED and RESUMED when something with higher priority interrupts it, and exactly one of COMPLETED, FAILED or CANCELED at the end. An activity the robot refused is a single REJECTED event, with preemptedBy naming what blocked it.
Alert on what matters
| Condition | Check |
|---|---|
| Offline | connectionStatus isn't GREEN on two polls in a row |
| Low battery away from the pad | batteryPercent under your threshold while activity isn't one of the docking activities |
| Patrol failed | A FAILED event for a ROUTE_RUN, with its failureReason |
| Dispatch refused | A REJECTED event, with preemptedBy |
| Stuck | The same activity, phase and waypointIndex for much longer than the route usually takes |
Polling the state list every minute or two is far below your rate limit. Robot events don't arrive on the event WebSocket or on organization webhooks; for real-time updates, connect to the robot live socket.