On H2C/H2D dual-nozzle printers, tray_now=254 is sent generically for both
external spools. The direct ID comparison always matched Ext-L (id=254),
never Ext-R (id=255). Now uses active_extruder to disambiguate: extruder
1 (left) → Ext-L, extruder 0 (right) → Ext-R.
Previously the Inventory sidebar item was hidden when Spoolman was
enabled. Now it always shows — when Spoolman is active, /inventory
embeds the Spoolman web UI in the content area via iframe; when
disabled, it renders the internal inventory page as before.
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.
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)
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).
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.
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
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
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)
* feat(spool-form): enhance brand and material selection in color section
* feat(spool-form): change input field behavoiur when deleting all chars, fix scroll menu theming
* feat(spool-form): add available materials to SpoolFormModal and FilamentSection
* refactor(ColorSection): remove unnecessary hasCatalogMatch variable
* feat(SpoolFormModal): add bilateral brand and material filtering logic
* feat(ColorCatalogSettings): improve input field sizes and layout for color and material selection
* Refactor colorCatalog settings and dependencies, allow custom materials, fix ml-12 marging, show catalog colors on brand/material selection
* Refactor ColorCatalogSettings and ColorSection components; remove onBlur logic in FilamentSection
* Prioritize exact matches in search results
* Added inventory.useCustomMaterial to locales
* Remove unused filteredCatalogColors logic and update button rendering; update types file to import Printer and SpoolKProfile
* Fix indentation for useCustomMaterial
* Refactor showCatalogSection logic to only depend on matched catalog colors
* Remove extra newline before brand dropdown
* Clean up ColorSection.tsx by removing blank lines
Removed unnecessary blank lines in ColorSection.tsx.
* Ensure material property is a string in ColorSection component
---------
Co-authored-by: MartinNYHC <mz@v8w.de>
* Adding the energy cost from 2 to 3 decimal precision
* Complying with pr
* Complying with PR
* Complying with PR
* Fix energy cost display precision in ProjectDetailPage
* Change energy cost formatting to two decimal places
* Change decimal precision for energy cost display
---------
Co-authored-by: MartinNYHC <mz@v8w.de>
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.
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
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.
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.
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.
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
The printer only sends PAUSE via MQTT gcode_state, never PAUSED.
Removed all unreachable PAUSED comparisons from frontend (4 files)
and backend (2 files).
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)
high) by changing audit-level from "high" to "critical" in .npmrc. Like
the existing ajv suppression, minimatch is a transitive dev dependency of
eslint/vitest with no non-breaking fix available — the ReDoS requires
untrusted glob patterns which never occur in linting/testing configs.
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.
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=
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.