Macros¶
Macros are small, reusable automations triggered by specific moments in a print's lifecycle. BamDude supports two kinds of macros: classic G-code macros (the printer runs custom G-code) and MQTT-action macros (BamDude tells the printer to do something via its MQTT control channel — toggle the chamber light, etc.).
Use them for plate-swap mechanisms, chamber lighting, enclosure control, or any other per-event automation that doesn't belong in your slicer's start/end G-code.
Action Types¶
A macro's action_type decides what happens when its event fires.
Sends a G-code snippet over MQTT (gcode_line), wrapped in
M1002 gcode_claim_action markers. The printer ACKs the snippet and
BamDude waits for the printer's stg_cur to return to idle before
reporting the macro complete.
Use cases
- Bed levelling, custom park positions, vibration calibration
- Plate swap-mode operations (eject + home + prep next plate)
- Anything you would put in a slicer's start/end G-code but want orchestrated by BamDude instead
Limitations
- G-code on
print_startedfights the print itself — the firmware is already running its own start sequence. Don't do this unless you know exactly why. - Long G-code on
swap_mode_change_tableis fine; it runs while the printer is idle between plates.
Invokes a named MQTT command from BamDude's catalog instead of sending G-code. Fire-and-forget — the printer doesn't ACK, so BamDude doesn't wait for completion.
Currently shipping commands
| ID | Value | Effect |
|---|---|---|
chamber_light |
on / off |
Turn the chamber light on or off |
print_speed |
1–4 |
Set the speed level: Silent, Standard, Sport, Ludicrous |
An action names the command; the value is a separate setting that appears in the editor once you pick the action.
Use cases
- Auto-light-on at print start, auto-light-off at print finish
- Drop to Silent speed part-way through an overnight print
- External automations that don't need the G-code pipeline
Chamber light macros made before 0.5.3
The light used to be two separate commands, chamber_light_on and
chamber_light_off. They are now one command with an On/Off value,
and existing macros are converted the first time the new version
starts — they keep doing exactly what they did.
Print speed only applies while printing
The printer accepts a speed change during a print. Bambu Studio
refuses one when the machine is idle, and so does BamDude's own
printer card, so a speed macro is worth binding to
layer_reached or print_started, not to print_finished.
The catalog lives in core/mqtt_macro_actions.py and is exposed
to the frontend via /macros/meta. New named commands are added
there as use cases come up.
Compatibility
Pre-0.4.0 G-code macros keep working unchanged. New macros default to G-code unless you explicitly pick MQTT-action in the editor.
Events¶
Each macro is bound to exactly one event. Action-type support is not symmetric across events — see the table below.
| Event | When It Fires | Allowed action types |
|---|---|---|
print_started |
When gcode_state transitions to RUNNING (via on_print_start) |
mqtt_action only. A G-code macro bound to this event is silently skipped at runtime (macro_trigger.py logs "Skipping gcode macro — only mqtt_action macros are supported for event-driven triggers"). G-code mid-print would fight the print itself. |
print_finished |
When the print reaches a terminal state (FINISH, FAILED, or IDLE-aborted), via on_print_complete |
mqtt_action only, same reason — gcode here isn't wired yet (the trigger is shared with print_started). |
swap_mode_start |
Before the print starts, when dispatch knows it's running with a swap-mode profile | G-code only in practice — the swap-mode dispatcher executes the macro synchronously and waits for stg_cur to return to idle. Pick G-code that prepares the swap mechanism. |
swap_mode_change_table |
After the print completes, before the queue picks up the next item | G-code only, same reason. The macro physically swaps plates. See Swap Mode. |
layer_reached |
While the print runs, when the layer counter crosses the Layer you set | mqtt_action only — refused at save time for G-code, for the same reason as print_started. |
Don't bind G-code to print_started / print_finished
The macro UI lets you save the combination, but it won't run. If you want chamber lights or external relays toggled on print events, use MQTT-action macros. G-code on these events is a planned future extension, not a current feature.
layer_reached in detail¶
One layer per macro — for two changes at two layers, make two macros.
The trigger is a crossing, not an exact match: MQTT reports do get dropped, so a print that jumps from layer 48 to 52 has still passed 50 and the macro for layer 50 still runs.
It runs once per print, and that survives more than the obvious cases:
- a printer that drops its MQTT connection mid-print gets a fresh client whose layer counter starts at 0 — the replay past your layer does not fire the macro again;
- neither does a BamDude restart mid-print, because what already ran is recorded on the print's archive entry as well as in memory;
- a cancelled print followed by a new one does start fresh, which is what you want.
It also waits for the print to genuinely start. Some models — the P1S among them — tick the layer counter during the calibration they run beforehand, up to half an hour before the first layer goes down. Those ticks don't count.
print_finished covers all terminal states
Whether the print finished cleanly, failed, or was aborted from the
printer's screen, print_finished fires once. Use it when you
want behaviour to apply regardless of outcome (turn the light off,
notify external systems, etc.).
Choosing macros per print¶
Macros are opt-in per print. The print dialog and the queue dialog list the macros that apply to the target printer, and only the ones you tick run for that job.
- The list is filtered to the printer's model, to enabled macros, and to a matching swap profile. If nothing applies, the panel does not appear.
- Your choice is remembered per printer model, exactly as the swap-macro panel's is. Open the dialog for another printer of the same model and the same ticks come back.
- What is remembered is what you turned off, so a macro you create later arrives ticked rather than quietly missing.
- Editing a queued job shows what that job stored. A macro created after it was queued starts unticked there — tick it if you want it.
A print started outside BamDude runs no macros
There is no dialog on a print launched from the printer's screen, sent straight from a slicer, started from Telegram, or received by the virtual printer — so nothing was ticked, and nothing runs. The same is true of jobs that were already sitting in a queue before this version. If you depend on a macro for those prints, queue them through BamDude and tick it.
Filter Fields¶
Both action types share the same filter set. A macro fires only when every filter matches.
| Field | Behaviour |
|---|---|
enabled |
Global toggle. Disabled macros are skipped without further evaluation. |
printer_models |
JSON array of model codes (e.g. ["A1 Mini", "X1 Carbon"]) or ["*"] for all models. |
swap_mode_only |
Fire only when the printer has swap mode enabled. Hidden in the UI for non-swap events. |
swap_profile |
Fire only when the printer's selected swap profile matches this value (a1mini_kit, a1mini_stl, or jobox-a1 — see Swap Mode). Lets multiple swap-mode G-code variants coexist. |
delay_seconds |
0–3600. Defer the action by N seconds after the trigger. 0 = immediate. |
Why delay_seconds matters¶
Some events fire just before the printer is in the visible state you'd
expect. Chamber-light-on at print_started looks premature on some
models because the firmware-side start sequence (heat-up, purge) hasn't
finished. A 10–30 s delay avoids flicker without you having to wire
up your own state-machine.
Editing Macros¶
- Go to Settings → Macros.
- Pick or create a macro. Choose the Action Type (G-code or MQTT-action).
- Pick the Event. For
layer_reached, a Layer field appears — set the layer the macro should fire on. - Set filter fields (printer models, swap mode, profile, delay).
- For G-code macros, type the snippet into the editor. For MQTT-action macros, pick the command from the dropdown — and if it takes a value, a second dropdown appears beneath it.
- Save.
Test G-code on the printer first
Bad G-code can damage your printer. Run new snippets manually before binding them to an event.
How Macros Run¶
Macros run as fire-and-forget asyncio tasks. A slow G-code send, a long delay, or even a network blip never blocks the surrounding orchestration — on_print_start returns immediately and the macro fires in the background.
For G-code macros, the dispatcher additionally waits for the on_macro_complete callback (printer's stg_cur returning to idle) before proceeding to the next G-code macro in the same event chain. MQTT-action macros are fully fire-and-forget; nothing waits on them.
Replacing the Old auto_light_off Flag¶
Pre-0.4.0 BamDude had an auto_light_off boolean on each printer. It was dropped in migration m021 because macros do the same job better — with delay control, on/off symmetry, per-model targeting, and per-swap-profile filters.
To restore the old behaviour:
| Field | Value |
|---|---|
| Action type | MQTT-action |
| Event | print_finished |
| Command | chamber_light |
| Value | off |
delay_seconds |
0 |
| Field | Value |
|---|---|
| Action type | MQTT-action |
| Event | print_started |
| Command | chamber_light |
| Value | on |
delay_seconds |
10 (let heat-up finish first) |
Pair both for full automatic-lighting cycles. Add swap_mode_only=true if you only want lights to cycle for swap-mode runs.
Swap Mode Integration¶
Swap-mode prints rely on G-code macros bound to swap_mode_start and swap_mode_change_table. See Swap Mode for the full lifecycle, restart-resilient event tracking, and dispatch ordering on multi-printer farms.
Macros vs G-code Injection¶
Macros and the G-code Injection feature look similar but solve different problems:
| Aspect | Macro | G-code injection |
|---|---|---|
| Where it runs | Server-side: BamDude sends gcode_line over MQTT at lifecycle events. |
Embedded in the file gcode: spliced into plate_*.gcode before upload, runs at the exact point in the print sequence the slicer would normally insert end-gcode. |
| Trigger | Lifecycle event (print_started, print_finished, swap mode, manual). |
Per-job toggle on the print itself. |
| Survives server outage | No — server has to be online to send. | Yes — once the file is on the printer, BamDude can disappear. |
| Best for | Lights, plugs, chamber heaters, status pings, swap-mode plate changes. | Park-to-back-left, purge-tower clean cuts, mid-print pauses, post-cool-down park positions that must run before the printer's own end-gcode resets state. |
Use macros first — they're simpler. Drop down to G-code injection when you need a snippet that has to fire inside the printer's own end-of-print sequence and survive even if BamDude crashed mid-print.
Tips¶
Pair print_started with print_finished
Use one MQTT-action macro on each event for clean symmetric automation (lights, fans, external relays).
Use delay_seconds for chamber-light-on
A 10–30 s delay hides the heat-up phase from the chamber camera and avoids the "premature lights-on" look on H2/X1.
Combine with smart plugs
For full plate-eject + cooldown + power-off cycles, pair end-event macros with Smart Plugs auto power-off.