Providing Macrofile: Optionally shutdown after printing

Hi *,

I have written some macro code that allows to shut down the printer after a print is completed. It can be dynamical set while printing. It’s rarely tested but what tested did work.
Follow the instructions at the end of the file.
If you want that the printer shutdowns after the print is complete run SET_COMPLETE_SHUTDOWN. If you want to unset it again run SET_COMPLETE_SHUTDOWN ENABLE=0

The response command is available if you add these lines to your config:

[respond]
default_type: echo
[gcode_macro SHUTDOWN]
variable_printer_active: 0
variable_wants_shutdown: 0
gcode:
  {action_call_remote_method("shutdown_machine")}

[gcode_macro CONDITIONAL_SHUTDOWN]
gcode:
  {% if printer["gcode_macro SHUTDOWN"].wants_shutdown == 1 %}
    SHOW_MSG MSG="Conditional shutdown wants shutdown, waiting for cooling down then i will shutdown"
    M140 S0 ; start heating the bed to what is set in Cura
    M104 S0 T0 ; start heating T0 to what is set in Cura
    TEMPERATURE_WAIT SENSOR="extruder" MAXIMUM=60
    SHUTDOWN
  {% endif %}

[gcode_macro GET_COMPLETE_SHUTDOWN]
gcode:
  {% set shutdown_wanted = printer["gcode_macro SHUTDOWN"].wants_shutdown %}
  SHOW_MSG MSG="Cmpl shutd: {shutdown_wanted}"

[gcode_macro SET_COMPLETE_SHUTDOWN]
gcode:
  {% set wants_shutdown = params.ENABLE|default(1)|int %}
  {% if wants_shutdown >= 1 %}
    {% set wants_shutdown = 1 %}
    SHOW_MSG MSG="print cmpl->shutdown"
  {% else %}
    SHOW_MSG MSG="print cmpl->stay on"
  {% endif %}
  SET_GCODE_VARIABLE MACRO=SHUTDOWN VARIABLE=wants_shutdown VALUE={wants_shutdown}

[gcode_macro SAFE_SHUTDOWN]
gcode:
  {% if printer_active == 0 %}
  SHOW_MSG MSG="Performing safe shutdown"
  SHUTDOWN
  {% else %}
  SHOW_MSG MSG="Job is active. No shutdown!"
  {% endif %}

[gcode_macro SHUTDOWN_HANDLER_START_PRINT]
gcode:
  SET_GCODE_VARIABLE MACRO=SHUTDOWN VARIABLE=printer_active VALUE=1

[gcode_macro SHUTDOWN_HANDLER_STOP_PRINT]
gcode:
  M400
  ; This needs to be at the end of your stop_gcode!!!!!!!
  SET_GCODE_VARIABLE MACRO=SHUTDOWN VARIABLE=printer_active VALUE=0
  CONDITIONAL_SHUTDOWN

[gcode_macro SHUTDOWN_HANDLER_CANCEL_PRINT]
gcode:
  SHUTDOWN_HANDLER_STOP_PRINT

[gcode_macro SHOW_MSG]
gcode:
  {% set MSG = params.MSG|default("No msg")|string %}
  M117 {MSG}
  RESPOND MSG={'"%s"' % MSG}

#TODO: AUTO SHUTDOWN POSSIBLY NOT WORK ON ERRORS!

#place the file shutdown.cfg into you config dir
#include this on the top of your printer.cfg
# [include shutdown.cfg]

#Call this in your start_gcode:
# SHUTDOWN_HANDLER_START_PRINT

#Call this AT THE END OF YOUR stop_gcode:
# SHUTDOWN_HANDLER_STOP_PRINT

#Call this in your cancel_gcode:
# SHUTDOWN_HANDLER_CANCEL_PRINT

#if non exists you can allways set one:
#[gcode_macro CANCEL_PRINT]
#rename_existing: CANCEL_PRINT_BASE
#gcode:
#  SHUTDOWN_HANDLER_CANCEL_PRINT
#  CANCEL_PRINT_BASE

Have fun!

3 Likes

Added a menu:
This allows to turn on/off the “shutdown on complete” function. Additionally, you can see the current status on the display.
Until now everything works fine.

Follow the instructions at the end of the file.

Remember: I have a presetup system. And there are possibly missing some user instructions/dependencies for setup this functionality. If your have trouble or find some missing instructions, please post here. I will add it to these instructions, so other users will not have this trouble!

[menu __main __adv_shutdown]
type: list
name: Adv.Shutdown


[menu __main __adv_shutdown]
type: list
name: "Adv. Shutdown"

[menu __main __adv_shutdown __status_complete_shutdown]
type: command
name: "Autoshutd. {'off' if (printer["gcode_macro SHUTDOWN"].wants_shutdown == 0) else 'on'}"
gcode:


[menu __main __adv_shutdown __activate_complete_shutdown]
type: command
enable: {printer["gcode_macro SHUTDOWN"].wants_shutdown == 0}
name: "Shutd on cmpl"
gcode:
  SET_COMPLETE_SHUTDOWN
  {menu.back(True,True)}

[menu __main __adv_shutdown __deactivate_complete_shutdown]
type: command
enable: {printer["gcode_macro SHUTDOWN"].wants_shutdown == 1}
name: "Awake on cmpl"
gcode:
  SET_COMPLETE_SHUTDOWN ENABLE=0
  {menu.back(True,True)}

# name this file shutdown_menu.cfg and add it to your printer config dir.
# add this line to your printer.cfg:
#
# [include shutdown_menu.cfg]
# 
# Have fun
1 Like

Hi I already have a macro to shutdown my printer, can I use your menu file to implement the option to my printer or I will need to copy the entire macro?

Hello @Luigisvc,

this shutdown method is special, as it can be triggered while printing and will shutdown the printer after the print is complete.

the easiest solution is probably just to use the macro as it is.

If you decide to use my method, remember also to modify your start and stop gcode in your slicer!

Of course, if you have basic programming skills, you can modify the menu configuration to support your shutdown method. But I think it would be less difficult to write you own menu macro file and use my code as example.

Regards
MasterQ

1 Like

Hi, I used your whole macros. Some issues:
I thought the menu was to appear on the Mainsail dashboard, but is only on the menu in the printer screen. Ok though, because on the macros menu I can set ENABLE 1 or 0
Other issue is that you call the command to shutdown the MCU not the Printer, I changed to turn off a smart plug and consequently the printer.
and I am receiving the RESPOND Command do not exist.
Anyways thanks for the macros, I had studied and learnt a little more about macros in klipper.

This should be possible, the macros are shown here, like this:
image

You should have an entry called SET_COMPLETE_SHUTDOWN this allow to control the shutdown over the Mainsail dashboard.
SET_COMPLETE_SHUTDOWN

The screenshot will differ from what you see. I currently have no access to one of the printer where this shutdown macro runs in plain.

It will shut down the host system.
https://moonraker.readthedocs.io/en/latest/configuration/
What’s happens after the host system has been should down depends on your setup. Many printer system does not support to turn off the supply of the printer. This is depending on hardware and can not be controlled by software only.

If you are using a raspberry there it is possible to toggle a pin if the rpi has been shut down. You need to change the config.txt and after this can be used disable a relay or something.
Add this line:

dtoverlay=gpio-poweroff,gpiopin=4,active_low

And build a hardware that disables you power supply on shutdown. Depending on your Hardware skills this can be an easy task or burn your house or even kill you if you get wiped. So be carefull with net supply!

Just for saying it, this not can be done by a klipper macro without touching the os and the hardware.

I sadly can not give more support on this :confused:

This command will be available if you enable this in your config. Add:

[respond]
default_type: echo

Sometimes it is not that hard, try google and you will find a solution :slight_smile:
However, thanks for the hint, I will add this to the description :slight_smile:

Klipper is a complex system, and it requires a lot of skills to control it, learn it and get benefits from it. The community can help with single questions, but you will need to invest time to learn how it works. I think the focus of klipper is not usability but expandability.

Best regards and happy learning :slight_smile:
MasterQ

1 Like

Hi, I’ve tested and is working as intended, many thanks for all your help!

Really nice!

Well played :slight_smile:

I know this is an old thread, but I just wanted to add that you can use
{% if printer.idle_timeout.state|upper != "PRINTING" %}
to check if a print job is active, instead of needing to update a print_active variable with the handler macros.
This would completely eliminate the need for the SHUTDOWN_HANDLERs and it would make the SAFE_SHUTDOWN work regardless of errors during a print.

1 Like

Never understood this old thread problem. The information is still value!
Thanks!

I think there was an issue about missing documentation when the state is “PRINTING”. E.g.: What is happens if the printer is paused: The required behavior is, that the printer not shutdown, at least this was what I decided. But I think the state is no longer “PRINTING” in pause. (I don’t remember exact behavior)
Just as example, but I had trouble with finding all edge cases to define an “Active Job” state.

In every case, good hint, next time this or something equal gets attention, I will give this some debugging as it could be much cooler to use the native klipper states.

If you going to change it, please share the results :slight_smile:

I will test the paused case for you, but just for reference the idle_timeout.state has 3 possible values: “Printing”, “Ready”, or “Idle”. (If I am remembering correctly)

I just tested it and it appears the state is not “Printing” when it is paused, so I will check what it is and see if I can update my if statement to include that

You are right, it uses the Ready status when it is paused, which is the same as when it is not printing. For now, I will just use
{% if printer.idle_timeout.state|upper == "IDLE" %}
but that is not the correct solution because it will only allow SAFE_SHUTDOWN to shutdown when it is after the idle_timeout has elapsed.
I use the mainsail.cfg [gcode_macro _CLIENT_VARIABLE] to have it update the idle_timeout when the print is paused so that it won’t idle out if Obico pauses a print over a long weekend.
If you leave the normal idle_timeout relatively short and have the paused idle_timeout set to 2 or 3 days like I do, then you could use {% if printer.idle_timeout.state|upper == "IDLE" %} in the SAFE_SHUTDOWN and get almost the same behavior.

Since the PAUSE is a klipper feature (rather than part of mainsail or fluid) it should be possible to update klipper to set this idle_timeout.state to “Paused” instead of “Ready”, which I’m sure would be helpful to a lot of people.

I added the following delayed_gcode to my shutdown.cfg so I can allow some time for the screen to display some “end of print” messages before it triggers the shutdown:

[delayed_gcode delayed_shutdown]
gcode:
    M400 ; wait for buffer to clear
    UPDATE_DELAYED_GCODE ID=delayed_shutdown DURATION=0
    CONDITIONAL_SHUTDOWN

Then I put UPDATE_DELAYED_GCODE ID=delayed_shutdown DURATION=60 at the end of my PRINT_END macro, so it waits 60 seconds before shutting down the printer

During that time, it is possible for the user to disable the Auto Shutdown if desired

I use the idle timeout in pause, for cooling down the hotend and turn off what else is not needed, after 5 or 10 minutes… So this is at least no generic option :frowning:

Yes, I agree :slight_smile: But that the API could change is another reason why people could implement their own solutions, for being independent of possible breaking changes. But ok, breaking changes is nothing that happens in klipper, it is more about no changes. I already waiting for long time for an experimental version that allows the project management to complete klippers feature set and have a lot of improvements that may have breaking changes ^^

I think this are good modifications. My shutdown handler at the end of the print, also has a problem, it will show the print as not completed in the stats. Maybe this could be repaired with your changes :slight_smile:
At least the M400 will not harm I will add it to my code too. I did not know M400 exists. But you should check where it is in your code. As the delayed_gcode can generally run pseudo async to your print. And if this will be called while print it will wait for no movement inside this call.
However, at SHUTDOWN_HANDLER_STOP_PRINT it would not harm, as this only is called at the end of the print… What I think, is what you are doing indirectly. And this is fine :slight_smile:

Thanks for testing :slight_smile:

The M400 just waits until previously buffered movement commands are executed, and the firmware only buffers a small number of such commands at a time. It will not interrupt indefinitely during a print; you can use it mid-print without it hanging. I use it often in my macros to avoid weird race conditions or out-of-sync non-movement actions

Understand!
Thanks

I have also seen people use {% if printer.idle_timeout.printing_time %} instead of the idle_timeout.state, because this value will only be non-zero during printing. It would assume this means it would return true during the paused state, but I have not tried it.
Then, you just need to either swap your if statement or add a not and it should behave as desired in the SAFE_SHUTDOWN

I have tested and confirmed that checking printing_time has the desired result both during printing and when paused. Here is my version of the SAFE_SHUTDOWN macro without any Handler macros:

[gcode_macro SAFE_SHUTDOWN]
gcode:
  {% if printer.idle_timeout.printing_time %}
    M117 "Job is active. No shutdown!"
  {% else %}
    M117 "Performing safe shutdown"
    SHUTDOWN
  {% endif %}
1 Like