# Openbambuapi > The API is available at a base path of`All URLs below are relative to this. --- # Basics ## URL The API is available at a base path of `https://api.bambulab.com`. All URLs below are relative to this. ## Authentication All requests (except to the token refresh endpoint) must be made by presenting an `Authorization` header in the form of `Bearer {ACCESS_TOKEN}`. ## Login and get a token ## POST https://api.bambulab.com/v1/user-service/user/login ### Request Either login with password OR verification code, not both ```json { "account": "", "password":"", "code": "", } ``` ### Response Here you grab the `accessToken` and `refreshToken` from the body of the response. They're usually valid for about 3 months. ```json { "accessToken": "[REMOVED]", "refreshToken": "[REMOVED]", "loginType": "", "expiresIn": 7776000, } ``` ## Requests Request bodies must be empty for GET requests, and otherwise valid JSON ## Responses Error responses tend to follow the following format. As do success messages for **/v1/iot-service/...** ```json { "message": "success", "code": null, "error": null } ``` # URLs ## GET /v1/user-service/my/messages Returns list of messages, looks like a pretty bare Elasticsearch response. Can take the optional query parameters `type`, `after` and `limit`. ### Response ```json { "hits": [ { "id": 0, "type": 6, "design": null, "comment": null, "taskMessage": { "id": 1, "title": "Untitled", "cover": "https://bbl-us-public.oss-us-west-1.aliyuncs.com/[REMOVED]", "status": 2, "deviceId": "[REMOVED]" }, "from": { "uid": 2, "name": "Doridian", "avatar": "https://bbl-us-public.oss-us-west-1.aliyuncs.com/avatar/[REMOVED]", "fanCount": 0, "followCount": 0, "likeCount": 0, "isFollowed": false }, "createTime": "2022-11-22T02:54:12Z" } ] } ``` ## GET /v1/user-service/my/tasks Returns list of tasks, looks like a pretty bare Elasticsearch response. Can take the optional query parameters `deviceId`, `after` and `limit`. ### Response ```json { "total": 5, "hits": [ { "id": 0, "designId": 0, "modelId": "[REMOVED]", "title": "Untitled", "cover": "https://bbl-us-public.oss-us-west-1.aliyuncs.com/[REMOVED]", "status": 2, "feedbackStatus": 0, "startTime": "2022-11-22T01:58:10Z", "endTime": "2022-11-22T02:54:12Z", "weight": 12.6, "costTime": 3348, "profileId": 0, "plateIndex": 1, "deviceId": "[REMOVED]", "amsDetailMapping": [], "mode": "cloud_file" } ] } ``` ## POST /v1/user-service/my/task Creates a task, expects a task object (see above) to be passed via the body. ## GET /v1/user-service/my/ticket/{TICKET_ID} Returns a ticket, probably means support tickets. Don't have any to test. ## POST /v1/user-service/user/refreshtoken > [!CAUTION] > This endpoint will only return 401 responses now. The refresh tokens are also equal to the access tokens in all known cases, and have the same validity as well. That makes this endpoint **practically useless** Send a valid `refreshToken` and get new tokens with new expiration times. ### Request ```json { "refreshToken": "{REFRESH_TOKEN}" } ``` ### Response ```json { "accessToken": "[REMOVED]", "refreshToken": "[REMOVED]", "expiresIn": 29501294, "refreshExpiresIn": 29501294 } ``` ## GET /v1/design-user-service/my/preference Fetches your user account preferences and information. Also is a mirror for `https://makerworld.com/api/v1/design-user-service/my/preference` Useful for numeric `uid`, which when prefixed with `u_` acts as your cloud mqtt username, as this is no longer provided within access tokens. ### Response ```json { "uid": 0000000000, "name": "name", "handle": "handle", "avatar": "url", "bio": "", "links": [ "url1", "url2" ], "backgroundUrl": "url" } ``` ## GET /v1/iot-service/api/slicer/resource Returns a bunch of resources, downloadable things for the slicer. Takes optional query arguments in the form of `type=version` to check for a type at that version. This is used for downloading the networking and camera plugins. Known types (with example version) are: - `slicer/plugins/cloud=01.01.00.00` ### Response ```json { "message": "success", "code": null, "error": null, "software": { "type": null, "version": "01.03.00.25", "description": "[Improvements] 1. Show the print sequence", "url": "https://upgrade-file.bambulab.com/studio/software/01.03.00.25/file.exe", "force_update": false }, "guide": null, "resources": [ { "type": "slicer/plugins/cloud", "version": "01.01.00.11", "description": "", "url": "https://upgrade-file.bambulab.com/studio/plugins/01.01.00.11/file.zip", "force_update": false } ] } ``` ## GET /v1/iot-service/api/slicer/setting?version={SLICER_VERSION} Returns a list of possible slicer profiles (`print`, `printer` and `material`) to query. ### Response ```json { "message": "success", "code": null, "error": null, "print": { "public": [ { "setting_id": "GP001", "version": "01.03.00.13", "name": "0.08mm Extra Fine @BBL X1C", "nickname": null, "filament_id": null } ], "private": [] }, "printer": { "public": [ { "setting_id": "GM001", "version": "01.03.00.13", "name": "Bambu Lab X1 Carbon 0.4 nozzle", "nickname": null, "filament_id": null } ], "private": [] }, "filament": { "public": [ { "setting_id": "GFSB98", "version": "01.03.00.13", "name": "Generic ASA", "nickname": null, "filament_id": "GFB98" } ], "private": [] } } ``` ## GET /v1/iot-service/api/slicer/setting/{SETTING_ID} Gets the full data of a slicer setting by its id. ### Response ```json { "message": "success", "code": null, "error": null, "public": true, "version": "01.00.03.08", "type": "print", "name": "0.12mm Fine @BBL X1C", "nickname": null, "base_id": null, "setting": { "from": "system", "name": "0.12mm Fine @BBL X1C", "type": "process", "version": "01.00.03.08", "inherits": "fdm_process_bbl_0.12" }, "filament_id": null } ``` ## GET /v1/iot-service/api/user/bind This lists devices "bound" to the current user. As in, all your devices. ### Response ```json { "message": "success", "code": null, "error": null, "devices": [ { "dev_id": "[REMOVED]", "name": "3DP-00M-000", "online": true, "print_status": "SUCCESS", "dev_model_name": "BL-P001", "dev_product_name": "X1 Carbon", "dev_access_code": "[REMOVED]" } ] } ``` ## PATCH /v1/iot-service/api/user/device/info This is to update device info. Likely only the name. ### Request ```json { "dev_id": "{DEVICE_ID}" } ``` ### Response ```json { "message": "success", "code": null, "error": null } ``` ## GET /v1/iot-service/api/user/device/version?dev_id={DEVICE_ID} Queries information about firmware version and updates for a device. ### Response ```json { "message": "success", "code": null, "error": null, "devices": [ { "dev_id": "00M00A280102436", "version": "01.01.01.00", "firmware": [ { "version": "01.01.01.00", "force_update": false, "url": "https://upgrade-file.bambulab.com/device/BL-P001/file.sig", "description": "Firmware update details" } ], "ams": [] } ] } ``` ## GET /v1/iot-service/api/user/notification?action={ACTION}&ticket={TICKET_ID} `ACTION` must be either `upload` or `import_mesh` ## GET /v1/iot-service/api/user/print This accepts the optional query parameter `force`, which the slicer always sets to `true`. The response is the current status of the printer. ### Response ```json { "message": "success", "code": null, "error": null, "devices": [ { "dev_id": "[REMOVED]", "dev_name": "3DP-00M-000", "dev_model_name": "BL-P001", "dev_product_name": "X1 Carbon", "dev_online": true, "dev_access_code": "[REMOVED]", "task_id": null, "task_name": null, "task_status": null, "model_id": null, "project_id": null, "profile_id": null, "start_time": null, "prediction": null, "progress": null, "thumbnail": null } ] } ``` ## GET /v1/iot-service/api/user/profile/{PROFILE_ID}?model_id={MODEL_ID} Queries the details of a profile for a certain model, likely for profile deviations? ### Response ```json { "message": "success", "code": null, "error": null, "profile_id": "[REMOVED]", "model_id": "[REMOVED]", "status": "ACTIVE", "name": "Untitled", "content": null, "create_time": "2022-11-22 09:58:04", "update_time": "2022-11-22 09:58:09", "context": { "compatibility": { "dev_model_name": "BL-P001", "dev_product_name": "X1 Carbon", "nozzle_diameter": 0.4 }, "pictures": null, "configs": [], "plates": [], "materials": [], "auxiliary_pictures": [], "auxiliary_bom": [], "auxiliary_guide": [], "auxiliary_other": [] }, "filename": "[REMOVED]", "url": "https://model-file.bambulab.com/[REMOVED]", "md5": "[REMOVED]", "keystore_xml": null } ``` ## GET /v1/iot-service/api/user/project Queries a list of projects for the current user. ### Response ```json { "message": "success", "code": null, "error": null, "projects": [ { "project_id": "[REMOVED]", "user_id": "[REMOVED]", "model_id": "[REMOVED]", "status": "ACTIVE", "name": "Untitled", "content": "null", "create_time": "2022-11-22 09:58:04", "update_time": "2022-11-22 09:58:10" } ] } ``` ## GET /v1/iot-service/api/user/project/{PROJECT_ID} Gets full details about a single project. ### Response ```json { "message": "success", "code": null, "error": null, "project_id": "[REMOVED]", "user_id": "[REMOVED]", "model_id": "[REMOVED]", "status": "ACTIVE", "name": "Untitled", "content": null, "create_time": "2022-11-22 09:58:04", "update_time": "2022-11-22 09:58:10", "profiles": [], "download_url": null, "download_md5": null, "keystore_xml": null, "upload_url": null, "upload_ticket": null } ``` ## GET /v1/iot-service/api/user/task/{TASK_ID} Gets information about a task. So far this has always yielded a 403 even on my own tasks. ### Response ```json { "message": "permission denied", "code": 8, "error": "Resource forbidden", "parent": null, "model_id": null, "project_id": null, "profile_id": null, "status": null, "name": null, "content": null, "context": null, "create_time": null, "update_time": null, "sub_task": null, "subtask": null, "est": null, "url": null, "md5": null } ``` ## POST /v1/iot-service/api/user/ttcode Gets the TTCode for the printer. This is used for authentication to the webcam stream. ### Request ```json { "dev_id": "{DEVICE_ID}" } ``` ### Response ```json { "message": "success", "code": null, "error": null, "ttcode": "[REMOVED]", "passwd": "[REMOVED]", "authkey": "[REMOVED]" } ``` --- # Basics URL: `ftps://{DEVICE_IP}:990` TLS: implicit **Username:** `bblp` **Password:** `dev_access_code` from a `Device` object (aka the LAN access code). --- # Basics This file describes both GCode macros Bambu uses as well as custom GCode found. ## Headers At least some of comment lines at the beginning of the file are necessary for functioning of the print. There are three major blocks with start and end guards: - `HEADER_BLOCK_START`/`HEADER_BLOCK_END` - `CONFIG_BLOCK_START`/`CONFIG_BLOCK_END` - `EXECUTABLE_BLOCK_START`/`EXECUTABLE_BLOCK_END` Excerpt: ```gcode ; CONFIG_BLOCK_START ; accel_to_decel_enable = 1 ; accel_to_decel_factor = 50% ``` Without some or most of these, the printer will hang with "Unzip" shown on the screen. ## Macros ## AMS unload ```gcode M620 S255 M104 S250 G28 X G91 G1 Z3.0 F1200 G90 G1 X70 F12000 G1 Y245 G1 Y265 F3000 M109 S250 G1 X120 F12000 G1 X20 Y50 F12000 G1 Y-3 T255 M104 S25 G1 X165 F5000 G1 Y245 G91 G1 Z-3.0 F1200 G90 M621 S255 ``` ## AMS load ```gcode M620 S[next_tray_index] M104 S250 G28 X G91 G1 Z3.0 F1200 G90 G1 X70 F12000 G1 Y245 G1 Y265 F3000 M109 S250 G1 X120 F12000 G1 X20 Y50 F12000 G1 Y-3 T[next_tray_index] G1 X54 F12000 G1 Y265 M400 M106 P1 S0 G92 E0 G1 E40 F200 M400 M109 S[new_filament_temp] M400 M106 P1 S255 G92 E0 G1 E5 F300 M400 M106 P1 S0 G1 X70 F9000 G1 X76 F15000 G1 X65 F15000 G1 X76 F15000 G1 X65 F15000 G1 X70 F6000 G1 X100 F5000 G1 X70 F15000 G1 X100 F5000 G1 X70 F15000 G1 X165 F5000 G1 Y245 G91 G1 Z-3.0 F1200 G90 M621 S[next_tray_index] ``` ## Manual axis move X, Y or Z ```gcode M211 S M211 X1 Y1 Z1 M1002 push_ref_mode G91 G1 X# F# M1002 pop_ref_mode M211 R ``` ## Manual E move ```gcode M83 G0 E# F# ``` ## Model scan for first layer ```gcode M976 S1 P1 M400 P100 ``` ## Custom commands ## G28 - `G28 Z P0 T300; home z with low precision,permit 300deg temperature` ## G29 Bambu does not follow Marlin's G29 manual. - `G29.2 S0 ; turn off ABL` - `G29.2 S1` - Probably enables recording of height when probe detects collision and also could load saved calibration - `G29 A1 X-1.4281 Y82.682 I67.984 J98.7461` - Probably does bed probing, guarded by g29_before_print_flag ## G39.4 Fires a canned command to detect build plate. Usually flag-guarded and followed by `M400`. It's likely important that Automatic Build Leveling is turned off first (with `G29.2 S0`). ## G380 Some have claimed it's a variant of `G38`, which is called "Probe Target". One use is in the section where the nozzle rubs itself on build plate overhang during cleaning, with code like: ```gcode G0 X90 Y-4 F30000 G380 S3 Z-5 F1200 G1 Z2 F1200 G1 X91 F10000 G380 S3 Z-5 F1200 G1 Z2 F1200 ... ``` `Z` is the axis being zeroed and its movement direction, `F` is feedrate. `S` is custom to Bambu and unknown. There are two known values, `S2` and `S3`. `S2` is only used in this one section: ```gcode ;=====avoid end stop ================= G91 G380 S2 Z30 F1200 G380 S3 Z-20 F1200 G1 Z5 F1200 G90 ``` The above section is used early and the head moves up, down, and back up slightly, just as it appears. ## G392 - `G392 S0` - Appears to *toggle* "clog detect"? Because it's commented as both enabling and disabling clog detect at different times. Done just after initial bed heating and before the "avoid end stop" then "reset machine status" sections. ## M73.2 - `M73.2 R1.0 ;Reset left time magnitude` ## M106 - `M106 P1 S0-255` - set part cooling fan speed (0 off, 255 100%) - `M106 P2 S0-255` - set aux fan speed - `M106 P3 S0-255` - set chamber fan speed ## M109 Causes screen to update to say "Heating", in addition to the usual wait for heating. ## M400 M400 is "Finish Moves". Marlin doesn't document any arguments. Bambu uses these: - `M400 S#` - sleep for about # seconds - `M400 P300` - probably sleep 300 milliseconds; S & P probably can be used together ## M620 - `M620 C#` - calibrate AMS by AMS index - `M620 R#` - refresh AMS by tray index - `M620 P#` - select AMS tray by tray index - `M620 M` - Commented ";enable remap"; only done once before the first M620 command. - `M620 S#` - Starts a branch; if "Enable AMS" is NOT checked, then gcode is skipped until the matching `M621 S#` is found. Commented "select AMS by tray index", but that likely refers to the whole block of commands and not this single command. - Seems to only be used for unloading filament during machine_end_gcode since at least 20221103. - `#` = 255 when unloading filament at end of print for non-H2D. It's just a constant that was in AMS code from the beginning. - `#` = 65535 and 65279 are used during H2D unload. They are simply hardcoded in the machine end code. (65279 is 0x100 less than 65535) - `M620 S#A` - Starts a branch; if "Enable AMS" is NOT checked, then gcode is skipped until the matching `M621 S#A` is found. - Not sure what the `A` does differently. Used for filament change during print, and not for the final unload during machine end sequence. - Does being in this branch as an effect on `T` commands? The `T1000` falls outside a `M620` branch, but `T255` is within the matching one. - Does this branch if the intended filament is already loaded? - What happens if external filament is loaded? ## M620.1 - `M620.1 E F[old_filament_e_feedrate] T{nozzle_temperature_range_high[previous_extruder]}` - Seems to set extrusion parameters that will be used by `T` during load/unload. - Used before and after `T` during filament changes and shortly after `T` during initial load. - `M620.1 X# Y# F# P<0/1/2>` - Prefaced with comment "; get travel path for change filament" when used with X1 and AMS? See [comment](https://github.com/bambulab/BambuStudio/issues/1661#issuecomment-1522800180). ## M620.3 - `M620.3 W1; === turn on filament tangle detection===` ## M620.10 - `M620.10 A0 F[old_filament_e_feedrate]` - It's used just prior to a `T` opcode during filament changes, but not during initial load or unload. Introduced with long retraction. - M620.10 A1 F[new_filament_e_feedrate] L[flush_length] H[nozzle_diameter] T[nozzle_temperature_range_high] - Used shortly after `T` during filament change. ## M620.11 Introduced with "long retraction when cut". - `M620.11 S1 I[previous_extruder] E-{retraction_distances_when_cut[previous_extruder]} F1200` - Run before tool change if the current filament has long retraction enabled. - `M620.11 S1 I[previous_extruder] E{retraction_distances_when_cut[previous_extruder]} F{old_filament_e_feedrate}` - Run after tool change if the old filament had retraction enabled. - `M620.11 S0` run when the filament-being-unloaded does not have a long retraction before and after tool change (`T`). - `M620.11 S1 I# E# F#` - Does not cause any action to occur - `I` always matches the number of the `M620 S#` block, but unknown whether this is enforced. ## M621 - `M621 S#` - End of branch from `M620 S#` - `M621 S#A` - End of branch from `M620 S#A` ## M622 Flags are judged by `M1002`. - `M622 J1` If last judged flag was true, then continue; otherwise skip commands until `M623` is found. - `M622 J0` If last judged flag was false, then continue; otherwise skip commands until `M623` is found. ## M622.1 - `M622.1 S0` - Always run just before dynamic extrusion M9833 - `M622.1 S1 ; for prev firware, default turned on` - seems this turns on whatever this is that is usually already on. ## M623 - `M623` end of branch from `M622` ## M630 - `M630 S0 P0` - used early in "reset machine status", unknown purpose ## M960 - `M960 S5 P0/1`- turn off (0) or on (1) the logo light on toolhead - `M960 S4 P0/1`- turn off (0) or on (1) the nozzle light ## M971 Take photo (for timelapse). ## M973 - `M973 S1` - likely enable scanner - `M973 S2 P16000` - likely perform scan - `M973 S3 P1` - camera start stream - `M973 S4` - disable scanner ## M975 - `M975 S1` - turn on vibration supression - Run many times so something could be resetting it ## M976 - `M976 S1 P1` - first layer scan - `M976 S2 P1` - hot bed scan before print ## M981 - `M981 S0 P20000` - disable spaghetti detector - `M981 S1 P20000` - enable spaghetti detector ## M982.2 - `M982.2 S1 ; turn on cog noise reduction` ## M991 - `M991 S0 P{layer_num}` - commented "notify layer change" - `M991 S0 P-1` - commented "end timelapse at safe pos" but that doesn't seem right. ## M1002 - `M1002 gcode_claim_action : 0` - Display message on screen, 0 is probably clear message. [More numbers on forum.bambulab](https://forum.bambulab.com/t/bambu-lab-x1-specific-g-code/666). Notably: - 1 is auto bed leveling - 2 is heatbed preheating - 4 is changing filament - 5 is M400 U1 pause - [Full(?) list at bambulab forum](https://forum.bambulab.com/t/bambu-lab-x1-specific-g-code/666) - `M1002 set_filament_type:TYPE` - Probably updates display? TYPE can be `UNKNOWN` or `PETG`, `PLA`, `TPU-AMS`, `TPU`, etc. - `M1002 judge_flag FLAGNAME` - Used by `M622` to branch. Some FLAGNAMEs: - `build_plate_detect_flag` - whether you have build plate detection enabled - `g29_before_print_flag` - G29 is automatic bed leveling - `extrude_cali_flag` if "Flow Dynamics Calibration" is enabled when starting print - `filament_need_cali_flag` - ?? - `timelapse_record_flag` - If timelapse is enabled when starting print - `g39_3rd_layer_detect_flag` ## M1003 - `M1003 S0` - disable power loss recovery - `M1003 S1` - enable power loss recovery ## M1006 Allows playing music with the stepper coils. If the printer is configured not to play the tone, it doesn't do anything, presumably. Example line: - `M1006 A44 B20 L100 C39 D20 M100 E48 F20 N100` ## M1007 - `M1007 S0` - unknown purpose. - Done as second command following clog detect disable, during i.e. filament change - `M1007 S1` - done before printing following filament change and during after initialization shortly before printing starts. ## M9833.2 - `M9833.2` - follows turning off clog detect in the beginning; unknown purpose. ## T Tool change. - `T255` (also `T65535` in H2D I think) - Only used during tool change when AMS is unloaded when the print is over. At least causes it to cut and have AMS retract filament. - `T1000` - Used twice in the following sequence during startup only AFAICT: ```gcode M975 S1 ; turn on vibration compensation G90 ; Absolute positioning M83 ; override G90 and put the E axis into relative mode independent of the other axes T1000 ; ??? ``` What does it do if there is a filament already loaded? What does it do if it doesn't? - `T#` - Many canned actions to orchestrate movements of AMS motor and extruder. Cuts filament, goes to purge location, has AMS retract current filament, uses both AMS and extruder motors in a delicate dance mediated by sensors (will try 3 times to load filament, for example, then error). The number is zero-indexed, but references the one-indexed list of filaments displayed in the GUI and also written in `slice_info.config` in the 3MF. - If a bare `M17` hasn't been run since steppers were last messed with, this command will freeze. - Seems to freeze if not set up perfectly; the printer will allow you to press stop and cancel print, but then the print is never cancelled. Requires power cycle. Unsure how many of the setup commands are required. --- # Basics All messages on the MQTT broker are JSON encoded There is two ways to connect to the MQTT broker ## Cloud MQTT server URL: `mqtt://us.mqtt.bambulab.com:8883` TLS: **yes** Authentication: **required** **Username:** `u_{USER_ID}`, where the user id can be grabbed via [the GET /v1/design-user-service/my/preference API](cloud-http.md#get-v1design-user-servicemypreference) **Password:** `{ACCESS_TOKEN}` (the entire access token, no prefix or suffix) ## Local MQTT server URL: `mqtt://{PRINTER_IP}:8883` TLS: **yes** Authentication: **required** **Username:** `bblp` **Password:** `dev_access_code` from a `Device` object (aka the LAN access code). Wildcard subscriptions with `#` possible > [!IMPORTANT] > Check out the [TLS Certificates](tls.md) page for more information on how to securely connect. # Topics ## device/{DEVICE_ID}/report For information from the device to the slicer, including responses to commands ## device/{DEVICE_ID}/request For commands to the device from the slicer # Requests and reports ## Overview ### Request structure ```json { "{TYPE}": { "sequence_id": "0", // Incremented by 1 on each command "command": "{COMMAND}", ... } } ``` ### Report structure ```json { "{TYPE}": { "sequence_id": "0", // Same as the one sent in the request "command": "{COMMAND}", "result": "success", // Case insensitive! "reason": "", // Might not be present for some commands ... // All parameters from the original command } } ``` ## info.get_version Get current version of printer ### Request ```json { "info": { "sequence_id": "0", "command": "get_version" } } ``` ### Report ```json { "info": { "command": "get_version", "module": [ { "hw_ver": "", "name": "ota", "sn": "", "sw_ver": "01.01.01.00" }, { "hw_ver": "AP05", "name": "rv1126", "sn": "[REDACTED]", "sw_ver": "00.00.14.74" }, { "hw_ver": "TH07", "name": "th", "sn": "[REDACTED]", "sw_ver": "00.00.03.79" }, { "hw_ver": "MC07", "name": "mc", "sn": "[REDACTED]", "sw_ver": "00.00.10.48/00.00.10.48" }, { "hw_ver": "", "name": "xm", "sn": "", "sw_ver": "00.00.00.00" } ], "sequence_id": "0" } } ``` ## pushing.pushall Reports the complete status of the printer. This is unnecessary for the X1 series since it already transmits the full object each time. However, the P1 series only sends the values that have been updated compared to the previous report. > [!CAUTION] > As a rule of thumb, refrain from executing this command at intervals less than 5 minutes on the P1P, as it may cause lag due to its hardware limitations. ### Request ```json { "pushing": { "sequence_id": "0", "command": "pushall", "version": 1, "push_target": 1 } } ``` ### Report ```json { "print": { "ams": { "ams": [ { "humidity": "4", "id": "0", "temp": "22.7", "tray": [ { "id": "0" // an empty tray }, { "bed_temp": "0", "bed_temp_type": "0", "cols": [ "000000FF" ], "drying_temp": "0", "drying_time": "0", "id": "1", "nozzle_temp_max": "240", "nozzle_temp_min": "190", "remain": 0, "tag_uid": "0000000000000000", "tray_color": "000000FF", "tray_diameter": "0.00", "tray_id_name": "", "tray_info_idx": "GFA00", "tray_sub_brands": "", "tray_type": "PLA", "tray_uuid": "00000000000000000000000000000000", "tray_weight": "0", "xcam_info": "000000000000000000000000" }, { "bed_temp": "0", "bed_temp_type": "0", "cols": [ "DFE2E3FF" ], "drying_temp": "0", "drying_time": "0", "id": "2", "nozzle_temp_max": "240", "nozzle_temp_min": "190", "remain": 0, "tag_uid": "0000000000000000", "tray_color": "DFE2E3FF", "tray_diameter": "0.00", "tray_id_name": "", "tray_info_idx": "GFA05", "tray_sub_brands": "", "tray_type": "PLA", "tray_uuid": "00000000000000000000000000000000", "tray_weight": "0", "xcam_info": "000000000000000000000000" }, { "bed_temp": "0", "bed_temp_type": "0", "cols": [ "F95959FF" ], "drying_temp": "0", "drying_time": "0", "id": "3", "nozzle_temp_max": "240", "nozzle_temp_min": "190", "remain": 0, "tag_uid": "0000000000000000", "tray_color": "F95959FF", "tray_diameter": "0.00", "tray_id_name": "", "tray_info_idx": "GFL00", "tray_sub_brands": "", "tray_type": "PLA", "tray_uuid": "00000000000000000000000000000000", "tray_weight": "0", "xcam_info": "000000000000000000000000" } ] } ], "ams_exist_bits": "1", "insert_flag": true, "power_on_flag": false, "tray_exist_bits": "e", "tray_is_bbl_bits": "e", "tray_now": "255", // 254 if external spool / vt_tray, otherwise is ((ams_id * 4) + tray_id) for current tray (ams 2 tray 2 would be (1*4)+1 = 5) "tray_pre": "255", "tray_read_done_bits": "e", "tray_reading_bits": "0", "tray_tar": "255", "version": 4 }, "ams_rfid_status": 6, "ams_status": 0, "aux_part_fan": true, // is aux fan installed "bed_target_temper": 25.0, "bed_temper": 25.0, "big_fan1_speed": "0", // Auxilliary fan "big_fan2_speed": "0", // Chamber fan "chamber_temper": 24.0, "command": "push_status", "cooling_fan_speed": "0", // Part Cooling fan "fail_reason": "0", "fan_gear": 0, "filam_bak": [], "force_upgrade": false, "gcode_file": "", "gcode_file_prepare_percent": "0", "gcode_start_time": "0", "gcode_state": "IDLE", "heatbreak_fan_speed": "0", "hms": [], "home_flag": 0, "hw_switch_state": 1, "ipcam": { "ipcam_dev": "1", "ipcam_record": "disable", "resolution": "1080p", "timelapse": "disable" }, "layer_num": 0, "lifecycle": "product", "lights_report": [ { "mode": "on", "node": "chamber_light" }, { "mode": "flashing", "node": "work_light" } ], "maintain": 3, "mc_percent": 0, "mc_print_error_code": "0", "mc_print_stage": "1", "mc_print_sub_stage": 0, "mc_remaining_time": 0, "mess_production_state": "active", "nozzle_diameter": "0.4", "nozzle_target_temper": 25.0, "nozzle_temper": 25.0, "online": { "ahb": false, "rfid": false, "version": 9 }, "print_error": 0, "print_gcode_action": 0, "print_real_action": 0, "print_type": "", "profile_id": "", "project_id": "", "queue_number": 0, "sdcard": true, "sequence_id": "2021", "spd_lvl": 2, "spd_mag": 100, "stg": [], "stg_cur": -1, "subtask_id": "", "subtask_name": "", "task_id": "", "total_layer_num": 0, "upgrade_state": { "ahb_new_version_number": "", "ams_new_version_number": "", "consistency_request": false, "dis_state": 0, "err_code": 0, "force_upgrade": false, "message": "", "module": "null", "new_version_state": 2, "ota_new_version_number": "", "progress": "0", "sequence_id": 0, "status": "IDLE" }, "upload": { "file_size": 0, "finish_size": 0, "message": "Good", "oss_url": "", "progress": 0, "sequence_id": "0903", "speed": 0, "status": "idle", "task_id": "", "time_remaining": 0, "trouble_id": "" }, "vt_tray": { // external spool "bed_temp": "0", "bed_temp_type": "0", "cols": [ "00000000" ], "drying_temp": "0", "drying_time": "0", "id": "254", "nozzle_temp_max": "0", "nozzle_temp_min": "0", "remain": 0, "tag_uid": "0000000000000000", "tray_color": "00000000", "tray_diameter": "0.00", "tray_id_name": "", "tray_info_idx": "", "tray_sub_brands": "", "tray_type": "", "tray_uuid": "00000000000000000000000000000000", "tray_weight": "0", "xcam_info": "000000000000000000000000" }, "wifi_signal": "-45dBm", "xcam": { "allow_skip_parts": false, "buildplate_marker_detector": false, "first_layer_inspector": true, "halt_print_sensitivity": "medium", "print_halt": true, "printing_monitor": true, "spaghetti_detector": true }, "xcam_status": "0" } } ``` ## upgrade.upgrade_confirm Part of firmware upgrade process ### Request ```json { "upgrade": { "sequence_id": "0", "command": "upgrade_confirm", "src_id": 1 // src_id is always 1 for the slicer } } ``` ### Report TODO ## upgrade.consistency_confirm Part of firmware upgrade process ### Request ```json { "upgrade": { "sequence_id": "0", "command": "consistency_confirm", "src_id": 1 // src_id is always 1 for the slicer } } ``` ### Report TODO ## upgrade.start Part of firmware upgrade process ### Request ```json { "upgrade": { "sequence_id": "0", "command": "start", "src_id": 1, // src_id is always 1 for the slicer "url": "...", "module": "ota", // ota or ams "version": "", } } ``` ### Report TODO ## upgrade.get_history Return the firmware history of the printer ### Request ```json { "upgrade": { "sequence_id": "0", "command": "get_history" } } ``` ### Report ```json { "upgrade": { "command": "get_history", "firmware_optional": [ { "ams": [ { "address": 0, "dev_model_name": "BL-A001", "device_id": "{DEVICE_ID}", "firmware": [ { "description": "", "force_update": false, "url": "http://public-cdn.bambulab.com/upgrade/device/BL-A001/00.00.05.96/product/ams-ota_v00.00.05.96-20230106163615.json.sig", "version": "00.00.05.96" } ], "firmware_current": null } ], "firmware": { "description": "", "force_update": false, "url": "https://public-cdn.bambulab.com/upgrade/device/BL-P001/01.04.01.00/product/ota-v01.04.01.00-20230227162230.json.sig", "version": "01.04.01.00" } } ], "reason": "", "result": "success", "sequence_id": "0" } } ``` ## Trigger downgrade TODO ## print.stop Stops a print. Sent with QoS of **1** for higher priority. ### Request ```json { "print": { "sequence_id": "0", "command": "stop", "param": "", // Always empty } } ``` ### Report See basic structure ## print.pause Pauses a print. Sent with QoS of **1** for higher priority. ### Request ```json { "print": { "sequence_id": "0", "command": "pause", "param": "", // Always empty } } ``` ### Report See basic structure ## print.resume Resumes a print. Sent with QoS of **1** for higher priority. ### Request ```json { "print": { "sequence_id": "0", "command": "resume", "param": "", // Always empty } } ``` ### Report See basic structure ## print.ams_change_filament Tells printer to perform a filament change using AMS. ### Request ```json { "print": { "sequence_id": "0", "command": "ams_change_filament", "target": 0, // ID of filament tray "curr_temp": 0, // Old print temperature "tar_temp": 0 // New print temperature } } ``` ### Report TODO ## print.ams_user_setting Changes the AMS settings of the given unit. ### Request ```json { "print": { "sequence_id": "0", "command": "ams_user_setting", "ams_id": 0, // Index of the AMS "startup_read_option": true, // Read RFID on startup "tray_read_option": true // Read RFID on insertion } } ``` ### Report TODO ## print.ams_filament_setting Changes the setting of the given filament in the given AMS. ### Request ```json { "print": { "sequence_id": "0", "command": "ams_filament_setting", "ams_id": 0, // Index of the AMS "tray_id": 0, // Index of the tray "tray_info_idx": "", // Probably the setting ID of the filament profile "tray_color": "00112233", // Formatted as hex RRGGBBAA (alpha is always FF) "nozzle_temp_min": 0, // Minimum nozzle temp for filament (in C) "nozzle_temp_max": 0, // Maximum nozzle temp for filament (in C) "tray_type": "PLA" // Type of filament, such as "PLA" or "ABS" } } ``` ### Report TODO ## print.ams_control Gives basic control commands for the AMS. ### Request ```json { "print": { "sequence_id": "0", "command": "ams_control", "param": "resume" // "resume", "reset" or "pause" } } ``` ### Report TODO ## print.print_speed Set print speed to one of the 4 presets. ### Request ```json { "print": { "sequence_id": "0", "command": "print_speed", "param": "2" // Print speed level as a string // 1 = silent // 2 = standard // 3 = sport // 4 = ludicrous } } ``` ### Report See basic structure ## print.gcode_file Print a gcode file. This takes absolute paths. ### Request ```json { "print": { "sequence_id": "0", "command": "gcode_file", "param": "filename.gcode" // Filename (on the printer's filesystem) to print } } ``` ### Report See basic structure ## print.gcode_line Send raw GCode to the printer. ### Request ```json { "print": { "sequence_id": "0", "command": "gcode_line", "param": "M420", // Gcode to execute, can use \n for multiple lines "user_id": "1234" // Optional } } ``` ### Report See basic structure ## print.calibration Starts calibration process. **Note:** Some printers might need `gcode_file` with `/usr/etc/print/auto_cali_for_user.gcode` instead! **Note:** The options parameter should be a bitmask like this: ```cpp if (lidarCalibration) bitmask |= 1; if (bedLevelling) bitmask |= 1 << 1; if (vibrationCompensation) bitmask |= 1 << 2; if (motorCancellation) bitmask |= 1 << 3; ``` ### Request ```json { "print": { "sequence_id": "0", "command": "calibration", "option": 0, } } ``` ### Report TODO ## print.unload_filament Unloads the filament. **Note:** Some printers might need `gcode_file` with `/usr/etc/print/filament_unload.gcode` instead! ### Request ```json { "print": { "sequence_id": "0", "command": "unload_filament" } } ``` ### Report ```json { // TODO } ``` ## print.project_file Prints a "project" ### Request ```json { "print": { "sequence_id": "0", "command": "project_file", "param": "Metadata/plate_X.gcode", "project_id": "0", // Always 0 for local prints "profile_id": "0", // Always 0 for local prints "task_id": "0", // Always 0 for local prints "subtask_id": "0", // Always 0 for local prints "subtask_name": "", "file": "", // Filename to print, not needed when "url" is specified with filepath "url": "file:///mnt/sdcard", // URL to print. Root path, protocol can vary. E.g., if sd card, "ftp:///myfile.3mf", "ftp:///cache/myotherfile.3mf" "md5": "", "timelapse": true, "bed_type": "auto", // Always "auto" for local prints "bed_levelling": true, "flow_cali": true, "vibration_cali": true, "layer_inspect": true, "ams_mapping": "", "use_ams": false } } ``` ### Report See basic structure ## AMS Mapping Configuration (`ams_mapping`) ### Overview The `ams_mapping` parameter is crucial for multi-color print jobs when using the AMS (Automatic Material System). It defines which AMS slot corresponds to each color in your print file. ### Structure ```json { "print": { "ams_mapping": [ -1, -1, -1, 1, 0 ], // ... rest of print command } } ``` ### How AMS Mapping Works The `ams_mapping` array uses a **reverse indexing system** where: - **Array positions** represent color indices in your print file (starting from 0) - **Array values** represent AMS slot numbers (0-3 for typical 4-slot AMS) - **-1** indicates unused color slots #### Key Rules: 1. **Fixed array length**: Always use 5 elements (supports up to 4 colors + padding) 2. **Right-to-left assignment**: Color assignments fill from the end of the array 3. **Pad with -1**: Fill unused positions at the beginning with -1 | Colors Used | Array Pattern | Description | |----------|---------------|-------------| | 1 color | `[-1, -1, -1, -1, X]` | Single color uses AMS slot X | | 2 colors | `[-1, -1, -1, X, Y]` | Colors map to slots X and Y | | 3 colors | `[-1, -1, X, Y, Z]` | Colors map to slots X, Y, and Z | | 4 colors | `[-1, W, X, Y, Z]` | Colors map to slots W, X, Y, and Z | ### Important Notes - **AMS slot numbers** are zero-indexed (0, 1, 2, 3) - **Color indices** in your print file start from 0 - The printer will just pause and won't start if the mapping is not correct - Ensure the specified AMS slots contain the correct filament types and colors - Always set `"use_ams": true` when using AMS mapping - Verify your AMS is properly loaded before sending the print job --- ## print.skip_objects Skips object ids defined in slice_info.config ### Request ```json { "print": { "sequence_id": "0", "command": "skip_objects", "timestamp": {unix_timestamp} // Current Unix timestamp, seems to be optional. "obj_list": [ 206 // List of canceled object IDs. ] } } ``` ### Report Updates [`pushing.pushall`](#pushingpushall) with ```json "s_obj": [ {obj ids} ], ``` ## print.print_option Modifies the printer's settings. ### Request ```json { "print": { "sequence_id": "0", "command": "print_option", "auto_recovery": true // can be "auto_recovery", "air_print_detect", "filament_tangle_detect", "nozzle_blob_detect", or "sound_enable" } } ``` ### Report See basic structure ## system.ledctrl Controls the LEDs of the printer. ### Request ```json { "system": { "sequence_id": "0", "command": "ledctrl", "led_node": "chamber_light", // Either "chamber_light" or "work_light" "led_mode": "on", // "on", "off" or "flashing" // The below effects are only used for "flashing" mode // but required to be present always "led_on_time": 500, // LED on time in ms "led_off_time": 500, // LED off time in ms "loop_times": 1, // How many times to loop "interval_time": 1000 // Looping interval } } ``` ### Report See basic structure ## system.get_access_code Gets the LAN access code of the printer ### Request ```json { "system": { "sequence_id": "0", "command": "get_access_code" } } ``` ### Report ```json { "system": { "sequence_id": "0", "command": "get_access_code", "access_code": "{ACCESS_CODE}", "result": "success" } } ``` ## system.set_accessories.nozzle Update the nozzle type and diameter. ```json { "system": { "sequence_id": "0", "accessory_type": "nozzle", "command": "set_accessories", "nozzle_diameter": 0.4, "nozzle_type": "stainless_steel" // "stainless_steel" or "hardened_steel" } } ``` ## camera.ipcam_record_set Turns on or off creating a recording of prints. ### Request ```json { "camera": { "sequence_id": "0", "command": "ipcam_record_set", "control": "enable" // "enable" or "disable" } } ``` ### Report See basic structure ## camera.ipcam_timelapse Turns on or off creating a timelapse of prints. ### Request ```json { "camera": { "sequence_id": "0", "command": "ipcam_timelapse", "control": "enable" // "enable" or "disable" } } ``` ### Report See basic structure ## xcam.xcam_control_set Configures the XCam (camera AI features, including Micro LIDAR features). ### Request ```json { "xcam": { "sequence_id": "0", "command": "xcam_control_set", "module_name": "first_layer_inspector", // "first_layer_inspector", "buildplate_marker_detector", "printing_monitor", "pileup_detector", "airprint_detector", "clump_detector", or "spaghetti_detector" "control": true, // Enable the module "print_halt": false // Cause the module to halt the print on error } } ``` ### Report See basic structure # Unsolicited (or semi-solicited) reports ## print.push_status Reports printer status ### X1 Series The X1 series always responds with the full object. See [`pushing.pushall`](#pushingpushall) for said object. ### P1 Series Due to performance limitations the P1P uses the same object, but only actually responds with values that have been changed since the previous report. The full data can be requested using the [`pushing.pushall`](#pushingpushall) request. ## mc_print.push_info Reports log lines of the printer. Can be requested with the [`pushing.pushall`](#pushingpushall) request. ```json { "mc_print": { "command": "push_info", "param": "[LINK] GcodeLine (8) l=15 from 0600 ok: M106 P2 S255 \n\n", "sequence_id": "107" } } ``` ## mc_print.command = "push_info", mc_print.param = "[BMC] M900 KX.XXXX, LX.XXXXX, MX.XXXXX" Report after Lidar calibration of the pressure advance (the reported K value) ```json { "mc_print": { "command": "push_info", "param": "[BMC] M900 KX.XXXX, LX.XXXXX, MX.XXXXX", "sequence_id": "2004" } } ``` ## mc_print.command = "push_info", mc_print.param = "[AMS][TASK]amsX temp:XX.X;humidity:XX%;humidity_idx:X" Report for the raw value for AMS humidity % per AMS id ```json { "mc_print": { "command":"push_info", "param":"[AMS][TASK]ams0 temp:18.4;humidity:30%;humidity_idx:4", "sequence_id":"83" } } ``` --- # TLS Certificates When working with cloud or local printer APIs, proper certificate validation is essential to prevent man-in-the-middle (MITM) attacks. ## Example Code See the [examples folder](./examples/) for Python and Node.js. This is the easiest way to get started without worrying about the technical details. ## Who issued the certificate? The most basic check is to ensure that the certificate is issued by a trusted Certificate Authority (CA). ### Cloud Typically automatically validated by libraries. - These certificates are issued by `DigiCert, Inc.`. ### Printer Configure your MQTT/FTP/TLS libraries to trust [BBL CA](./examples/ca_cert.pem). - The certificate in your printer is issued by Bambu Lab. ## Who is the certificate issued to? This is another important check to ensure you are not only communicating with _any_ server, but the _correct_ server. Certificates contain a common name (CN) field that specifies the host name of the server. ### Cloud (Certificate Hostname) Typically automatically validated by libraries. - Example: certificate contains `CN=*.mqtt.bambulab.com`, you connect to `us.mqtt.bambulab.com` ### Printer (Certificate Hostname) Requires [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication). - Example: certificate contains `CN=`, you connect to `192.168.0.5` - Not all libraries support SNI, in this case you need to manually check the common name or disable host name validation at your own risk. ## Common Errors **Do not** disable all SSL/TLS validations when encountering errors like the following. If it's not clear how to fix it, please open an issue at the libraries you are using or at [OpenBambuAPI](https://github.com/Doridian/OpenBambuAPI/issues/new). - ```text [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain ``` This errors can happen when connecting to a printer without trusting the Bambu Lab CA. **Fix**: Refer to [Who issued the certificate?](#who-issued-the-certificate) - ```text [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: IP: 192.168.0.5 is not in the cert's list [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '192.168.0.1' ``` These errors can happen when connecting to a printer without configuring SNI. **Fix**: Refer to [Who is the certificate issued to?](#who-is-the-certificate-issued-to) - ```text [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: CA cert does not include key usage extension ``` This error can happen when using python 3.13+ to connect to a printer. **Workaround**: Refer to note about [VERIFY_X509_STRICT](https://docs.python.org/3/library/ssl.html#ssl.create_default_context). --- # Basics ## X1 and P2S The X1 series and P2S printers run a RTSP server that streams video over the network. ### Local RTSP Server - **URL**: `rtsps://{PRINTER_IP}:322/streaming/live/1` - **TLS**: [yes](./tls.md) - **Authentication**: required - **Username**: `bblp` - **Password**: `dev_access_code` from a `Device` object (aka the LAN access code). ## A1 and P1 The A1 and P1 series printers run a simple TCP server that streams 1280x720 JPEG images over the network. Integers are encoded in little-endian byte order. ### Local Video Server - **Host**: `{PRINTER_IP}:6000` - **TLS**: [yes](./tls.md) - **Authentication**: required - **Username**: `bblp` - **Password**: `dev_access_code` from a `Device` object (aka the LAN access code). ### Authentication This packet must be sent when connecting to the server. | Offset | Size | Value | | ------ | -------- | -------------------------------------------------------- | | 0 | 4 bytes | Inlined Payload size (0x40) | | 4 | 4 bytes | Type (0x3000) | | 8 | 4 bytes | Flags (0) | | 12 | 4 bytes | 0 | | 16 | 32 bytes | Username encoded as ASCII, right-padded with null bytes. | | 48 | 32 bytes | Password encoded as ASCII, right-padded with null bytes. | ### Header The server sends a 16-byte header for each frame. | Offset | Size | Value | | ------ | ------- | ------------ | | 0 | 4 bytes | Payload size | | 4 | 4 bytes | itrack (0) | | 8 | 4 bytes | Flags (1) | | 12 | 4 bytes | 0 | ### Image After the header, the server sends a total of `payload size` bytes containing the JPEG image. The image data might be received in multiple chunks (e.g. up to 4096 bytes each), depending on the programming language and operating system. These chunks need to be concatenated to reconstruct the complete image. The following magic bytes can be checked to verify that the image has been successfully received: - Start of Image: `FF D8` - End of Image: `FF D9`