MQTT Remote Access Proxy¶
OctoEverywhere's Bambu Connect is our plugin for Bambu Lab 3D printers.
Bambu Lab 3D printers work differently from OctoPrint, Klipper, and Prusa Connect 3D printers. They rely on a protocol called MQTT to command and control the printer.
MQTT is a lightweight protocol on top of TCP, which makes remote access tricky. However, we developed a WebSocket proxy for MQTT to allow Bambu Lab app developers to use OctoEverywhere's App Connections remote access.
Our MQTT proxy can be almost like an MQTT tunnel through a web socket. We designed the messages to be very similar to the MQTT messages, so only minor changes have to be made to swap our WebSocket proxy for an actual MQTT implementation.
Connection & Authentication¶
Websocket Endpoint¶
The OctoEverywhere MQTT Websocket Proxy can be used from any App Connection URL or Shared Connection URL.
wss://app-<id>.octoeverywhere.com/octoeverywhere-command-api/proxy/mqtt
For the WebSocket authentication to OctoEverywhere's servers, use the same auth headers that you would use for other App Connection HTTP or WebSocket requests.
When your client makes a WebSocket connection to the OctoEverywhere endpoint, the proxy MQTT connection will automatically be started, connected, and ready to use. If the printer's MQTT connection fails, the WebSocket will close with one of the OctoEverywhere common error codes.
Bambu Lab MQTT Authentication¶
The Bambu Lab 3D printer's MQTT authentication is handled for you! The OctoEverywhere plugin will automatically use the same Bambu Lab serial number and access code that the user set for the OctoEverywhere plugin connection.
WebSocket MQTT Protocol¶
Our WebSocket proxy protocol attempts to mirror MQTTs protocol as closely as possible. Thus, concepts like subscribing and publishing work the same way.
Message Flow¶
Here's an example of what the MQTT websocket proxy messages might look like when connecting to a Bambu Lab 3D printer.
Your App (Websocket MQTT Proxy) Bambu Lab 3D Printer MQTT Server
subscribe-> subscribe-> subscribe->
<-subscribe_ack <-subscribe_ack
...
<-on_subscribe <-on_subscribe <-on_subscribe
...
<-on_message <-on_message <-on_message
<-on_message <-on_message <-on_message
...
publish-> publish-> publish->
<-publish_ack <-publish_ack
...
<-on_message <-on_message <-on_message
...
The subscribe message can have an ID your client randomly generates which will then be paired with the subscribe_ack, allowing you to match the subscribe_ack and subscribe_ack messages. The subscribe_ack will include the MQTT MID (message id). When the MQTT server sends back the on_subscribe event, it will also include a MQTT MID, thus you are able to link each subscribe request message with the resulting on_subscribe event.
After your app is subscribed to a topic, the server will start sending on_message messages whenever a topic update occurs. For Bambu Lab 3D printers, these messages include printer-state changes.
If you want to send a command, your app sends the publish request message, which the MQTT Websocket Proxy will give you as the end result of using publish_ack. The publish_ack can be used to verify the publish message was forwarded successfully.
Message Concepts¶
As you can see in the example above, there are two message concepts for our WebSocket proxy.
-
Remote Function Calls Style Messages
- A "remote function call" (RPC) type message where you send a request and get an ack response back:
subscribe->subscribe_ackunsubscribe->unsubscribe_ackpublish->publish_ack
- A "remote function call" (RPC) type message where you send a request and get an ack response back:
-
Event Style Messages
- These are messages from the MQTT protocol, which the server sends as responses to function calls and/or streaming messages.
on_messageon_subscribeon_unsubscribe
- These are messages from the MQTT protocol, which the server sends as responses to function calls and/or streaming messages.
RPC Style Messages¶
Subscribe¶
Here's an example of a subscribe request message and the subscribe_ack response.
{
"Type": "subscribe",
"Topic": "device/serial_number/report",
"Id": 1337,
"NoAck": false
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: subscribe. |
Topic | string | The MQTT topic to subscribe to. |
Id | int | Optional - A client created random int that will be repeated back to match the subscribe_ack response. |
NoAck | bool | Optional - If set to true, no subscribe_ack response will be sent. |
{
"Type": "subscribe_ack",
"AckResult": 0,
"MqttMessageId": 222,
"Id": 1337,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the request ack: subscribe_ack. |
AckResult | int | The MQTT error code of the action. 0 indicates success, all other values are errors. |
MqttMessageId | int | The MQTT MID (Message Id) for the subscribe request. |
Id | int | Optional - If a random Id was sent in the subscribe request, the same Id will be returned here for matching. |
Unsubscribe¶
Here's an example unsubscribe request message and the unsubscribe_ack response.
{
"Type": "unsubscribe",
"Topic": "device/serial_number/report",
"Id": 2000,
"NoAck": false
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: subscribe. |
Topic | string | The MQTT topic to subscribe to. |
Id | int | Optional - A client created random int that will be repeated back to match the subscribe_ack response. |
NoAck | bool | Optional - If set to true, no unsubscribe_ack response will be sent. |
{
"Type": "unsubscribe_ack",
"AckResult": 0,
"MqttMessageId": 223,
"Id": 2000,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the request ack: unsubscribe_ack. |
AckResult | int | The MQTT error code of the action. 0 indicates success, all other values are errors. |
MqttMessageId | int | The MQTT MID (Message Id) for the subscribe request. |
Id | int | Optional - If a random Id was sent in the unsubscribe request, the same Id will be returned here for matching. |
Publish¶
Here's an example publish request message and the publish_ack response.
{
"Type": "publish",
"Topic": "device/serial_number/report",
"Payload": "base64 encoded data",
"Id": 5000,
"NoAck": false
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: publish. |
Topic | string | The MQTT topic to publish to. |
Payload | string | Any binary data base64 encoded as a string. (usually a json string) |
Id | int | Optional - A client created random int that will be repeated back to match the subscribe_ack response. |
NoAck | bool | Optional - If set to true, no unsubscribe_ack response will be sent. |
{
"Type": "publish_ack",
"AckResult": 0,
"MqttMessageId": 224,
"Id": 5000,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the request ack: publish_ack. |
AckResult | int | The MQTT error code of the action. 0 indicates success, all other values are errors. |
MqttMessageId | int | The MQTT MID (Message Id) for the subscribe request. |
Id | int | Optional - If a random Id was sent in the publish request, the same Id will be returned here for matching. |
Event Messages¶
OnMessage¶
Here's an example on_message event message sent from the MQTT server. These messages are sent as updates once a topic has been subscribed to.
{
"Type": "on_message",
"Payload": "base64 encoded data",
"MqttMessageId": 255,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: on_message. |
Payload | string | Any binary data base64 encoded as a string. (usually a json string) |
MqttMessageId | int | The MQTT MID (Message Id) from the MQTT server. |
OnSubscribe¶
Here's an example on_subscribe event message sent from the MQTT server. This is sent by the MQTT server in response to a subscribe request.
{
"Type": "on_subscribe",
"ReasonCodeList": [0, ...],
"MqttMessageId": 255,
"Id": 5000,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: on_message. |
ReasonCodeList | list [int] | A list of MQTT version 5.0 reason codes. Code 0 is success, all others are failures. |
MqttMessageId | int | The MQTT MID (Message Id) from the MQTT server. |
Id | int | Optional - If a random Id was sent in the subscribe request, the same Id will be returned here for matching. |
OnUnsubscribe¶
Here's an example on_unsubscribe event message sent from the MQTT server. This is sent by the MQTT server in response to a unsubscribe request.
{
"Type": "on_unsubscribe",
"ReasonCodeList": [0, ...],
"MqttMessageId": 255,
"Id": 5000,
}
Parameters
| Name | Type | Description |
|---|---|---|
Type | string | Must be the action: on_unsubscribe. |
ReasonCodeList | list [int] | A list of MQTT version 5.0 reason codes. Code 0 is success, all others are failures. |
MqttMessageId | int | The MQTT MID (Message Id) from the MQTT server. |
Id | int | Optional - If a random Id was sent in the unsubscribe request, the same Id will be returned here for matching. |