Commit Graph
588 Commits
Author SHA1 Message Date
maziggy 77677cb07f Add clear-plate dot indicator on sidebar Printers icon
When a printer has pending queue items and is in FINISH/FAILED state
awaiting plate clearing, a yellow dot now appears on the Printers
sidebar icon. Uses useQueries to check statuses of printers with
pending queue items from the existing WebSocket-warmed React Query
cache — no new backend endpoints or additional polling needed.
2026-02-23 08:13:29 +01:00
maziggy d5c9f2b9ea Moved Spoolbuddy frontend to Bambuddy 2026-02-23 07:58:00 +01:00
maziggy 9381edf0cb Removed new Spoolbuddy frontend 2026-02-22 18:01:01 +01:00
maziggy 38af5df050 Add filament override for model-based queue assignment (#486)
When scheduling a print to "any printer" (model-based assignment),
users can now override the 3MF's original filament choices per slot.
The override dropdown shows compatible filaments loaded across all
printers of the selected model, filtered by type and nozzle (H2D).
The scheduler matches against overridden type/color instead of the
original 3MF values, preferring printers with exact color matches.

Backend:
- New GET /printers/available-filaments endpoint (aggregates loaded
  filaments across printers of a model, includes extruder_id for
  dual-nozzle nozzle-aware filtering)
- New filament_overrides JSON column on print_queue table
- Scheduler applies overrides before AMS mapping, clears tray_info_idx
  on overridden slots so color matching takes priority
- Printer selection prefers printers with most override color matches

Frontend:
- New FilamentOverride component with per-slot override dropdowns
- Type-filtered options (PLA slots only show PLA)
- Nozzle-aware filtering (H2D: only shows filaments on correct extruder)
- Integrated into PrintModal for model-based queue mode

Tests:
- 11 backend unit tests (color match counting, override application)
- 12 frontend tests (rendering, type/nozzle filtering, interactions)

i18n: All 6 locales (en, de, fr, it, ja, pt-BR)
2026-02-22 16:43:16 +01:00
maziggy aeca15b585 Add SpoolBuddy integration as optional filament management add-on
SpoolBuddy turns a Raspberry Pi 4B with a PN5180 NFC reader and
NAU7802 scale into a filament management station that integrates
with Bambuddy via REST API and WebSocket.

Backend: SpoolBuddyDevice model, 10 REST endpoints (/spoolbuddy/*),
6 WebSocket broadcast types, background offline-detection watchdog.

RPi daemon: asyncio service with concurrent NFC polling (300ms,
MIFARE Classic + Bambu HKDF key derivation), scale reading (10 SPS,
5-sample moving average, stability detection), and 10s heartbeat
with exponential backoff reconnect.

Frontend: kiosk-optimized 1024x600 UI at /spoolbuddy with Dashboard
(live weight + NFC spool detection), AMS Overview, Inventory,
Printers, and Settings pages. useSpoolBuddyState reducer hook
driven by WebSocket CustomEvents.

i18n: all 6 locales (en, de, fr, it, ja, pt-BR).
2026-02-22 15:27:54 +01:00
maziggy e450c3b728 Add visual spool grouping and optional brand/subtype in quick-add (#480)
Group toggle in inventory toolbar collapses identical unused/unassigned
spools into expandable rows/cards with count badges. Grouping key uses
material, subtype, brand, color, and label weight. Brand and subtype
fields now appear in quick-add mode as optional (no asterisk, no
validation). Quantity field restricted to quick-add mode only.
2026-02-22 10:05:03 +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 16fa84bc16 Add bulk spool addition and stock spools
Add Quick Add mode to the spool form for simplified entry (material +
color + weight only), and a quantity field (1-100) for creating multiple
identical spools at once. Stock spools (no slicer profile) are computed
rather than stored — any spool without slicer_filament shows an amber
"Stock" badge. New filter chips (All/Stock/Configured) on the inventory
page. Backend bulk endpoint creates N spools in one transaction.

Backend:
- SpoolBulkCreate schema with quantity validation (1-100)
- POST /inventory/spools/bulk endpoint

Frontend:
- Quick Add toggle in SpoolFormModal (create mode only)
- Quantity field in FilamentSection (both modes)
- bulkCreateSpools API method and bulkCreateMutation
- Stock column (hidden by default) and stock filter chips
- Inline error rendering moved into FilamentSection
- CellCtx extended with t() for translatable cell content

Tests:
- 13 backend tests (schema validation + endpoint logic)
- 10 frontend tests (validateForm quickAdd + UI behavior)

i18n: 6 locales (en, de, fr, it, ja, pt-BR)
Docs: CHANGELOG, README, website features, wiki inventory
2026-02-21 10:39:18 +01:00
maziggy 7d46b44561 Fix PCTG filament misidentified as PC (#478) 2026-02-21 10:01:26 +01:00
maziggy 839be41133 Fix support bundle leaking personal data (#473)
The log sanitizer only used regex patterns, missing arbitrary user-chosen
strings (printer names, usernames). Tasmota smart plug credentials were
logged verbatim in URLs by httpx.

- Make _sanitize_log_content() database-aware: query Printer names/serials,
  User usernames, and Bambu Cloud email for exact-string replacement
  (longest-first, skip <3 chars to prevent over-redaction)
- Fix serial regex leaking first 3 chars (remove capture group partial
  redaction), add case-insensitive flag
- Move Tasmota credentials from URL-embedded (http://user:pass@host) to
  httpx auth= parameter so they never appear in logs
- Add URL credentials regex as defense-in-depth for user:pass@ in logs
- Add 'username' and 'path' to settings sensitive_keys filter (catches
  smtp_username, slicer_binary_path in support-info.json)
2026-02-21 08:17:59 +01:00
maziggy 63efc2281d Sync 2026-02-20 19:10:36 +01:00
maziggy 1404242e0c Fix cost tracking issues 2026-02-20 16:31:16 +01:00
maziggy 40403ae584 Fix inventory date format and AMS-HT slot labels (#463)
Two bugs in the inventory page:

1. Dates always displayed as DD/MM/YY regardless of the user's date
   format setting. The page had a local formatDate() hardcoding en-GB
   locale. Replaced with the shared formatDateOnly() from utils/date.ts.

2. AMS-HT slot locations showed accented characters (e.g., "Á1") instead
   of proper labels (e.g., "HT-A"). The code used String.fromCharCode(65
   + ams_id), which produces garbled chars for AMS-HT IDs (128+). Now
   uses the shared formatSlotLabel() from utils/amsHelpers.ts.
2026-02-20 15:22:44 +01:00
maziggy e4e6e9f8c4 Decouples file uploads and print start commands so that they run in the background asynchronously. When a print is started, the print modal no longer waits for the print to upload and start. Instead, a new toast-based UI appears with the status of prints being dispatched. Prints being dispatched can be cancelled during upload. If cancelled, the partially-uploaded gcode is deleted from the printer automatically before the FTP connection is closed.
This allows users sending particularly large prints to slow printers such as the P1-series to start a print and then move onto another task in Bambuddy immediately (such as starting more prints on more printers). It also gives the user visibility into what's happening instead of a loading indicator appearing for an indefinite period of time.

The new toast-based UI uses websockets to update in real time. It will also appear for other users / instances of Bambuddy, not just the user who started the prints, allowing more transparency and handling cases where the user closes the page and then comes back wanting to know the status of the dispatching.

fix: review fixes for background dispatch PR #408

- Restore missing imports in main.py (inventory, print_log, virtual_printers, mqtt_smart_plug_service)
- Guard voidresp() for A1 printers to prevent hang after upload
- Don't fail upload on voidresp() error since data transfer already completed
- Add ams_mapping to register_expected_print calls for Spoolman usage tracking
- Fix cancel_job TOCTOU race by using single lock acquisition
- Fix batch counter reset TOCTOU by re-checking condition inside second lock
- Add backgroundDispatch translations to fr.ts and pt-BR.ts
- Remove dead upload_progress_callback definitions
- Skip redundant "Print queued" toast in reprint mode (dispatch toast handles it)
- 5 backend tests: cancel_job single-lock TOCTOU, batch reset re-check,
  job lifecycle
- 2 FTP regression tests: voidresp error handling (upload-loop fix),
  A1 model voidresp skip
- 1 frontend test: reprint toast suppression
- CHANGELOG: background dispatch feature + test coverage entries
- README: add background dispatch to Scheduling & Automation
- Website: add feature item to Automation section
- Wiki: add Background Print Dispatch section to print-queue.md
2026-02-20 09:26:50 +01:00
Ryan Ewen 31f4193ce0 Update frontend build 2026-02-20 08:52:55 +01:00
Ryan Ewen 060b253669 Update frontend build 2026-02-20 08:51:21 +01:00
maziggy d71e56c3e6 Minor layout improvements 2026-02-19 17:08:07 +01:00
maziggy 2f51bec40d feat: add "Include beta updates" setting to filter prerelease notifications
Users on the Docker `latest` tag were seeing update notifications for beta
releases (e.g. v0.2.1b) they couldn't install. The update checker now fetches
/releases instead of /releases/latest and filters by parse_version() prerelease
detection. A new toggle in Settings (default: off) lets users opt in to beta
notifications.
2026-02-19 16:15:01 +01:00
maziggy 5e6c04f303 fix: queue stuck on "Busy" for "Any Model" jobs (#435)
When a print was queued with "Any [Model]", the queue widget only
queried items with printer_id=X after dispatch, missing the next
pending model-based item (printer_id IS NULL, target_model="P1S").
The "Clear Plate & Start Next" button never appeared, leaving the
scheduler stuck reporting "Busy".

Add optional target_model query parameter to GET /queue/. When
combined with printer_id, uses OR logic to also return unassigned
items whose target_model matches. The frontend now passes the
printer's model through PrinterQueueWidget to this query.
2026-02-19 14:22:28 +01:00
maziggy 7b39026429 fix: resolve PFUS preset IDs causing slicer slot resets + fill level for new spools
BambuStudio actively resets AMS slots configured with unrecognized PFUS*
(user-local) preset IDs. Replace PFUS* with generic Bambu filament IDs
(e.g. GFL99 for PLA) in both the slot configure and inventory assignment
endpoints. When the slot already has a recognized cloud-synced preset for
the same material, reuse it to preserve K-profile calibration.

Also fix fill level bar not showing for brand new spools (weight_used=0)
by changing the condition from weight_used > 0 to weight_used != null.
2026-02-19 12:54:15 +01:00
maziggy d24e7cdf84 feat: multiple virtual printers with dedicated bind IPs + dual bind/detect ports (#445)
Multiple Virtual Printers:
- Each VP gets a dedicated bind IP with independent FTP, MQTT, SSDP, and Bind services
- New VirtualPrinter DB model, CRUD API (/api/virtual-printers), React UI
- VirtualPrinterList, VirtualPrinterCard, VirtualPrinterAddDialog components
- Per-instance TLS certificates (shared CA), 11 printer models, all 4 modes
- Auto-incremented serial suffixes, network interface override per VP

Dual Bind/Detect Ports (#445):
- Listen on both ports 3000 and 3002 for slicer bind/detect handshake
- Different BambuStudio/OrcaSlicer versions use different ports
- Applies to BindServer (server mode) and SlicerProxyManager (proxy mode)
- Updated Dockerfile, docker-compose.yml, firewall rules in wiki

Also:
- Rewrote VP test suite for new multi-instance architecture (75 tests)
- Rewritten "How it works" section with 3-step workflow explanation
- Updated all 5 locales (en, de, ja, fr, it)
- Updated wiki and website for multi-VP + dual ports
- New multi-VP screenshot
2026-02-19 11:22:28 +01:00
maziggy cb3363733e fix: remove dead PAUSED printer state checks (#447)
The printer only sends PAUSE via MQTT gcode_state, never PAUSED.
Removed all unreachable PAUSED comparisons from frontend (4 files)
and backend (2 files).
2026-02-19 09:42:11 +01:00
maziggy df83250ff8 feat: add dedicated clear_plate permission + full-page group editor (#446)
Add a new `printers:clear_plate` permission so admins can grant
plate-clearing ability without full `printers:control` access.
Existing groups with `printers:control` automatically receive the
new permission on startup via migration.

Replace the cramped permission modal in Settings with a dedicated
full-page group editor (`/groups/new`, `/groups/:id/edit`) featuring
search filtering, select all/clear all, category-level toggles,
and a responsive 2-column permission grid. Remove dead GroupsPage
code that was never routed.

- Backend: new Permission enum, category, defaults, endpoint guard, migration
- Frontend: GroupEditPage, updated routes, removed modal from SettingsPage
- Tests: 10 backend + 10 frontend tests for new permission and editor
- Docs: updated wiki (auth, printer-control, API), website, changelog
- i18n: added group editor keys to all 6 locale files (en/de/ja/fr/pt-BR/it)
2026-02-19 09:11:42 +01:00
maziggy 9e317bd775 Fix npm audit high-severity minimatch ReDoS (GHSA-3ppc-4f35-3m26)
by adding an npm override for minimatch@^10.2.1 in package.json.
2026-02-19 08:23:53 +01:00
maziggy ec751424b6 Fix AMS slot mapping for A1 Mini (#364)
Add color-matching fallback for slot-to-tray mapping on printers that
don't provide the MQTT mapping field (snow encoding) or reject request
topic subscription. Matches 3MF filament slot colors against AMS tray
colors to resolve the correct physical tray. Returns no match when
colors are ambiguous (duplicate tray colors) to safely fall through
to existing fallbacks.

Mapping priority chain: print_cmd → MQTT snow → queue → color match →
tray_now → slot_id-1.
2026-02-19 07:56:15 +01:00
MartinNYHC d119219e4d Merge branch '0.2.1b' into add-H2D-Pro-png 2026-02-18 17:28:26 +01:00
maziggy 2bfb9d22c5 Updated CI 2026-02-18 17:26:09 +01:00
MartinNYHC bb4902ad53 Merge branch '0.2.1b' into add-H2D-Pro-png 2026-02-18 17:16:12 +01:00
maziggy e5f3b259ea Sync 2026-02-18 17:00:33 +01:00
maziggy a903816b8a fix(slicer): token-based auth for Open in Slicer downloads (#433)
Slicer protocol handlers (bambustudio://, orcaslicer://) launch the
slicer app which fetches files via HTTP but cannot send auth headers.
The global auth middleware returned 401, causing "importing failed."

Add short-lived, single-use download tokens: the frontend fetches a
token via authenticated POST, then builds a /dl/{token}/{filename} URL
the slicer can access without auth headers. Tokens are validated
server-side (5-min expiry, single-use). Applies to archive files,
source 3MFs, and library files.

Also fix platform-specific URL formats to match slicer source code:
- macOS: bambustudioopen:// with encodeURIComponent
- Windows/Linux: bambustudio://open?file= (was wrongly using macOS scheme on Linux)
- OrcaSlicer: orcaslicer://open?file=
2026-02-18 16:37:35 +01:00
maziggy 11d9c98b55 fix(inventory): require brand and subtype in spool form (#417)
The spool add/edit modal allowed saving without brand or subtype,
causing incomplete tray_sub_brands when assigned to AMS slots.
BambuStudio then failed to recognize the filament profile.

Brand and Subtype are now mandatory with validation errors on submit.
2026-02-18 14:59:03 +01:00
tridev 5794f0ffe7 Add H2D Pro printer image and mapping 2026-02-18 14:08:11 +01:00
maziggy e0ee3a3e5c Housekeeping 2026-02-18 13:07:07 +01:00
maziggy 2fd90e9dce refactor(api): rename /filaments/ to /filament-catalog/ for clarity (#427)
The /filaments/ endpoint manages material type definitions (cost, temps,
density) but shares its name with the UI's "Filament" page which shows
the spool inventory (/inventory/spools/). This caused users to expect
the API to return their spool inventory.

Rename to /filament-catalog/ to make the distinction clear. No frontend
behavior change — these API methods are defined but unused in the UI.
2026-02-18 11:58:17 +01:00
maziggy 29e9593f6c AMS mapping fixes (7 bugs):
- backend/app/services/print_scheduler.py — hard nozzle filter + external spool extruder
- backend/app/api/routes/printers.py — ams_extruder_map race condition
- backend/app/utils/threemf_tools.py — group_id priority + dual-nozzle detection
- frontend/src/hooks/useFilamentMapping.ts — external spool extruder
- frontend/src/pages/PrintersPage.tsx — AMS-HT globalTrayId
- frontend/src/components/PrintModal/FilamentMapping.tsx — dropdown nozzle filter

Usage tracking fix:
- backend/app/services/bambu_mqtt.py — capture last valid progress/layer
- backend/app/services/usage_tracker.py — use captured values on cancel

UI fix:
- frontend/src/components/spool-form/ColorSection.tsx — color tooltip z-index

Tests:
- backend/tests/unit/test_scheduler_ams_mapping.py — 63 tests
- frontend/src/__tests__/hooks/useFilamentMapping.test.ts — 43 tests

Static assets:
- static/ — rebuilt frontend bundles
2026-02-18 09:17:50 +01:00
maziggy b02e9da943 WIP: multi virtual printer support 2026-02-18 06:37:27 +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 bc78f3f456 feat(timelapse): add upload and remove timelapse management (#406)
When the auto-scan attached the wrong timelapse (e.g. from a different
print), there was no way to fix it — the file couldn't be removed and
re-scanning was disabled once a timelapse was attached.
2026-02-17 08:49:32 +01:00
maziggy c82521efc9 fix(timelapse): support AVI format from P1-series printers (#405)
P1S/P1P save timelapses as AVI (MJPEG), but the scanner only looked
for MP4 files — so P1-series timelapses were never found or attached.
2026-02-17 08:29:47 +01:00
maziggy cf7c459099 fix(inventory): fix usage tracking for remapped AMS slots and slicer prints
Three fixes for inventory spool usage not updating after prints:

1. Store ams_mapping from print command (reprint, library print, queue)
   so the usage tracker maps 3MF slots to the actual physical trays
   the user selected, not the default slicer mapping.

2. Track last_loaded_tray on printer state — the last valid tray_now
   (0-253) seen during printing. On H2D printers, tray_now is always
   255 in AMS data; the real tray resolves via snow field ~44s after
   print start but reverts to "unloaded" at completion. The fallback
   chain is: tray_now_at_start > current tray_now > last_loaded_tray.

3. Use color similarity (Euclidean RGB distance, threshold 50) instead
   of exact hex match for auto-unlink fingerprint checks. RFID sensors
   report slightly different shades across reads (e.g. distance ~43.6
   for the same spool), causing false assignment unlinks after prints.
2026-02-16 15:46:32 +01:00
maziggy 6ba5d43a18 fix(queue): show library file name in printer queue widget (#364)
The "Next in queue" widget on the printer card only checked
archive_name/archive_id, so file-manager-sourced queue items
displayed "Archive #null". Now falls back to library_file_name
and library_file_id, matching the Queue page display logic.
2026-02-16 13:46:22 +01:00
maziggy 2f61973af2 Housekeeping 2026-02-16 10:57:39 +01:00
maziggy bc15d49a7e feat: add clear HMS errors button to dismiss stale print errors
Add a "Clear Errors" button to the HMS error modal that sends
clean_print_error via MQTT and locally clears hms_errors for
immediate UI feedback. Useful for dismissing stale print_error
values that persist after print cancellation or transient events.
2026-02-16 10:29:07 +01:00
maziggy 2a06ebd9f6 feat: add click-to-enlarge lightbox to skip objects modal (#396)
The small 208px image panel in the skip objects modal made it hard to
distinguish object markers when parts are close together. Clicking the
image now opens a fullscreen lightbox overlay (up to 600px) with the
same markers at a proportionally smaller size, solving the overlap
problem. Close via X button, Escape key, or backdrop click. Escape
cascades correctly — closes lightbox first, then the modal.
2026-02-16 09:50:59 +01:00
maziggy cf0287d5c8 fix: auto-select first plate for multi-plate files in print modal (#394)
When scheduling a multi-plate file from the file manager, the modal
allowed submission without selecting a plate. Now auto-selects the
first plate by default and enforces plate selection for both archive
and library files.
2026-02-16 09:11:08 +01:00
maziggy 8981c11bac fix: spool form scrollbar flicker in Edge (#364), delete SD card file after print (#374)
Add scrollbar-gutter: stable to spool form modal to prevent scrollbar
layout thrashing on Windows Edge at non-default zoom levels.

Delete uploaded .3mf from printer SD card root after print completion
to prevent phantom prints on power cycle (reported on P1S).
2026-02-16 08:21:09 +01:00
maziggy be569f7577 fix: prevent spool weight corruption, add AMS weight sync, fix extruder_id
Three bugs fixed and one recovery feature added:

1. AMS remain=0 zeroed all spools on printer power-off. The weight sync
   treated 0% remain as "fully consumed," setting weight_used to
   label_weight for every assigned spool. Fix: skip remain=0 in AMS
   weight sync — empty spools are tracked by the usage tracker.

2. Editing any spool field sent stale weight_used from the React Query
   cache back to the server, resetting usage-tracked weight. Fix: only
   include weight_used in PATCH when the user explicitly changes it.

3. K-profile auto-select crashed on dual-nozzle printers for non-BL
   spools with 'SpoolKProfile has no attribute extruder_id'. The model
   attribute is 'extruder', not 'extruder_id'.

4. New "Sync Weights from AMS" button in Settings > Filament Tracking
   (built-in inventory mode) to force-recover spool weights from live
   AMS sensor data. Bypasses the "only increase" guard for explicit
   user-initiated recovery.
2026-02-15 15:47:41 +01:00
maziggy 9babf5d345 fix: spool edit form overwrites usage-tracked weight
When editing a spool (changing note, color, etc.), the form always sent
weight_used back to the server. If the React Query cache held a stale
pre-print value, saving would silently reset remaining weight to full.

Track whether the user explicitly touched the weight field and only
include weight_used in the PATCH request when creating a new spool or
when the weight was intentionally changed.
2026-02-15 15:18:52 +01:00
maziggy 461fcf20e5 Sync 2026-02-15 14:54:03 +01:00
maziggy aa9db501ec Changed the inventory icon from Package (box) to Disc3 (spool/reel shape) 2026-02-15 13:24:00 +01:00