Bambu Lab X1 X1C MQTT

So it won’t add anything until it connects but what you’re seeing is a different problem.

The “connecting” you see is from HomeAssistant’s MQTT broker which you also need to setup. The “printer’s” mqtt is higher up. When you setup the MQTT broker for homeassistant, you will also have an MQTT integration in addition to the addon to setup, which is where entities will appear.

You also need to deploy it to work.

image

Also my nodered stuff is not exactly compatible having it side by side with the HACS integration. It will work if you keep with the basic flow but the dashboard YAMLs or advanced flows might have conflicts.

If you want to continue using the HACs integration since you had it setup, then I’d suggest making a GH issue there for recommendations. Swapping over to my nodered stuff won’t fix the connection limit.

Hey just wanted to share, I learned some cool use cases from this thread and made a python library for interacting too. I know it’s not home assistant, but hopefully someone looking for a python option like me is able to find it. It’s on pypi here: bambu-connect · PyPI

1 Like

Speaking of Python, I’ve looked over the documentation and in the advanced method, I’m a bit unclear how to add python to the NR add-on. I clicked the link, but it didn’t clearly explain how to get it installed. Any pointers there?

If you’re using the NodeRed Addon in HA, you need to add it as per the linked instructions to the nodered configuration file - there may also be a UI config for it in homeassistant but it’s been a while since I checked an addon-version. Just need to add python3 to system_packages in the config, it gives examples if you scroll up on the linked page.

If you run NodeRed as a docker, it already has python as a system package in most cases.

also @mcar not sure if you’re aware but the python package used internally in the HACs repo has been extracted again and is receiving updates soon. Pybambu

Hi, I’m using your basic and advanced nodered flows side by side with the HACS integration. So far, I haven’t noticed any problem. What could be the friction points?

Thanks for your amazing work.

Mostly it comes down to the dashboard yamls as even though I have separate versions for each, if both the HACs integration and my flows are used, some sensor names are “duplicates” and HA appends like a _2 etc to some, which aren’t accounted for in my YAML, and it depends which came first etc.

The advanced flow also does some lookups to HA sensors by entity name and there is a non-zero chance it can get the wrong one if this happened. If it works fully as expected then I’d say it’s lucky lol.

I mostly say they are incompatible because most who tried end up breaking something.

1 Like

I see. Thanks for the answer. In my dashboard I have a mix of entities from both integrations, but luckily it seems that I have broken nothing yet :slight_smile:

This would be awesome. Please do keep us updated on the progress :grinning:

It seems that since the last update BambuStudio and HomeAssistant again can connect again at the same time to the printer, maybe I’m lucky for now, but thought I would share it :wink: Will also try the Xtouch

Yes recent P1 series updates have increased the MQTT connection limit, though there is a chance they introduced a timeout feature (like the cloud now has). It will need testing for sure.

I don’t understand that they don’t mention it in their release notes… But happy for now :smiley:

I also noticed that not all errors are reported as errors in HA, and hence doesn’t trigger a notification with the usual automation setup. In this case it was an unloading issue in the AMS during a filament/color change. I did however notice that my xtouch screen displayed the issue, so the data should be available to the HA integration too. Unfortunately i dismissed the issue on the xtouch screen before taking note on the exact error :melting_face:

I implemented your automation solution for the remaining time, hopefully it will be more foolproof for all kind of issues:

- id: "1705074334"
  alias: P1S Print Time Remaining Monitor
  description: if print time doesn't change, something is wrong
  trigger:
    - platform: template
      value_template: >-
        {{ as_timestamp(now()) | round -
        as_timestamp(states.sensor.p1s_01p00a3b15xxxxxx_remaining_time.last_changed)
        | round > 180 }}
      alias: Print Time Remaining Unchanged
  condition:
    - condition: or
      conditions:
        - condition: state
          entity_id: sensor.p1s_01p00a3b15xxxxxx_print_status
          state:
            - "Running"
            - "running"
            - "RUNNING"
        - condition: state
          entity_id: sensor.p1s_01p00a3b15xxxxxx_current_stage
          state:
            - "Printing"
            - "printing"
            - "PRINTING"

If I remember correctly most other solutions have a list of pre-defined errors to match for display so some might be missing from them. If it isn’t in the pre-defined errors it may not show.

I believe the HA-Bambulab has at the moment a few defined, and xtouch has most from the API but not all as it updates constantly.

It’s why with my nodered solution I parse out the severity level and have counts for each type (Common, Info, Serious, Fatal), then use a dictionary pulled from bambu’s API to get the error text rather than hard-coding it.

Getting the severity level count helps because many things come up as HMS notifications that aren’t errors but are Info or Common level (inspecting first layer is a common-level notification for example).

Hi guys, I’m working on a project involving 2 Bambu Lab A1 Mini’s and a Fanuc robot arm which will be pulling parts off the bed. I’ve setup an MQTT client on a raspberry pi which is connected to my PLC/Robot. I’m able to receive all the message from my printer, and I am able to send JSON payloads such as turning the LED on and OFF. I was wondering if anyone has been able to start a print from the SD-Card using MQTT JSON payloads? I can tell when the printer is running but I need a way to start a print. Any help would be appreciated, Thank you!

Yep, there’s a project_file command that will let you start a print remotely using a path on the SD card, and configure all the additional settings (including AMS mapping, which itself is quite complex).

{
    "print": {
        "ams_mapping": [ <array of id's, explained below >    ],
        "bed_leveling": true/false,
        "bed_type": "hot_plate"/"eng_plate"/"textured_plate"/"cool_plate",
        "command": "project_file",
        "file": "" (not needed),
        "flow_cali": true/false,
        "layer_inspect": true/false,
        "param": "Metadata/plate_1.gcode", (replace "1" with whichever plate id you want to print from 3mf. They are always in the format of "Metadata/plate_#.gcode". Open 3mf in 7zip to view)
        "profile_id": "" (not needed, big number as str),
        "project_id": "" (not needed, big number as str),
        "sequence_id": "2222" (just any number as a str),
        "subtask_id": "" (not needed, big number as str),
        "subtask_name": "Some Name" (Name of print, for convenience, make same as 3mf filename without path, optional leave extensions in, as this is what it usually is unless manually overridden),
        "task_id": "" (not needed, big number as str),
        "timelapse": true/false,
        "url": "file:///sdcard/YOURFILE.3mf" (more explained below),
        "use_ams": true/false,
        "vibration_cali": true/false
    }
}

‘url’ logic

It can be like “file:///sdcard/filename.gcode.3mf” too, browse the printer’s sd card via FTPS to see exactly.

Could also be “ftp:///file.3mf” or “ftp:///file.gcode.3mf”, these will self-resolve internally too if at root, but may need /sdcard or /cache infront depending on true location.

You’ll need to play around. AFAIK A1 series is only one that specifies an sdcard in the path. X1 and P1 usually treat it as root, and a /cache folder.

Fun fact, this will also take 3mf’s from HTTP addresses too, including locally hosted ones if you’re into that.

AMS Mapping logic

AMS mapping logic is tricky to get right. If you do not use an ams, you can leave it out of the request and set use_ams to false. If you have an ams, you will likely want to set the AMS mapping. Note, that if you have use_ams to false, an ams_mapping would essentially end up being [0], but if true, it’s different still.

So backstory is, when you slice the 3mf file, it embeds all filament profiles you had “loaded” in the project in the slicer, even if it’s not used for the plate you are printing or any plates. As long as it’s added with the + button to the project, it’s embedded.

The values in the array are the tray id’s from AMS units. So if you have 1 AMS unit, it’s id’s 0,1,2,3 for tray 1,2,3,4. Since you have A1 series, atm it only can have 1 AMS, so you don’t need to worry for more. But if they expand it or you tinker with P1/X1 series, then it’ll continue increasing (so AMS 2 tray 1 is id 4, tray 2 is id 5 etc).

When making the AMS mapping, it uses the same order as filaments you had ordered in the slicer at time of slicing, and assigns a tray id to that slot.

So if you had 3 filaments loaded into the slicer when you made your 3mf, but only used 2 for a plate you’re printing, and let’s say the first one mapped to slot 3, second mapped to slot 4, and third to slot 1, then your AMS mapping would be: [2, -1, 0].

The -1 means that the second filament profile you had in the slicer is unused, but ones after it were, so it tells the printer to skip over it. If you used 4 profiles in the slicer, and used all 4, it would have 4 elements, but the order depends on which AMS tray you mapped to which profile in the slicer, which is not usually in sync.

So yeah, you need pretty good knowledge of what the filament mappings were in a 3mf to assign them properly, and even more knowledge of what’s actually in your AMS unit. The length of the array for a specific 3mf will always be for however many filaments were embedded when slicing, and will share the same order. Though keep in mind the tray id’s incrementing and the position in the array are not at all linked, if they ever are it’s purely by coincidence.

If you want to build some fancy logic on this, you can figure out the embedded profiles’ ordering and information (type, colour, amt used, profile name etc etc) by extracting and unzipping the 3mf file, and parsing the gcode file (and some other files in there too can help).

1 Like

i don’t frequent this thread much anymore, but the last update i did back in December '23 broke my error notifications; data was coming in differently (split into new entities with attributes). i updated the dashboard and my notifications and now use these. hopefully it’ll help others.

for dashboard, i added to the " Printer Control YAML Configurator" yaml to display errors, if any. I have my ignore list of attributes and errors. I essentially use the same template for the notification.

  - type: markdown
    content: |2-
          {%- set ignore_list = ['hms_0c00_0300_0002_0004', 'hms_0c00_0300_0003_000b',
          'hms_0c00_0100_0001_0004'] %} {% set ignore_substrings = ['friendly_name',
          'unit_of_measurement', 'custom_ui_state_card', 'icon', 'supported_features',
          'entity_picture', 'severity', 'module'] %}

          {%- set hms = namespace(errors=[], matched=false) %}

          {%- set sensors_to_check = [
              states.sensor.x1c_x1_carbon_hms_common,
              states.sensor.x1c_x1_carbon_hms_fatal,
              states.sensor.x1c_x1_carbon_hms_info,
              states.sensor.x1c_x1_carbon_hms_serious,
              states.sensor.x1c_x1_carbon_hms_unknown
          ] %}
          {%- for sensor in sensors_to_check if sensor.state != '0' %}
              {%- for attr in sensor.attributes %}
                  {%- set lowercase_attr = attr|lower %}
                  {%- set hms.matched = false %}
                  {%- for substring in ignore_substrings %}
                      {%- set lowercase_substring = substring|lower %}
                      {%- if lowercase_substring in lowercase_attr %}
                          {%- set hms.matched = true %}
                          {%- break %}
                      {%- endif %}
                  {%- endfor %}
                  {%- for ignore_item in ignore_list %}
                      {%- set lowercase_ignore_item = ignore_item|lower %}
                      {%- if lowercase_ignore_item in lowercase_attr %}  
                          {%- set hms.matched = true %}
                          {% break %}
                      {%- endif %}
                  {%- endfor %}
                  {%- if not hms.matched %}
                      {%- set hms.errors = hms.errors + [attr ~ ": " ~ sensor.attributes[attr]] %}
                  {%- endif %}
              {%- endfor %}
          {%- endfor %} {{ hms.errors | join("\n") }}

for error notification:

alias: X1 Printer Error Notification
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.x1c_x1_carbon_hms
condition:
  - condition: numeric_state
    entity_id: sensor.x1c_x1_carbon_hms
    above: 0
  - condition: template
    value_template: >
      {% set ignore_list = ['hms_0c00_0300_0002_0004',
      'hms_0c00_0300_0003_000b', 'hms_0c00_0100_0001_0004'] %}

      {% set ignore_substrings = ['friendly_name', 'unit_of_measurement',
      'custom_ui_state_card', 'icon', 'supported_features', 'entity_picture',
      'severity', 'module'] %}


      {% set hms = namespace(errors=[], matched=false) %}


      {% set sensors_to_check = [
          states.sensor.x1c_x1_carbon_hms_common,
          states.sensor.x1c_x1_carbon_hms_fatal,
          states.sensor.x1c_x1_carbon_hms_info,
          states.sensor.x1c_x1_carbon_hms_serious,
          states.sensor.x1c_x1_carbon_hms_unknown
      ] %}


      {% for sensor in sensors_to_check if sensor.state != '0' %}
          {% for attr in sensor.attributes %}
              {% set lowercase_attr = attr|lower %}
              {% set hms.matched = false %}
              {% for substring in ignore_substrings %}
                  {% set lowercase_substring = substring|lower %}
                  {% if lowercase_substring in lowercase_attr %}
                      {% set hms.matched = true %}
                      {% break %}
                  {% endif %}
              {% endfor %}
              {% for ignore_item in ignore_list %}
                  {% set lowercase_ignore_item = ignore_item|lower %}
                  {% if lowercase_ignore_item in lowercase_attr %}  
                      {% set hms.matched = true %}
                      {% break %}
                  {% endif %}
              {% endfor %}
              {% if not hms.matched %}
                  {% set hms.errors = hms.errors + [attr ~ ": " ~ sensor.attributes[attr]] %}
              {% endif %}
          {% endfor %}
      {% endfor %}

      {{ hms.errors|count > 0 }}
action:
  - service: notify.mobile_app_spiderfold
    data:
      message: >-
        PRINTER ERROR - CHECK NOW

        Print Status - {{states('sensor.x1c_x1_carbon_print_status')}}

        Print Stage - {{states('sensor.x1c_x1_carbon_stage')}}

        Print Progress - {{states('sensor.x1c_x1_carbon_print_progress')}}

        Print Remaining Time -
        {{states('sensor.x1c_x1_carbon_print_remaining_time')}} mins

        HMS Errors - {{states('sensor.x1c_x1_carbon_hms')}}

        Nozzle Target Temp -
        {{state_attr('sensor.x1c_x1_carbon_nozzle_temperature','target_temperature')}}
        C

        Nozzle Temp - {{states('sensor.x1c_x1_carbon_nozzle_temperature')}} C

        Bed Target Temp -
        {{state_attr('sensor.x1c_x1_carbon_bed_temperature','target_temperature')}}
        C

        Bed Temp - {{states('sensor.x1c_x1_carbon_bed_temperature')}} C
  - service: notify.pushover
    data:
      data:
        title: 3D Printer Status
        priority: 2
        expire: 3600
        retry: 60
        sound: gamelan
      message: >-
        PRINTER ERROR - CHECK NOW

        Print Status - {{ states('sensor.x1c_x1_carbon_print_status') }}

        Print Stage - {{ states('sensor.x1c_x1_carbon_stage') }}

        Print Progress - {{ states('sensor.x1c_x1_carbon_print_progress') }}

        Print Remaining Time - {{
        states('sensor.x1c_x1_carbon_print_remaining_time') }} mins

        HMS Errors - {{ states('sensor.x1c_x1_carbon_hms') }}

        {%- set ignore_list = ['hms_0c00_0300_0002_0004',
        'hms_0c00_0300_0003_000b', 'hms_0c00_0100_0001_0004'] %} {% set
        ignore_substrings = ['friendly_name', 'unit_of_measurement',
        'custom_ui_state_card', 'icon', 'supported_features', 'entity_picture',
        'severity', 'module'] %}

        {%- set hms = namespace(errors=[], matched=false) %}

        {%- set sensors_to_check = [
            states.sensor.x1c_x1_carbon_hms_common,
            states.sensor.x1c_x1_carbon_hms_fatal,
            states.sensor.x1c_x1_carbon_hms_info,
            states.sensor.x1c_x1_carbon_hms_serious,
            states.sensor.x1c_x1_carbon_hms_unknown
        ] %} {%- for sensor in sensors_to_check if sensor.state != '0' %}
            {%- for attr in sensor.attributes %}
                {%- set lowercase_attr = attr|lower %}
                {%- set hms.matched = false %}
                {%- for substring in ignore_substrings %}
                    {%- set lowercase_substring = substring|lower %}
                    {%- if lowercase_substring in lowercase_attr %}
                        {%- set hms.matched = true %}
                        {%- break %}
                    {%- endif %}
                {%- endfor %}
                {%- for ignore_item in ignore_list %}
                    {%- set lowercase_ignore_item = ignore_item|lower %}
                    {%- if lowercase_ignore_item in lowercase_attr %}  
                        {%- set hms.matched = true %}
                        {% break %}
                    {%- endif %}
                {%- endfor %}
                {%- if not hms.matched %}
                    {%- set hms.errors = hms.errors + [attr ~ ": " ~ sensor.attributes[attr]] %}
                {%- endif %}
            {%- endfor %}
        {%- endfor %} {{ hms.errors | join("\n") }}

        Nozzle Target Temp - {{
        state_attr('sensor.x1c_x1_carbon_nozzle_temperature','target_temperature')
        }} C

        Nozzle Temp - {{ states('sensor.x1c_x1_carbon_nozzle_temperature') }} C

        Bed Target Temp - {{
        state_attr('sensor.x1c_x1_carbon_bed_temperature','target_temperature')
        }} C

        Bed Temp - {{ states('sensor.x1c_x1_carbon_bed_temperature') }} C
    alias: Pushover Notification
mode: single

i also still use the time remaining notification, as well as an idle notification (printer on, but not doing anything).

1 Like

Yeah sorry about that breaking change lol. Did it so simplify splitting up the severity types to help making basic automations and notifications for people only interested in serious and fatal ones. since many ignorable ones were in “Common” severity.

Hey, seeing as this thread is still active, can anyone help me figure something out?

Im trying to get status information from a bambu x1c and prusa mk4 to get sent and updated on a microsoft sharepoint webpage graph, all this in the local network not using bambu cloud, etc (data privacy). Thus I’ve been searching the internet and using copilot to help find a solution.

The idea I have is to connect the printers tp a raspberry pi hosting a wifi network, and using red node on the pi to “translate” the mqtt information into a short text form message? (not sure about this), and then sending a update message in a microsoft teams to use power automate and update the sharepoint webpage without using any other cloud services, print data should remain hidden, only intersting things would be:

Print start time
print duration
print end time
printer status?

any help about this would be awesome, btw im new to all of this so i’ll need a lot of explaining :slight_smile:

You should probably start a separate thread about that so that we can keep this thread on-topic.

2 Likes

Fun fact, this will also take 3mf’s from HTTP addresses too, including locally hosted ones if you’re into that.

Can you please elaborate on this? I tried this, but wasnt able to see a outbound connection attempt from the printer… Tried with http & https