Bambu Lab X1 X1C MQTT

Interesting, the ones I have here are of the model are all transparent. A dozen or so I checked, before assuming that was always the case. I’m using the fork of the slicer, wonder if that has any impact on it?

Could very well be. I’m running whatever is the latest official release for Windows, latest official firmwares etc. I may look into a fork of it this weekend and test the difference.

May also explain why I didn’t have the 3mf file in the ftp directory with one print, but did for the second. May be a few bugs in how it treats the whole file and contents.

Modified my thumbnail/preview fetch to be more error-safe. Since it pulls down the 3mf anyways and doesn’t delete, I put in a flow such that if it’s not connected to the printer’s mqtt (and thus, probably also ftp) then it will try to extract from the last downloaded 3mf.

I decided to just roll with the http endpoint for serving the image. In the response, I set the cache-control header to max-age=300, so HA’s caching problems shouldn’t be as stubborn. I did end up exposing my node-red instance through my domain/reverse proxy to make use of it though (auth and necessary security stuff in place :wink: ).

For others, if your NodeRed is running as an addon in HomeAssistant or something similar, you probably can just directly call the internal server but I’m not sure - I’ve only ever run Home-Assistant-Core docker container. At the very least, you can make use of the written file in a directory (or write to www / local), then change the http endpoint to a button service call or something.

Updated Image Fetch

Summary

My updated flow for image fetching:

The switch node just checks if the buffer from the 3mf file is empty or not. If it’s empty (no 3mf exists), it tries to see if there’s a previously extract image, so backup #2. Also as part of the flow, I also write the image to a directory for this backup purpose.

Entire Flow + json

Summary

Here’s my entire flow so far. I haven’t implemented all the stuff we’ve come up with here, and it isn’t perfect (I was not very consistent with naming of entities in some places). It’s also heavily focused on some of my use cases, such as the http server, attributes over entities, only one AMS etc.

And here’s the flow json! I’ve modified a few things to hide private information. You may also want to change things such as paths, names, etc.

WIP NodeRed Flow for Bambulabs X1C + AMS into HomeAssistant · GitHub

Change the following:

  • BAMBU_IP_ADDRESS - With IP or Hostname of your printer. This is for both the MQTT in, out, and ftp in
  • BAMBU_SERIAL_ID - This is the unique ID for your printer. I have a simple text state sensor for it, but it’s used in all MQTT commands and nodes as it’s in the topic path.

Required palettes:

  • node-red-contrib-home-assistant-websocket
  • node-red-contrib-ftp
  • node-red-contrib-moment
  • node-red-contrib-zip
  • node-red-node-base64

Other:

  • I have a folder mounted/setup as /data/fetched/x1c_meta for fetched files via FTP, written files, and sent via HTTP.
  • HTTP endpoint is setup as /get/media/X1C/preview.png
  • FTP username is same for all printers and is preset. For password, it’s the “access code” prompt you get when you put your printer in LAN Only Mode. It does not change per printer unless you regenerate it.
  • For multiple AMS units, you can copy the section after and including GET AMS 0, and rename / make new entities and device(s) accordingly.
1 Like

Which fork of the slicer are you using? I’ve noticed there’s a few different ones floating around, but can’t tell immediately if any improved the png generation for the 3mf file.

Think a transparent background would make this WIP look really nice :slight_smile:

This one SoftFever/BambuStudio-SoftFever: A forked version of BambuStudio (github.com)

1 Like

Well, I can 100% confirm it is the fork! It actually does some whole entire other process I’m imagining as the angles and such are also different.

A random 3d model saved in a 3mf file from the official bambu slicer (most recent as of this post):

Using the SoftFever forked slicer and the same exact model:

Guess I might switch, unless they update the official one someway to make it transparent. The angle in the fork is also better so, maybe I’ll stay switched as long as it keeps up to date. Else I may dig around for just the change that generates this and keep my own private fork. Eh, I got time to figure it out :slight_smile:

The fork is - at the moment - updated as often or slightly more often than the official one. Are some other features coming the official one won’t have for a bit.

1 Like

Threw together a function node which can let you overwrite or set some settings on filament types that are recognized by Bambu Studio (such as the Generics), using the tray_info_idx’s.

Mostly so that I can check in homeassistant what sub-brand type the filament is, since despite setting this in Bambu slicer, it doesn’t actually save any of it, just the idx info. This would also be a useful starting point for additional ways to inject information on the filament for HA.

I’ll update this in my flow gist above.

Known Filament Translator Function

// Bambu spools are not needed for translation if they have the rfid tags
// but this can be used to overwrite them for HA displaying only
// This only overwrites the tray_type and tray_sub_brands, if you want other fields overwritten you can add them
// e.g.  tray_diameter, tray_weight, temps etc

var PolyLite_PLA = {
    "tray_info_idx": "GFL00", 
    "tray_type": "PLA",
    "tray_sub_brands": "PolyLite PLA"
}

var PolyTerra_PLA = {
    "tray_info_idx": "GFL01",
    "tray_type": "PLA",
    "tray_sub_brands": "PolyTerra PLA"
}

var Bambu_ABS = {
    "tray_info_idx": "GFB00",
    "tray_type": "ABS"
}

var Bambu_PACF = {
    "tray_info_idx": "GFN03",
    "tray_type": "PC-CF"
}

var Bambu_PC = {
    "tray_info_idx": "GFC00",
    "tray_type": "PC"
}

var Bambu_PLA_Basic = {
    "tray_info_idx": "GFA00",
    "tray_type": "PLA",
    "tray_sub_brands": "PLA Basic"
}
var Bambu_PLA_Matte = {
    "tray_info_idx": "GFA01",
    "tray_type": "PLA",
    "tray_sub_brands": "PLA Matte"
}

var Support_G = {
    "tray_info_idx": "GFS01",
    "tray_type": "Support",
    "tray_sub_brands": "Support G"
}

var Support_W = {
    "tray_info_idx": "GFS00",
    "tray_type": "Support",
    "tray_sub_brands": "Support W"
}

var Bambu_TPU_95A = {
    "tray_info_idx": "GFU01",
    "tray_type": "TPU",
    "tray_sub_brands": "TPU 95A"
}

var Generic_ABS = {
    "tray_info_idx": "GFB99",
    "tray_type": "ABS",
    "tray_sub_brands": "ABS"
}

var Generic_ASA = {
    "tray_info_idx": "GFB98",
    "tray_type": "ASA",
    "tray_sub_brands": "ASA"
}

var Generic_PA = {
    "tray_info_idx": "GFN99",
    "tray_type": "PA",
    "tray_sub_brands": "PA"
}

var Generic_PACF = {
    "tray_info_idx": "GFN98",
    "tray_type": "PA-CF",
    "tray_sub_brands": "PA-CF"
}

var Generic_PC = {
    "tray_info_idx": "GFC99",
    "tray_type": "PC",
    "tray_sub_brands": "PC"
} 

var Generic_PETG = {
    "tray_info_idx": "GFG99",
    "tray_type": "PETG",
    "tray_sub_brands": "PETG"
}
var Generic_PLA = {
    "tray_info_idx": "GFL99",
    "tray_type": "PLA",
    "tray_sub_brands": "PLA"
}
var Generic_PLACF = {
    "tray_info_idx": "GFL98",
    "tray_type": "PLA-CF",
    "tray_sub_brands": "PLA-CF"
}
var Generic_PVA = {
    "tray_info_idx": "GFS99",
    "tray_type": "PVA",
    "tray_sub_brands": "PVA"
}
var Generic_TPU = {
    "tray_info_idx": "GFU99",
    "tray_type": "TPU",
    "tray_sub_brands": "TPU"
}

var filament_library = {
    "GFU99": Generic_TPU,
    "GFS99": Generic_PVA,
    "GFL98": Generic_PLACF,
    "GFL99": Generic_PLA,
    "GFG99": Generic_PETG,
    "GFC99": Generic_PC,
    "GFN98": Generic_PACF,
    "GFN99": Generic_PA,
    "GFB98": Generic_ASA,
    "GFB99": Generic_ABS,
    "GFU01": Bambu_TPU_95A,
    "GFS00": Support_W,
    "GFS01": Support_G,
    "GFA01": Bambu_PLA_Matte,
    "GFA00": Bambu_PLA_Basic,
    "GFC00": Bambu_PC,
    "GFN03": Bambu_PACF,
    "GFB00": Bambu_ABS,
    "GFL01": PolyTerra_PLA,
    "GFL00": PolyLite_PLA
}

if (msg.ams !== undefined) {
    for (var ams of msg.ams) {
        for (var tray of ams.tray) {
            if (tray.tray_info_idx !== undefined && tray.tray_info_idx !== "") {
                if(filament_library.hasOwnProperty(tray.tray_info_idx)) {
                    var match = filament_library[tray.tray_info_idx];
                    tray.tray_type = match.tray_type;
                    tray.tray_sub_brands = match.tray_sub_brands;
                }
            }
        }
    }
}

node.send(msg);

Wow - ya’ll have really taken this to the next level!

How are you getting to colors to reflect in HA? as in:

Also, I seem to be using some different type of Node Red as I do not have the same screens most of you seems to have. How can I set the “Device” name? Is it the same as “device_class”?

For the colours there I was using some card-mod, but have since switched to using a picture-element with both card-mod styling and custom:config-template-card, pulling colour from the state attributes. Reason for the picture-element is I now have the AMS as a background photo to overlay it.

Here’s the YAML for my “AMS” display. Do note that it references custom-uploaded icons for spool threads, and an image of the AMS in my www/media.

AMS YAML

Still a WIP as for “black” filament I tend to make it just a tad lighter in bambu slicer, so you can actually see dimensionality in the preview, but then this causes issues with the filament icon blending in too much. Trying to figure out some styling to add a stroke or outline to each svg icon.

image

For changing the icon to a non custom one, replace “custom:filament-1” with an available icon you have.

type: picture-elements
elements:
  - type: custom:config-template-card
    entities:
      - sensor.filament_tray_0
    element:
      type: state-icon
      entity: sensor.filament_tray_0
      icon: >-
        ${states['sensor.filament_tray_0'].state != 'Empty' ?
        'custom:filament-1' : 'mdi:tray' }
    style:
      top: 28%
      left: 21.4%
      '--paper-item-icon-color': var(--tray_0_color)
      background-color: rgba(0,0,0,0.5)
      box-shadow: 0 0 5px 5px var(--tray_0_bg)
      border-radius: 50px
      '--mdc-icon-size': 2.4em
  - type: state-label
    entity: sensor.filament_tray_0
    attribute: type
    tap_action:
      action: none
    style:
      top: 77%
      left: 21%
      text-align: center
      font-size: 1em
      background-color: rgba(0,0,0,0.4)
      box-shadow: 0 0 5px 5px rgba(0, 0, 0, 0.4)
      border-radius: 50px
      pointer-events: none
  - type: custom:config-template-card
    entities:
      - sensor.filament_tray_1
    element:
      type: state-icon
      entity: sensor.filament_tray_1
      icon: >-
        ${states['sensor.filament_tray_1'].state != 'Empty' ?
        'custom:filament-1' : 'mdi:tray' }
    style:
      top: 28%
      left: 39.7%
      '--paper-item-icon-color': var(--tray_1_color)
      background-color: rgba(0,0,0,0.5)
      box-shadow: 0 0 5px 5px  var(--tray_1_bg)
      border-radius: 50px
      '--mdc-icon-size': 2.4em
  - type: state-label
    entity: sensor.filament_tray_1
    attribute: type
    tap_action:
      action: none
    style:
      top: 77%
      left: 40%
      text-align: center
      font-size: 1em
      background-color: rgba(0,0,0,0.4)
      box-shadow: 0 0 5px 5px rgba(0, 0, 0, 0.4)
      border-radius: 50px
      pointer-events: none
  - type: custom:config-template-card
    entities:
      - sensor.filament_tray_2
    element:
      type: state-icon
      entity: sensor.filament_tray_2
      icon: >-
        ${states['sensor.filament_tray_2'].state != 'Empty' ?
        'custom:filament-1' : 'mdi:tray' }
    style:
      top: 28%
      left: 59.7%
      '--paper-item-icon-color': var(--tray_2_color)
      background-color: rgba(0,0,0,0.5)
      box-shadow: 0 0 5px 5px  var(--tray_2_bg)
      border-radius: 50px
      '--mdc-icon-size': 2.4em
  - type: state-label
    entity: sensor.filament_tray_2
    attribute: type
    tap_action:
      action: none
    style:
      top: 77%
      left: 60%
      text-align: center
      font-size: 1em
      background-color: rgba(0,0,0,0.4)
      box-shadow: 0 0 5px 5px rgba(0, 0, 0, 0.4)
      border-radius: 50px
      pointer-events: none
  - type: custom:config-template-card
    entities:
      - sensor.filament_tray_3
    element:
      type: state-icon
      entity: sensor.filament_tray_3
      icon: >-
        ${states['sensor.filament_tray_3'].state != 'Empty' ?
        'custom:filament-1' : 'mdi:tray' }
    style:
      top: 28%
      left: 79.6%
      '--paper-item-icon-color': var(--tray_3_color)
      background-color: rgba(0,0,0,0.5)
      box-shadow: 0 0 5px 5px var(--tray_3_bg)
      border-radius: 50px
      '--mdc-icon-size': 2.4em
  - type: state-label
    entity: sensor.filament_tray_3
    attribute: type
    tap_action:
      action: none
    style:
      top: 77%
      left: 79.6%
      text-align: center
      font-size: 1em
      background-color: rgba(0,0,0,0.4)
      box-shadow: 0 0 5px 5px rgba(0, 0, 0, 0.4)
      border-radius: 50px
      pointer-events: none
image: /local/media/AMS_2.png
style: |
  ha-card {
    background: none !important;
    border: none !important;
    --tray_0_color: {% if is_state('sensor.filament_tray_0', 'Empty') %} grey; {% else %} {{
        state_attr('sensor.filament_tray_0', 'colour') }}; {% endif %}
    --tray_1_color: {% if is_state('sensor.filament_tray_1', 'Empty') %} grey; {% else %} {{
        state_attr('sensor.filament_tray_1', 'colour') }}; {% endif %}
    --tray_2_color: {% if is_state('sensor.filament_tray_2', 'Empty') %} grey; {% else %} {{
        state_attr('sensor.filament_tray_2', 'colour') }}; {% endif %}
    --tray_3_color: {% if is_state('sensor.filament_tray_3', 'Empty') %} grey; {% else %} {{
        state_attr('sensor.filament_tray_3', 'colour') }}; {% endif %}
    --tray_0_bg: {% if is_state_attr('sensor.filament_tray_0', 'id', state_attr('sensor.current_ams_filament_in_use', 'id')) %} rgba(255, 255, 126, 0.5); {% else %} rgba(0,0,0,0.5); {% endif %}
    --tray_1_bg: {% if is_state_attr('sensor.filament_tray_1', 'id', state_attr('sensor.current_ams_filament_in_use', 'id')) %} rgba(255, 255, 126, 0.5); {% else %} rgba(0,0,0,0.5); {% endif %}
    --tray_2_bg: {% if is_state_attr('sensor.filament_tray_2', 'id', state_attr('sensor.current_ams_filament_in_use', 'id')) %} rgba(255, 255, 126, 0.5); {% else %} rgba(0,0,0,0.5); {% endif %}
    --tray_3_bg: {% if is_state_attr('sensor.filament_tray_3', 'id', state_attr('sensor.current_ams_filament_in_use', 'id')) %} rgba(255, 255, 126, 0.5); {% else %} rgba(0,0,0,0.5); {% endif %}
  }

For the one I posted a while back with that screenshot, that was just custom-button cards and cardmod for changing the icon and icon colour based on state attribute.

As for your other question, you may want to update your NodeRed or the HA Websocket palette for NR, as that looks like it’s using just the entity node, which is deprecated.
image
image

Device class is just telling you what type of device it is for the HA entity to auto configure some stuff like icon and units unless overridden. The actual device you would configure with an entity config then entity device config.

2 Likes

I didn’t realize that parts of Node-Red had to be updated within Node-Red. I think I got it now.
Still working out the color coding. I think the overlays are nice and fancy but I’m not looking to go that far with it.
I run the Bambu software in a VM so can access anywhere but use my HA instance for quick at a glance things.

Pretty awesome work, got it working, once I found out I had to change the device ID (doooh)

I got 4 AMS, havent worked with Node Red before. Is there an easy way to add 3 more devices with out haveing to duplicate the entities 3 more times so I get AMS0 with filament tray 0->3, AMS1 with filament tray 0->3 etc. and wont have filament 0-15

As a note, there are MQTT changes coming in the near future according to a Bambu staffer on Discord, resulting in some (I don’t know what) information going away.

1 Like

Easiest way I can see about going it is to manually copy and paste the device and entity configs in the flow json, and change the generated IDs appropriately. Can probably keep the names still as tray 0-3, just in NodeRed to add an identifier for which device they go to, then set the entity config’s device to AMS 0, AMS 1, etc. So maybe won’t end up with filament 0-15, but 0-3 AMS X?

I wish there was a way to just clone it easily, like “here is device type Y, I have 3 of them, let me increment their id’s and entity id’s if I clone it”. Would make it almost scriptable in a NR function for any number of AMS’s. If I had more than one AMS unit, it’d be enough to make me consider ditching NR and making my own integration, replicating everything I’ve done in my NR flow.

I may actually consider it at some point, though I guess that depends on how big the upcoming MQTT changes may be :stuck_out_tongue:

Well I think I finally have my dashboard 90% where I want it in regards to the capabilities we’ve been able to find. Threw in a power-monitoring smart plug to also have those stats, eventually going to make a grafana dashboard for it to track energy cost per specific prints, but for now just have it setup for daily usage and cost.

The only thing missing is an IP camera preview. I finally joined the bambu discord and saw someone in there had it working in a docker, but I think I’ll wait until a future update. I saw some hints of improved camera streaming support in the documentation coming soon, which could make the process easier.

opera_4kWkepUXht

Next steps for me is to just wait for future changes, see what’s removed (or added), adjust accordingly etc. Would love to get the camera feed working sometime. I think I convinced myself earlier that as long as enough MQTT stuff is still available I may make a custom integration for the printer, but definitely not until the new year :slight_smile:

1 Like

I went ahead and did that. Brute force for the win!

1 Like

I’m still trying to figure out how to get the visible color (like #057748FF) to be shown in the icon. Not even trying to be fancy about it, just want to color the mdi:printer-3d-nozzle.

Yeah that can be tricky, there’s a few different ways you can go about it using some custom HACS frontend stuff.

Card mod is a pretty easy one to go through which can work on pretty much any HA card. For that you would modify the --paper-item-icon-color style/property, and just pull the hex using a template to the state attribute.

Using a custom-button-card you could instead do it by state, then template it probably using the attribute.

And finally a config-template-card can also work or help the others. In my previous screen shots I used both the card-mod and custom button cards, but now I use just the card mod and config-template-card. This way I just set the colour in the parent card as a css variable and use it via cardmod.

E.g.

On the parent card to set the variable and reuse it in other places

style: |
  ha-card {
    --tray_0_color: {% if is_state('sensor.filament_tray_0', 'Empty') %} grey; {% else %} {{
        state_attr('sensor.filament_tray_0', 'colour') }}; {% endif %}
  }

Then on the individual icon/card that you want the colour on:

style:
      '--paper-item-icon-color': var(--tray_0_color)
1 Like

I see you got a power reading as well, I assume that is from a metered plug or something.

Im currently trying to sum the energy used on a print. In theory its fairly simple. We have the start time so its just to sum the metered sensor from the gcode attribute start time and up untill now (or in theory until state change to end). It wouldnt save it or anything, but it could be interesting.

1 Like

Yep, using a metered plug :slight_smile: From Athom via ESPHome.

That’s my plan as well. I was going to setup a query for the electric usage for each unique combo of current-task and start-time (both do not change during a print) while the state of the gcode is “RUNNING”. Either calculate the kWh using all the wattage readings over the time, or to simplify it, just subtract the “total energy” in kWh of the start of the print (lowest value in query) from the total by the end of the query (the largest value in the query). This should technically be just as accurate and a lot easier if you already have “total”.

Then for cost per print I’ll just multiply it by my electric rate which I already have in my db as I scrape it from my electric provider’s site often. I’d make some grafana dashboards for showing these values in a table, webpage card in HA to that table, etc.

I just wish we could then get an accurate reading of filament used in a print easily. It’s unfortunately not exposed through the MQTT topic so hard to automate. Would love to then match amount of filament used, separate it by different filaments used in case of AMS multi-filament print, then break it down by cost per filament per print, but now I’m just dreaming :stuck_out_tongue: Probably some way to analyze the 3mf file for it to be honest.