Bambu Lab X1 X1C MQTT

@WolfwithSword I tried to setup the camera view as shown in your example. Unfortunately without success. Is it a must to change to LAN mode? Other setting on the printer?

Donā€™t need LAN Mode enabled, but you do need the LAN Liveview enabled

Then I suggest restarting the printer after once and give it a few minutes.

1 Like

@WolfwithSword Thank you very much ā€¦ this was the missing part ā€¦ but for me it is not working stabile ā€¦ for a short while it shows the video and after some second stream is ā€œinactiveā€. Any advice how to make it more reliable?

I have a new, or at least newly noticed problemā€¦ My ā€˜Start Timeā€™ keeps resetting. Odd. I thought I noticed it yesterday but was elbow deep in a NAS rebuild. Just fired off a new print about 5 minutes ago and see that it keeps resetting to 0/1. Has anyone else experienced that?

image

Yep, so that is what happens when first enabling. If you restart the printer it should be stable once it loads up after a few minutes. I find that on the first boot after firmware update or just booting with it disabled then enabling it, it is unstable until you reboot with it enabled from the get-go.

I havenā€™t noticed this behaviour before, however if youā€™re using my nodered flows it could be a bug in an older version. I plan on updating them likely tomorrow sometime with a bunch of new features and sensors.

:slight_smile: I think Iā€™m using a partial part of your old code (circa Jan '23). I never built it all the way out but might now that I saw your fancy configuration page!

Iā€™d say if you want my new and improved one, wait until tomorrow. Currently Iā€™m editing the files now for easy token replacement for my configurators. All kinds of fun new stuff like camera resolution changing, fully detailed HMS errors, and much much more.

2 Likes

Alright, got all of my flows updated now. Because itā€™s quite a major overhaul I wouldnā€™t be surprised if there was maybe a minor bug here or there but need to have others try to see if it will be fine or not.

As mentioned, all the flows have been updated, but Iā€™ll just re-link to the basic flow here.

As part of this, I switched from using the gists as the codebase to a new repository so it is easier for people to add issues and track changes all in one place. It is still a WIP and I need to add the dashboard YAMLs to it still, among other things. My plan soon is to create a github pages site for it to also do the configuration in case my site ever goes down, itā€™s still an option. Additionally, I plan to soon document the featureset of each flow so people have a better idea of what my flows provide.
What I will also likely do is once a few people test to make sure this all works, I will do a fake ā€œreleaseā€ on the new repository. That way anyone watching/following the repo can get notified when I have a new significant update much more easily.

Some recent additions with the newly updated basic flow, aside from a much needed reorganization, is that variables are now consolidated in one area in case something needs quick changing. Some new sensors and controls are available, such as:

  • Reorganized HA sensors/controls based on sensor/config/diagnostic data.
  • Toggle video recording on/off (not timelapse)
  • Change camera resolution
  • Detect if printer is on 220v or not (and create a max bed-temp accordingly)
  • Detect if the extruder is ā€œloadedā€ with filament currently or not
  • Read-Only ā€œselectsā€ to assist with automation setup (gives you a pre-defined list of states now)
  • Enriched HMS Errors. Instead of just the link to the wiki, it will also give the Severity, Source Module and English Description of the HMS error as long as it exists in Bambuā€™s own HMS documentation (requires nodered to be able to access an external URL to fetch)
  • Start Time Override option for P1* users since the printer may not provide start time. This is toggleable.
  • And a few more things :slight_smile:

Additionally, I made a new writeup for the X1C Camera setup, since it cannot be done via MQTT.

Looks awesome! I appreciate all your efforts into researching, creating, and sharing this!

Iā€™m having 2 issues, both of which are probably user errorā€¦

  1. I can only seem to get a single stack on my card. Here is the starting snippet if anything jumps out:
  - type: custom:horizontal-layout
    title: newBBLB
    path: newbblb
    badges: []
    cards:
      - type: custom:layout-card
        layout_type: custom:horizontal-layout
        layout:
          max_cols: 3
        cards:
          - type: picture-elements
            view_layout:
              column: 1
            elements:
              - type: custom:config-template-card
                entities:
                  - sensor.ams_0_x1c_tray_0

  1. I appear to be losing connectivity or the communication at least. All the great data & icons will populate but then go offline for a bit and comeback. I noted this in a previous response (start time) so this may be due to having the printer and HA/MQTT on separate VLans but in case anyone else is experiencing I figured I would share. Iā€™ll work on troubleshooting this (after the print is done :slight_smile: )

@WolfwithSword : Thank you very much for your effort and great work. I have also still one issue with the ā€œNozzle Soll Tempā€. It is regular switching from a right value to unkown ā€¦ any idea?

image

I think I might know the source of this (also caused by the change in behaviour of the MQTT messages) but do you currently have a target temperature set? Or is this while idle.

@Corey_Johnson Only thing I can see if maybe play around with other dashboard layouts? Iā€™m using a grid layout and letting them take up specific columns. Wish your second issue, it could be the vlan? I have in the nodered flow some dependency on a ping command to help make sure the printer is online, helps to set state when mqtt is disconnected but printer is still pingable for example.

@WolfwithSword it is during printing ā€¦ so target temp ist set.

Ah okay then, I was curious if it would be an issue but it didnā€™t come up for me, but given the nature of the new MQTT format, I must have missed it. Issue is because it must be sending just the target temp in a message and not including the nozzle temp in it I think. I will do some testing today/tomorrow and if thatā€™s the case, will do a small bug fix in the flow.

If it is that, it would likely be just the contents of one node youā€™d need to replace so Iā€™ll also make a gist to send you the update so you donā€™t need to redo the whole import again.

1 Like

Ping is what was missing - I added it to the pfSense rule between the vlans and now it appears stable, last 10 minutes or so at least.

I cleared my cache but am not seeing the filament-1.svgā€¦ hmm itā€™s in config/custom_icons/filament-1.svg unsure. Maybe that gets called in the beginning of a job and since this is a long-running job Iā€™ll see it next time? **Update-FIXED - I reinstalled FontAwesome and restarted and all good now. Must have fumbled it the 1st time.

Not sure how exactly but was able to get it to 2 columns at least. Iā€™ll keep playing with it. Still very impressive!

For the ā€œStart Timeā€ sensor, (in my case sensor.x1c_x1c_print_start_time), itā€™s almost like it is taking the time OF the message, or the update message, instead of the actual start time. Trying to understand the flow & settings (Iā€™m still a NR newb).

Wowā€¦thanks to everyone that contributed to get the MQTT integration going. It works great with my P1S. That and the inspiring dashboard feom @WolfwithSword allowed me to switch to LAN Only mode and still monitor my printer via HA mobile app.

I made a few of my own customizations to the great dashboards that were shared (used mushroom cards in a few places instead).

When Idle:

When Printing:
SmartSelect_20230806_170232_Chrome

3 Likes

Start time comes directly from the message - the only case where I create it is in the P1S/P1P since it isnā€™t reliably sent. What you probably see is me creating the end-time.

@stephack looks clean! I ended up making it very complex to be compact, but itā€™s annoying to edit. I initially did end up having something similar to this before sharing it, but I love how youā€™ve positioned everything still. Also good to know itā€™s working on the P1S - afaik youā€™re the first person Iā€™ve heard using it for P1S :slight_smile:

1 Like

Very true. I wanted as much as possible on the screen without scrolling on my phone. I still need to add the completion time and an icon to open my camera streamā€¦not the built in camā€¦hopefully Bambu allows lan access to the buitin cam on the P1S in the near future.

Fixed the temperature related bug I believe, as well as a few others. Ended up only modifying 3 nodes so rather than require a reimport for you guys Iā€™ll paste it here. Already committed so regenerating would work still.

@Obicom

Bugs fixed:

  • Nozzle & Bed Temp would sometimes go unknown when a target temperature was set via printer. Cause was due to new mqtt message structure and I wasnā€™t handling when I set attributes of sensors without actual sensor value
  • Nozzle & Bed temperature Setting control was not working. Typoā€™d in their mqtt topics when I remade their flow. woops.
  • Possibly a bug with the new ā€œPrint Stageā€ sensor. Added in an override for ā€œIdleā€ even though it shouldnā€™t be necessary.

For the 3 nodes you need to change if you donā€™t want to do a reimport this time:

Manual Edits

Open up these nodes on version 2.0.0 and replace their full contents as below. This will make it equivalent to 2.0.1

  • Stage Parser

let parseAction = flow.get("stageParser");

if(msg.payload.print.stg_cur != undefined){
    var current_action = parseAction(msg.payload.print.stg_cur);
    flow.set("stage_override", msg.payload.print.stg_cur);
    msg.payload.print.stg_cur = current_action;
    if (current_action == "Idle" && msg.payload.print.print_real_action == undefined) {
        msg.payload.print.print_action = current_action;
    }
}
var translated_stages = [];
if(msg.payload.print.stg  != undefined) {
    for (var stage of msg.payload.print.stg) {
        translated_stages.push(parseAction(stage))
    }
    msg.payload.print.stg = translated_stages;
}

if (msg.payload.print.print_real_action != undefined) {
    if (flow.get("stage_override") <= -1 && msg.payload.print.print_real_action <=0) {
        msg.payload.print.print_real_action = flow.get("stage_override") ;
    }
    msg.payload.print.print_action = parseAction(msg.payload.print.print_real_action);
}

node.send(msg);
  • ā€œSubā€ (Going into nozzle & temp setting)
let topic = flow.get("root_topic");
let device = flow.get("HA_DEVICE");
let config1 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/number/" + device + "/set_bed_temp/set",
        "qos": 2
    }
}

let config2 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/number/" + device + "/set_nozzle_temp/set",
        "qos": 2
    }
}

let config3 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/big_fan1/percent/set",
        "qos": 2
    }
}

let config4 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/big_fan2/percent/set",
        "qos": 2
    }
}

let config5 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/cooling_fan/percent/set",
        "qos": 2
    }
}

let config6 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/big_fan1/set",
        "qos": 2
    }
}

let config7 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/big_fan2/set",
        "qos": 2
    }
}

let config8 = {
    "action": "subscribe",
    "topic": {
        "topic": topic + "/fan/" + device + "/cooling_fan/set",
        "qos": 2
    }
}

node.send([config1, config2, config3. config4, config5, config6, config7, config8]);
  • ā€œPrinter Stateā€
function sendNonEmpty(message) {
    if(message.payload != undefined && message.payload != "" && msg.payload != {}) {
        node.send(message);
    }
}

let hasAttr = false;

if (msg.payload == undefined) {
    return;
}
let root_topic = flow.get("root_topic");
let ha_device_name = flow.get("HA_DEVICE");
if (msg.topic == "machine_serial" || msg.topic == "print_preview" || msg.topic == "msg") {
    return;
}
if (msg.topic == "reset_filter" || msg.topic == "resume_print" || msg.topic == "pause_print"
    || msg.topic == "clear_external_spool" || msg.topic == "force_ftp_fetch"
    || msg.topic.includes("logo_light") || msg.topic.includes("nozzle_light")) {
    return;
}
if(msg.topic == "nozzle_diameter") {
    flow.set("nozzle_diameter", msg.payload);
}
if(msg.topic.startsWith("set_") && msg.topic.endsWith("_temp")) {
    return;
}
else if (msg.topic == "last_calibrated_pa" && msg.payload == "") {
    return;
}
else if (msg.topic == "toggle_timelapse" || msg.topic == "toggle_recording") {
    return;
}
else if (msg.topic == "hw_switch_state") {
    msg.topic = "extruder_loaded";
    if(msg.payload == "1") {
        msg.payload = true;
    }
    else {
        msg.payload = false;
    }
}
else if (msg.topic == "machine_name") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    let oldPayload = msg.payload;
    msg.payload = { "Serial Number": flow.get("printer_serial")};
    node.send(msg);
    msg.payload = oldPayload;
    msg.topic = oldTopic;
}
else if (msg.topic == "wifi_signal")
    msg.payload = msg.payload.match(/^-?\d+/)[0];
else if (msg.topic == "xcam") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.status;
}
else if (msg.topic == "upload") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.message;
}
else if (msg.topic == "camera_rtsp") {
    var oldTopic = msg.topic;
    var oldPayload = msg.payload;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    if (oldPayload != "Disabled") {
        msg.payload = {"URL": oldPayload, "User": "bblp", "Password": "<access_code>", "Protocol": "TCP", "SSL Verify": "disable", "Framerate": 30, "Port (Optional)": 322, "LAN Mode": "Not Required"};
        node.send(msg);
        oldPayload = "Enabled";
    }
    msg.topic = oldTopic;
    msg.payload = oldPayload;
}
else if (msg.topic == "upgrade_state") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    let state = "AVAILABLE";
    if (msg.payload.new_version_state == 1) {
        msg.payload = state;
    }
    else {
        msg.payload = msg.payload.status;
    }
}
else if (msg.topic == "stage") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";

    var current = msg.payload.current;
    var prev = {};
    var count = 0;
    
    if(msg.payload.prev != undefined) {
        for (var stg of msg.payload.prev) {
            prev[count.toString()] = stg;
            count += 1;
        }
            msg.payload = prev;
            node.send(msg);
    }

    msg.topic = oldTopic;
    msg.payload = current;
}
else if (msg.topic == "ipcam") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.ipcam_dev;
}
else if (msg.topic == "vt_tray") {
    var oldTopic = msg.topic;
    if (msg.payload.color != undefined
        && !msg.payload.color.startsWith("#")) {
        msg.payload.color = "#" + msg.payload.color;
    }
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.type;
}
else if (msg.topic == "subtask") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.name;
}
else if (msg.topic == "print") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.type;
    if(msg.payload == undefined || msg.payload == "") {
        msg.payload = "None";
    }
}
else if (msg.topic == "bed_temperature") {
    var oldTopic = msg.topic;
    hasAttr = true;
    if (msg.payload.target_temperature != undefined) {
        msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
        node.send(msg);
    }
    if(msg.payload.temperature == undefined) {
        return;
    }
    msg.topic = oldTopic;
    msg.payload = msg.payload.temperature;
}
else if (msg.topic == "nozzle_temperature") {
    var oldTopic = msg.topic;
    hasAttr = true;
    if(msg.payload.target_temperature != undefined) {
        msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
        node.send(msg);
    }
    if (msg.payload.temperature == undefined) {
        return;
    }
    msg.topic = oldTopic;
    msg.payload = msg.payload.temperature;
}
else if (msg.topic == "chamber_temperature") {
    msg.payload = msg.payload.temperature;
}
else if (msg.topic == "gcode") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = msg.payload.file;
}
else if (msg.topic == "hms") {
    msg.topic = "HMS";
    var oldTopic = msg.topic;
    hasAttr = true;
    var numOfCodes = msg.payload == undefined ? 0 : msg.payload.length;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/attr";
    var links = {};
    for (var data of msg.payload) {
        let m = data.url;

        if ( data.description != undefined && data.description != "") {
            m = data.description + "\n"+ m;    
        }
        if(data.module != undefined && data.module != "") {
            m = "["+data.module+"]" + "\n" + m;
        }
        if (data.type != undefined && data.type != "") {
            m = "["+data.type+"]" + " " + m;
        }

        links[data.code.replaceAll(" ", "_")] = m;
    }
    msg.payload = links;
    node.send(msg);
    msg.topic = oldTopic;
    msg.payload = numOfCodes;
}
else if (msg.topic == "speed") {
    var oldTopic = msg.topic;
    hasAttr = true;
    msg.topic = root_topic + "/select/" + ha_device_name + "/" + msg.topic + "/attr";
    node.send(msg);
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + "speed_info" + "/attr";
    node.send(msg);
    msg.topic = "speed";
    msg.payload = msg.payload.profile;
    msg.topic = root_topic + "/select/" + ha_device_name + "/" + "speed" + "/state";
    node.send(msg);
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + "speed_info" + "/state";
    node.send(msg);
    return;
}
else if (msg.topic == "camera_resolution") {
    msg.topic = root_topic + "/select/" + ha_device_name + "/" + msg.topic + "/state";
    node.send(msg);
    return;
}
else if (msg.topic == "chamber_light") {
    msg.payload = msg.payload.toUpperCase();
    msg.topic = root_topic + "/light/" + ha_device_name + "/" + msg.topic + "/state";
    node.send(msg);
    return;
}
else if (msg.topic.match(/(.*)fan(.*)/) && msg.topic !== "fan_gear" && msg.topic != "heatbreak_fan"
    && msg.topic != "aux_part_fan") {
    let orig_topic = msg.topic;
    msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + orig_topic + "/state";
    node.send(msg);
    msg.topic = root_topic + "/fan/" + ha_device_name + "/" + orig_topic + "/percent/state";
    msg.payload = parseInt(msg.payload) / 10;
    node.send(msg);
    if(msg.payload == 0 || msg.payload == "0") {
        msg.payload = "OFF";
        msg.topic = root_topic + "/fan/" + ha_device_name + "/" + orig_topic+ "/state";
        node.send(msg);
    }
    else {
        msg.payload = "ON";
        msg.topic = root_topic + "/fan/" + ha_device_name + "/" + orig_topic + "/state";
        node.send(msg);
    }
    return;
}
msg.topic = root_topic + "/sensor/" + ha_device_name + "/" + msg.topic + "/state";

if(hasAttr) {
    sendNonEmpty(msg);
}
else {
    node.send(msg);
}
1 Like