Commit Graph
56 Commits
Author SHA1 Message Date
maziggy e77e10896f feat(ams): name the expected slot when a paused print hits an AMS runout (#2587)
The firmware's runout HMS text says "insert into the same AMS slot", which is
wrong under AMS Filament Backup: the firmware won't re-accept the depleted slot
and advances to the next compatible one. Bambuddy parsed print.ams.tray_now only
and dropped tray_tar/tray_pre, so the expected slot never reached the UI.

Capture tray_tar/tray_pre on PrinterState and, while paused, resolve them to
global tray IDs (expected_tray/previous_tray) on both the REST and WebSocket
status payloads via a shared resolver: single-AMS passthrough, multi-AMS
snow-mapping resolution, AMS-HT/external passthrough, and an honest null when the
slot can't be placed. The AMS graphic highlights the expected slot (amber) and
the ran-out slot (red); the HMS modal re-describes runout codes to name both,
falling back to "check the printer" when unresolved. Runout copy translated in
all 11 locales.

Reporter @Jostxxl confirmed tray_pre=1/tray_tar=2 during the pause (ran out in
Slot 2, printer expected Slot 3).
2026-07-18 13:06:01 +02:00
maziggy ca3f6e5ee0 fix(drying): P1 AMS drying is screen-only — stop offering it (#2533)
The reporter found what his P1S was doing, and it is in Bambu's P1 manual:
"P1S connected AMS drying functions may only be controlled from the P1S screen."
The firmware acks ams_filament_drying with result: success and then discards it,
which is why three commands on an idle printer left the AMS 2 Pro at dry_status 0.
No command can start a cycle on a P1, on any firmware, so don't offer one.

supports_drying() now excludes the P1 series outright, replacing the 01.08+ gate
carried since #292 — that version is when P1 firmware gained AMS 2 Pro support,
not remote drying, and it was never checked against a live P1. Both drying routes
refuse with a specific 400 instead of publishing a message the printer will drop;
queue and ambient auto-drying skip P1s via the same helper.

A new drying_screen_only flag keeps the control on the card, disabled, saying why
— a P1 owner needs to learn where to dry, not watch the button disappear. A cycle
started at the printer still shows with its countdown; only Stop goes away, since
a P1 ignores stop exactly as it ignores start.

Also corrects the wiki firmware matrix, which listed P1P/P1S as supported and
(separately) P2S/H2S/H2C as unsupported. 8 tests.
2026-07-11 09:33:52 +02:00
maziggy f6c6cfbad3 fix(ams): show "?" not "Empty" for non-RFID spools using tray_exist_bits (#2527)
A spool with no readable RFID was reported by the standard AMS with an empty
tray_type and state=9 — structurally identical to a truly-empty slot at the
tray level — so the AMS card rendered it "Empty" while Bambu Studio correctly
showed "?". The authoritative "a spool is physically here" signal is firmware's
AMS-level tray_exist_bits bitmask (what Studio uses), but Bambuddy inferred
emptiness from the per-tray state/tray_type. Confirmed from the reporter's
bundle: tray_exist_bits=f (all four slots present) with tray_is_bbl_bits=5
(only slots 0,2 Bambu) — the present-but-non-Bambu slots were the ones shown
Empty. Supersedes closed #1838.

apply_tray_exist_bits() already parses the bitmask to clear stale fields on
absent slots; it now also annotates each slot with an authoritative `exists`
bool, gated behind a new annotate_exists flag so only the printer-card path
sets it. The VP bridge leaves it off, so the `exists` key never reaches the
slicer wire format. `exists` flows through the AMSTray schema/serialization to
the frontend, where getEmptySlotKind() uses it: exists===true + no tray_type
-> "?" (present, unconfigured), exists===false -> "Empty", exists absent ->
the previous state=9/10 heuristic (AMS-HT and missing-bitmask paths unchanged).
H2D/X1C already reported present-unknown slots with a non-9 state and took the
"?" path; with the fix they reach it via `exists` and are unaffected.
2026-07-09 09:27:13 +02:00
maziggy d4ad41d850 fix(hms): action buttons actually reach the printer (#1830)
Three distinct bugs combined into one user-facing failure: clicking
Stop / Problem-solved-and-resume / Ignore-and-resume returned 200 OK
but the printer didn't act, modal stayed up, print stayed paused.
Verified by injecting candidate command shapes on device/<sn>/request
against a live H2D paused on a wrong-plate HMS (print_error=0x05008051).

(1) hms_resume / hms_stop dispatched the "err"-bearing shape that
BambuStudio doesn't actually send; Bambu firmware silently rejects it.
Both now send the plain shape ({"print":{"command":"<x>","param":"",
"sequence_id":"0"}}). PAUSE -> FAILED in 1.7s for stop, PAUSE -> RUNNING
in <2s for resume.

(2) IGNORE_RESUME mapped to idle_ignore, which is BambuStudio's
"dismiss a warning" command and only works for non-pause warnings.
hms_ignore now branches on state.state == "PAUSE": paused -> plain
resume; not-paused -> idle_ignore with the full-length err.

(3) 64-bit hms[]-array faults were truncated to a non-matching err.
short_code in _parse_status discarded 32 of the 64 identifier bits, so
the firmware didn't match it to the active fault. HMSError.full_code
now carries the canonical hex identifier (16 chars for hms[] faults,
8 chars for print_error faults). Catalog lookup tries 16-char first,
falls back to 8-char. HmsActionBody.print_error pattern relaxed to
^[0-9A-Fa-f]{8}([0-9A-Fa-f]{8})?$.

(4) execute_hms_action returned publish-success as success, masking
every silent-rejection bug above as 200 OK. Route now snapshots
(state.state, len(state.hms_errors)) before dispatch, awaits
HMS_ACTION_ACK_WAIT_SECONDS (default 2.5s, module-level so tests
override), and returns 502 with "Printer did not acknowledge HMS
action within 2.5s" if state didn't move.
2026-06-27 09:18:57 +02:00
Zelda 3ddf8d847e [Feature]: HMS Actions (#1743) 2026-06-26 14:40:25 +02:00
maziggy fd61812d01 feat(drying): show active-cycle filament + target temperature on the AMS drying badge
Bambu's per-tick AMS push carries only the dry_time countdown — the
  filament name and target temperature the user chose are never echoed on
  the wire. The AMS card had no source of truth for them and rendered the
  bare "Drying · 11h 35m left". The badge now shows
  "Drying · PETG @ 65°C · 11h 35m left", matching the cycle the user
  actually started.

  BambuMQTTClient caches {ams_id: {filament, temp}} on send_drying_command
  (mode=1), clears on mode=0 and on the dry_time falling edge to 0 — the
  same per-AMS edge detector that drives the smart-plug-after-drying
  callback. PrinterManager.get_drying_targets exposes it, the four
  printer_state_to_dict call sites thread it through, AMS schema gains
  dry_target_temp + dry_filament, and routes/printers.py builds the same
  fields into the manually-constructed AMSUnit response.

  When no cached target exists (drying started in a previous backend
  lifetime, or initiated outside Bambuddy), the badge falls back to the
  first loaded tray's tray_type + RFID-recommended drying_temp — the
  heuristic the popover already uses to seed defaults.

  i18n: printers.drying.targetSummary = "{{filament}} @ {{temp}}°C" in
  all 11 locales. Parity check 5356 leaves per locale.

  Note: a user reported the H2D's own physical display still labels the
  cycle by the loaded tray's filament (e.g. "PLA" instead of the
  Bambuddy-requested "PETG"). The wire payload is correct end-to-end —
  journalctl shows filament: "PETG" sent and result: success ACKed — and
  the badge in Bambuddy's own UI now reflects what we actually sent,
  independent of the firmware's display choice.
2026-06-25 13:27:32 +02:00
maziggy 8d6f701f1d feat(drying): continue drying while printing + gate rotate-spool when tray loaded (issue #1816)
Continue Auto-Drying while a print is running on capable hardware.
  New Settings > Print Queue > "Continue drying while printing" toggle
  (default OFF). Extends _check_auto_drying in print_scheduler.py to
  evaluate running printers when supports_drying_while_printing(model,
  firmware) returns true. Strict allowlist verified per Bambu wiki
  release notes for "Print While Drying" / "printing while filament is
  drying": H2D 01.03.00.00+, H2C/H2S/P2S/H2D Pro 01.02.00.00+, X2D/A2L
  01.01.00.00+, X1C 01.11.02.00+. P1*, A1, A1 Mini, X1 (non-C), X1E
  intentionally excluded. Mid-print drying temperature is capped at
  max(40, preset_temp - 5) to protect spools from heat damage inside the
  hot enclosure during a print, matching Bambu's own "lower drying
  temperature during printing" guidance.

  Rotate-spool toggle in the drying popover is now disabled when any tray
  in the targeted AMS has filament threaded into the feed tube
  (tray.state === 11). The whole AMS rotates as one mechanism, so a
  single loaded slot locks the entire unit. Previously the toggle was
  always clickable and the firmware rejected with dry_sf_reason=[3]
  (ConsumableAtAmsOutlet) after the click. The first cut keyed on the
  printer-level tray_now but missed the H2D's typical post-print state
  where tray_now resets to 255 while filament stays in the tube — the
  per-tray state field reports it correctly. Submission also clamps
  rotateTray off so a stale-true state from a previous AMS can't leak
  through.

  Backend: supports_drying_while_printing in printer_manager.py covers
  display names and internal SSDP/MQTT codes (O1D, O1E/O2D, O1C/O1C2,
  O1S, N6, BL-P001, N7, N9). New print_drying_enabled boolean in
  settings schema. Frontend: toggle on SettingsPage, gate + clamp on
  PrintersPage drying popover using existing amsData cache. i18n: 3 new
  keys x 11 locales, no English fallback. Tests: 7 cases on the gate
  matrix (TestSupportsDryingWhilePrinting), 4 cases on the scheduler
  mid-print path (TestMidPrintDrying), 9 cases on the rotate gate state
  transitions. Full backend pytest -n 30 green (4251/4251), ruff clean,
  frontend npm run build clean, i18n parity 5355 leaves per locale.
2026-06-25 12:47:26 +02:00
maziggy b1cb26f6ee feat(ams-backup): add status badge + toggle, fix prefer-lowest (#1766)
Two tightly-coupled deliverables in one drop -- a new AMS Filament Backup
  status/control surface, and the #1766 fix that depends on it.

  Added -- AMS Filament Backup status + control
  - Parse bit 18 of top-level print.cfg into PrinterState.ams_filament_backup
    on every push_status. Verified against OrcaSlicer source
    (DeviceManager.cpp:4961) and a live H2D ON/OFF capture. Tri-state
    (None = A1 family / pre-cfg push) preserves today's behaviour.
  - Hold-timer guard (3 s) prevents stale frames from flickering the badge
    back to the printer's old cfg after a user-initiated toggle.
  - POST /printers/{id}/ams-backup toggle, set_ams_filament_backup() client
    method calling _set_print_option("auto_switch_filament", enabled).
  - GET /printers/{id}/inventory-remain endpoint exposes the same map the
    dispatcher uses (internal and Spoolman modes both work uniformly).
  - Small icon badge in the printer card's "Filaments" section header
    (placement reads as printer-wide because the cfg bit is printer-wide,
    not per-AMS). Click to toggle, success toast.
  - 5 i18n keys x 11 locales for the badge UI.

  Fixed -- #1766: prefer_lowest didn't pick lowest, ignored backup state
  - Backend gate in _compute_ams_mapping_for_printer: coerce prefer_lowest
    to False when status.ams_filament_backup is False; log the skip.
  - New effectivePreferLowest(setting, backup) helper applied at every
    frontend sort entry point: single-printer PrintModal, multi-printer
    hook per-printer, PrinterSelector InlineMappingEditor, FilamentMapping
    standalone editor (the last had NO preferLowest awareness at all
    before this change).
  - New preferLowestSortKey(f, inventoryByTrayId) mirrors backend's two-tier
    key exactly, including the banding tie-break (regular AMS < AMS-HT <
    external) so the client-side pre-compute matches the dispatch-time pick.
    An earlier draft used a flat `amsId * 4 + trayId` priority which gave
    external slots (ams_id = -1) a NEGATIVE priority -- caught in code
    review before commit.
  - Settings -> Filament -> "Prefer lowest remaining filament" gets an
    explanatory note about the printer-side AMS Backup dependency, with
    i18n key in all 11 locales.
2026-06-20 12:07:20 +02:00
Ed 0f99b54d7e feat: Update printer card UI for structure and readability (#1661) 2026-06-18 11:46:07 +02:00
maziggy 9a432f0050 Restrict printer secrets to update-authority callers
GET /api/v1/printers/ and /api/v1/printers/{id} return access_code
  only when the caller holds PRINTERS_UPDATE. Adds PrinterResponseWithSecret
  as the elevated response shape; PrinterResponse no longer carries the
  field. Auth-disabled single-trust mode preserved.
2026-06-18 07:36:16 +02:00
maziggy 76e327f4a1 feat: connection diagnostic for "printer won't connect" triage
A triage review of the last 200 closed issues found ~1/3 were
  user-side setup errors — printer not in LAN developer mode, blocked
  ports, Docker bridge networking, wrong access code, cross-subnet —
  each costing a multi-round-trip support exchange.

  Add a Connection Diagnostic that runs those checks automatically:
  - backend/app/services/printer_diagnostic.py: TCP probes of MQTT
    8883 / FTPS 990 / RTSPS 322, LAN developer mode, Docker network
    mode, printer/host subnet match, MQTT credential class; each
    check returns pass/fail/warn/skip with a localized fix.
  - Routes: GET /printers/{id}/diagnostic (saved printer) and
    POST /printers/diagnostic (pre-save Add-Printer flow).
  - ConnectionDiagnostic.tsx: modal + shared checklist, surfaced from
    the printer card actions menu, an offline-printer quick button,
    the Add-Printer dialog, and a new System-page section.
  - The in-app bug reporter scans configured printers when the form
    opens and always shows the result inline — a healthy confirmation,
    or the detected problem and its fix.
  - config.yml troubleshooting link repointed to the rendered wiki
    page; bug_report.yml gains a diagnostic checkbox.

  Diagnostic strings translated across all 8 locales. Backend service
  unit tests (15) + frontend modal tests (3). Ruff clean, frontend
  build clean, i18n parity green.
2026-05-21 11:00:59 +02:00
maziggy 01787eb1e6 fix(printers): normalize serial numbers + diagnose connect-but-no-reports (#1465)
Reporter's H2C connected over MQTT+TLS but every status field stayed
  unknown. Root cause was layer-8: the MQTT broker is the printer, it
  authenticates on the access code and SUBACKs any topic string, so a
  wrong or mis-cased serial connects fine and silently receives nothing
  (the report topic device/<serial>/report is case-sensitive; Bambu
  serials are uppercase). Bambuddy stored and used the serial verbatim.

  - schemas/printer.py: field_validator strip()+upper()s serial_number on
    create, rejects blank-after-strip. The subscribed topic now always
    matches the printer's correctly-cased one.
  - bambu_mqtt.py: count report-topic messages per connection; when a
    stale reconnect fires with zero reports received, log a one-shot
    hint pointing at the serial number instead of looping silently.
2026-05-21 08:30:17 +02:00
maziggy abc8e97050 feat(camera): optional snapshot URL override for external cameras (#1177)
go2rtc and several IP cameras still emit a warm-up / black frame on every
  fresh MJPEG connection — even with the v0.2.4b2 warm-up-skip fix it
  slipped through intermittently for @nkm8's setup. His own bisect named
  the clean solution: go2rtc exposes /api/frame.jpeg as a dedicated
  single-frame endpoint that never returns the encoder's stale keyframe.

  Adds an optional external_camera_snapshot_url column on printers. When
  set, every single-frame capture path (snapshot endpoint, [SNAPSHOT]
  notification thumbnails, [PHOTO-BG] finish photo, layer timelapse,
  Obico ML, plate-detect / calibrate-plate) routes through _capture_snapshot
  on the override URL via plain HTTP GET, bypassing the warm-up dance.

  Live view stays on the configured stream URL — only single-frame
  captures use the override. Override is camera-type-agnostic. SSRF guard
  applies (existing _sanitize_camera_url allowlist). Empty string treated
  as unset.

  Settings UI: new "Snapshot URL (optional)" input + Test button under
  External Cameras, hidden for camera_type=snapshot since the live URL is
  already a single-frame source. en + de fully translated; 6 other locales
  seeded with English copy.

  5 backend tests pin the routing contract; 3 frontend tests pin the
  input + debounced PATCH. Documented in
  bambuddy-wiki/docs/features/camera.md with the go2rtc example.
2026-05-03 07:59:02 +02:00
maziggy b45ca2a662 feat(printer): support Filament Track Switch (FTS) accessory in print modal (#1162)
The FTS routes any AMS slot to either extruder, so AMS info reports
  bits 8-11 = 0xE (uninitialized) and ams_extruder_map ends up empty.
  The print modal's per-nozzle dropdown filter then hides every loaded
  slot, leaving the user with an empty filament dropdown.

  Detection: parse print.device.fila_switch from MQTT push_status into a
  new FilaSwitchState dataclass on PrinterState; surface it through the
  GET /printers/{id}/status response as a nullable FilaSwitchResponse.

  Frontend: useFilamentMapping and FilamentMapping skip the per-extruder
  filter when fila_switch.installed is true. Slots currently fed into a
  track display an [L]/[R] routing badge in the dropdown so the user
  can see where the FTS is currently routing them.

  Tests: 4 backend unit (TestFilamentTrackSwitchDetection), 2 backend
  integration (status route), 2 hook regression, 2 component regression.
2026-04-29 16:18:45 +02:00
maziggy fa1c46d9a5 feat(printers): show plate name on card for multi-plate active prints (#881)
When two printers were running different plates of the same multi-plate
  3MF, the Printers page cards displayed the same file name on both and
  there was no way to tell them apart. The Queue view already had this
  information by cross-referencing the archive's plate list; the card
  didn't have the linkage.

  Expose `current_archive_id` (resolved by matching the MQTT `subtask_id`
  against `PrintArchive.subtask_id` — the bridge introduced in #972 for
  restart-resume) and `current_plate_id` (parsed from `gcode_file` by a
  new shared `parse_plate_id` helper) on the status endpoint. The helper
  is also called from the WebSocket push path so plate transitions
  reflect within 100 ms instead of waiting 30 s for the next REST poll;
  the archive id itself stays REST-only since it's stable for the life
  of a print and shouldn't make the push path touch the DB.

  The card fetches plate metadata via the same `api.getArchivePlates()`
  call QueuePage uses — shared React Query cache keeps it cheap across
  polls — and renders the actual plate name (or a "Plate N" fallback)
  only when `is_multi_plate` is true. Single-plate prints stay clean.
  Falls back to the previous `plate_N.gcode` regex path when there's no
  archive linkage (e.g. prints started directly from the printer LCD).

  Tests cover the plate-id extraction across Bambu Studio path shapes
  (backend parse_plate_id, printer_state_to_dict wiring) and the label
  override precedence in formatPrintName (frontend).
2026-04-21 09:46:42 +02:00
maziggy 8af0966e68 feat(printers): airduct mode + status badges + force refresh on printer card
Surface four Home Assistant-style controls on the Printers page card:

  - SD Card badge in the top status row (green / red, icon-only).
  - Enclosure Door badge in the top status row (green / yellow, icon-only).
    Detection per printer family — X1/X1C/X1E read home_flag bit 23, all
    others read top-level `stat` (hex string) bit 23 — so X1 firmware that
    does not flip stat bit 23 stops false-triggering "open". WebSocket
    status-change dedup key now includes door_open so toggling the door
    alone publishes a push, no 30s REST-poll wait.
  - Airduct Mode badge beside the speed control (cooling / heating)
    for P2S/H2D/H2C/H2S; one-click dropdown calls the existing
    set_airduct MQTT command via a new POST /printers/{id}/airduct-mode
    route.
  - Force Refresh entry in the kebab menu — calls the existing
    /printers/{id}/refresh-status endpoint to request a pushall snapshot
    without forcing a reconnect.

  Tests: door-open parsing (X1 home_flag, non-X1 stat, ignore mismatched
  source, invalid hex) and airduct route (validation, not-connected,
  success, failure).
2026-04-13 12:50:17 +02:00
maziggy de7fff0be4 fix: persist plate-clear gate so Auto Off power cycles can't bypass the queue confirmation (#961)
With Auto Off enabled and another job queued, the smart plug cut power when a
  print finished and immediately re-powered the printer because the scheduler
  saw pending items. The printer booted fresh into IDLE and the next job
  auto-dispatched, bypassing the "Clear Plate & Start Next" confirmation.

  Root cause: the plate-clear gate lived only in PrinterManager._plate_cleared
  (in-memory set) and _is_printer_idle treated IDLE as unconditionally idle. On
  power cycle the in-memory flag was lost and the IDLE-on-boot state skipped
  the gate entirely.

  Fix:
  - Replace the in-memory flag with an awaiting_plate_clear column on the
    printers table, rehydrated into the PrinterManager at startup.
  - Set the flag in on_print_complete for completed/failed prints (not user
    cancellations); clear it on ack and on scheduler dispatch.
  - _is_printer_idle now short-circuits to not-idle whenever require_plate_clear
    is on and the flag is set, regardless of the currently reported state —
    so the gate holds through power cycles, Bambuddy restarts, and the printer
    booting back into IDLE.
  - /printers/{id}/clear-plate no longer requires the printer to report
    FINISH/FAILED; it accepts the ack whenever the flag is raised.
  - Frontend widgets (PrinterQueueWidget, Layout, BulkPrinterToolbar) gate on
    the flag rather than reported state.

  Tests: added regression tests for IDLE+awaiting=True (the #961 case) and
  full DB round-trip tests for the persistence layer.
2026-04-13 09:12:12 +02:00
maziggy c9efa4b8bb Fix stale AMS slot data and empty slot button visibility (#784)
Some printers (e.g. H2D) only send {id, state} in incremental MQTT
  tray updates — no tray_type, tray_color, or other fields. When
  filament is unloaded (state changes from 11 to 10), the old tray
  data persisted indefinitely because the merge logic only updates
  fields present in the incoming message.

  Backend:
  - Detect tray state != 11 without tray_type in incremental updates
    and clear stale tray data (bambu_mqtt.py)
  - Expose tray `state` field via REST API and WebSocket broadcasts
    (printer.py schema, printers.py route, printer_manager.py)
  - Include AMS tray content in WebSocket dedup key so load/unload
    transitions trigger broadcasts (main.py)

  Frontend:
  - Add `state` to AMSTray interface (client.ts)
  - Show configure/assign buttons for state=10 (spool present, not
    loaded) but hide for state=9 (truly empty) on AMS/HT slots
  - Hide fill level bar on empty slots

  Tests:
  - 5 new tests covering state-based clearing, preservation, reload,
    and idempotency
2026-03-24 16:03:34 +01:00
maziggy fe3c1983af Add camera image rotation option (#672)
Per-printer camera rotation (0°/90°/180°/270°) for cameras mounted
  in portrait or upside-down. CSS rotation for live views, Pillow
  rotation for notification snapshots. Setting visible in external
  camera config when enabled.
2026-03-17 13:59:01 +01:00
maziggy cffba4a911 Add queue auto-drying, configurable drying presets, and AMS PSU detection (#292)
Queue auto-drying: scheduler automatically starts drying on idle printers
  with scheduled queue prints when AMS humidity exceeds the configured
  threshold. Uses conservative parameters (lowest temp, longest duration)
  for mixed filaments. Drying stops when humidity drops below threshold
  (30-minute minimum prevents oscillation), when scheduled items are
  removed, or when the feature is disabled. Optional "block queue" mode
  delays the next print until drying completes.

  Configurable presets: temperature and duration per filament type,
  editable in Settings → Print Queue, used by both manual drying popover
  and queue auto-drying. Separate presets for AMS 2 Pro (n3f) and AMS-HT
  (n3s) reflecting different heating capabilities.

  PSU detection: drying button disabled with tooltip when dry_sf_reason
  indicates insufficient power. Parses drying status bits and dry_sf_reason
  from AMS info hex string via MQTT.

  Backend: print_scheduler.py (+316 lines), bambu_mqtt.py, printer_manager,
  schemas, settings route. Frontend: PrintersPage drying presets prop,
  SettingsPage drying config UI, i18n (7 locales). Tests: 27 new tests in
  test_scheduler_auto_drying.py covering conservative params, presets,
  state sync, stop logic, minimum drying time, and auto-stop regressions.
2026-03-14 14:21:36 +01:00
maziggy f21e502b8f Add remote AMS drying controls (#292)
Start, monitor, and stop drying sessions for AMS 2 Pro (n3f) and
  AMS-HT (n3s) directly from the Printers page. Flame icon in AMS card
  header opens a popover with filament-based temperature/duration presets
  from BambuStudio. Live countdown status bar shows time remaining.

  Backend: cache module_type from MQTT get_version, expose dry_time and
  module_type in both WebSocket and REST paths, add supports_drying()
  with firmware gating, add server-side guard on start_drying endpoint.

  Frontend: drying button, popover with filament select + temp/duration
  sliders, status bar, start/stop mutations. i18n for all 7 locales.

  Supported: X1/X1C (fw 01.09+), P1P/P1S (fw 01.08+), H2D (fw 01.02.30+),
  H2D Pro, X1E. Not supported: P2S, A1, A1 Mini, H2S, H2C.
2026-03-12 16:46:10 +01:00
maziggy 26a3b2172d Fix ethernet badge shown on WiFi-only printers (#585)
home_flag bit 18 is set on all printers regardless of hardware,
    causing the ethernet badge to appear on WiFi-only models (A1, P1P,
    etc.). The previous fix removed the feature entirely, so ethernet
    printers lost their badge too.

    Now only trusts bit 18 on models with an ethernet port (X1C, X1E,
    P1S, P2S, H2D, H2D Pro, H2C, H2S). WiFi-only models always show
    the WiFi signal badge.
2026-03-11 12:56:23 +01:00
maziggy 121b3d53df fix: remove incorrect ethernet badge on printer cards
home_flag bit 18 was incorrectly interpreted as "wired/ethernet
  connection", causing the ethernet badge to always show — even on
  printers without an ethernet port (e.g. A1, P1S). This hid the
  WiFi signal indicator entirely.

  Removed the wired_network field and ethernet badge UI. The WiFi
  signal badge now shows correctly whenever the printer reports
  signal strength.
2026-03-09 12:16:22 +01:00
Thomas RambachandMartinNYHC 7ba67012f6 Feature AMS Info Card (#570)
* AMS Labels addition to PrintersPage

* Added database reinitialization for schema migrations on database restore

* Bug fixes and AMS Label persistence updates

* Update database.py to resolve PR conflicts

* PR Comment Resolution

* Resolve conflicts in database.py for PR#570

* Updates to address PR#570 additional comments

* Optimize visibility handling for popup component

* Improve serial key handling in printers.py

Refactor serial key assignment to handle empty AMS serial gracefully.

* Implement error handling in serial number mapping

Add error handling for serial number mapping.

* Add migration to drop old ams_labels table

---------

Co-authored-by: MartinNYHC <mz@v8w.de>
2026-03-05 08:36:39 +01:00
maziggy 6388b0df9b Show ethernet indicator instead of WiFi signal for wired printers (#585)
Parse home_flag bit 18 (0x00040000) from MQTT to detect ethernet
  connections. When set, the printer card shows a green "Ethernet"
  badge with a cable icon instead of WiFi signal strength in dBm.
  The printer info modal also displays "Ethernet" instead of WiFi
  signal details.
2026-03-04 09:44:08 +01:00
maziggy 0e87c377e6 Add developer LAN mode detection and warning banner
Parse the MQTT "fun" field bit 0x20000000 to detect whether connected
printers have Developer LAN Mode enabled. Show a persistent orange
warning banner when any printer lacks it, since newer firmware silently
rejects MQTT write commands without developer mode.

- Parse fun field into developer_mode on PrinterState
- Add /printers/developer-mode-warnings lightweight polling endpoint
- Include developer_mode in printer status API and support bundle
- Orange banner with affected printer names and wiki link
- Translations for all 6 locales (en, de, fr, it, ja, pt-BR)
- 7 backend + 4 frontend tests
2026-02-21 12:06:16 +01:00
maziggy a0abeba394 fix(queue): persist clear plate state across page refresh (#410)
After clicking "Clear Plate & Start Next", refreshing the page showed
the button again because the frontend determined the state purely from
the printer's FINISH/FAILED status. The backend already tracked a
plate_cleared flag in memory but never exposed it to the frontend.

- Add plate_cleared field to PrinterStatus schema and API response
- Pass plateCleared prop to PrinterQueueWidget
- Skip clear plate UI when plate is already acknowledged
- Add 2 tests for plateCleared=true behavior
2026-02-17 12:14:47 +01:00
maziggy a37dfaf7fb feat: dual external spool support, AMS slot model filtering & pre-population
Backend:
- Add dual external spool support for H2D (vt_tray as list: Ext-L/Ext-R)
- Add cloud filament ID map endpoint (/cloud/filament-id-map)
- Fix RFID spool data erased by periodic AMS updates (skip tag matcher
  for RFID-tagged trays)
- Fix AMS slot config overwrites RFID spool state
- Fix K-profile selection corrupts existing profiles on X1C/P1S
- Resolve K-profiles filament name via cloud filament ID map
- Update print scheduler and usage tracker for dual external spools

Frontend:
- Add printer model filtering to ConfigureAmsSlotModal (cloud/local/builtin
  presets filtered by @BBL model suffix and compatible_printers)
- Add pre-population for configured slots (preset, color, K-profile)
- Add K-Profiles view with accurate filament name resolution
- Internationalize all ConfigureAmsSlotModal strings (en/de/fr/it/ja — 21 keys)
- Add 5 new ConfigureAmsSlotModal tests (model filtering, pre-selection,
  color pre-population, i18n)
- Update PrintersPage for dual external spool rendering

Docs:
- Update CHANGELOG, README, website features, and wiki AMS docs
2026-02-15 08:04:01 +01:00
maziggy 30b8887e79 H2C nozzle rack: show all 6 nozzles, translate types, add flow & filament (#300)
- Show all 6 nozzles including mounted: backend includes all nozzle_info
  entries (removes id >= 2 filter), frontend filters to non-empty
- Translate nozzle type codes to full names: HS→Hardened Steel, 00/01/05
  mapped to Stainless Steel/Hardened Steel/Tungsten Carbide
- Add flow type to hover card: HH→High Flow, HS→Standard
- Show filament material type in hover card (PLA, PETG etc.) from MQTT
  tray_type/filament_type field
- Add filament_type to NozzleRackSlot schema (backend + frontend)
- Add translations (en, de, ja, it): nozzleTungstenCarbide, nozzleFlow,
  nozzleHighFlow, nozzleStandardFlow
2026-02-10 11:34:12 +01:00
maziggy 6661bbf866 Add full nozzle rack metrics and per-slot hover cards for H2C printers
Capture 4 additional fields from MQTT nozzle data (max_temp, serial_number,
filament_color, filament_id) and surface them through REST/WebSocket APIs.
Add per-slot hover popover to the NozzleRackCard showing all metrics, filament
color backgrounds on slots, and i18n labels in all 4 locales.
2026-02-09 15:10:49 +01:00
maziggy 48174d3a08 Add H2C nozzle rack support — store and display 6-position tool-changer dock
The H2C printer has a tool-changer with a 6-nozzle rack, but
device.nozzle.info entries beyond index 1 were being dropped due to a
hardcoded 2-nozzle limit. This adds full nozzle rack storage, API
exposure, and a frontend card showing all dock positions.
2026-02-09 09:21:50 +01:00
maziggy 8944943981 Allow hostnames for printers in addition to IPv4 addresses (fixes #290)
Users with local DNS can now add printers using hostnames like
printer.local or my-printer.home.lan. Updated backend schema
validation, database column width, frontend form patterns/placeholders,
and i18n labels across all locales. Added integration tests for
hostname and FQDN creation plus invalid hostname rejection.
2026-02-07 16:16:43 +01:00
maziggy 9d6164ab1c Add USB camera support (V4L2)
- Add USB camera type to external camera service
- Auto-detect available V4L2 devices on Linux
- New API endpoint: GET /api/v1/printers/usb-cameras
- Use ffmpeg for USB camera capture and streaming
- Add "USB Camera (V4L2)" option in Settings UI
- Debounce camera URL input to avoid saving on every keystroke

Closes #143
2026-01-27 06:40:14 +01:00
maziggy 888891fdc6 Add build plate empty detection feature
Automatically detect if objects are on the build plate before printing
and pause the print immediately if detected.

Features:
- Per-printer toggle to enable/disable plate detection
- Multi-reference calibration: store up to 5 reference images per printer
  for different plate types (textured, smooth, high-temp, etc.)
- Automatic print pause when objects detected at print start
- Push notification and WebSocket alert when print is paused
- ROI (Region of Interest) calibration UI with sliders to adjust
  detection area
- Reference management: view thumbnails, add labels, delete references
- Works with both built-in and external cameras
- Uses buffered camera frames when stream is active (no blocking)
- Split button UI: main button opens modal, chevron toggles on/off
- Green visual indicator when plate detection is enabled
- Included in backup/restore
2026-01-26 13:04:29 +01:00
maziggy 691fb133b7 Add external network camera support for printers
Add support for external network cameras (MJPEG, RTSP, HTTP snapshot)
that replace a printer's built-in camera when configured.

Features:
- Live streaming on printers page (replaces built-in camera)
- Finish photo capture from external camera on print complete
- Layer-based timelapse: captures frame on each layer change,
  stitches to MP4 video on print completion

Backend changes:
- Add external_camera_url, external_camera_type, external_camera_enabled
  fields to Printer model with database migration
- New external_camera.py service: MJPEG/RTSP/snapshot frame capture,
  connection testing, MJPEG stream generation
- New layer_timelapse.py service: TimelapseSession management,
  layer-by-layer frame capture, ffmpeg video stitching
- Add on_layer_change callback to MQTT client and printer manager
- Update camera routes with external camera streaming and tracking
- Update print lifecycle hooks for timelapse start/stitch/cancel
- Add external camera fields to backup/restore
- Rate limiting for external camera streams (prevents browser freeze)

Frontend changes:
- Add external camera configuration UI in Settings > Camera
- Per-printer enable toggle, URL input, type selector, test button
- Toast notification on save

Closes #143
2026-01-24 09:58:45 +01:00
maziggy 6fb71de6a2 Add firmware update helper for LAN-only printers
Enables checking and uploading firmware updates for printers operating
  in LAN-only mode without Bambu Cloud connectivity.

  Features:
  - Automatic firmware version checking against Bambu Lab servers
  - Orange "Update" badge on printer cards when updates available
  - Firmware update modal with version info and release notes
  - One-click firmware upload to printer SD card via FTP
  - Real-time upload progress (actual bytes transferred)
  - Step-by-step instructions for triggering update from printer
  - Local firmware caching for faster re-uploads
  - Supports all Bambu Lab printer models

  New files:
  - backend/app/services/firmware_check.py - Version checking service
  - backend/app/services/firmware_update.py - Upload orchestration
  - backend/app/api/routes/firmware.py - REST API endpoints

  Also includes:
  - FTP upload progress callback support
  - 10-minute upload timeout protection
  - Firmware cache directory in .gitignore
2026-01-04 18:41:24 +01:00
maziggy 94c961dbaf Add real-time fan status monitoring to printer cards
- Controls section: New section before Filaments with fan status and printer controls
  - Part cooling fan (Fan icon, cyan)
  - Auxiliary fan (Wind icon, blue)
  - Chamber fan (AirVent icon, green)
  - Stop/Pause/Resume buttons moved from Temperature section
- Real-time updates: Fan speeds now broadcast via WebSocket
- Backend: Parse MQTT fan data (cooling_fan_speed, big_fan1_speed, big_fan2_speed)
  - Convert 0-15 scale to 0-100%
- Always-visible badges with dynamic coloring based on fan state
- Removed horizontal dividers before Controls and Filaments sections
2026-01-02 16:05:20 +01:00
maziggy 2dcd1a2ef2 - Enhanced skip objects feature with visual improvements and better UX
- Skip Objects Modal:
    - Object ID markers overlaid on preview image (grid layout)
    - Large prominent ID badges to match printer display
    - Info banner explaining to match IDs with printer screen
    - Layer warning when skip unavailable (layer <= 1)
    - Red badge on button showing skipped count
    - Preview image now shown when paused (not just running)
    - Close with ESC key or click outside modal
    - Full light/dark theme support

  - Backend:
    - Extract object positions from 3MF (include_positions=True)
    - API returns x/y coordinates for objects (when available)
    - Parse s_obj from printer MQTT for skipped state persistence
    - Cover URL available in PAUSE/PAUSED states

  - Tests:
    - Added test for objects with position data
2026-01-01 18:01:41 +01:00
maziggy ccdb084f23 - Refactored printer card's AMS section for better experience 2026-01-01 12:08:55 +01:00
maziggy e688c35719 sync 2025-12-07 14:29:54 +00:00
Martin Ziegler e44bc318ac Refactoring AMS module 2025-12-05 16:00:56 +01:00
Martin Ziegler 065f22a0e3 Replaced camera settings with wifi signal in top bar 2025-12-05 13:22:34 +01:00
Martin Ziegler 879a68fea0 Wiring up AMS module 2025-12-05 11:21:03 +01:00
Martin Ziegler 6a6ce3cc22 Wiring up AMS module 2025-12-04 17:59:54 +01:00
Martin Ziegler dd02acd97a Fixed bug in HMS module 2025-12-04 08:18:26 +01:00
Martin Ziegler 3917f0a3d1 Wired motion and extruder controls 2025-12-03 20:07:30 +01:00
Martin Ziegler 9e638e2d0f Wired air condition, speed and light buttons 2025-12-03 16:44:55 +01:00
Martin Ziegler b6af768482 Wiring up calibration modal 2025-12-03 13:44:03 +01:00
Martin Ziegler 071ca1cf4e Wiring up print options: first try. needs mofre reverse engineering for sync process 2025-12-03 11:36:08 +01:00
Martin Ziegler 50a8ed8ea9 Wired up printer parts button 2025-12-02 17:57:08 +01:00