Commit Graph
187 Commits
Author SHA1 Message Date
maziggy becc4669b1 Fix greenlet_spawn error in spool auto-assign
Two lazy-load bugs caused greenlet_spawn errors in the RFID auto-assign
  flow:

  1. create_spool_from_tray set spool.k_profiles=[] AFTER db.flush(),
     but assigning to a relationship on a persistent object triggers a
     lazy load (SQLAlchemy loads the current collection before replacing).
     Moved initialization to BEFORE db.add().

  2. spool.assignments was never initialized or eagerly loaded, so
     db.add(SpoolAssignment) triggered a back_populates lazy load
     outside the async greenlet. Added assignments=[] in create and
     selectinload(Spool.assignments) in get_spool_by_tag.

  Also added exc_info=True to error handlers for full tracebacks, and
  19 new tests including greenlet regression tests that reproduce the
  exact failure.
2026-03-05 12:40:53 +01:00
maziggy 058f74a7da Add in-app bug reporting with relay, debug log collection, and privacy controls
Floating bug report button submits issues via bambuddy.cool relay (no GitHub
  token needed locally). Collects 30s debug logs with printer push_all, sanitizes
  all sensitive data, uploads logs as files to GitHub. Screenshot upload/paste/drag
  with JPEG compression. Translated into all 7 languages. Includes 21 tests.
2026-03-04 13:33:19 +01:00
maziggy 8d9894793a Fix queue 500 error when cancelled print exists (#558)
The MQTT completion handler stored "aborted" as the queue item status
  when a print was cancelled mid-print, but the response schema only
  allows "cancelled". Pydantic validation failed on the invalid status
  when listing all queue items, returning 500. Filtering by specific
  status excluded the bad row so those still worked.

  Normalise "aborted" → "cancelled" before storing. A startup fixup
  also converts any existing "aborted" rows in the database.
2026-03-01 10:03:33 +01:00
maziggy 1eeeb37b42 Updated commit message (now includes the test fix):
Fix camera button permissions & ffmpeg process leak (#550)

  Camera button on printer card was clickable without camera:view
  permission. ffmpeg processes (~240MB each) accumulated after closing
  camera streams because: (1) stop endpoint called terminate() without
  wait()/kill(), (2) HTTP disconnect detection only ran between frames
  so was blocked when the generator was stuck on stdout read, and
  (3) no mechanism caught processes orphaned by generator abandonment
  or app restarts.

  - Add camera:view permission check + tooltip to camera button
  - Fix stop endpoint: terminate() → wait(2s) → kill() → wait()
  - Add background disconnect monitor (polls every 2s, kills ffmpeg
    directly on disconnect)
  - Add periodic /proc scan (every 60s) that SIGKILLs any ffmpeg
    with rtsps://bblp: not in an active stream
  - Add noCamera i18n key to all 6 locales
  - Fix camera API test mocks for async wait() and pid attribute
2026-02-28 12:58:31 +01:00
maziggy fe5bb1fd90 Fix naive-vs-aware datetime crash from 0.2.1 timezone migration
The timezone fix (ed36eaf) replaced datetime.utcnow() with
datetime.now(timezone.utc) across ~80 call sites, but SQLAlchemy's
SQLite DateTime columns strip tzinfo on read, returning naive datetimes.
Any Python-side comparison (subtraction, <, >) between an aware "now"
and a naive DB value raises TypeError.

Add `if value.tzinfo is None: value = value.replace(tzinfo=timezone.utc)`
guards at all 8 affected comparison sites:
- maintenance.py: last_performed_at subtraction (500 on /maintenance/overview)
- auth.py: API key expires_at check (2 locations)
- print_scheduler.py: scheduled_time comparison
- smart_plug_manager.py: auto_off_pending_since elapsed calc
- smart_plugs.py: power_alert_last_triggered cooldown
- main.py: last_runtime_update elapsed calc
- archives.py: timelapse completed_at fallback
2026-02-27 17:39:32 +01:00
maziggy 2c8fbf3e33 Fix queue empty after restart by checkpointing WAL on shutdown (#523)
The SQLite WAL was never checkpointed during container shutdown, leaving
uncommitted data (including schema migrations) in the -wal file. On
restart, partial WAL recovery could cause inconsistent query results,
making the queue appear empty until a filter was applied. Add
PRAGMA wal_checkpoint(TRUNCATE) and engine.dispose() to the lifespan
shutdown handler.
2026-02-27 07:41:03 +01:00
maziggy 37d9b4c841 Fix manual spool weight overwritten by AMS auto-sync (#525)
Add weight_locked flag to spools that auto-sets when weight_used is
explicitly updated via the API. Both the MQTT AMS remain% auto-sync and
the manual force-sync endpoint skip locked spools. Usage tracker delta
tracking is unaffected. Users can re-enable AMS sync by setting
weight_locked to false.
2026-02-26 08:42:19 +01:00
maziggy c2b7b6a99a Fix inconsistent print cost calculation on reprints (#505)
Three independent code paths wrote to archive.cost with conflicting
strategies, causing the same model to produce different prices on each
reprint (e.g. £0.77, £1.54, £2.03).

- Remove add_reprint_cost (Path 3) — redundant cost accumulation that
  double-counted on top of usage tracker
- Fix usage tracker (Path 2) — compute cost from current session's
  results only, not a SUM of all historical SpoolUsageHistory rows
- Remove _reprint_archives tracking set from main.py
- Update test mocks to match simplified query pattern

archive.cost now always reflects the cost of a single print.
2026-02-24 08:58:35 +01:00
maziggy ed36eafbec Fix timestamps off by timezone offset in non-UTC containers (#504)
All backend timestamps used datetime.now() (server local time) or the
deprecated datetime.utcnow(). The frontend's parseUTCDate() assumes
timestamps without timezone indicators are UTC and appends 'Z', so
stored timestamps were off by the timezone offset when the container's
timezone wasn't UTC.

Backend: replaced datetime.now() and datetime.utcnow() with
datetime.now(timezone.utc) across 16 files (~80 call sites) for all
database fields and DB comparisons. Cosmetic timestamps (filenames,
user-facing local time formatting) intentionally left as local time.

Frontend: replaced 13 new Date(backendTimestamp) calls with
parseUTCDate() across 8 files to correctly interpret UTC timestamps.
2026-02-24 08:18:02 +01:00
maziggy faff453dec Fix bed cooled notification never triggering (#497)
The bed cooldown monitor was defined at the end of on_print_complete,
after an early return that exits when no archive is found. Prints
started from BambuStudio or the printer's touchscreen have no archive,
so the function returned before the bed cooldown task was ever created.

Moved the bed cooldown block (function def + task creation) to before
the archive_id early-return so it fires for all completed prints.
Also hardened the temperature dict check from truthiness to isinstance.
2026-02-23 13:57:58 +01:00
maziggy 32b50e02d8 Fix external spool assignments lost on restart (#493)
External spool assignments (ams_id=255) were silently deleted on every
AMS change because _find_tray_in_ams_data only searches AMS units, not
vt_tray. Now looks up external assignments in the printer's vt_tray data
instead, and preserves assignments when vt_tray hasn't arrived yet.
2026-02-23 09:07:42 +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 7232ddb5a6 Fix finish photo not captured when archive has no source 3MF (#484)
When the 3MF source file wasn't downloaded from the printer (e.g. FTP
failure on P2S), archive.file_path was null. The finish photo capture
silently skipped because it derived the save directory from file_path.

Now falls back to archive/{id}/ directory and logs a warning.
2026-02-21 17:19:47 +01:00
maziggy 055243683c Fix archive endpoints crash with "Is a directory" for BambuStudio prints (#475)
When FTP download of a 3MF fails (e.g. BambuStudio-initiated prints),
the fallback archive has file_path="". Path.exists() on
settings.base_dir / "" resolves to the base directory itself and
returns True, so ZipFile() then fails with [Errno 21] Is a directory.

Replace .exists() with .is_file() across all 15 archive route checks
and 1 in main.py. Add file_path truthiness guard for finish photo
capture to prevent saving photos under the base directory.
2026-02-21 11:35:18 +01:00
maziggy e6236301b4 Fix phantom prints from lingering SD card files (#477)
Three bugs allowed uploaded gcode files to survive on the printer's SD
card, causing firmware restarts to auto-start old prints hours later.

1. The post-print cleanup retry loop always broke after attempt 1
   regardless of delete success — delete_file_async returns False on
   failure instead of raising, so the except-based retry never fired.

2. When start_print() failed after uploading, the file was never cleaned
   up since on_print_complete never fires for prints that never started.

3. Cleanup failures were logged at DEBUG level, invisible in production.
2026-02-21 09:51:42 +01:00
maziggy d7a15c79bb Filter non-actionable HMS errors from notifications (#470)
Infrastructure/auth HMS error codes like 0500_0007 (MQTT command
verification failed) triggered false-alarm printer error notifications
even though they don't indicate actual print problems.

- Add suppress list for non-actionable error codes: 0500_0007 (MQTT
  auth failure), 0500_4001 (cloud connection failure), 0500_400E
  (print cancelled by user)
- Skip suppressed codes in the notification loop so they are still
  tracked in HMS state and shown in the UI, but don't fire notifications
2026-02-21 08:36:16 +01:00
maziggy 1404242e0c Fix cost tracking issues 2026-02-20 16:31:16 +01:00
MartinNYHC 965000c3ca Merge branch '0.2.1b2' into feature/cost_tracker 2026-02-20 16:26:01 +01:00
Wesley Reuel Marques SilvaandMartinNYHC ceb2de7164 Adding the energy cost from 2 to 3 decimal precision (#416)
* 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>
2026-02-20 16:23:00 +01:00
Keybored 85645ce184 Merge branch '0.2.1b2' into feature/cost_tracker 2026-02-20 15:44:34 +01:00
maziggy b22f091dd0 Fix SD card cleanup silently failing after print completion (#374)
The post-print SD card cleanup used printer_manager.get_printer(),
which returns a PrinterInfo object with only name and serial_number.
The code then accessed .ip_address, .access_code, and .model, which
don't exist on PrinterInfo — raising AttributeError on every print
completion. The outer except Exception handler silently swallowed it
at debug level.

Result: uploaded gcode files were never deleted from the printer's
SD card root. On some printers (e.g. P1S), files left in root
auto-start on power cycle, causing phantom prints.

Replaced with a DB query for the Printer model, matching the pattern
used everywhere else in on_print_complete().
2026-02-20 14:30:26 +01:00
maziggy 3f201594f1 Fix spool usage lost when spool runs empty mid-print (#459)
When a spool ran empty during a print and the AMS auto-switched to a
backup, on_ams_change eagerly deleted the SpoolAssignment (fingerprint
mismatch). on_print_complete then queried the now-deleted assignment
and silently dropped usage.

Snapshot all spool assignments into PrintSession at print start. At
completion, prefer the snapshot over live DB queries in both the 3MF
primary path and the AMS remain% delta fallback. Fall back to live
query for backward compatibility with in-flight sessions started
before upgrade.
2026-02-20 14:08:35 +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 88fa58fff8 Add background print dispatching + tests 2026-02-20 08:52:49 +01:00
Ryan Ewen 4a625c1ef2 Add background print dispatching + tests 2026-02-20 08:51:13 +01:00
Matteo Parenti 04ffca204f Add cost tracking for spools and usage history 2026-02-19 14:13:18 +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
b8d48b0362 Fix/hms flapping notifications (#444)
* Debounce HMS error tracking to prevent flapping notifications

HMS errors on newer printers (e.g. H2S) can flicker on and off
every few seconds as conditions fluctuate around thresholds. For
example, chamber temperature regulation during PETG prints sends
HMS code 0300A70000030001 repeatedly as temps oscillate. Previously
each reappearance after a brief hms:[] gap was treated as a new
error, triggering duplicate notifications. This adds a 30-second
grace period before clearing the notification tracking state so
flapping errors are only notified once per episode.

* build(deps): bump aquasecurity/trivy-action

Bumps the github_actions group with 1 update in the /.github/workflows directory: [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action).


Updates `aquasecurity/trivy-action` from 0.33.1 to 0.34.0
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](https://github.com/aquasecurity/trivy-action/compare/0.33.1...0.34.0)

---
updated-dependencies:
- dependency-name: aquasecurity/trivy-action
  dependency-version: 0.34.0
  dependency-type: direct:production
  dependency-group: github_actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updated CI

* build(deps): bump aquasecurity/trivy-action

Bumps the github_actions group with 1 update in the /.github/workflows directory: [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action).


Updates `aquasecurity/trivy-action` from 0.33.1 to 0.34.0
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](https://github.com/aquasecurity/trivy-action/compare/0.33.1...0.34.0)

---
updated-dependencies:
- dependency-name: aquasecurity/trivy-action
  dependency-version: 0.34.0
  dependency-type: direct:production
  dependency-group: github_actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updated CI

* Updated CI

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: MartinNYHC <mz@v8w.de>
2026-02-19 08:30:52 +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 b02e9da943 WIP: multi virtual printer support 2026-02-18 06:37:27 +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 4cd36abfef fix(queue): update queue item status before archive lookup early return
The queue item status update (printing → completed/failed) was placed
after `if not archive_id: return` in on_print_complete. When archive
lookup failed (app restart mid-print, manual deletion), the function
returned early and the queue item stayed stuck in "printing" forever.

Over multiple print cycles, stale items accumulated — showing double
the actual printer count in the "Printing" stat and completed prints
remaining in the "Currently Printing" section.

Move the entire queue item status update block (status transition,
MQTT relay, queue-completed notification, auto-power-off) to before
the archive_id early return, matching the pattern used for SD card
cleanup.
2026-02-17 07:41:12 +01:00
maziggy 27e4fe49bd fix(ghost-print): run SD card cleanup before archive lookup and add retry logic (#374)
The SD card file cleanup (which prevents phantom prints on power cycle)
was placed after the archive_id early-return in on_print_complete. Users
with auto-archiving disabled never reached the cleanup code, so ghost
prints persisted on every reboot.

- Move cleanup before the archive_id check so it always runs
- Add retry loop (3 attempts, 2s delay) for printers that briefly lock
  the filesystem after a print ends
- Try both .3mf and .gcode extensions

Reported by @DIYofThings in #374.
2026-02-17 07:26:28 +01:00
maziggy 12399abfea fix(usage-tracker): capture ams_mapping from MQTT request topic for slicer-initiated prints
Subscribe to the MQTT request topic (device/{serial}/request) to intercept
print commands from any source (BambuStudio, OrcaSlicer, Bambu Handy, or
Bambuddy itself). Captures ams_mapping from project_file commands and passes
it through print start/complete callbacks. Fixes wrong tray attribution on
H2D Pro when prints are started from external slicers.
2026-02-17 07:07:39 +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 f35e662e8b fix: delete uploaded 3MF from SD card after print completes (#374)
The print scheduler uploads .3mf files to the printer's SD card root
but never cleans them up. Some printers (e.g. P1S) auto-start files
found in root on power cycle, causing phantom/ghost prints on every
reboot. Now deletes the uploaded file via FTP after print completion
(best-effort, non-blocking).
2026-02-16 08:12:14 +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 27cecbed87 feat: add print log timeline view in archives page
Add a chronological, table-based print log as a 4th view mode in the
Archives page. Log entries are stored in a separate print_log_entries
table — clearing the log never touches archives or queue items.

Backend:
- New PrintLogEntry model with independent table
- GET /print-log/ endpoint with search, printer, user, status, date filters
- DELETE /print-log/ clears only log entries
- Thumbnail serving endpoint for log entries
- write_log_entry() service called on print completion
- Auth: ARCHIVES_READ for viewing, ARCHIVES_DELETE_ALL for clearing

Frontend:
- New 'log' ViewMode with ClipboardList icon toggle
- Filterable/searchable table with pagination (10/25/50/100 rows)
- Colored status badges, duration formatting, filament color swatches
- Clear button with confirmation modal
- All filter state persisted to localStorage
- i18n: EN, DE, JA, FR, IT translations
2026-02-15 13:14:15 +01:00
maziggy caedfffa5b fix: add logging and harden archive matching for phantom print investigation (#374)
Suppress SQL/aiosqlite debug noise (~90% log volume reduction), add
caller-traced PRINT COMMAND logging to start_print(), log scheduler
queue checks, tighten stale archive ilike match to exact match, and
warn on multiple queue items in "printing" status. Includes 18 new
unit tests.
2026-02-15 11:35:24 +01:00
maziggy 53a4933c36 feat: add bed cooled notification after print completes (#378)
Notify users when the print bed cools below a configurable threshold
(default 35°C) after a print finishes, so they know when to remove parts.

- Backend: DB migration, model, schemas, notification template, service
  method, background cooldown monitor (polls every 15s, 30min timeout)
- Frontend: event toggle in provider card/modal, threshold setting in
  Settings > Notifications, i18n keys for all 5 locales
- Tests: 4 backend + 4 frontend tests
- Docs: README, website, wiki updated
2026-02-15 10:59:07 +01:00
maziggy e1b3329b68 fix: reduce MQTT log noise at INFO level (#365)
Downgrade 58 diagnostic logger.info calls to logger.debug in
bambu_mqtt.py — payload dumps, detector state changes, field
discovery, H2D disambiguation, and periodic status updates no longer
flood logs at the default INFO level. User-initiated actions (print,
stop, calibration, AMS load/unload) remain at INFO. Also suppress
paho-mqtt library INFO messages in production mode.
2026-02-15 09:56:51 +01:00
maziggy d67f1a1c03 fix: inventory spool weight_used reset by AMS remain% sync
The on_ams_change() handler recalculated weight_used from the AMS
remain% sensor on every status update, overwriting precise values
from the usage tracker. For small prints on large spools (e.g. 1.6g
on 1000g), the integer-resolution sensor (10g steps) still reported
100% remain, resetting weight_used back to 0. The sync now only
increases weight_used, never decreases it.
2026-02-15 08:40:12 +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 f88c7704bc Add AMS slot info to filament_details notification variable (#336)
filament_details now shows which AMS unit and tray each filament came
from (e.g. "AMS-A T1 PLA: 12.4g | AMS-A T3 PETG: 2.8g"). External
spool holders show "Ext". Falls back to type-only format when usage
tracking data is unavailable (e.g. Spoolman mode).

- usage_tracker: include spool material in result dicts
- main.py: pass usage_results to notification flow via archive_data
- notification_service: build filament_details with AMS labels when
  usage_results available, fall back to existing slot-based format
- Update sample data and changelog
2026-02-13 16:10:58 +01:00
maziggy 2d989bc6e8 Unified 3MF-based filament tracking and notification usage variables (#336)
3MF slicer estimates are now the primary tracking source for ALL spools
(BL and non-BL), with AMS remain% delta as fallback. Per-layer G-code
analysis provides accurate partial usage for failed/cancelled prints.

Fix wrong-spool tracking bug: 3MF slot_id was mapped directly to AMS
tray position, but the printer remaps slicer slots at print time. Now
uses queue ams_mapping for queue prints and tray_now from printer state
for single-filament non-queue prints.

Add filament_grams, filament_details, and progress template variables
to print_complete, print_failed, and print_stopped notification events.
Webhook payloads include per-slot filament breakdown. Per-slot data
stored in archive extra_data during 3MF parsing.
2026-02-13 14:48:50 +01:00
maziggy f9b47282a1 Nozzle-aware AMS mapping for dual-nozzle printers, BL spool detection fix, AMS startup fix, SQLite WAL (#318)
Dual-nozzle H2D/H2D Pro: filament matching now respects nozzle assignments
from the 3MF file. Each AMS unit feeds a specific nozzle (L/R), and the
scheduler/frontend constrain matching to only trays on the correct nozzle.
Falls back to unfiltered matching when no trays exist on the target nozzle.
L/R badges shown in the filament mapping UI. Translated in en/de/ja/it.

Fix AMS slot config overwritten on startup: on_ams_change unconditionally
unlinked BL spool assignments on every MQTT pushall, then re-assigned them
sending ams_filament_setting without setting_id — clearing the printer's
filament preset. Now compares spool RFID identifiers before unlinking.

Fix BL spool detection false positives: removed tray_info_idx from detection
logic in both backend is_bambu_lab_spool() and frontend isBambuLabSpool().
Third-party spools using Bambu generic presets had GF-prefixed tray_info_idx
values, causing misidentification. Now uses only tray_uuid and tag_uid.

SQLite WAL mode with 5s busy timeout reduces "database is locked" errors.
2026-02-13 11:48:32 +01:00
MartinNYHC c5246e60fc Merge branch '0.2.0b' into fix/duplicate-error-notifications 2026-02-13 08:07:44 +01:00
MartinNYHC 7c36283665 Merge branch '0.2.0b' into fixes/327 2026-02-13 08:01:52 +01:00
maziggy e3f3c90c3d Sync 2026-02-13 07:53:14 +01:00
MartinNYHC 2af5289736 Merge branch '0.2.0b' into fixes/327 2026-02-13 06:42:50 +01:00