WebSocket and MQTT clients

The meili-sdk provides two clients to communicate with the FMS in real time: MeiliWebsocketClient and MeiliMqttClient. Both manage the connection, message sending and receiving, and use the same callback functions to handle events and messages from the FMS. They are the recommended way to integrate your robots with the FMS, independent of the robot’s way of communication.

The idea is to create a script that acts as a bridge between the FMS and your robot’s control system. This script should communicate with the robot using its native protocol and with the FMS using one of the clients, so as status updates and commands can be exchanged.

The only differences between the two clients are the communication protocol and how they authenticate. MQTT is more lightweight, but WebSocket might be more robust. Choose the one that matches the communication channel configured for your fleet (WS_SDK or MQTT_SDK).

Importing

from meili_sdk.websockets.client import MeiliWebsocketClient
from meili_sdk.websockets.mqtt_client import MeiliMqttClient

Authentication

Provide the fleet token when creating the MeiliWebsocketClient instance. You can find the fleet token in the FMS, searching for your fleet and looking for the ‘password’ field.

client = MeiliWebsocketClient(
    token="fdd27e8e1a2b4c3d8e9f0a1b2c3d4e5f",
    ... # Callbacks here
)
MQTT communication channel details

Provide three identification parameters, which can be found on Channel details on the Communication channels page:

  • client_id: Corresponds to the field Client id. Should start with meili-agent-....
  • token: Corresponds to the field Password.
  • fleet_uuid: Corresponds to the field Fleet.
client = MeiliMqttClient(
    client_id="meili-agent-XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX",
    token="fdd27e8e1a2b4c3d8e9f0a1b2c3d4e5f",
    fleet_uuid="7935baee221644a9b3aa19fca7fabe99",
    ... # Callbacks here
)

Callback Functions

When creating either client, you can provide any combination of the following callback functions. Each is called when a specific event or message is received from the FMS:

  • open_handler: Called when the connection is established.
  • close_handler: Called when the connection is closed.
  • error_handler: Called when an error occurs.
  • task_v2_handler: Called when a new task is assigned.
  • task_cancellation_handler: Called when a task cancellation message is received.
  • slow_down_handler: Called when a slow down message is received.
  • path_rerouting_handler: Called when a path rerouting message is received.
  • collision_clearance_handler: Called when a collision clearance message is received.
  • message_error_handler: Called when a message error occurs.
  • update_map_handler: Called when an update map message is received.
  • vehicle_initial_position_handler: Called when a vehicle initial position message is received.
  • update_vehicle_settings: Called when vehicle settings are updated.
  • pause_task_handler: Called when a pause task message is received.
  • resume_task_handler: Called when a resume task message is received.
  • remove_vehicle_from_fleet_handler: Called when a vehicle is removed from the fleet.
  • set_initial_position_handler: Called when a set initial position message is received.
  • update_map_id_handler: Called when an update map ID message is received.
  • topic_list_handler: Called when a topic list message is received.
  • topic_list_initializer_handler: Called when a topic list initializer message is received.
  • docking_routine_request_handler: Called when a docking routine request message is received.
  • docking_routine_finalize_handler: Called when a docking routine is finalized.

You only need to implement the callbacks relevant to your application.

Example of a basic client implementation
from meili_sdk.websockets.client import MeiliWebsocketClient
from meili_sdk.websockets.models.task_v2 import TaskV2

def open_handler():
    print("Connection opened")

def close_handler():
    print("Connection closed")

def error_handler(_, error):
    print(f"Error: {error}")

def task_v2_handler(self, task: TaskV2, vehicle_uuid: str):
    print(f"Received task: {task} for vehicle: {vehicle_uuid}")
    send_task_to_robot(task, vehicle_uuid) # Your task handling logic here

client = MeiliWebsocketClient(
    token="fdd27e8e1a2b4c3d8e9f0a1b2c3d4e5f",
    open_handler=open_handler,
    close_handler=close_handler,
    error_handler=error_handler,
    task_v2_handler=task_v2_handler,
)

client.add_vehicle("afe4b5c6d7e8f9a0b1c2d3e4f5g6h7i8j") # FMS vehicle UUID
client.run_in_thread()
from meili_sdk.websockets.mqtt_client import MeiliMqttClient
from meili_sdk.websockets.models.task_v2 import TaskV2

def open_handler():
    print("Connection opened")

def close_handler():
    print("Connection closed")

def error_handler(_, error):
    print(f"Error: {error}")

def task_v2_handler(self, task: TaskV2, vehicle_uuid: str):
    print(f"Received task: {task} for vehicle: {vehicle_uuid}")
    send_task_to_robot(task, vehicle_uuid) # Your task handling logic here

client = MeiliMqttClient(
    client_id="meili-agent-XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX",
    token="fdd27e8e1a2b4c3d8e9f0a1b2c3d4e5f",
    fleet_uuid="7935baee221644a9b3aa19fca7fabe99",
    open_handler=open_handler,
    close_handler=close_handler,
    error_handler=error_handler,
    task_v2_handler=task_v2_handler,
)

client.add_vehicle("afe4b5c6d7e8f9a0b1c2d3e4f5g6h7i8j") # FMS vehicle UUID
client.run_in_thread()

Message Sending

Both clients let you send various message types to the FMS to report robot status and events. Below are the main message types you can use:


Location

Report the robot’s current position. Supported formats:

  • Meters: xm, ym
  • Latitude/Longitude: lat, lon
  • ROS Coordinates: x, y
message_loc = Message(
    event=constants.EVENT_LOCATION,
    value={"xm": 1, "ym": 2, "rotation": 90, "map_id": "first_floor"},
    vehicle=uuid
)
message_loc.validate()
client.send(message_loc)

Speed

Report the robot’s speed in meters per second.

message_speed = Message(
    event=constants.EVENT_SPEED,
    value={"speed": 0.5},
    vehicle=uuid
)
message_speed.validate()
client.send(message_speed)

Battery

Report battery percentage and charging status.

class PowerSupplyStatuses:
    DISCHARGING = 2
    NOT_CHARGING = 3

message_battery = Message(
    event=constants.EVENT_BATTERY,
    value={"value": 81, "power_supply_status": PowerSupplyStatuses.NOT_CHARGING},
    vehicle=uuid
)
message_battery.validate()
client.send(message_battery)

Goal Status

Update the status of a mission or task.

class GoalStatuses:
    ACTIVE = 1
    SUCCEEDED = 3
    ABORTED = 4

message_goal = Message(
    event=constants.EVENT_GOAL_STATUS,
    value={"goal_id": "fms_task_uuid", "status_id": GoalStatuses.ACTIVE},
    vehicle=uuid
)
message_goal.validate()
client.send(message_goal)

Notification

Send notifications to the FMS interface. Levels: info, warning, error.

message_notification = Message(
    event=constants.EVENT_NOTIFICATION,
    value={"level": "error", "message": "Lost communication"},
    vehicle=uuid
)
message_notification.validate()
client.send(message_notification)

State

Send a combined update of location, speed, and battery.

location = {"xm": 1, "ym": 2, "rotation": 90, "map_id": "first_floor"}
speed = {"speed": 1.5}
battery = {"value": 82, "power_supply_status": PowerSupplyStatuses.NOT_CHARGING}
message_state = Message(
    event=constants.EVENT_STATE,
    value={"location": location, "speed": speed, "battery": battery},
    vehicle=uuid
)
message_state.validate()
client.send(message_state)

Map Update

Send updates to the map, such as new stations.

message_map = Message(
    event=constants.EVENT_MAP_UPDATE,
    value={
        "stations": [
            {
                "uuid": "ff2a1b1c1e5d4f0c9f3d1234567890ab",
                "title": "Station 1",
                "station_type": "default",
                "points_in_meters": [1, 0],
                "rotation": 0.0
            },
            {
                "uuid": "aa3b2c3d4e5f67890abcdef123456789",
                "title": "Station 2",
                "station_type": "default",
                "points_in_meters": [1, -1],
                "rotation": 0.0
            }
        ],
    },
    vehicle=uuid
)
message_map.validate()
client.send(message_map)

As a result, the FMS will update its map with the new stations provided in the message and send back a confirmation message, indicating the UUIDs assigned to the stations. This will be handled by the update_map_handler callback if implemented.


Synchronization

When the user sends a synchronization request via the FMS, update_map_handler is triggered, the message contains the current map configuration, available actions, stations, paths, and presets. Key fields include:

  • action_definitions: List of actions the robot can perform
  • stations: Map stations with coordinates
  • paths: Connections between stations
  • presets: Predefined action sequences
  • displayable_image, height, width: Map image and dimensions
  • yaml_file: Map configuration details
  • uuid: Map identifier

This message provides all necessary information for the robot agent to operate and interact with the FMS.

Last updated 3 November 2026