Skip to content

bambuprinter

The main bambu-printer-manager class BambuPrinter lives here.

Classes:

Name Description
BambuPrinter

BambuPrinter is the main class within bambu-printer-manager

BambuPrinter

Python
BambuPrinter(config: BambuConfig | None = None)

BambuPrinter is the main class within bambu-printer-manager It is responsible for interacting with and managing your Bambu Lab 3d printer. It provides an object oriented abstraction layer between your project and the mqtt and ftps based mechanisms in place for communicating with your printer.

Parameters

  • config : Optional[BambuConfig] = None An optional BambuConfig instance that provides configuration information for the printer connection. If not provided, a default BambuConfig instance will be created.

Methods:

Name Description
delete_sdcard_file

Delete the specified file on the printer's SDCard and returns an updated dict of all files on the printer

delete_sdcard_folder

Delete the specified folder on the printer's SDCard and returns an updated dict of all files on the printer

download_sdcard_file

Downloads a file from the printer

ftp_connection

Open an FTPS connection to the printer's SD card for file management operations.

get_current_bind_list

Build the manual_ams_bind list required by the H2D dual-extruder firmware.

get_sdcard_3mf_files

Returns a dict (json document) of all .3mf files on the printer's SD card.

get_sdcard_contents

Returns a dict (json document) of ALL files on the printer's SD card.

load_filament

Requests the printer to load filament into the extruder using the requested spool (slot #)

make_sdcard_directory

Creates the specified directory on the printer and returns an updated dict of all files on the printer

pause_printing

Pauses the current print job if one is running.

pause_session

Pauses the BambuPrinter session is it is active. Under the covers this

print_3mf_file

Submits a request to print a .3mf file already stored on the printer's SD card.

quit

Shuts down all threads. Your BambuPrinter instance should probably be

refresh

Triggers a full data refresh from the printer (if it is connected). You should use this

refresh_nozzles

Requests the printer to push back current nozzle state.

refresh_spool_rfid

Request the printer to re-read the RFID tag for the specified AMS slot.

rename_printer

Rename the printer to the specified new name.

rename_sdcard_file

Renames the specified file on the printer and returns an updated dict of all files on the printer

resume_printing

Resumes the current print job if one is paused.

resume_session

Resumes a previously paused session by re-subscribing to the /report topic.

sdcard_file_exists

Checks to see if a file exists on the printer at the path specified

select_extrusion_calibration_profile

Sets the k factor profile for the specified tray.

send_ams_control_command

Send an AMS control command to pause, resume, or reset the AMS.

send_anything

Publish an arbitrary JSON string directly to the printer's MQTT request topic.

send_gcode

Submit one, or more, gcode commands to the printer. To submit multiple gcode commands, separate them with a newline (\n) character.

set_active_tool

Set the active extruder on multi-tool machines (H2D / H2D Pro).

set_airprinting_detector

Enable or disable the air-printing / no-extrusion detector (X-Cam AI vision).

set_ams_user_setting

Enable or disable one of the AMSUserSetting options on the specified AMS unit.

set_aux_fan_speed_target_percent

sets the aux (chamber recirculation) fan speed target represented in percent

set_bed_temp_target

Sets the bed temperature target.

set_buildplate_marker_detector

Enable or disable the buildplate marker detector (X-Cam AI vision).

set_chamber_temp

for printers that do not have managed chambers, this enables you to inject

set_chamber_temp_target

set chamber temperature target if printer supports it, otherwise just

set_exhaust_fan_speed_target_percent

sets the exhaust (chamber) fan speed target represented in percent

set_nozzle_details

Inform the printer of the nozzle currently installed.

set_nozzle_temp_target

Sets the nozzle temperature target.

set_nozzleclumping_detector

Enable or disable the nozzle clumping / blob detector (X-Cam AI vision).

set_part_cooling_fan_speed_target_percent

sets the part cooling fan speed target represented in percent

set_print_option

Enable or disable one of the PrintOption options.

set_purgechutepileup_detector

Enable or disable the purge chute pile-up detector (X-Cam AI vision).

set_spaghetti_detector

Enable or disable the spaghetti / failed print detector (X-Cam AI vision).

set_spool_details

Sets spool / tray details such as filament type, color, and nozzle temperature range.

set_spool_k_factor

Set the linear advance (pressure advance) k factor for a specific spool tray.

skip_objects

Instructs the printer to skip (cancel) a list of objects during the current print job.

start_session

Initiates a connection to the Bambu Lab printer and provides a stateful

stop_printing

Requests the printer to stop printing if a job is currently running.

toJson

Returns a dict (json document) representing this object's private class

turn_off_ams_dryer

Sends a command to the printer to turn off the AMS dryer.

turn_on_ams_dryer

Sends a command to the printer to turn on the AMS dryer with specified parameters.

unload_filament

Requests the printer to unload whatever filament / spool is currently loaded.

upload_sdcard_file

Uploads the local filesystem file to the printer and returns an updated dict of all files on the printer

Attributes:

Name Type Description
active_job_info ActiveJobInfo

Details related to the current / last active job

bed_temp_target_time

Timestamp of the last change to the heatbed target temperature.

cached_sd_card_3mf_files

A list of only the 3mf files found on the SD card.

cached_sd_card_contents

A list of all files and folders found on the printer's SD card.

chamber_temp_target_time

Timestamp of the last change to the chamber target temperature.

client Client

The networking client used to communicate with the printer.

config BambuConfig

The settings used to connect to and configure the printer's behavior.

fan_speed_target_time

Timestamp of the last change to the part fan target speed.

internalException

The last error captured during printer communication.

light_state

The status of the printer lights. Toggling this will update all lights on the machine.

nozzle_diameter NozzleDiameter

The diameter of the nozzle currently installed on the printer.

nozzle_type NozzleType

The type of nozzle currently installed on the printer.

on_update

The callback function executed whenever the printer's state is updated.

printer_state BambuState

The current status and sensor data for the printer.

recent_update

Indicates if the printer's state has been updated recently.

service_state

The current service connection state.

skipped_objects

A list of objects that have been excluded from the current print.

speed_level

The active print speed mode. Updating this will change the printer's execution speed.

tool_temp_target_time

Timestamp of the last change to the nozzle target temperature.

Source code in src/bpm/bambuprinter.py
Python
def __init__(self, config: BambuConfig | None = None):
    """
    Sets up all internal storage attributes for `BambuPrinter` and bootstraps the
    logging engine.

    Parameters
    ----------
    * config : Optional[BambuConfig] = None
        An optional `BambuConfig` instance that provides configuration information
        for the printer connection.  If not provided, a default `BambuConfig` instance
        will be created.
    """
    self._mqtt_client_thread = None
    self._watchdog_thread = None

    self._internalException = None
    self._lastMessageTime = None
    self._recent_update = False

    if config is None:
        config = BambuConfig("", "", "")
    self._config = config
    self._service_state = ServiceState.NO_STATE

    self._client = None
    self._on_update = None

    self._tool_temp_target_time = 0
    self._bed_temp_target_time = 0
    self._chamber_temp_target_time = 0
    self._fan_speed_target_time = 0

    self._light_state = ""
    self._speed_level = 0

    self._printer_state = BambuState()
    self._active_job_info = ActiveJobInfo()

    self._sdcard_contents = None
    self._sdcard_3mf_files = None

    self._print_type = ""
    self._skipped_objects = []

    self._nozzle_type = ""
    self._nozzle_diameter = 0.0

active_job_info property

Python
active_job_info: ActiveJobInfo

Details related to the current / last active job

bed_temp_target_time property

Python
bed_temp_target_time

Timestamp of the last change to the heatbed target temperature.

cached_sd_card_3mf_files property

Python
cached_sd_card_3mf_files

A list of only the 3mf files found on the SD card.

cached_sd_card_contents property

Python
cached_sd_card_contents

A list of all files and folders found on the printer's SD card.

chamber_temp_target_time property

Python
chamber_temp_target_time

Timestamp of the last change to the chamber target temperature.

client property writable

Python
client: Client

The networking client used to communicate with the printer.

This is a private property

config property writable

Python
config: BambuConfig

The settings used to connect to and configure the printer's behavior.

fan_speed_target_time property

Python
fan_speed_target_time

Timestamp of the last change to the part fan target speed.

internalException property

Python
internalException

The last error captured during printer communication.

light_state property writable

Python
light_state

The status of the printer lights. Toggling this will update all lights on the machine.

nozzle_diameter property

Python
nozzle_diameter: NozzleDiameter

The diameter of the nozzle currently installed on the printer.

Deprecated

This property is deprecated (v1.0.0). Use _printer_state.active_nozzle.diameter_mm instead.

nozzle_type property

Python
nozzle_type: NozzleType

The type of nozzle currently installed on the printer.

Deprecated

This property is deprecated (v1.0.0). Use _printer_state.active_nozzle.material instead.

on_update property writable

Python
on_update

The callback function executed whenever the printer's state is updated.

printer_state property

Python
printer_state: BambuState

The current status and sensor data for the printer.

recent_update property

Python
recent_update

Indicates if the printer's state has been updated recently.

service_state property writable

Python
service_state

The current service connection state.

skipped_objects property

Python
skipped_objects

A list of objects that have been excluded from the current print.

speed_level property writable

Python
speed_level

The active print speed mode. Updating this will change the printer's execution speed.

tool_temp_target_time property

Python
tool_temp_target_time

Timestamp of the last change to the nozzle target temperature.

delete_sdcard_file

Python
delete_sdcard_file(file: str)

Delete the specified file on the printer's SDCard and returns an updated dict of all files on the printer

Parameters
  • file : str - the full path filename to be deleted
Source code in src/bpm/bambuprinter.py
Python
def delete_sdcard_file(self, file: str):
    """
    Delete the specified file on the printer's SDCard and returns an updated dict of all files on the printer

    Parameters
    ----------
    * file : str - the full path filename to be deleted
    """
    logger.debug(f"delete_sdcard_file - deleting remote file: [{file}]")

    with self.ftp_connection() as ftps:
        ftps.delete_file(file)

    # Invalidate all cached plate metadata for this file
    filename = file.lstrip("/").replace("/", "-")
    serial = self.config.serial_number
    cache_path = self.config.bpm_cache_path if self.config.bpm_cache_path else Path()
    if serial:
        cache_path = cache_path / serial
    for cached in (cache_path / "metadata").glob(f"{filename}-*.json") if (cache_path / "metadata").exists() else []:
        cached.unlink(missing_ok=True)
        logger.debug(f"delete_sdcard_file - removed cache entry [{cached.name}]")

    def search_for_and_remove_file(file: str, entry: dict):
        if "children" in entry:
            entry["children"] = list(
                filter(lambda i: i["id"] != file, entry["children"])
            )
            for child in entry["children"]:
                search_for_and_remove_file(file, child)

    if self._sdcard_contents:
        search_for_and_remove_file(file, self._sdcard_contents)
    if self._sdcard_3mf_files:
        search_for_and_remove_file(file, self._sdcard_3mf_files)

    logger.debug(f"delete_sdcard_file - deleted file [{file}] from sdcard")
    return self._sdcard_contents

delete_sdcard_folder

Python
delete_sdcard_folder(path: str)

Delete the specified folder on the printer's SDCard and returns an updated dict of all files on the printer

Parameters
  • path : str - the full path to folder to be deleted
Source code in src/bpm/bambuprinter.py
Python
def delete_sdcard_folder(self, path: str):
    """
    Delete the specified folder on the printer's SDCard and returns an updated dict of all files on the printer

    Parameters
    ----------
    * path : str - the full path to folder to be deleted
    """
    logger.debug(f"delete_sdcard_folder - deleting remote folder: [{path}]")

    def delete_all_contents(ftps: IoTFTPSClient, path: str):
        fs = ftps.list_files_ex(path)
        if fs is not None:
            for item in fs:
                if item.is_dir:
                    delete_all_contents(ftps, item.path)
                else:
                    ftps.delete_file(item.path)
        ftps.delete_folder(path)

    with self.ftp_connection() as ftps:
        delete_all_contents(ftps, path)

    # Invalidate all cached plate metadata for files under this folder
    prefix = path.strip("/").replace("/", "-")
    serial = self.config.serial_number
    cache_path = self.config.bpm_cache_path if self.config.bpm_cache_path else Path()
    if serial:
        cache_path = cache_path / serial
    metadata_dir = cache_path / "metadata"
    if metadata_dir.exists():
        for cached in metadata_dir.glob(f"{prefix}-*.json"):
            cached.unlink(missing_ok=True)
            logger.debug(f"delete_sdcard_folder - removed cache entry [{cached.name}]")

    def search_for_and_remove_folder(path: str, entry: dict):
        if not path.endswith("/"):
            path = f"{path}/"
        if "children" in entry:
            entry["children"] = list(
                filter(lambda i: not i["id"].startswith(path), entry["children"])
            )
            for child in entry["children"]:
                search_for_and_remove_folder(path, child)

    if self._sdcard_contents is not None:
        search_for_and_remove_folder(path, self._sdcard_contents)
    if self._sdcard_3mf_files is not None:
        search_for_and_remove_folder(path, self._sdcard_3mf_files)
    return self._sdcard_contents

download_sdcard_file

Python
download_sdcard_file(src: str, dest: str)

Downloads a file from the printer

Parameters
  • src : str - the full path filename on the printer to be downloaded to the host
  • dest : str - the full path filename on the host to store the downloaded file
Source code in src/bpm/bambuprinter.py
Python
def download_sdcard_file(self, src: str, dest: str):
    """
    Downloads a file from the printer

    Parameters
    ----------
    * src : str - the full path filename on the printer to be downloaded to the host
    * dest : str - the full path filename on the host to store the downloaded file
    """
    logger.debug(
        f"download_sdcard_file - downloading file src: [{src}] dest: [{dest}]"
    )
    with self.ftp_connection() as ftps:
        ftps.download_file(src, dest)

ftp_connection

Python
ftp_connection()

Open an FTPS connection to the printer's SD card for file management operations.

Intended to be used as a context manager so the connection is closed automatically on exit. All SD card file operations (upload_sdcard_file, download_sdcard_file, delete_sdcard_file, etc.) use this internally.

Example
Python
with printer.ftp_connection() as ftps:
    ftps.upload_file("/local/myfile.3mf", "/jobs/myfile.3mf")
Source code in src/bpm/bambuprinter.py
Python
@contextlib.contextmanager
def ftp_connection(self):
    """
    Open an FTPS connection to the printer's SD card for file management operations.

    Intended to be used as a context manager so the connection is closed
    automatically on exit.  All SD card file operations (`upload_sdcard_file`,
    `download_sdcard_file`, `delete_sdcard_file`, etc.) use this internally.

    Example
    -------
    ```python
    with printer.ftp_connection() as ftps:
        ftps.upload_file("/local/myfile.3mf", "/jobs/myfile.3mf")
    ```
    """
    ftps = None
    try:
        ftps = IoTFTPSClient(
            self._config.hostname,
            990,
            self._config.mqtt_username,
            self._config.access_code,
            ssl_implicit=True,
        )
        yield ftps
    finally:
        if ftps and ftps.ftps_session:
            ftps.disconnect()

get_current_bind_list

Python
get_current_bind_list(state: BambuState) -> list[dict[str, Any]]

Build the manual_ams_bind list required by the H2D dual-extruder firmware.

Maps each connected AMS unit to its assigned extruder using the H2D's hardware register inversion (RIGHT_EXTRUDER logical index 0 → hardware index 1, LEFT_EXTRUDER logical index 1 → hardware index 0).

When only one AMS unit is connected the firmware still requires a two-entry array; a sentinel placeholder entry (Unit ID 1) is appended automatically to satisfy that requirement.

Parameters
  • state : BambuState - Current printer state supplying ams_units and ams_connected_count.
Returns

list[dict] - List of dicts with keys ams_f_bind (int), ams_s_bind (int), and extruder (int, hardware index).

Source code in src/bpm/bambuprinter.py
Python
def get_current_bind_list(self, state: "BambuState") -> list[dict[str, Any]]:
    """
    Build the `manual_ams_bind` list required by the H2D dual-extruder firmware.

    Maps each connected AMS unit to its assigned extruder using the H2D's
    hardware register inversion (RIGHT_EXTRUDER logical index 0 → hardware index 1,
    LEFT_EXTRUDER logical index 1 → hardware index 0).

    When only one AMS unit is connected the firmware still requires a two-entry
    array; a sentinel placeholder entry (Unit ID 1) is appended automatically to
    satisfy that requirement.

    Parameters
    ----------
    * state : BambuState - Current printer state supplying `ams_units` and
        `ams_connected_count`.

    Returns
    -------
    list[dict] - List of dicts with keys `ams_f_bind` (int), `ams_s_bind` (int),
        and `extruder` (int, hardware index).
    """
    bind_list = []

    # 1. Map physical units based on current logical assignments
    for unit in state.ams_units:
        # Hardware register inversion logic
        hw_target = 1 if unit.assigned_to_extruder == ActiveTool.RIGHT_EXTRUDER else 0

        bind_list.append(
            {"ams_f_bind": int(unit.ams_id), "ams_s_bind": 0, "extruder": hw_target}
        )

    # 2. Dual-Extruder Array Completeness Requirement
    # If only one AMS is connected, firmware requires a placeholder for the unused register.
    if state.ams_connected_count == 1 and len(bind_list) == 1:
        existing_assignment = bind_list[0]

        # Determine the unassigned hardware register
        placeholder_hw_target = 0 if existing_assignment["extruder"] == 1 else 1

        # Add sentinel placeholder (Unit ID 1) to complete the dual-path mapping
        bind_list.append(
            {
                "ams_f_bind": 1,  # Placeholder Unit ID
                "ams_s_bind": 0,
                "extruder": placeholder_hw_target,
            }
        )

    return bind_list

get_sdcard_3mf_files

Python
get_sdcard_3mf_files()

Returns a dict (json document) of all .3mf files on the printer's SD card.

Usage

The return value of this method is very useful for binding to things like a clientside TreeView

Source code in src/bpm/bambuprinter.py
Python
def get_sdcard_3mf_files(self):
    """
    Returns a `dict` (json document) of all `.3mf` files on the printer's SD card.

    Usage
    -----
    The return value of this method is very useful for binding to things like a clientside `TreeView`
    """
    logger.debug("get_sdcard_3mf_files - returning sdcard_3mf_files")
    self.get_sdcard_contents()
    return self._sdcard_3mf_files

get_sdcard_contents

Python
get_sdcard_contents()

Returns a dict (json document) of ALL files on the printer's SD card. The private class level _sdcard_contents attribute is also populated.

Usage

The return value of this method is very useful for binding to things like a clientside TreeView

Source code in src/bpm/bambuprinter.py
Python
def get_sdcard_contents(self):
    """
    Returns a `dict` (json document) of ALL files on the printer's SD card.
    The private class level `_sdcard_contents` attribute is also populated.

    Usage
    -----
    The return value of this method is very useful for binding to things like a clientside `TreeView`
    """

    with self.ftp_connection() as ftps:
        fs = self._get_sftp_files(ftps, "/")
    if fs is None:
        logger.warning("get_sdcard_contents - failed to retrieve files from sdcard")
        self._sdcard_contents = None
        self._sdcard_3mf_files = None
        return None

    self._sdcard_contents = sortFileTreeAlphabetically(fs)

    def search_for_and_remove_all_other_files(mask: str, entry: dict):
        if "children" in entry:
            entry["children"] = [
                x
                for x in entry["children"]
                if x["id"].endswith(mask) or "children" in x
            ]
            for child in entry["children"]:
                search_for_and_remove_all_other_files(mask, child)

    self._sdcard_3mf_files = json.loads(json.dumps(self._sdcard_contents))
    search_for_and_remove_all_other_files(".3mf", self._sdcard_3mf_files)

    logger.debug("get_sdcard_contents - retrieved all files from sdcard")
    return fs

load_filament

Python
load_filament(slot_id: int, ams_id: int = 0)

Requests the printer to load filament into the extruder using the requested spool (slot #)

Parameters

slot_id : int

  • 0 - AMS Spool #1
  • 1 - AMS Spool #2
  • 2 - AMS Spool #3
  • 3 - AMS Spool #4
  • 254 - External Spool
Source code in src/bpm/bambuprinter.py
Python
def load_filament(self, slot_id: int, ams_id: int = 0):
    """
    Requests the printer to load filament into the extruder using the requested spool (slot #)

    Parameters
    ----------
    slot_id : int

    * `0` - AMS Spool #1
    * `1` - AMS Spool #2
    * `2` - AMS Spool #3
    * `3` - AMS Spool #4
    * `254` - External Spool
    """
    # TODO: refactor to support multiple AMSs

    msg = copy.deepcopy(AMS_CHANGE_FILAMENT)

    msg["print"]["ams_id"] = ams_id
    msg["print"]["target"] = slot_id
    msg["print"]["slot_id"] = slot_id
    msg["print"]["soft_temp"] = 0
    msg["print"]["tar_temp"] = -1
    msg["print"]["curr_temp"] = -1

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(msg)
    )
    logger.debug(
        f"load_filament - published AMS_CHANGE_FILAMENT to [device/{self.config.serial_number}/request] - target: [{slot_id}], bambu_msg: [{msg}]"
    )

make_sdcard_directory

Python
make_sdcard_directory(dir: str)

Creates the specified directory on the printer and returns an updated dict of all files on the printer

Parameters
  • dir : str - the full path directory name to be created
Source code in src/bpm/bambuprinter.py
Python
def make_sdcard_directory(self, dir: str):
    """
    Creates the specified directory on the printer and returns an updated dict of all files on the printer

    Parameters
    ----------
    * dir : str - the full path directory name to be created
    """
    logger.debug(f"make_sdcard_directory - creating remote directory [{dir}]")
    with self.ftp_connection() as ftps:
        ftps.mkdir(dir)
    return self.get_sdcard_contents()

pause_printing

Python
pause_printing()

Pauses the current print job if one is running.

Source code in src/bpm/bambuprinter.py
Python
def pause_printing(self):
    """
    Pauses the current print job if one is running.
    """
    self.client.publish(
        f"device/{self.config.serial_number}/request",
        json.dumps(PAUSE_PRINT),
    )
    logger.debug(
        f"pause_printing - published PAUSE_PRINT to [device/{self.config.serial_number}/request]"
    )

pause_session

Python
pause_session()

Pauses the BambuPrinter session is it is active. Under the covers this method unsubscribes from the /report topic, essentially disabling all printer data refreshes.

Source code in src/bpm/bambuprinter.py
Python
def pause_session(self):
    """
    Pauses the `BambuPrinter` session is it is active.  Under the covers this
    method unsubscribes from the `/report` topic, essentially disabling all
    printer data refreshes.
    """
    if self.service_state != ServiceState.PAUSED:
        self.client.unsubscribe(f"device/{self.config.serial_number}/report")
        logger.debug(
            f"pause_session - unsubscribed from [device/{self.config.serial_number}/report]"
        )
        self.service_state = ServiceState.PAUSED

print_3mf_file

Python
print_3mf_file(
    name: str,
    plate: int,
    bed: PlateType,
    use_ams: bool,
    ams_mapping: str | None = "",
    bedlevel: bool | None = True,
    flow: bool | None = True,
    timelapse: bool | None = False,
)

Submits a request to print a .3mf file already stored on the printer's SD card.

The ams_mapping value to pass here is available directly from ProjectInfo.metadata["ams_mapping"] returned by get_project_info. It is the same absolute-tray-ID encoding used by BambuStudio / OrcaSlicer and stored in the 3MF's Metadata/slice_info.config.

Parameters
  • name : str - Full SD card path to the .3mf file, including leading / (e.g. "/jobs/my_project.3mf" or "/jobs/my_project.gcode.3mf").
  • plate : int - The 1-indexed plate number from the slicer to print. Available plate numbers are in ProjectInfo.plates.
  • bed : PlateType - Bed surface type to use (e.g. PlateType.HOT_PLATE). Pass PlateType.AUTO to let the printer decide based on slicer metadata.
  • use_ams : bool - True to route filament through the AMS; False to use the external spool regardless of ams_mapping content.
  • ams_mapping : Optional[str] = "" - JSON array string mapping each project filament (0-indexed) to an absolute AMS tray ID. Sourced from ProjectInfo.metadata["ams_mapping"] (serialised to JSON string). ams_mapping2 (per-filament {"ams_id": int, "slot_id": int} dicts) is auto-generated from this value for firmware compatibility. Pass "" or None to omit the mapping entirely.
  • bedlevel : Optional[bool] = True - Auto-level the bed before printing.
  • flow : Optional[bool] = True - Run extrusion flow calibration before printing.
  • timelapse : Optional[bool] = False - Record a timelapse during printing.
Examples
  • print_3mf_file("/jobs/my_project.3mf", 1, PlateType.HOT_PLATE, False, "") — AMS disabled
  • print_3mf_file("/jobs/my_project.gcode.3mf", 1, PlateType.HOT_PLATE, True, "[0,1,2]") — 3 filaments from AMS trays 1–3
AMS Mapping Encoding

The ams_mapping string is a JSON integer array. Each element is an absolute tray ID whose encoding depends on the AMS unit type:

AMS type Formula Range
Standard 4-slot (AMS 2 / LITE / N3F) ams_id * 4 + slot_id 0–103
Single-slot (N3S / AMS HT) ams_id (starts at 128) 128–135
External spool 254
Unmapped / no AMS -1
ams_mapping Meaning
"[0]" 1 filament → AMS 0 slot 0
"[0,1,4]" 3 filaments → AMS 0 slots 0–1, AMS 1 slot 0
"[0,-1,5]" 3 filaments → AMS 0 slot 0, unmapped, AMS 1 slot 1
"[0,1,2,3]" 4 filaments → all 4 slots of AMS 0
"[0,4,128]" 3 filaments → AMS 0 slot 0, AMS 1 slot 0, AMS HT

The array is generated by the slicer (BambuStudio / OrcaSlicer) via color-distance matching and stored in Metadata/slice_info.config inside the .3mf. get_project_info parses it into ProjectInfo.metadata["ams_mapping"].

See BambuStudio DevMapping.cpp / DeviceManager.cpp for the encoding logic. See OpenBambuAPI spec: https://github.com/Doridian/OpenBambuAPI/blob/main/mqtt.md#ams-mapping-configuration-ams_mapping

Source code in src/bpm/bambuprinter.py
Python
def print_3mf_file(
    self,
    name: str,
    plate: int,
    bed: PlateType,
    use_ams: bool,
    ams_mapping: str | None = "",
    bedlevel: bool | None = True,
    flow: bool | None = True,
    timelapse: bool | None = False,
):
    """
    Submits a request to print a `.3mf` file already stored on the printer's SD card.

    The `ams_mapping` value to pass here is available directly from
    `ProjectInfo.metadata["ams_mapping"]` returned by `get_project_info`.
    It is the same absolute-tray-ID encoding used by BambuStudio / OrcaSlicer and
    stored in the 3MF's `Metadata/slice_info.config`.

    Parameters
    ----------
    * name : str - Full SD card path to the `.3mf` file, including leading `/`
        (e.g. `"/jobs/my_project.3mf"` or `"/jobs/my_project.gcode.3mf"`).
    * plate : int - The 1-indexed plate number from the slicer to print.
        Available plate numbers are in `ProjectInfo.plates`.
    * bed : PlateType - Bed surface type to use (e.g. `PlateType.HOT_PLATE`).
        Pass `PlateType.AUTO` to let the printer decide based on slicer metadata.
    * use_ams : bool - `True` to route filament through the AMS; `False` to use
        the external spool regardless of `ams_mapping` content.
    * ams_mapping : Optional[str] = `""` - JSON array string mapping each project
        filament (0-indexed) to an absolute AMS tray ID.  Sourced from
        `ProjectInfo.metadata["ams_mapping"]` (serialised to JSON string).
        `ams_mapping2` (per-filament `{"ams_id": int, "slot_id": int}` dicts) is
        auto-generated from this value for firmware compatibility.
        Pass `""` or `None` to omit the mapping entirely.
    * bedlevel : Optional[bool] = `True` - Auto-level the bed before printing.
    * flow : Optional[bool] = `True` - Run extrusion flow calibration before printing.
    * timelapse : Optional[bool] = `False` - Record a timelapse during printing.

    Examples
    --------
    * `print_3mf_file("/jobs/my_project.3mf", 1, PlateType.HOT_PLATE, False, "")` — AMS disabled
    * `print_3mf_file("/jobs/my_project.gcode.3mf", 1, PlateType.HOT_PLATE, True, "[0,1,2]")` — 3 filaments from AMS trays 1–3

    AMS Mapping Encoding
    --------------------
    The `ams_mapping` string is a JSON integer array.  Each element is an absolute
    **tray ID** whose encoding depends on the AMS unit type:

    | AMS type                        | Formula                   | Range   |
    |---------------------------------|---------------------------|---------|
    | Standard 4-slot (AMS 2 / LITE / N3F) | `ams_id * 4 + slot_id` | 0–103  |
    | Single-slot (N3S / AMS HT)      | `ams_id` (starts at 128)  | 128–135 |
    | External spool                  | `254`                     | —       |
    | Unmapped / no AMS               | `-1`                      | —       |

    | ams_mapping    | Meaning                                          |
    |----------------|--------------------------------------------------|
    | `"[0]"`        | 1 filament → AMS 0 slot 0                        |
    | `"[0,1,4]"`    | 3 filaments → AMS 0 slots 0–1, AMS 1 slot 0     |
    | `"[0,-1,5]"`   | 3 filaments → AMS 0 slot 0, unmapped, AMS 1 slot 1 |
    | `"[0,1,2,3]"`  | 4 filaments → all 4 slots of AMS 0              |
    | `"[0,4,128]"`  | 3 filaments → AMS 0 slot 0, AMS 1 slot 0, AMS HT |

    The array is generated by the slicer (BambuStudio / OrcaSlicer) via
    color-distance matching and stored in `Metadata/slice_info.config` inside
    the `.3mf`.  `get_project_info` parses it into `ProjectInfo.metadata["ams_mapping"]`.

    See BambuStudio `DevMapping.cpp` / `DeviceManager.cpp` for the encoding logic.
    See OpenBambuAPI spec: <https://github.com/Doridian/OpenBambuAPI/blob/main/mqtt.md#ams-mapping-configuration-ams_mapping>
    """
    _3mf_file = f"{name}"
    _plate_num = int(plate)

    cmd = copy.deepcopy(PRINT_3MF_FILE)

    subtask = name[name.rindex("/") + 1 : :] if "/" in name else name
    subtask = (
        subtask[::-1].replace(".3mf"[::-1], "", 1)[::-1]
        if subtask.endswith(".3mf")
        else subtask
    )
    subtask = (
        subtask[::-1].replace(".gcode"[::-1], "", 1)[::-1]
        if subtask.endswith(".gcode")
        else subtask
    )

    cmd["print"]["file"] = _3mf_file

    if getPrinterSeriesByModel(self.config.printer_model) in (
        PrinterSeries.A1,
        PrinterSeries.P1,
    ):
        cmd["print"]["url"] = f"file:///sdcard{_3mf_file}"
    else:
        cmd["print"]["url"] = f"ftp://{_3mf_file}"

    cmd["print"]["subtask_name"] = subtask
    cmd["print"]["bed_type"] = bed.name.lower()
    cmd["print"]["param"] = cmd["print"]["param"].replace("#", str(_plate_num))
    cmd["print"]["use_ams"] = use_ams

    def decode_ams_mapping_entry(tray_id: int) -> tuple[int, int]:
        if tray_id < 0:
            return 255, 255

        if tray_id in (254, 255):
            return tray_id, 0

        # Legacy HT shorthand from some clients: 128..253 => ams_id, slot 0
        if 128 <= tray_id <= 253:
            return tray_id, 0

        # Standard protocol tray IDs (including normalized HT values like 512)
        return tray_id // 4, tray_id % 4

    # Parse ams_mapping JSON array. Values are absolute tray IDs from BambuStudio/OrcaSlicer:
    # 0-103 for 4-slot units, 128-135 for 1-slot units, -1 for unmapped.
    # See: https://github.com/bambulab/BambuStudio/blob/main/src/slic3r/GUI/DeviceCore/DevMapping.cpp
    if ams_mapping and len(ams_mapping) > 0:
        parsed_ams_mapping = [int(x) for x in json.loads(ams_mapping)]
        parsed_ams_mapping2 = []
        for tray_id in parsed_ams_mapping:
            ams_id, slot_id = decode_ams_mapping_entry(tray_id)
            parsed_ams_mapping2.append({"ams_id": ams_id, "slot_id": slot_id})

        cmd["print"]["ams_mapping"] = parsed_ams_mapping
        cmd["print"]["ams_mapping2"] = parsed_ams_mapping2

    cmd["print"]["bed_leveling"] = bedlevel
    cmd["print"]["flow_cali"] = flow
    cmd["print"]["timelapse"] = timelapse
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"print_3mf_file - published PRINT_3MF_FILE to [device/{self.config.serial_number}/request] print_command: [{cmd}]"
    )

quit

Python
quit()

Shuts down all threads. Your BambuPrinter instance should probably be considered dead after making this call although you may be able to restart a session with start_session().

Source code in src/bpm/bambuprinter.py
Python
def quit(self):
    """
    Shuts down all threads.  Your `BambuPrinter` instance should probably be
    considered dead after making this call although you may be able to restart a
    session with [start_session](#bpm.bambuprinter.BambuPrinter.start_session)().
    """
    if self.client and self.client.is_connected():
        self.client.disconnect()
        logger.debug("quit - mqtt client was connected and is now disconnected")
    else:
        logger.debug("quit - mqtt client was already disconnected")

    self._service_state = ServiceState.QUIT
    self._notify_update()

    if self._mqtt_client_thread and self._mqtt_client_thread.is_alive():
        self._mqtt_client_thread.join()
    if self._watchdog_thread and self._watchdog_thread.is_alive():
        self._watchdog_thread.join()
    logger.debug("quit - all threads have terminated")

refresh

Python
refresh()

Triggers a full data refresh from the printer (if it is connected). You should use this method sparingly as resorting to it indicates something is not working properly.

Source code in src/bpm/bambuprinter.py
Python
def refresh(self):
    """
    Triggers a full data refresh from the printer (if it is connected).  You should use this
    method sparingly as resorting to it indicates something is not working properly.
    """
    if self.service_state == ServiceState.CONNECTED:
        logger.debug(
            f"refresh - publishing ANNOUNCE_VERSION to [device/{self.config.serial_number}/request]"
        )
        self.client.publish(
            f"device/{self.config.serial_number}/request",
            json.dumps(ANNOUNCE_VERSION),
        )
        logger.debug(
            f"refresh - publishing ANNOUNCE_PUSH to [device/{self.config.serial_number}/request]"
        )
        self.client.publish(
            f"device/{self.config.serial_number}/request",
            json.dumps(ANNOUNCE_PUSH),
        )

refresh_nozzles

Python
refresh_nozzles()

Requests the printer to push back current nozzle state. Used for multi-extruder models (H2D, H2D Pro) where nozzles are manually swapped.

Source code in src/bpm/bambuprinter.py
Python
def refresh_nozzles(self):
    """
    Requests the printer to push back current nozzle state.
    Used for multi-extruder models (H2D, H2D Pro) where nozzles are manually swapped.
    """
    cmd = copy.deepcopy(REFRESH_NOZZLE)

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"refresh_nozzles - published REFRESH_NOZZLE to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

refresh_spool_rfid

Python
refresh_spool_rfid(slot_id: int, ams_id: int = 0)

Request the printer to re-read the RFID tag for the specified AMS slot.

Only RFID-equipped Bambu Lab spools carry tag data. The printer will push an updated telemetry message containing the spool details after scanning.

Parameters
  • slot_id : int - The slot within the AMS unit to scan (0–3 for standard AMS).
  • ams_id : int = 0 - The AMS unit containing the slot (default is 0).
Source code in src/bpm/bambuprinter.py
Python
def refresh_spool_rfid(self, slot_id: int, ams_id: int = 0):
    """
    Request the printer to re-read the RFID tag for the specified AMS slot.

    Only RFID-equipped Bambu Lab spools carry tag data.  The printer will push
    an updated telemetry message containing the spool details after scanning.

    Parameters
    ----------
    * slot_id : int - The slot within the AMS unit to scan (0–3 for standard AMS).
    * ams_id : int = 0 - The AMS unit containing the slot (default is 0).
    """
    cmd = copy.deepcopy(AMS_GET_RFID)
    cmd["print"]["ams_id"] = ams_id
    cmd["print"]["slot_id"] = slot_id

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"refresh_spool_rfid - published AMS_GET_RFID to [device/{self.config.serial_number}/request] command: [{cmd}]"
    )

rename_printer

Python
rename_printer(new_name: str)

Rename the printer to the specified new name.

Source code in src/bpm/bambuprinter.py
Python
def rename_printer(self, new_name: str):
    """
    Rename the printer to the specified new name.
    """
    cmd = copy.deepcopy(RENAME_PRINTER)
    cmd["update"]["name"] = new_name

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"rename_printerq - published RENAME_PRINTER to [device/{self.config.serial_number}/request] command: [{cmd}]"
    )

rename_sdcard_file

Python
rename_sdcard_file(src: str, dest: str)

Renames the specified file on the printer and returns an updated dict of all files on the printer

Parameters
  • src : str - the full path name to be renamed
  • dest : str - the full path name to be renamed to
Source code in src/bpm/bambuprinter.py
Python
def rename_sdcard_file(self, src: str, dest: str):
    """
    Renames the specified file on the printer and returns an updated dict of all files on the printer

    Parameters
    ----------
    * src : str - the full path name to be renamed
    * dest : str - the full path name to be renamed to
    """
    logger.debug(f"rename_sdcard_file - renaming printer file [{src}] to [{dest}]")
    with self.ftp_connection() as ftps:
        ftps.move_file(src, dest)
    return self.get_sdcard_contents()

resume_printing

Python
resume_printing()

Resumes the current print job if one is paused.

Source code in src/bpm/bambuprinter.py
Python
def resume_printing(self):
    """
    Resumes the current print job if one is paused.
    """
    self.client.publish(
        f"device/{self.config.serial_number}/request",
        json.dumps(RESUME_PRINT),
    )
    logger.debug(
        f"resume_printing - published RESUME_PRINT to [device/{self.config.serial_number}/request]"
    )

resume_session

Python
resume_session()

Resumes a previously paused session by re-subscribing to the /report topic.

Source code in src/bpm/bambuprinter.py
Python
def resume_session(self):
    """
    Resumes a previously paused session by re-subscribing to the /report topic.
    """
    if (
        self.client
        and self.client.is_connected()
        and self.service_state == ServiceState.PAUSED
    ):
        self.client.subscribe(f"device/{self.config.serial_number}/report")
        logger.debug(
            f"resume_session - subscribed to [device/{self.config.serial_number}/report]"
        )
        self._lastMessageTime = time.monotonic()
        self.service_state = ServiceState.CONNECTED
        return
    self.service_state = ServiceState.QUIT

sdcard_file_exists

Python
sdcard_file_exists(path: str) -> bool

Checks to see if a file exists on the printer at the path specified

Parameters
  • path : str - the full path name to check
Source code in src/bpm/bambuprinter.py
Python
def sdcard_file_exists(self, path: str) -> bool:
    """
    Checks to see if a file exists on the printer at the `path` specified

    Parameters
    ----------
    * path : str - the full path name to check
    """
    logger.debug(f"sdcard_file_exists - checking if printer file [{path}] exists")
    with self.ftp_connection() as ftps:
        return ftps.fexists(path)

select_extrusion_calibration_profile

Python
select_extrusion_calibration_profile(tray_id: int, cali_idx: int = -1)

Sets the k factor profile for the specified tray.

Parameters

tray_id : int - tray id cali_idx : calibration index , optional, defaults to -1 (the default profile)

Source code in src/bpm/bambuprinter.py
Python
def select_extrusion_calibration_profile(self, tray_id: int, cali_idx: int = -1):
    """
    Sets the k factor profile for the specified tray.

    Parameters
    ----------
    tray_id : int - tray id
    cali_idx : calibration index , optional, defaults to -1 (the default profile)
    """
    cmd = copy.deepcopy(EXTRUSION_CALI_SEL)

    ams_id = math.floor(tray_id / 4)
    slot_id = tray_id % 4
    if tray_id == 254 or tray_id == 255:
        ams_id = tray_id
        slot_id = 0

    cmd["print"]["ams_id"] = ams_id
    cmd["print"]["tray_id"] = tray_id
    cmd["print"]["slot_id"] = slot_id
    cmd["print"]["cali_idx"] = cali_idx

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"select_extrusion_calibration_profile - published EXTRUSION_CALI_SEL to [device/{self.config.serial_number}/request] cmd: [{cmd}]"
    )

send_ams_control_command

Python
send_ams_control_command(ams_control_cmd: AMSControlCommand)

Send an AMS control command to pause, resume, or reset the AMS.

When AMSControlCommand.RESUME is sent, resume_printing() is also called automatically to restart the paused print job.

Parameters
  • ams_control_cmd : AMSControlCommand - The control command to send (AMSControlCommand.PAUSE, AMSControlCommand.RESUME, or AMSControlCommand.RESET).
Source code in src/bpm/bambuprinter.py
Python
def send_ams_control_command(self, ams_control_cmd: AMSControlCommand):
    """
    Send an AMS control command to pause, resume, or reset the AMS.

    When `AMSControlCommand.RESUME` is sent, `resume_printing()` is also called
    automatically to restart the paused print job.

    Parameters
    ----------
    * ams_control_cmd : AMSControlCommand - The control command to send
        (`AMSControlCommand.PAUSE`, `AMSControlCommand.RESUME`, or `AMSControlCommand.RESET`).
    """
    ams_cmd = ams_control_cmd.name.lower()
    cmd = copy.deepcopy(AMS_CONTROL)
    cmd["print"]["param"] = ams_cmd

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"send_ams_control_commandpublished AMS_CONTROL to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

    # trigger resume print for good measure
    if ams_control_cmd == AMSControlCommand.RESUME:
        self.resume_printing()

send_anything

Python
send_anything(anything: str)

Publish an arbitrary JSON string directly to the printer's MQTT request topic.

Intended for advanced use / debugging — bypasses all validation and command abstraction. The string is parsed then re-serialised before publishing, so it must be valid JSON.

Parameters
  • anything : str - A valid JSON string to publish.
Source code in src/bpm/bambuprinter.py
Python
def send_anything(self, anything: str):
    """
    Publish an arbitrary JSON string directly to the printer's MQTT request topic.

    Intended for advanced use / debugging — bypasses all validation and command
    abstraction.  The string is parsed then re-serialised before publishing, so
    it must be valid JSON.

    Parameters
    ----------
    * anything : str - A valid JSON string to publish.
    """
    self.client.publish(
        f"device/{self.config.serial_number}/request",
        json.dumps(json.loads(anything)),
    )
    logger.debug(
        f"send_anything - published message to [device/{self.config.serial_number}/request] message: [{anything}]"
    )

send_gcode

Python
send_gcode(gcode: str)

Submit one, or more, gcode commands to the printer. To submit multiple gcode commands, separate them with a newline (\n) character.

Parameters

gcode : str

Examples
  • send_gcode("G91\nG0 X0\nG0 X50") - queues 3 gcode commands on the printer for processing
  • send_gcode("G28") - queues 1 gcode command on the printer for processing
Source code in src/bpm/bambuprinter.py
Python
def send_gcode(self, gcode: str):
    """
    Submit one, or more, gcode commands to the printer.  To submit multiple gcode commands, separate them with a newline (\\n) character.

    Parameters
    ----------
    gcode : str

    Examples
    --------
    * `send_gcode("G91\\nG0 X0\\nG0 X50")` - queues 3 gcode commands on the printer for processing
    * `send_gcode("G28")` - queues 1 gcode command on the printer for processing
    """
    cmd = copy.deepcopy(SEND_GCODE_TEMPLATE)
    cmd["print"]["param"] = f"{gcode} \n"
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"send_gcode - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] gcode: [{gcode}]"
    )

set_active_tool

Python
set_active_tool(id: int)

Set the active extruder on multi-tool machines (H2D / H2D Pro).

Sends a SET_ACTIVE_TOOL command that switches which extruder the printer uses for subsequent moves and extrusions.

Parameters
  • id : int - The extruder index to activate (0 = right extruder, 1 = left extruder on H2D; see ActiveTool enum for named constants).
Source code in src/bpm/bambuprinter.py
Python
def set_active_tool(self, id: int):
    """
    Set the active extruder on multi-tool machines (H2D / H2D Pro).

    Sends a `SET_ACTIVE_TOOL` command that switches which extruder the printer
    uses for subsequent moves and extrusions.

    Parameters
    ----------
    * id : int - The extruder index to activate (`0` = right extruder, `1` = left extruder
        on H2D; see `ActiveTool` enum for named constants).
    """
    cmd = copy.deepcopy(SET_ACTIVE_TOOL)
    cmd["print"]["extruder_index"] = id
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_active_tool - published SET_ACTIVE_TOOL to [device/{self.config.serial_number}/request] command: [{cmd}]"
    )

set_airprinting_detector

Python
set_airprinting_detector(enabled: bool, sensitivity: DetectorSensitivity = MEDIUM)

Enable or disable the air-printing / no-extrusion detector (X-Cam AI vision).

When triggered, the printer halts the print because the nozzle is detected to be extruding into open air rather than onto the bed or model. State is persisted to config.airprinting_detector and config.airprinting_detector_sensitivity.

Parameters
  • enabled : bool - True to enable the detector, False to disable it.
  • sensitivity : DetectorSensitivity = DetectorSensitivity.MEDIUM - Detection threshold.
Source code in src/bpm/bambuprinter.py
Python
def set_airprinting_detector(
    self, enabled: bool, sensitivity: DetectorSensitivity = DetectorSensitivity.MEDIUM
):
    """
    Enable or disable the air-printing / no-extrusion detector (X-Cam AI vision).

    When triggered, the printer halts the print because the nozzle is detected to
    be extruding into open air rather than onto the bed or model.  State is persisted
    to `config.airprinting_detector` and `config.airprinting_detector_sensitivity`.

    Parameters
    ----------
    * enabled : bool - `True` to enable the detector, `False` to disable it.
    * sensitivity : DetectorSensitivity = `DetectorSensitivity.MEDIUM` - Detection threshold.
    """
    cmd = copy.deepcopy(XCAM_CONTROL_SET)
    cmd["xcam"]["module_name"] = "airprint_detector"
    cmd["xcam"]["control"] = enabled
    cmd["xcam"]["enable"] = enabled
    cmd["xcam"]["print_halt"] = True
    cmd["xcam"]["halt_print_sensitivity"] = sensitivity.value

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    self.config.airprinting_detector = enabled
    self.config.airprinting_detector_sensitivity = sensitivity.value

    logger.debug(
        f"set_airprinting_detector - published XCAM_CONTROL_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_ams_user_setting

Python
set_ams_user_setting(setting: AMSUserSetting, enabled: bool, ams_id: int = 0)

Enable or disable one of the AMSUserSetting options on the specified AMS unit.

The three settings — CALIBRATE_REMAIN_FLAG, STARTUP_READ_OPTION, and TRAY_READ_OPTION — are all sent together in a single command, with only setting toggled to the new value. The corresponding BambuConfig attribute is also updated.

Parameters
  • setting : AMSUserSetting - The AMS user setting to change.
  • enabled : bool - True to enable the setting, False to disable it.
  • ams_id : int = 0 - The AMS unit to target (default is 0).
Source code in src/bpm/bambuprinter.py
Python
def set_ams_user_setting(
    self, setting: AMSUserSetting, enabled: bool, ams_id: int = 0
):
    """
    Enable or disable one of the `AMSUserSetting` options on the specified AMS unit.

    The three settings — `CALIBRATE_REMAIN_FLAG`, `STARTUP_READ_OPTION`, and
    `TRAY_READ_OPTION` — are all sent together in a single command, with only
    `setting` toggled to the new value.  The corresponding `BambuConfig` attribute
    is also updated.

    Parameters
    ----------
    * setting : AMSUserSetting - The AMS user setting to change.
    * enabled : bool - `True` to enable the setting, `False` to disable it.
    * ams_id : int = 0 - The AMS unit to target (default is 0).
    """
    cmd = copy.deepcopy(AMS_USER_SETTING)
    cmd["print"]["ams_id"] = ams_id
    cmd["print"][AMSUserSetting.CALIBRATE_REMAIN_FLAG.name.lower()] = (
        self.config.calibrate_remain_flag
    )
    cmd["print"][AMSUserSetting.STARTUP_READ_OPTION.name.lower()] = (
        self.config.startup_read_option
    )
    cmd["print"][AMSUserSetting.TRAY_READ_OPTION.name.lower()] = (
        self.config.tray_read_option
    )

    cmd["print"][setting.name.lower()] = enabled

    if setting == AMSUserSetting.STARTUP_READ_OPTION:
        self.config.startup_read_option = enabled
    elif setting == AMSUserSetting.TRAY_READ_OPTION:
        self.config.tray_read_option = enabled
    elif setting == AMSUserSetting.CALIBRATE_REMAIN_FLAG:
        self.config.calibrate_remain_flag = enabled

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_ams_user_setting - published AMS_USER_SETTING to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_aux_fan_speed_target_percent

Python
set_aux_fan_speed_target_percent(value: int)

sets the aux (chamber recirculation) fan speed target represented in percent

Parameters
  • value : int - The target speed in percent
Source code in src/bpm/bambuprinter.py
Python
@deprecated(
    "This method is deprecated. The closest alternative is `select_extrusion_calibration_profile`."
)
def set_aux_fan_speed_target_percent(self, value: int):
    """
    sets the aux (chamber recirculation) fan speed target represented in percent

    Parameters
    ----------
    * value : int - The target speed in percent
    """
    if value < 0:
        value = 0
    self._printer_state.climate.zone_aux_percent = value
    speed = round(value * 2.55, 0)
    gcode = SEND_GCODE_TEMPLATE
    gcode["print"]["param"] = f"M106 P2 S{speed}\n"
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(gcode)
    )
    logger.debug(
        f"set_aux_fan_speed_target_percent - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] command: [{gcode}]"
    )

set_bed_temp_target

Python
set_bed_temp_target(value: int)

Sets the bed temperature target.

Parameters
  • value : float - The target bed temperature.
Source code in src/bpm/bambuprinter.py
Python
def set_bed_temp_target(self, value: int):
    """
    Sets the bed temperature target.

    Parameters
    ----------
    * value : float - The target bed temperature.
    """
    if value < 0:
        value = 0
    gcode = SEND_GCODE_TEMPLATE
    gcode["print"]["param"] = f"M140 S{value}\n"
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(gcode)
    )
    logger.debug(
        f"set_bed_temp_target - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] command: [{gcode}]"
    )
    self._bed_temp_target_time = round(time.time())

set_buildplate_marker_detector

Python
set_buildplate_marker_detector(enabled: bool)

Enable or disable the buildplate marker detector (X-Cam AI vision).

When enabled, the printer's camera checks for the calibration marker on the build plate before starting a print. The state is also written to config.buildplate_marker_detector.

Parameters
  • enabled : bool - True to enable the detector, False to disable it.
Source code in src/bpm/bambuprinter.py
Python
def set_buildplate_marker_detector(self, enabled: bool):
    """
    Enable or disable the buildplate marker detector (X-Cam AI vision).

    When enabled, the printer's camera checks for the calibration marker on the
    build plate before starting a print.  The state is also written to
    `config.buildplate_marker_detector`.

    Parameters
    ----------
    * enabled : bool - `True` to enable the detector, `False` to disable it.
    """
    cmd = copy.deepcopy(XCAM_CONTROL_SET)
    cmd["xcam"]["module_name"] = "buildplate_marker_detector"
    cmd["xcam"]["control"] = enabled
    cmd["xcam"]["enable"] = enabled

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    self.config.buildplate_marker_detector = enabled

    logger.debug(
        f"set_buildplate_marker_detector - published XCAM_CONTROL_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_chamber_temp

Python
set_chamber_temp(value: float)

for printers that do not have managed chambers, this enables you to inject a chamber temperature value from an external source

Parameters
  • value : float - The chamber temperature.
Source code in src/bpm/bambuprinter.py
Python
def set_chamber_temp(self, value: float):
    """
    for printers that do not have managed chambers, this enables you to inject
    a chamber temperature value from an external source

    Parameters
    ----------
    * value : float - The chamber temperature.
    """
    self._printer_state.climate.chamber_temp = value

set_chamber_temp_target

Python
set_chamber_temp_target(value: int, temper_check: bool = True)

set chamber temperature target if printer supports it, otherwise just store the value

Parameters
  • value : float - The target chamber temperature.
  • temper_check : OPTIONAL bool - perform a temperature check?
Source code in src/bpm/bambuprinter.py
Python
def set_chamber_temp_target(self, value: int, temper_check: bool = True):
    """
    set chamber temperature target if printer supports it, otherwise just
    store the value

    Parameters
    ----------
    * value : float - The target chamber temperature.
    * temper_check : OPTIONAL bool - perform a temperature check?
    """
    if self.config.capabilities.has_chamber_temp:
        cmd = copy.deepcopy(SET_CHAMBER_TEMP_TARGET)
        cmd["print"]["ctt_val"] = value
        cmd["print"]["temper_check"] = temper_check

        self.client.publish(
            f"device/{self.config.serial_number}/request", json.dumps(cmd)
        )
        logger.debug(
            f"set_chamber_temp_target - published SET_CHAMBER_TEMP_TARGET to [device/{self.config.serial_number}/request] command: [{cmd}]"
        )

        cmd = copy.deepcopy(SET_CHAMBER_AC_MODE)

        if value < 40:
            cmd["print"]["modeId"] = 0
        else:
            cmd["print"]["modeId"] = 1
        self.client.publish(
            f"device/{self.config.serial_number}/request", json.dumps(cmd)
        )
        logger.debug(
            f"set_chamber_temp_target - published SET_CHAMBER_AC_MODE to [device/{self.config.serial_number}/request] command: [{cmd}]"
        )

    self._printer_state.climate.chamber_temp_target = value
    self._chamber_temp_target_time = round(time.time())

set_exhaust_fan_speed_target_percent

Python
set_exhaust_fan_speed_target_percent(value: int)

sets the exhaust (chamber) fan speed target represented in percent

Parameters
  • value : int - The target speed in percent
Source code in src/bpm/bambuprinter.py
Python
def set_exhaust_fan_speed_target_percent(self, value: int):
    """
    sets the exhaust (chamber) fan speed target represented in percent

    Parameters
    ----------
    * value : int - The target speed in percent
    """
    if value < 0:
        value = 0
    self._printer_state.climate.zone_exhaust_percent = value
    speed = round(value * 2.55, 0)
    gcode = SEND_GCODE_TEMPLATE
    gcode["print"]["param"] = f"M106 P3 S{speed}\n"
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(gcode)
    )
    logger.debug(
        f"set_exhaust_fan_speed_target_percent - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] command: [{gcode}]"
    )

set_nozzle_details

Python
set_nozzle_details(nozzle_diameter: NozzleDiameter, nozzle_type: NozzleType)

Inform the printer of the nozzle currently installed.

Sends a SET_ACCESSORIES command so the firmware can apply the correct temperature limits, flow rates, and material compatibility checks.

Parameters
  • nozzle_diameter : NozzleDiameter - The physical nozzle diameter (e.g. NozzleDiameter.DIAMETER_0_4).
  • nozzle_type : NozzleType - The nozzle material / type (e.g. NozzleType.HARDENED_STEEL, NozzleType.STAINLESS_STEEL).
Source code in src/bpm/bambuprinter.py
Python
def set_nozzle_details(
    self, nozzle_diameter: NozzleDiameter, nozzle_type: NozzleType
):
    """
    Inform the printer of the nozzle currently installed.

    Sends a `SET_ACCESSORIES` command so the firmware can apply the correct
    temperature limits, flow rates, and material compatibility checks.

    Parameters
    ----------
    * nozzle_diameter : NozzleDiameter - The physical nozzle diameter
        (e.g. `NozzleDiameter.DIAMETER_0_4`).
    * nozzle_type : NozzleType - The nozzle material / type
        (e.g. `NozzleType.HARDENED_STEEL`, `NozzleType.STAINLESS_STEEL`).
    """
    cmd = copy.deepcopy(SET_ACCESSORIES)
    cmd["system"]["nozzle_diameter"] = nozzle_diameter.value
    cmd["system"]["nozzle_type"] = nozzle_type_to_telemetry(nozzle_type)

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_nozzle_details - published SET_ACCESSORIES to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_nozzle_temp_target

Python
set_nozzle_temp_target(value: int, tool_num: int = -1)

Sets the nozzle temperature target.

Parameters
  • value : float - The target nozzle temperature.
  • tool_num : int - The tool number (default is 0).
Source code in src/bpm/bambuprinter.py
Python
def set_nozzle_temp_target(self, value: int, tool_num: int = -1):
    """
    Sets the nozzle temperature target.

    Parameters
    ----------
    * value : float - The target nozzle temperature.
    * tool_num : int - The tool number (default is 0).
    """
    if value < 0:
        value = 0
    gcode = SEND_GCODE_TEMPLATE
    gcode["print"]["param"] = (
        f"M104 S{value}{'' if tool_num == -1 else ' T' + str(tool_num)}\n"
    )
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(gcode)
    )
    logger.debug(
        f"set_nozzle_temp_target - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] command: [{gcode}]"
    )
    self._tool_temp_target_time = round(time.time())

set_nozzleclumping_detector

Python
set_nozzleclumping_detector(enabled: bool, sensitivity: DetectorSensitivity = MEDIUM)

Enable or disable the nozzle clumping / blob detector (X-Cam AI vision).

When triggered, the printer halts the print to prevent damage from filament build-up on the nozzle. State is persisted to config.nozzleclumping_detector and config.nozzleclumping_detector_sensitivity.

Parameters
  • enabled : bool - True to enable the detector, False to disable it.
  • sensitivity : DetectorSensitivity = DetectorSensitivity.MEDIUM - Detection threshold.
Source code in src/bpm/bambuprinter.py
Python
def set_nozzleclumping_detector(
    self, enabled: bool, sensitivity: DetectorSensitivity = DetectorSensitivity.MEDIUM
):
    """
    Enable or disable the nozzle clumping / blob detector (X-Cam AI vision).

    When triggered, the printer halts the print to prevent damage from filament
    build-up on the nozzle.  State is persisted to `config.nozzleclumping_detector`
    and `config.nozzleclumping_detector_sensitivity`.

    Parameters
    ----------
    * enabled : bool - `True` to enable the detector, `False` to disable it.
    * sensitivity : DetectorSensitivity = `DetectorSensitivity.MEDIUM` - Detection threshold.
    """
    cmd = copy.deepcopy(XCAM_CONTROL_SET)
    cmd["xcam"]["module_name"] = "clump_detector"
    cmd["xcam"]["control"] = enabled
    cmd["xcam"]["enable"] = enabled
    cmd["xcam"]["print_halt"] = True
    cmd["xcam"]["halt_print_sensitivity"] = sensitivity.value

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    self.config.nozzleclumping_detector = enabled
    self.config.nozzleclumping_detector_sensitivity = sensitivity.value

    logger.debug(
        f"set_nozzleclumping_detector - published XCAM_CONTROL_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_part_cooling_fan_speed_target_percent

Python
set_part_cooling_fan_speed_target_percent(value: int)

sets the part cooling fan speed target represented in percent

Parameters
  • value : int - The target speed in percent
Source code in src/bpm/bambuprinter.py
Python
def set_part_cooling_fan_speed_target_percent(self, value: int):
    """
    sets the part cooling fan speed target represented in percent

    Parameters
    ----------
    * value : int - The target speed in percent
    """
    if value < 0:
        value = 0
    self._printer_state.climate.part_cooling_fan_speed_target_percent = value
    speed = round(value * 2.55, 0)
    gcode = SEND_GCODE_TEMPLATE
    gcode["print"]["param"] = f"M106 P1 S{speed}\n"
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(gcode)
    )
    logger.debug(
        f"set_part_cooling_fan_speed_target_percent - published SEND_GCODE_TEMPLATE to [device/{self.config.serial_number}/request] command: [{gcode}]"
    )
    self._fan_speed_target_time = round(time.time())

set_print_option

Python
set_print_option(option: PrintOption, enabled: bool)

Enable or disable one of the PrintOption options.

The corresponding BambuConfig attribute is also updated so the new state persists in the local configuration object.

Parameters
  • option : PrintOption - The print option to change (e.g. PrintOption.AUTO_RECOVERY).
  • enabled : bool - True to enable the option, False to disable it.
Source code in src/bpm/bambuprinter.py
Python
def set_print_option(self, option: PrintOption, enabled: bool):
    """
    Enable or disable one of the `PrintOption` options.

    The corresponding `BambuConfig` attribute is also updated so the new state
    persists in the local configuration object.

    Parameters
    ----------
    * option : PrintOption - The print option to change (e.g. `PrintOption.AUTO_RECOVERY`).
    * enabled : bool - `True` to enable the option, `False` to disable it.
    """
    cmd = PRINT_OPTION_COMMAND
    cmd["print"][option.name.lower()] = enabled

    if option == PrintOption.AUTO_RECOVERY:
        cmd["print"]["option"] = 1 if enabled else 0
        self.config.auto_recovery = enabled
    elif option == PrintOption.AUTO_SWITCH_FILAMENT:
        self.config.auto_switch_filament = enabled
    elif option == PrintOption.FILAMENT_TANGLE_DETECT:
        self.config.filament_tangle_detect = enabled
    elif option == PrintOption.SOUND_ENABLE:
        self.config.sound_enable = enabled
    elif option == PrintOption.NOZZLE_BLOB_DETECT:
        self.config.nozzle_blob_detect = enabled
    elif option == PrintOption.AIR_PRINT_DETECT:
        self.config.air_print_detect = enabled

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_print_option - published PRINT_OPTION_COMMAND to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_purgechutepileup_detector

Python
set_purgechutepileup_detector(enabled: bool, sensitivity: DetectorSensitivity = MEDIUM)

Enable or disable the purge chute pile-up detector (X-Cam AI vision).

When triggered, the printer halts the print to prevent purge waste from blocking the toolhead. State is persisted to config.purgechutepileup_detector and config.purgechutepileup_detector_sensitivity.

Parameters
  • enabled : bool - True to enable the detector, False to disable it.
  • sensitivity : DetectorSensitivity = DetectorSensitivity.MEDIUM - Detection threshold.
Source code in src/bpm/bambuprinter.py
Python
def set_purgechutepileup_detector(
    self, enabled: bool, sensitivity: DetectorSensitivity = DetectorSensitivity.MEDIUM
):
    """
    Enable or disable the purge chute pile-up detector (X-Cam AI vision).

    When triggered, the printer halts the print to prevent purge waste from
    blocking the toolhead.  State is persisted to `config.purgechutepileup_detector`
    and `config.purgechutepileup_detector_sensitivity`.

    Parameters
    ----------
    * enabled : bool - `True` to enable the detector, `False` to disable it.
    * sensitivity : DetectorSensitivity = `DetectorSensitivity.MEDIUM` - Detection threshold.
    """
    cmd = copy.deepcopy(XCAM_CONTROL_SET)
    cmd["xcam"]["module_name"] = "pileup_detector"
    cmd["xcam"]["control"] = enabled
    cmd["xcam"]["enable"] = enabled
    cmd["xcam"]["print_halt"] = True
    cmd["xcam"]["halt_print_sensitivity"] = sensitivity.value

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    self.config.purgechutepileup_detector = enabled
    self.config.purgechutepileup_detector_sensitivity = sensitivity.value

    logger.debug(
        f"set_purgechutepileup_detector - published XCAM_CONTROL_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_spaghetti_detector

Python
set_spaghetti_detector(enabled: bool, sensitivity: DetectorSensitivity = MEDIUM)

Enable or disable the spaghetti / failed print detector (X-Cam AI vision).

When triggered, the printer halts the print. Sensitivity controls how aggressively the camera flags anomalies. State is persisted to config.spaghetti_detector and config.spaghetti_detector_sensitivity.

Parameters
  • enabled : bool - True to enable the detector, False to disable it.
  • sensitivity : DetectorSensitivity = DetectorSensitivity.MEDIUM - Detection threshold.
Source code in src/bpm/bambuprinter.py
Python
def set_spaghetti_detector(
    self, enabled: bool, sensitivity: DetectorSensitivity = DetectorSensitivity.MEDIUM
):
    """
    Enable or disable the spaghetti / failed print detector (X-Cam AI vision).

    When triggered, the printer halts the print.  Sensitivity controls how
    aggressively the camera flags anomalies.  State is persisted to
    `config.spaghetti_detector` and `config.spaghetti_detector_sensitivity`.

    Parameters
    ----------
    * enabled : bool - `True` to enable the detector, `False` to disable it.
    * sensitivity : DetectorSensitivity = `DetectorSensitivity.MEDIUM` - Detection threshold.
    """
    cmd = copy.deepcopy(XCAM_CONTROL_SET)
    cmd["xcam"]["module_name"] = "spaghetti_detector"
    cmd["xcam"]["control"] = enabled
    cmd["xcam"]["enable"] = enabled
    cmd["xcam"]["print_halt"] = True
    cmd["xcam"]["halt_print_sensitivity"] = sensitivity.value

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    self.config.spaghetti_detector = enabled
    self.config.spaghetti_detector_sensitivity = sensitivity.value

    logger.debug(
        f"set_spaghetti_detector - published XCAM_CONTROL_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_spool_details

Python
set_spool_details(
    tray_id: int,
    tray_info_idx: str,
    tray_id_name: str | None = "",
    tray_type: str | None = "",
    tray_color: str | None = "",
    nozzle_temp_min: int | None = -1,
    nozzle_temp_max: int | None = -1,
    ams_id: int | None = 0,
)

Sets spool / tray details such as filament type, color, and nozzle temperature range.

ams_id and slot_id are derived automatically from tray_id. For the external tray (254), pass "no_filament" as tray_info_idx to clear the tray entirely. Colors may be supplied as CSS color names (e.g. "red") or as 6- or 8-character hex strings (e.g. "FF0000" / "FF0000FF").

Parameters
  • tray_id : int - Absolute tray ID. For standard 4-slot AMS: ams_id * 4 + slot_id. Use 254 for the external spool holder.
  • tray_info_idx : str - Filament info index string as defined by Bambu Lab (e.g. "GFA00"). Pass "no_filament" to clear the tray.
  • tray_id_name : Optional[str] - Friendly filament name (e.g. "Bambu PLA Basic").
  • tray_type : Optional[str] - Short filament type string (e.g. "PLA", "PETG", "ABS").
  • tray_color : Optional[str] - Filament color as a CSS name or RRGGBB/RRGGBBAA hex string.
  • nozzle_temp_min : Optional[int] = -1 - Minimum nozzle temperature in °C (pass -1 to leave unchanged).
  • nozzle_temp_max : Optional[int] = -1 - Maximum nozzle temperature in °C (pass -1 to leave unchanged).
  • ams_id : Optional[int] = 0 - Unused; derived automatically from tray_id.
Source code in src/bpm/bambuprinter.py
Python
def set_spool_details(
    self,
    tray_id: int,
    tray_info_idx: str,
    tray_id_name: str | None = "",
    tray_type: str | None = "",
    tray_color: str | None = "",
    nozzle_temp_min: int | None = -1,
    nozzle_temp_max: int | None = -1,
    ams_id: int | None = 0,
):
    """
    Sets spool / tray details such as filament type, color, and nozzle temperature range.

    `ams_id` and `slot_id` are derived automatically from `tray_id`.
    For the external tray (254), pass `"no_filament"` as `tray_info_idx` to
    clear the tray entirely.  Colors may be supplied as CSS color names (e.g.
    `"red"`) or as 6- or 8-character hex strings (e.g. `"FF0000"` / `"FF0000FF"`).

    Parameters
    ----------
    * tray_id : int - Absolute tray ID.  For standard 4-slot AMS: `ams_id * 4 + slot_id`.
                Use `254` for the external spool holder.
    * tray_info_idx : str - Filament info index string as defined by Bambu Lab (e.g. `"GFA00"`).
                Pass `"no_filament"` to clear the tray.
    * tray_id_name : Optional[str] - Friendly filament name (e.g. `"Bambu PLA Basic"`).
    * tray_type : Optional[str] - Short filament type string (e.g. `"PLA"`, `"PETG"`, `"ABS"`).
    * tray_color : Optional[str] - Filament color as a CSS name or RRGGBB/RRGGBBAA hex string.
    * nozzle_temp_min : Optional[int] = -1 - Minimum nozzle temperature in °C (pass -1 to leave unchanged).
    * nozzle_temp_max : Optional[int] = -1 - Maximum nozzle temperature in °C (pass -1 to leave unchanged).
    * ams_id : Optional[int] = 0 - Unused; derived automatically from `tray_id`.
    """
    cmd = copy.deepcopy(AMS_FILAMENT_SETTING)

    ams_id = math.floor(tray_id / 4)
    slot_id = tray_id % 4
    if tray_id == 254 or tray_id == 255:
        ams_id = tray_id
        slot_id = 0

    cmd["print"]["ams_id"] = ams_id
    cmd["print"]["tray_id"] = tray_id
    cmd["print"]["slot_id"] = slot_id

    if tray_info_idx == "no_filament":
        tray_info_idx = ""
        tray_id_name = ""
        tray_type = ""
        tray_color = "FFFFFF00"
        nozzle_temp_min = 0
        nozzle_temp_max = 0

    cmd["print"]["tray_info_idx"] = tray_info_idx
    cmd["print"]["tray_id_name"] = tray_id_name
    cmd["print"]["tray_type"] = tray_type

    if tray_color and tray_color != "":
        color = ""
        try:
            color = f"{name_to_hex(tray_color)}FF".replace("#", "").upper()
        except Exception:
            color = tray_color
        cmd["print"]["tray_color"] = color

    cmd["print"]["nozzle_temp_min"] = nozzle_temp_min
    cmd["print"]["nozzle_temp_max"] = nozzle_temp_max

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_spool_details - published AMS_FILAMENT_SETTING to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

set_spool_k_factor

Python
set_spool_k_factor(
    tray_id: int,
    k_value: float,
    n_coef: float | None = 1.399999976158142,
    nozzle_temp: int | None = -1,
    bed_temp: int | None = -1,
    max_volumetric_speed: int | None = -1,
)

Set the linear advance (pressure advance) k factor for a specific spool tray.

Deprecated

Broken in recent Bambu firmware. Use select_extrusion_calibration_profile instead.

Parameters
  • tray_id : int - Absolute tray ID (ams_id * 4 + slot_id, or 254 for external).
  • k_value : float - The linear advance k factor to apply.
  • n_coef : Optional[float] = 1.4 - Pressure advance n coefficient.
  • nozzle_temp : Optional[int] = -1 - Nozzle temperature in °C (pass -1 to omit).
  • bed_temp : Optional[int] = -1 - Bed temperature in °C (pass -1 to omit).
  • max_volumetric_speed : Optional[int] = -1 - Max volumetric speed (pass -1 to omit).
Source code in src/bpm/bambuprinter.py
Python
def set_spool_k_factor(
    self,
    tray_id: int,
    k_value: float,
    n_coef: float | None = 1.399999976158142,
    nozzle_temp: int | None = -1,
    bed_temp: int | None = -1,
    max_volumetric_speed: int | None = -1,
):
    """
    Set the linear advance (pressure advance) k factor for a specific spool tray.

    !!! warning "Deprecated"
        Broken in recent Bambu firmware.  Use
        [`select_extrusion_calibration_profile`][bpm.bambuprinter.BambuPrinter.select_extrusion_calibration_profile]
        instead.

    Parameters
    ----------
    * tray_id : int - Absolute tray ID (`ams_id * 4 + slot_id`, or `254` for external).
    * k_value : float - The linear advance k factor to apply.
    * n_coef : Optional[float] = 1.4 - Pressure advance n coefficient.
    * nozzle_temp : Optional[int] = -1 - Nozzle temperature in °C (pass -1 to omit).
    * bed_temp : Optional[int] = -1 - Bed temperature in °C (pass -1 to omit).
    * max_volumetric_speed : Optional[int] = -1 - Max volumetric speed (pass -1 to omit).
    """
    cmd = copy.deepcopy(EXTRUSION_CALI_SET)

    cmd["print"]["tray_id"] = tray_id
    cmd["print"]["slot_id"] = tray_id % 4

    cmd["print"]["k_value"] = k_value
    cmd["print"]["n_coef"] = n_coef

    if nozzle_temp != -1:
        cmd["print"]["nozzle_temp"] = nozzle_temp
    if bed_temp != -1:
        cmd["print"]["bed_temp"] = bed_temp
    if max_volumetric_speed != -1:
        cmd["print"]["max_volumetric_speed"] = max_volumetric_speed

    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"set_spool_k_factor - published EXTRUSION_CALI_SET to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

skip_objects

Python
skip_objects(objects)

Instructs the printer to skip (cancel) a list of objects during the current print job.

The printhead physically avoids skipped objects for the remainder of the print. This is equivalent to the "Cancel Object" feature in BambuStudio.

Object ID mapping

Object IDs sent here are the identify_id values from Metadata/slice_info.config inside the .3mf. get_project_info extracts them and writes each one into the corresponding bbox_objects[N]["id"] entry in ProjectInfo.metadata["map"].

To get the IDs for the current plate:

Python
info = get_project_info(file_id, printer, plate_num=1)
ids = [obj["id"] for obj in info.metadata["map"]["bbox_objects"] if "id" in obj]
printer.skip_objects(ids)   # cancel all objects on the plate

The bbox_objects list also carries name and bounding-box coordinates so a UI can let the user pick individual objects before calling this method.

Parameters
  • objects : list[int | str] - One or more identify_id values to cancel. Values are coerced to int before being sent to the printer.
Source code in src/bpm/bambuprinter.py
Python
def skip_objects(self, objects):
    """
    Instructs the printer to skip (cancel) a list of objects during the current print job.

    The printhead physically avoids skipped objects for the remainder of the print.
    This is equivalent to the "Cancel Object" feature in BambuStudio.

    **Object ID mapping**

    Object IDs sent here are the `identify_id` values from `Metadata/slice_info.config`
    inside the `.3mf`.  `get_project_info` extracts them and writes each one into the
    corresponding `bbox_objects[N]["id"]` entry in `ProjectInfo.metadata["map"]`.

    To get the IDs for the current plate:

    ```python
    info = get_project_info(file_id, printer, plate_num=1)
    ids = [obj["id"] for obj in info.metadata["map"]["bbox_objects"] if "id" in obj]
    printer.skip_objects(ids)   # cancel all objects on the plate
    ```

    The `bbox_objects` list also carries `name` and bounding-box coordinates so a UI
    can let the user pick individual objects before calling this method.

    Parameters
    ----------
    * objects : list[int | str] - One or more `identify_id` values to cancel.
        Values are coerced to `int` before being sent to the printer.
    """
    objs = []
    for obj in objects:
        objs.append(int(obj))

    cmd = copy.deepcopy(SKIP_OBJECTS)
    cmd["print"]["obj_list"] = objs
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )
    logger.debug(
        f"skip_objects - published SKIP_OBJECTS to [device/{self.config.serial_number}/request] bambu_msg: [{cmd}]"
    )

start_session

Python
start_session()

Initiates a connection to the Bambu Lab printer and provides a stateful session, with built-in recovery in the event BambuPrinter becomes disconnected from the machine.

This method is required to be called before any commands or data collection / callbacks can take place with the machine.

Source code in src/bpm/bambuprinter.py
Python
def start_session(self):
    """
    Initiates a connection to the Bambu Lab printer and provides a stateful
    session, with built-in recovery in the event `BambuPrinter`
    becomes disconnected from the machine.

    This method is required to be called before any commands or data
    collection / callbacks can take place with the machine.
    """
    logger.debug("start_session - starting session")
    if (
        self.config.hostname is None
        or self.config.access_code is None
        or self.config.serial_number is None
    ):
        raise Exception("hostname, access_code, and serial_number are required")
    if self.client and self.client.is_connected():
        raise Exception("a session is already active")

    def on_connect(client, userdata, flags, reason_code, properties):
        logger.debug("on_connect - session on_connect")
        if self.service_state != ServiceState.PAUSED:
            self.service_state = ServiceState.CONNECTED
            client.subscribe(f"device/{self.config.serial_number}/report")
            logger.debug(
                f"on_connect -subscribed to [device/{self.config.serial_number}/report]"
            )

    def on_disconnect(client, userdata, flags, reason_code, properties):
        logger.debug("on_disconnect - session on_disconnect")
        if self._internalException:
            logger.exception("on_disconnect - an internal exception occurred")
            self.service_state = ServiceState.QUIT
            raise self._internalException
        if self.service_state != ServiceState.PAUSED:
            self.service_state = ServiceState.DISCONNECTED

    def on_message(client, userdata, msg):
        if self._lastMessageTime and self._recent_update:
            self._lastMessageTime = time.monotonic()
        self._on_message(msg.payload.decode("utf-8"))

    def loop_forever(printer: BambuPrinter):
        logger.debug("loop_forever - starting mqtt loop_forever")
        try:
            printer.client.loop_forever(retry_first_connection=True)
        except Exception as e:
            logger.exception("loop_forever - an internal exception occurred")
            printer._internalException = e
            if printer.client and printer.client.is_connected():
                printer.client.disconnect()
        printer.service_state = ServiceState.QUIT

    self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)  # type: ignore

    self.client.on_connect = on_connect
    self.client.on_disconnect = on_disconnect
    self.client.on_message = on_message

    self.client.tls_set(tls_version=ssl.PROTOCOL_TLS, cert_reqs=ssl.CERT_NONE)
    self.client.tls_insecure_set(True)
    self.client.reconnect_delay_set(min_delay=1, max_delay=1)

    self.client.username_pw_set(
        self.config.mqtt_username, password=self.config.access_code
    )
    self.client.user_data_set(self.config.mqtt_client_id)

    try:
        self.client.connect(self.config.hostname, self.config.mqtt_port, 60)
    except Exception as e:
        self._internalException = e
        logger.warning(
            f"start_session - unable to connect to printer - reason: [{e}] stacktrace: [{traceback.format_exc()}]"
        )
        self.service_state = ServiceState.QUIT
        return

    self._mqtt_client_thread = threading.Thread(
        target=loop_forever, name="bambuprinter-session", args=(self,)
    )
    self._mqtt_client_thread.start()

    self._start_watchdog()

stop_printing

Python
stop_printing()

Requests the printer to stop printing if a job is currently running.

Source code in src/bpm/bambuprinter.py
Python
def stop_printing(self):
    """
    Requests the printer to stop printing if a job is currently running.
    """
    self.client.publish(
        f"device/{self.config.serial_number}/request",
        json.dumps(STOP_PRINT),
    )
    logger.debug(
        f"stop_printing - published STOP_PRINT to [device/{self.config.serial_number}/request]"
    )

toJson

Python
toJson()

Returns a dict (json document) representing this object's private class level attributes that are serializable (most are).

Source code in src/bpm/bambuprinter.py
Python
def toJson(self):
    """
    Returns a `dict` (json document) representing this object's private class
    level attributes that are serializable (most are).
    """
    response = json.dumps(self, default=jsonSerializer, indent=4, sort_keys=True)
    # logger.debug(f"toJson - json: [{response}]")

    return json.loads(response)

turn_off_ams_dryer

Python
turn_off_ams_dryer(ams_id: int = 0)

Sends a command to the printer to turn off the AMS dryer.

Also resets ams_unit.temp_target to 0 in the local printer state. Raises an exception if ams_id does not match a connected AMS unit.

Parameters
  • ams_id : int = 0 - The AMS unit whose dryer should be turned off (default is 0).
Source code in src/bpm/bambuprinter.py
Python
def turn_off_ams_dryer(self, ams_id: int = 0):
    """
    Sends a command to the printer to turn off the AMS dryer.

    Also resets `ams_unit.temp_target` to `0` in the local printer state.
    Raises an exception if `ams_id` does not match a connected AMS unit.

    Parameters
    ----------
    * ams_id : int = 0 - The AMS unit whose dryer should be turned off (default is 0).
    """
    cmd = copy.deepcopy(AMS_FILAMENT_DRYING)
    cmd["print"]["ams_id"] = ams_id
    cmd["print"]["mode"] = 0  # Turn off drying mode
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    ams = next((u for u in self._printer_state.ams_units if u.ams_id == ams_id), None)
    if not ams:
        raise Exception("invalid ams_id provided")

    ams.temp_target = 0

    logger.debug(
        f"turn_off_ams_dryer - published AMS_FILAMENT_DRYING to [device/{self.config.serial_number}/request] command: [{cmd}]"
    )

turn_on_ams_dryer

Python
turn_on_ams_dryer(
    target_temp: int,
    duration: int,
    target_humidity: int = 0,
    cooling_temp: int = 45,
    rotate_tray: bool = False,
    ams_id: int = 0,
    filament_type: str = "",
)

Sends a command to the printer to turn on the AMS dryer with specified parameters.

Parameters
  • target_temp : int - The target drying temperature.
  • duration : int - The drying duration in hours.
  • target_humidity : int - The target humidity level.
  • cooling_temp : int - The cooling temperature after drying (default is 45).
  • rotate_tray : bool - Whether to rotate the tray during drying (default is False).
  • ams_id : int - The AMS ID to control (default is 0).
  • filament_type : str - The filament type string (e.g. 'ABS'). Passed to firmware for validation.
Source code in src/bpm/bambuprinter.py
Python
def turn_on_ams_dryer(
    self,
    target_temp: int,
    duration: int,
    target_humidity: int = 0,
    cooling_temp: int = 45,
    rotate_tray: bool = False,
    ams_id: int = 0,
    filament_type: str = "",
):
    """
    Sends a command to the printer to turn on the AMS dryer with specified parameters.

    Parameters
    ----------
    * target_temp : int - The target drying temperature.
    * duration : int - The drying duration in hours.
    * target_humidity : int - The target humidity level.
    * cooling_temp : int - The cooling temperature after drying (default is 45).
    * rotate_tray : bool - Whether to rotate the tray during drying (default is False).
    * ams_id : int - The AMS ID to control (default is 0).
    * filament_type : str - The filament type string (e.g. 'ABS'). Passed to firmware for validation.
    """
    cmd = copy.deepcopy(AMS_FILAMENT_DRYING)
    cmd["print"]["ams_id"] = ams_id
    cmd["print"]["mode"] = 1  # Turn on drying mode
    cmd["print"]["filament"] = filament_type
    cmd["print"]["temp"] = target_temp
    cmd["print"]["duration"] = duration
    cmd["print"]["humidity"] = target_humidity
    cmd["print"]["cooling_temp"] = cooling_temp
    cmd["print"]["rotate_tray"] = rotate_tray
    self.client.publish(
        f"device/{self.config.serial_number}/request", json.dumps(cmd)
    )

    ams = next((u for u in self._printer_state.ams_units if u.ams_id == ams_id), None)
    if not ams:
        raise Exception("invalid ams_id provided")

    ams.temp_target = target_temp

    logger.debug(
        f"turn_on_ams_dryer - published AMS_FILAMENT_DRYING to [device/{self.config.serial_number}/request] command: [{cmd}]"
    )

unload_filament

Python
unload_filament(ams_id: int = 0)

Requests the printer to unload whatever filament / spool is currently loaded.

Parameters
  • ams_id : int = 0 - The AMS unit to unload from (default is 0).
Source code in src/bpm/bambuprinter.py
Python
def unload_filament(self, ams_id: int = 0):
    """
    Requests the printer to unload whatever filament / spool is currently loaded.

    Parameters
    ----------
    * ams_id : int = 0 - The AMS unit to unload from (default is 0).
    """
    msg = copy.deepcopy(AMS_CHANGE_FILAMENT)
    msg["print"]["ams_id"] = ams_id

    self.client.publish(
        f"device/{self.config.serial_number}/request",
        json.dumps(msg),
    )
    logger.debug(
        f"unload_filament - published AMS_CHANGE_FILAMENT to [device/{self.config.serial_number}/request] - bambu_msg: [{msg}]"
    )

upload_sdcard_file

Python
upload_sdcard_file(src: str, dest: str)

Uploads the local filesystem file to the printer and returns an updated dict of all files on the printer

Parameters
  • src : str - the full path filename on the host to be uploaded to the printer
  • dest : str - the full path filename on the printer to upload to
Source code in src/bpm/bambuprinter.py
Python
def upload_sdcard_file(self, src: str, dest: str):
    """
    Uploads the local filesystem file to the printer and returns an updated dict of all files on the printer

    Parameters
    ----------
    * src : str - the full path filename on the host to be uploaded to the printer
    * dest : str - the full path filename on the printer to upload to
    """
    logger.debug(f"upload_sdcard_file - uploading file src: [{src}] dest: [{dest}]")
    with self.ftp_connection() as ftps:
        ftps.upload_file(src, dest)

    if src.endswith(".3mf"):
        get_project_info(dest, self, local_file=src)

    remote_files = self.get_sdcard_contents()
    return remote_files