THE-SIMPLE-MARK/bambu-node

Edge case detected while updating printer status

Opened this issue · 12 comments

Hi! Thank you so much for your library!

I'm encountering an exception I'm not able to catch each times a print finish and another starts (so that is not an edge case ^^).

The printer's status has changed from FINISH to PREPARE! file:///.../node_modules/bambu-node/dist/index.js:333

Error: Edge case detected while updating printer status! at D.onMessage (file:///.../node_modules/bambu-node/dist/index.js:333:14924) at MqttClient.listener (file:///.../node_modules/bambu-node/dist/index.js:333:12437) at MqttClient.emit (node:events:529:35) at handlePublish (...\node_modules\mqtt\build\lib\handlers\publish.js:97:20) at handle (...\node_modules\mqtt\build\lib\handlers\index.js:28:35) at work (...\node_modules\mqtt\build\lib\client.js:227:40) at writable._write (...\node_modules\mqtt\build\lib\client.js:252:13) at writeOrBuffer (...\node_modules\readable-stream\lib\internal\streams\writable.js: 334:12) at _write (...\node_modules\readable-stream\lib\internal\streams\writable.js:283:10) at Writable.write (...\node_modules\readable-stream\lib\internal\streams\writable.js :286:10)

More context:

Node.js v18.19.1

When the status of my printer changes for RUNNING, my app starts to send the command PushAllCommand each 5s to obtain the current progression (mc_percent).

If the status changes for any other value, it stops to send this command.

Maybe it is the cause of the crash?
Maybe there is an easier way to get the progress of the print?

Another small thing. If you instanciate a client and the connection fails (because the printer is off for example), there is no way to reuse it to retry. You have to create a new instance of the client each time.

Sorry for my bad english and thank you again for your work, now I have a stripe of leds displaying the progress of my prints from another room on the top of my ultra wide desk (3 meters).

Hey @morgan-wild! It has been a minute since I have touched this codebase, so I'm sorry for any inaccuracies.

There's actually a .data property that you can access on the instantiated class which always has the most up to date data. If you want to continuously update things, as you mentioned, you can use the client#on("printer:dataUpdate") event, and then every time it's called you can access client#data for mc_percent.

A much prettier solution however, is to use the client#on("job: ... events. These events give you a Job object, which contains the percentDone property, which is the alias of mc_percent.

I do not know how your LED light works, but a potential way to do this would be to:

  1. add event listeners for the job:start, job:pause, job:unpause, job:finish, job:update events.
  2. on job start, you can reset the LED or read job.percentDone (would have the same effect since percentDone is 0 on job start)
  3. on job update, you can read job.percentDone
  4. on job pause, you can turn the entire strip orange (just an idea)
  5. on job unpause, you can read job.percentDone
  6. on job finish, you can make it blink green if outcome (second argument in callback) is "SUCCESS", red if it is "FAILED" or "UNEXPECTED" (just an idea)

So in summary, there's no need for you to call PushAllData manually. The whole purpose of the client is to abstract the MQTT layer and provide an almost SDK level access to your printer.

In the meanwhile you're implementing this, I'll look into the edge case. However I do think that refactoring your code as I described above will fix your issue.

Also in regard to the second smaller issue, can't you just retry by calling client#connect on the same instance?

If that's not the case then as of right now, all I can recommend is to try in a setInterval every 15 seconds, and set a let outside of the interval to the client once a connection has been successfully made.

The client is also automatically set up to retry connecting to your printer every second, but if what you described is true, then that only works if the client was able to connect in the first place.

I have figured out the cause of the edge case error.

The library treats both the PREPARE and SLICING statuses as IDLE, as they do not indicate printing technically.

// we treat PREPARE and SLICING as an alias of idle because they're of no use
if (newStatus === "PREPARE" || newStatus === "SLICING") newStatus = "IDLE"
if (oldStatus === "PREPARE" || oldStatus === "SLICING") oldStatus = "RUNNING"

The problem is that in the if else if statements below, which handle the status changes, I failed to account for FINISH changing to IDLE, even though this is normal behaviour because PREPARE and SLICING is before a job start.

I will push a fix to just quitely ignore FINISH | FAILED => IDLE changes as a minor update within the hour.

I have pushed the fix under semver 3.22.21. As this was the primary issue, I will close this issue. Don't hesitate to reopen it or open another issue if you have found another bug.

As for the "connection retry on first connection", it is technically a feature request, so feel free to open another issue and a PR. If everything looks good in the implementation, I don't see any issues with implementing it.

Hi!

Thank you so much for your fast response.

add event listeners for the job:start, job:pause, job:unpause, job:finish, job:update events.
on job start, you can reset the LED or read job.percentDone (would have the same effect since percentDone is 0 on job start)
on job update, you can read job.percentDone
on job pause, you can turn the entire strip orange (just an idea)
on job unpause, you can read job.percentDone
on job finish, you can make it blink green if outcome (second argument in callback) is "SUCCESS", red if it is "FAILED" or "UNEXPECTED" (just an idea)

Yes! That is exactly that I planned to do!

But I tried something like before to use the PushAllData command :

 _client = new BambuClient(_myCredentials);

 _client.on('job:start', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:start` + job);
 });

 _client.on('job:pause', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:pause` + job);
 });

 _client.on('job:unpause', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:unpause` + job);
 });

 _client.on('job:update', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:update` + job);
 });

 _client.on('job:finish', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:finish` + job);
 });

 _client.on('message', (topic, key, data) => {
     // Is fired
 });

 _client.on('printer:statusUpdate', (oldStatus, newStatus) => {
     // Is fired
 });

I updated to 3.22.21 and it is still true.

Still using the 3.22.21 I tried again to reuse a client instance after a connection failure:

initializeClient(); // Create a new instance of client with listeners

connect();

async function connect() {

    console.log('Connecting...');

    if (_client.connected) {
        return;
    }

    try {

        await _client.connect();

        console.log('Connected');
    }
    catch(exception) {

        console.log('Failed to connect');

        setTimeout(connect, 10000);
    }
}
$ node .     <- printer is off
Connecting...
Failed to connect    <- long
Connecting...        <- printer turned on
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
...

<program restarted, printer online>
Connecting...
Connected             <- OK

But:

connect();

async function connect() {

    if (_client?.connected) {
        return;
    }

    console.log('Connecting...');

    try {

        initializeClient(); // Create a new instance of client with listeners

        await _client.connect();

        console.log('Connected');
    }
    catch(exception) {

        console.log('Failed to connect');

        setTimeout(connect, 10000);
    }
}
$ node .            <- printer is off
Connecting...       
Failed to connect   <- long
Connecting...       <- printer turned on
Connected

I will try to understand :)

Hi!

Thank you so much for your fast response.

add event listeners for the job:start, job:pause, job:unpause, job:finish, job:update events.
on job start, you can reset the LED or read job.percentDone (would have the same effect since percentDone is 0 on job start)
on job update, you can read job.percentDone
on job pause, you can turn the entire strip orange (just an idea)
on job unpause, you can read job.percentDone
on job finish, you can make it blink green if outcome (second argument in callback) is "SUCCESS", red if it is "FAILED" or "UNEXPECTED" (just an idea)

Yes! That is exactly that I planned to do!

But I tried something like before to use the PushAllData command :

 _client = new BambuClient(_myCredentials);

 _client.on('job:start', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:start` + job);
 });

 _client.on('job:pause', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:pause` + job);
 });

 _client.on('job:unpause', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:unpause` + job);
 });

 _client.on('job:update', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:update` + job);
 });

 _client.on('job:finish', job => {
     // Never fired
     console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:finish` + job);
 });

 _client.on('message', (topic, key, data) => {
     // Is fired
 });

 _client.on('printer:statusUpdate', (oldStatus, newStatus) => {
     // Is fired
 });

I updated to 3.22.21 and it is still true.

Still using the 3.22.21 I tried again to reuse a client instance after a connection failure:

initializeClient(); // Create a new instance of client with listeners

connect();

async function connect() {

    console.log('Connecting...');

    if (_client.connected) {
        return;
    }

    try {

        await _client.connect();

        console.log('Connected');
    }
    catch(exception) {

        console.log('Failed to connect');

        setTimeout(connect, 10000);
    }
}
$ node .     <- printer is off
Connecting...
Failed to connect    <- long
Connecting...        <- printer turned on
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
Connecting...
Failed to connect    <- instant
...

<program restarted, printer online>
Connecting...
Connected             <- OK

But:

connect();

async function connect() {

    if (_client?.connected) {
        return;
    }

    console.log('Connecting...');

    try {

        initializeClient(); // Create a new instance of client with listeners

        await _client.connect();

        console.log('Connected');
    }
    catch(exception) {

        console.log('Failed to connect');

        setTimeout(connect, 10000);
    }
}
$ node .            <- printer is off
Connecting...       
Failed to connect   <- long
Connecting...       <- printer turned on
Connected

I will try to understand :)

Oh, that's interesting. I haven't done anything about the connection retry, but those events should fire. I seriously don't know why they don't.

The code you have for retrying the connection is what I had in mind. I do not think I'll implement something like that right into the library, so that's fine in my opinion.

Which printer model do you have?

Also, I should note that the events you said never fired are only actually fired when there's a print-related event. A job is created when the printer starts it's work, finish is called when it finishes, and so on.

It would be also really helpful if you could share what message and printer:statusUpdate logs. That might have the secret as to why those events never fire.

Also, I should note that the events you said never fired are only actually fired when there's a print-related event. A job is created when the printer starts it's work, finish is called when it finishes, and so on.

Yes for sure!

My printer is a P1S with the latest firmware.

It is currently printing, so:

// Example of message without a push all command:
New print message! {
  nozzle_temper: 220.125,
  bed_temper: 54.96875,
  wifi_signal: '-27dBm',
  mc_print_line_number: '69627',
  command: 'push_status',
  msg: 1,
  sequence_id: '1151'
}

// Example of message after a pushAll command:
New print message! {
  upgrade_state: {
    sequence_id: 0,
    progress: '',
    status: '',
    consistency_request: false,
    dis_state: 0,
    err_code: 0,
    force_upgrade: false,
    message: '0%, 0B/s',
    module: '',
    new_version_state: 0,
    cur_state_code: 1,
    new_ver_list: []
  },
  ipcam: {
    ipcam_dev: '1',
    ipcam_record: 'enable',
    timelapse: 'disable',
    resolution: '',
    tutk_server: 'disable',
    mode_bits: 3
  },
  upload: { status: 'idle', progress: 0, message: '' },
  nozzle_temper: 219.96875,
  nozzle_target_temper: 220,
  bed_temper: 55,
  bed_target_temper: 55,
  chamber_temper: 5,
  mc_print_stage: '2',
  heatbreak_fan_speed: '15',
  cooling_fan_speed: '15',
  big_fan1_speed: '11',
  big_fan2_speed: '8',
  mc_percent: 72,
  mc_remaining_time: 15,
  ams_status: 768,
  ams_rfid_status: 2,
  hw_switch_state: 1,
  spd_mag: 100,
  spd_lvl: 2,
  print_error: 0,
  lifecycle: 'product',
  wifi_signal: '-30dBm',
  gcode_state: 'RUNNING',
  gcode_file_prepare_percent: '0',
  queue_number: 0,
  queue_total: 0,
  queue_est: 0,
  queue_sts: 0,
  project_id: '0',
  profile_id: '0',
  task_id: '0',
  subtask_id: '0',
  subtask_name: 'hook-30',
  gcode_file: 'hook-30.gcode.3mf',
  stg: [ 2, 14, 1 ],
  stg_cur: 0,
  print_type: 'local',
  home_flag: 23086383,
  mc_print_line_number: '66213',
  mc_print_sub_stage: 0,
  sdcard: true,
  force_upgrade: false,
  mess_production_state: 'active',
  layer_num: 54,
  total_layer_num: 109,
  s_obj: [],
  filam_bak: [],
  fan_gear: 9876223,
  nozzle_diameter: '0.4',
  nozzle_type: 'stainless_steel',
  cali_version: 0,
  hms: [],
  online: { ahb: false, rfid: false, version: 1900360653 },
  ams: {
    ams: [ [Object] ],
    ams_exist_bits: '1',
    tray_exist_bits: '7',
    tray_is_bbl_bits: '7',
    tray_tar: '0',
    tray_now: '0',
    tray_pre: '0',
    tray_read_done_bits: '7',
    tray_reading_bits: '0',
    version: 3,
    insert_flag: true,
    power_on_flag: false
  },
  vt_tray: {
    id: '254',
    tag_uid: '0000000000000000',
    tray_id_name: '',
    tray_info_idx: 'GFU01',
    tray_type: 'TPU',
    tray_sub_brands: '',
    tray_color: 'E05028FF',
    tray_weight: '0',
    tray_diameter: '0.00',
    tray_temp: '0',
    tray_time: '0',
    bed_temp_type: '0',
    bed_temp: '0',
    nozzle_temp_max: '250',
    nozzle_temp_min: '200',
    xcam_info: '000000000000000000000000',
    tray_uuid: '00000000000000000000000000000000',
    remain: 0,
    k: 0.019999999552965164,
    n: 1,
    cali_idx: -1
  },
  lights_report: [ { node: 'chamber_light', mode: 'on' } ],
  command: 'push_status',
  msg: 0,
  sequence_id: '1115'
}

I can't give you a printer:statusUpdate because it is currently running.

Also, I should note that the events you said never fired are only actually fired when there's a print-related event. A job is created when the printer starts it's work, finish is called when it finishes, and so on.

Yes for sure!

My printer is a P1S with the latest firmware.

It is currently printing, so:

// Example of message without a push all command:
New print message! {
  nozzle_temper: 220.125,
  bed_temper: 54.96875,
  wifi_signal: '-27dBm',
  mc_print_line_number: '69627',
  command: 'push_status',
  msg: 1,
  sequence_id: '1151'
}

// Example of message after a pushAll command:
New print message! {
  upgrade_state: {
    sequence_id: 0,
    progress: '',
    status: '',
    consistency_request: false,
    dis_state: 0,
    err_code: 0,
    force_upgrade: false,
    message: '0%, 0B/s',
    module: '',
    new_version_state: 0,
    cur_state_code: 1,
    new_ver_list: []
  },
  ipcam: {
    ipcam_dev: '1',
    ipcam_record: 'enable',
    timelapse: 'disable',
    resolution: '',
    tutk_server: 'disable',
    mode_bits: 3
  },
  upload: { status: 'idle', progress: 0, message: '' },
  nozzle_temper: 219.96875,
  nozzle_target_temper: 220,
  bed_temper: 55,
  bed_target_temper: 55,
  chamber_temper: 5,
  mc_print_stage: '2',
  heatbreak_fan_speed: '15',
  cooling_fan_speed: '15',
  big_fan1_speed: '11',
  big_fan2_speed: '8',
  mc_percent: 72,
  mc_remaining_time: 15,
  ams_status: 768,
  ams_rfid_status: 2,
  hw_switch_state: 1,
  spd_mag: 100,
  spd_lvl: 2,
  print_error: 0,
  lifecycle: 'product',
  wifi_signal: '-30dBm',
  gcode_state: 'RUNNING',
  gcode_file_prepare_percent: '0',
  queue_number: 0,
  queue_total: 0,
  queue_est: 0,
  queue_sts: 0,
  project_id: '0',
  profile_id: '0',
  task_id: '0',
  subtask_id: '0',
  subtask_name: 'hook-30',
  gcode_file: 'hook-30.gcode.3mf',
  stg: [ 2, 14, 1 ],
  stg_cur: 0,
  print_type: 'local',
  home_flag: 23086383,
  mc_print_line_number: '66213',
  mc_print_sub_stage: 0,
  sdcard: true,
  force_upgrade: false,
  mess_production_state: 'active',
  layer_num: 54,
  total_layer_num: 109,
  s_obj: [],
  filam_bak: [],
  fan_gear: 9876223,
  nozzle_diameter: '0.4',
  nozzle_type: 'stainless_steel',
  cali_version: 0,
  hms: [],
  online: { ahb: false, rfid: false, version: 1900360653 },
  ams: {
    ams: [ [Object] ],
    ams_exist_bits: '1',
    tray_exist_bits: '7',
    tray_is_bbl_bits: '7',
    tray_tar: '0',
    tray_now: '0',
    tray_pre: '0',
    tray_read_done_bits: '7',
    tray_reading_bits: '0',
    version: 3,
    insert_flag: true,
    power_on_flag: false
  },
  vt_tray: {
    id: '254',
    tag_uid: '0000000000000000',
    tray_id_name: '',
    tray_info_idx: 'GFU01',
    tray_type: 'TPU',
    tray_sub_brands: '',
    tray_color: 'E05028FF',
    tray_weight: '0',
    tray_diameter: '0.00',
    tray_temp: '0',
    tray_time: '0',
    bed_temp_type: '0',
    bed_temp: '0',
    nozzle_temp_max: '250',
    nozzle_temp_min: '200',
    xcam_info: '000000000000000000000000',
    tray_uuid: '00000000000000000000000000000000',
    remain: 0,
    k: 0.019999999552965164,
    n: 1,
    cali_idx: -1
  },
  lights_report: [ { node: 'chamber_light', mode: 'on' } ],
  command: 'push_status',
  msg: 0,
  sequence_id: '1115'
}

I can't give you a printer:statusUpdate because it is currently running.

Thanks! What I can conclude from this is that the code is fine. Please make sure that the client is running when the printer starts working, as the job events are not fired if the application didn't see a IDLE | FINISH | FAILED => RUNNING change before.

You can verify if this is the case by checking if client#currentJob is set to null.

Yes!

So I started a new small print with less traces but checking the client#currentJob value:

$ node .
Connecting...
New info message!
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 100% <- Was the previous print
The printer's status has changed from OFFLINE to FINISH!
Connected
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 100% SENT!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 100% SENT!
cilent.currentJob: null
New print message!
cilent.currentJob: null
New print message!
New info message!
cilent.currentJob: null
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 100%
The printer's status has changed from FINISH to PREPARE!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 0% SENT!
cilent.currentJob: null
New print message!
The printer's status has changed from PREPARE to RUNNING!
New print message!
cilent.currentJob: null
Sending pushAll command
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11% SENT!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
New print message!
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
New print message!
cilent.currentJob: null
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
New print message!
cilent.currentJob: null
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
cilent.currentJob: null
New print message!
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
cilent.currentJob: null
Sending pushAll command
New print message!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 11%
New print message!
cilent.currentJob: null
New print message!

And it is still null, that the reason why we haven't events about it.

Maybe it is time to give you my entire poc ;)

import { BambuClient, PushAllCommand } from 'bambu-node'; // https://www.npmjs.com/package/bambu-node

let _client = null;
let _pushAllTimer = null;
let _percent = 0;

function initializeClient() {

    disconnect();

    _client = new BambuClient(_credentials);

    _client.on('job:start', job => {

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:start` + job);
    });

    _client.on('job:pause', job => {

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:pause` + job);
    });

    _client.on('job:unpause', job => {

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:unpause` + job);
    });


    _client.on('job:update', job => {

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:update` + job);
    });

    _client.on('job:finish', job => {

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX job:finish` + job);
    });


    // more about the available events below
    _client.on('message', (topic, key, data) => {

        console.log(`New ${key} message!` /*, data*/);

        if (!data.mc_percent) {
            return;
        }

        const percent = parseInt(data.mc_percent);

        console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ${percent}%`);

        if (_percent === percent) {

            return;
        }

        sendProgress(percent);

        _percent = percent;

    })

    _client.on('printer:statusUpdate', (oldStatus, newStatus) => {
        
        console.log(`The printer's status has changed from ${oldStatus} to ${newStatus}!`);

        switch(newStatus) {

            case 'FINISH':

                sendProgress(100, '00FF00');

            break;

            case 'PREPARE':

                sendProgress(0);

            break;
        }

        newStatus === 'RUNNING' ? startPushAllCommandTimer() : stopPushAllCommandTimer();

    });

    _client.on('client:disconnect', () => {

        connect();

    });

}


// initializeClient();
connect();

async function disconnect() {

    stopPushAllCommandTimer();

    if (_client) {
        _client.disconnect(true).catch();
    }
}

async function connect() {

    if (_client?.connected) {
        return;
    }

    console.log('Connecting...');

    try {

        // A client cannot be reused after a connection failure :s
        initializeClient(); // Create a new instance of client with listeners

        await _client.connect();

        console.log('Connected');
    }
    catch(exception) {

        console.log('Failed to connect');

        setTimeout(connect, 10000);
    }
}

function stopPushAllCommandTimer() {

    clearInterval(_pushAllTimer);

    _pushAllTimer = null;

}

function startPushAllCommandTimer() {

    stopPushAllCommandTimer();

    // Required to get all parameters, including the 'mc_percent'
    _pushAllTimer = setInterval(async () => {

        console.log('Sending pushAll command');

        await _client.executeCommand(new PushAllCommand()).catch();

    }, 5000);

}

// Check client#currentJob periodically
setInterval(() => {
    
    console.log(`cilent.currentJob: ${_client?.currentJob}`);

}, 5000);


// Send the progress to the leds stripe
async function sendProgress(percent, color='FFFFFF') {

    await fetch(`${_ledsStripProgressEndpoint}?percent=${percent}&color=${color}`, { method: 'PUT', signal: AbortSignal.timeout(10000) })
        .catch(exception => {
            console.error('Send progress failed');
        });

    console.log(`XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ${percent}% SENT!`);
}

Hi! Any news?

Hi! Any news?

Hey!

Sorry for not getting back to you sooner. Unfortunately, I do not think I have the bandwidth to work on this.

Feel free to make a PR and if it's good quality I'll merge it for sure :)