bambuddy/backend/app/services
maziggy 14d0d14365 Power on a printer for jobs queued to a printer class (#2786)
Queue a print against a printer class -- "Any X1C", or a Slicer Pipeline whose
target type is Printer class -- with every printer of that class switched off,
and nothing happened. The job sat pending and no smart plug was touched, while
the same file pinned to a specific printer powered that printer on within one
queue check. The reporter's log holds both halves: thirteen minutes of the item
being polled as (133, None, ...) and passed over, then a PATCH onto printer 2,
then "Printer 2 offline, attempting to power on via smart plug(s)" on the very
next tick. Same item, same plug, same Auto On setting.

Powering a printer on had only ever been written inside `if item.printer_id:`.
The model-based branch below it walks the same queue but its matcher classes an
offline printer as a reason to keep waiting -- printers_offline collects the
*name*, for the waiting reason -- and nothing on that path ever looks at plugs.

_wake_printer_for_model adds it. The model query moves into _printers_for_model
so the matcher and the wake step answer "which printers can this job run on"
from one place: a job can only be woken onto a printer the matcher would also
have considered. Candidates that failed the cross-model gate are excluded --
switching a printer on for a file that can never legally run on it leaves the
job just as stuck, with the printer now drawing power.

Two things it does that the fixed-printer branch does not:

A printer awaiting plate-clear acknowledgment is skipped. Waking it buys
nothing; it boots into IDLE and is held by the gate. That is what the reporter's
log shows for the eighty minutes after their manual edit -- "printer 2 not
available -- connected=True, state=IDLE, awaiting_plate_clear=True" every thirty
seconds to the end of the capture. The flag is Bambuddy-side and persisted, so
it is readable while the printer is still off.

At most one printer per pass, because each wake blocks the queue loop for the
boot wait. Several queued jobs bring several printers up over the following
minutes rather than a whole shelf at once.

A failed power-on opens a 600s per-printer cool-off. Without it the walk is by
id, the pass spends its single attempt on the same broken printer every time,
and a healthy sibling two slots down is never reached -- one unreachable plug
starves its whole model, and costs a 180s boot timeout out of every 30s pass.
Entries expire on read: a printer inside its cool-off is skipped before the
power-on is reached, so a live entry can never be overwritten by a success.

The failed printer is deliberately NOT added to busy_printers. It is off, not
busy; labelling it busy would misdescribe it in every later item's waiting
reason and, because an all-busy reason is treated as needing no user action,
suppress the notification too.

Assignment is left to the next pass. AMS trays arrive with the first status push
after connect, so matching filament against a printer that booted five seconds
ago can reject the printer we just woke.

Finally, the waiting reason separates "Offline: X1C-1" from "Offline, no Auto On
smart plug: X1C-2". Those are different problems and only the second is one the
user has to go and fix -- it was also the first question the reporter had to be
asked, and the queue could not answer it.

Tests cover the wake, the plate-clear skip in both gate states, all-candidates-
awaiting-plate-clear waking nothing, one wake per pass, the starvation case over
two passes, cool-off expiry, no-Auto-On-plug being left alone and named, an
incompatible sliced model waking nothing, connected printers being left alone,
scheduled-for-later and manual-start jobs switching nothing on, and a regression
pin on the fixed-printer branch.
2026-08-08 09:35:43 +02:00
..
git_providers Accept Forgejo tokens scoped to a single repository (#2775) 2026-08-06 12:08:36 +02:00
virtual_printer Add CAP_NET_BIND_SERVICE everywhere the service is defined (#2549) 2026-08-04 08:33:30 +02:00
__init__.py
archive.py fix(vp): scope saved AMS mapping to the printer it was resolved against 2026-07-31 11:08:58 +03:00
archive_comparison.py
archive_purge.py
backup_path.py fix(backup): diagnose an unwritable backup path instead of quoting errno 30 (#2544) 2026-07-12 08:44:53 +02:00
bambu_cloud.py fix(cloud): complete the CSRF handshake on Bambu Cloud TOTP sign-in (#2696) 2026-07-28 15:05:28 +02:00
bambu_ftp.py Stop retrying a printer whose FTPS handshake fails, and name the cause (#2780) 2026-08-08 09:00:03 +02:00
bambu_mqtt.py Resolve a spool's material to a known drying preset before starting a cycle (#2774) 2026-08-06 08:19:01 +02:00
bug_report.py
camera.py fix(camera): rotate every still exactly once, and cover the sources that let ffmpeg write the file 2026-07-31 09:30:06 +02:00
camera_diagnose.py fix(camera): share one connection between concurrent one-shot captures (#2705) 2026-07-30 09:49:56 +02:00
camera_fanout.py fix(camera): stop /camera/stop from letting a second socket open (#2521) 2026-07-14 11:52:56 +02:00
camera_profiles.py
design_settings.py feat(slicer): keep the designer's print settings when re-slicing for another printer (#2622) 2026-07-28 14:49:50 +02:00
diagnostic_snapshot.py
discovery.py
email_service.py
export.py fix(projects): drop deleted prints from their project, and refresh the view (#2731) 2026-07-31 14:50:25 +02:00
external_camera.py Security hardening (maziggy/bambuddy-security #9) 2026-08-05 15:21:42 +02:00
failure_analysis.py fix(projects): drop deleted prints from their project, and refresh the view (#2731) 2026-07-31 14:50:25 +02:00
filament_deficit.py Check filament deficit for Library-backed queue items (#2779) 2026-08-06 12:21:50 +02:00
filament_requirements.py fix(queue): scope force-colour overrides to the plate the item prints (#2551) 2026-07-13 09:18:17 +02:00
firmware_check.py
firmware_update.py
ftp_profiles.py fix(ftp): cap H2C FTPS to TLS 1.2 so the sliced 3MF downloads reliably (#2582) 2026-07-18 09:33:22 +02:00
github_backup.py fix(backup): resolve a restored archive's owner by username, not by id (#2656) 2026-08-05 16:09:20 -04:00
github_restore.py fix(backup): report the categories a failed restore already committed (#2656) 2026-08-06 08:46:02 +02:00
ha_sensor_manager.py Show Home Assistant sensors on the printer card (#1148, #448) 2026-08-05 14:26:38 +02:00
hms_actions.py
hms_errors.py
homeassistant.py Show Home Assistant sensors on the printer card (#1148, #448) 2026-08-05 14:26:38 +02:00
label_renderer.py feat(labels): scannable QR on 203 dpi thermal printers + monochrome mode (#1870) 2026-07-07 10:31:19 +02:00
layer_timelapse.py Merge branch 'dev' into fix/camera-rotation-finish-photo-timelapse 2026-07-31 09:33:35 +02:00
ldap_service.py Survive a directory that defines no posixGroup class (#2769) 2026-08-05 11:14:57 +02:00
library_trash.py Add variant-group endpoints and cross-model queue creation (#671, #2570) 2026-08-03 11:10:11 +02:00
local_backup.py fix(backup): diagnose an unwritable backup path instead of quoting errno 30 (#2544) 2026-07-12 08:44:53 +02:00
location_service.py
log_health.py
log_reader.py Security hardening (maziggy/bambuddy-security #N) 2026-07-29 12:36:17 +02:00
long_lived_tokens.py fix(overlay): authenticate the OBS overlay with a token when login is enabled (#2613) 2026-07-20 13:04:35 +02:00
loop_watchdog.py
makerworld.py fix(cloud): only sign out on Bambu's real token-expiry 401, not any 401 2026-07-21 07:26:19 +02:00
mqtt_relay.py feat(printers): expose P2S/X2D accessory fans (left aux + exhaust) 2026-07-28 11:16:22 -05:00
mqtt_smart_plug.py
network_utils.py fix(vp): populate bind-interface list on macOS (route non-Linux to psutil) 2026-07-06 13:03:22 +02:00
notification_service.py Show Home Assistant sensors on the printer card (#1148, #448) 2026-08-05 14:26:38 +02:00
obico_actions.py
obico_detection.py feat(obico): authenticate to a token-protected ML API (#2733) 2026-07-31 13:39:17 +02:00
obico_smoothing.py
oidc_icon.py feat(auth): proxy OIDC provider icons server-side (#1333) (#1342) 2026-05-15 08:50:37 +02:00
opentag3d.py
orca_cloud.py feat(orca-cloud): pair via RFC 8628 device flow, replacing the paste-based sign-in 2026-07-17 08:16:35 +02:00
orca_profiles.py
pipeline_eligibility.py
plate_detection.py fix(camera): reuse the live view's frame for external-camera captures (#2707) 2026-07-30 13:05:15 +02:00
plate_thumbnail.py
plug_energy_history.py fix(smart-plugs): read a REST plug's lifetime counter, and derive Today/Yesterday from it (issue #2539) 2026-07-11 14:22:31 +02:00
preset_resolver.py
print_batch.py Add batch orders with a quantity per plate (#342) 2026-08-04 11:11:36 +02:00
print_dispatch_context.py fix(camera): take the finish photo when the print ends, not when its last layer starts (#2547) 2026-07-31 12:55:05 +02:00
print_log.py Add batch orders with a quantity per plate (#342) 2026-08-04 11:11:36 +02:00
print_scheduler.py Power on a printer for jobs queued to a printer class (#2786) 2026-08-08 09:35:43 +02:00
printer_diagnostic.py Stop retrying a printer whose FTPS handshake fails, and name the cause (#2780) 2026-08-08 09:00:03 +02:00
printer_manager.py Stop the drying badge inventing a temperature on a uniform AMS (#2759) 2026-08-05 08:12:42 +02:00
rest_smart_plug.py Security hardening (maziggy/bambuddy-security #8) 2026-07-31 08:18:05 +02:00
slice_dispatch.py Security fix (security-issue #6) 2026-07-24 09:15:03 +02:00
slice_preview.py fix(slicer): bound slices by silence, not by total slicing time (#2730) 2026-07-31 15:14:01 +02:00
slicer_3mf_convert.py fix(slice): report a finished slice once, not once per queued poll 2026-07-30 17:14:07 +02:00
slicer_api.py Add auto-orient and auto-arrange to server-side slicing (#2548) 2026-08-03 14:10:02 +02:00
slicer_filament_resolver.py
slot_preset_writer.py
smart_plug_manager.py fix(smart-plugs): don't blank printer state when an accessory plug switches off (#2629) 2026-07-22 09:57:27 +02:00
sponsor_prompt.py
spool_assignment_notifications.py fix(a2l): normalise AMS Lite unit 16->6 so slots load and deduct (#a2l-am-unit-16) 2026-07-21 10:21:11 +02:00
spool_csv.py
spool_tag_matcher.py
spoolbuddy_ssh.py
spoolman.py
spoolman_tracking.py Read the printer's own slot mapping when Spoolman has none (#2768) 2026-08-05 11:04:59 +02:00
stl_thumbnail.py
tasmota.py Security hardening (maziggy/bambuddy-security #8) 2026-07-31 08:18:05 +02:00
timelapse_processor.py
usage_tracker.py fix(inventory): record the mapped spool's material, not the sliced type (#2563) 2026-07-15 07:29:16 +02:00