From c9c13b644033081849332ef5fd45ef28ef3813f3 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 18 Oct 2026 16:41:47 +0200 Subject: [PATCH] fix(archives): the A1/A1 Mini/P1/A2L built-in calibration is not archived as a print --- CHANGELOG.md | 1 + backend/app/main.py | 4 ++++ backend/app/utils/print_jobs.py | 3 +++ .../test_kill_switch_owned_prints.py | 17 +++++++++++++++++ .../tests/unit/test_internal_printer_jobs.py | 7 +++++++ 5 files changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 836ae79df..1c036ba8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -164,6 +164,7 @@ All notable changes to Bambuddy will be documented in this file. - **The frontend build no longer warns about `path` and `crypto` being externalized for the STEP previewer (#2976)** — `occt-import-js`, the Emscripten build behind STEP previews, requires both modules, but only inside its `ENVIRONMENT_IS_NODE` branches; in the browser it loads its `.wasm` from the URL the preview worker passes and draws randomness from `crypto.getRandomValues`. Vite still externalized both and printed two warnings on every build. `vite.config.ts` now drops exactly those two warnings for that one package through `build.rolldownOptions.onLog`, so an externalization anywhere else, or of any other module, still shows. ### Fixed +- **The A1, A1 Mini, P1 and A2L built-in calibration is not archived as a print** — These printers report their calibration run as a bare `auto_cali_for_user_param.gcode`, not under the system path other models use, so Bambuddy archived each run as a print (without a 3MF) and announced it. It is now recognised as the printer's own job. The printer kill switch still stops it on these models, since nothing tells it apart from a file given that name: switch the kill switch off to calibrate them. - **A notification channel limited to one printer gets that printer's events only** — A job added for **Any ** or not yet assigned, and any job waiting (for filament, a plate, a printer), were sent without a printer, so no printer filter applied: every channel limited to a printer received all of them, other printers' included. A job that waits on a printer now names it, and reaches that printer's channels; one with no printer yet goes to channels set to all printers, and a channel limited to one printer hears of it once it is assigned to or started there. A power alert from a plug linked to no printer likewise goes to channels set to all printers. Events about the whole setup (the queue finishing, stock, app messages) still reach every channel. - **Queue drying pre-dries in blocking mode** — With **Wait for drying to complete** on, a new drying cycle was refused on any printer with a pending job, scheduled ones included, so queue drying never dried anything ahead of a scheduled print. Only a job that could start now keeps a new cycle off; a printer whose job is scheduled for later is dried ahead of it, and the job still waits if the cycle has not finished by then. - **An "Any model" job waiting for a drying printer says so** — It read "Busy: X1C-01", as if that printer were printing; it now reads "Busy: X1C-01 (drying)", as a job for that printer does. diff --git a/backend/app/main.py b/backend/app/main.py index 688ca5b38..054afda0c 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -945,6 +945,10 @@ async def _is_bambuddy_authorized_print(printer_id: int, state: PrinterState, db # path, and any print can be given that name. if str(getattr(state, "gcode_file", "") or "").startswith("/usr/"): return True + # A1, A1 Mini, P1 and A2L report the same calibration as a bare file name, + # which nothing distinguishes from a user's file given that name, so it is + # not exempt: with the kill switch on, calibrate on those printers with the + # switch off. possible_keys = _build_status_print_keys(printer_id, state) diff --git a/backend/app/utils/print_jobs.py b/backend/app/utils/print_jobs.py index a434f4bab..6a5efed52 100644 --- a/backend/app/utils/print_jobs.py +++ b/backend/app/utils/print_jobs.py @@ -40,6 +40,9 @@ print-complete callbacks can share one answer. INTERNAL_JOB_NAMES = frozenset( { "auto_cali_for_user", + # A1, A1 Mini, P1 and A2L report their built-in calibration under this + # bare name, with no /usr/ path (support bundles, A1 firmware 01.08). + "auto_cali_for_user_param", "auto_pa_line_calib_mode", "pa_line_calib_mode", "pa_pattern_calib_mode", diff --git a/backend/tests/integration/test_kill_switch_owned_prints.py b/backend/tests/integration/test_kill_switch_owned_prints.py index 141ae7423..86bb454c5 100644 --- a/backend/tests/integration/test_kill_switch_owned_prints.py +++ b/backend/tests/integration/test_kill_switch_owned_prints.py @@ -231,3 +231,20 @@ async def test_a_print_resumed_after_a_restart_is_owned_only_with_bambuddys_mark ) assert (archive.id in main_module._owned_print_archive_ids) is owned + + +async def test_a_bare_calibration_name_is_not_exempt_from_the_kill_switch(): + """A1, A1 Mini, P1 and A2L report their built-in calibration as a bare + auto_cali_for_user_param.gcode, which a user's own file can be called too; + nothing on these printers tells the two apart (no project URL is ever + recorded there), so the kill switch does not exempt it (maintainer + decision 2026-10-18).""" + state = SimpleNamespace( + current_print=None, + subtask_name="auto_cali_for_user_param.gcode", + subtask_id="0", + gcode_file="auto_cali_for_user_param.gcode", + current_project_url=None, + ) + + assert await main_module._is_bambuddy_authorized_print(9, state, _db(None)) is False diff --git a/backend/tests/unit/test_internal_printer_jobs.py b/backend/tests/unit/test_internal_printer_jobs.py index 463d5e414..e818f1cd5 100644 --- a/backend/tests/unit/test_internal_printer_jobs.py +++ b/backend/tests/unit/test_internal_printer_jobs.py @@ -278,3 +278,10 @@ class TestPrintCompleteStaysQuiet: spawned = [c for c in mock_spawn.call_args_list if "notify-no-archive" in str(c)] assert spawned, "An unmatched real print must still notify" + + +def test_the_a1_built_in_calibration_reported_as_a_bare_name_is_internal(): + """A1/P1S report it without the /usr/ path (support bundle, A1 01.08).""" + from backend.app.utils.print_jobs import is_internal_printer_job + + assert is_internal_printer_job("auto_cali_for_user_param.gcode", "auto_cali_for_user_param.gcode")