Commit Graph
201 Commits
Author SHA1 Message Date
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 cf91531aa2 Add next-available printer indicator and fix bulk archive delete race condition (#354)
Status summary bar on the Printers page now shows availability count
("X available") and a "Next available" indicator with printer name,
progress bar, percentage, and remaining time for the printer finishing
soonest. Always visible when printers are printing; hidden otherwise.

Fix race condition in bulk archive deletion where files were removed
from disk before the database commit. Concurrent SQLite writes could
cause a lock timeout, rolling back the DB delete while files were
already gone — leaving orphaned records with broken thumbnails. Now
deletes the DB record first and only removes files after a successful
commit.
2026-02-13 13:06:22 +01:00
maziggy cffe7e62bc Fix maintenance tasks showing wrong rod type for printer models (#351)
H2/A1 series have linear rails, not carbon rods. X1/P1/P2S have carbon
rods, not linear rails. Split rod-specific maintenance into model-aware
pairs: "Lubricate/Clean Carbon Rods" for X1/P1/P2S and "Lubricate/Clean
Linear Rails" for A1/H2. Added rod type classification to printer_models
and startup cleanup for stale/duplicate system maintenance types.
2026-02-13 12:23:34 +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
maziggy ee2af79f4a Add confirmation modal to skip objects (#346)
Prevent accidentally skipping the wrong object during a print by showing
a warning confirmation dialog with the object name and an irreversible
action warning. Uses the existing ConfirmModal component with warning
variant. Translations added for all 4 locales (en, de, ja, it).
2026-02-13 08:46:15 +01:00
MartinNYHC 44df253c67 Merge branch '0.2.0b' into feature/inventory 2026-02-12 16:38:03 +01:00
maziggy e6e62f2a68 Add spool inventory: AMS slot assignment, usage tracking, and remaining weight editing
Built-in spool inventory with AMS slot assignment for non-BL spools,
automatic filament usage tracking via 3MF estimates, and remaining
weight editing in the spool form.

Inventory features:
- AMS slot assignment: assign/unassign manual spools to AMS tray slots
- Filter out Bambu Lab spools (RFID-managed) from assignment modal
- Filter out already-assigned spools (one spool per slot)
- Auto-unlink manual assignments when a BL spool is inserted
- Hide assign/unassign UI on BL-occupied slots
- Case-insensitive fingerprint matching for auto-unlink logic

Usage tracking:
- 3MF-based filament consumption tracking for non-BL spools
- Extract per-filament used_g from archived 3MF slice_info
- Map 3MF slot_id to AMS (ams_id, tray_id) positions
- Scale estimates by print progress for failed/aborted prints
- Skip BL spools (tracked via AMS remain% delta, Path 1)
- Prevent double-counting with handled_trays set

Other:
- Add remaining weight field to spool edit form
- Fix hardcoded "Empty" string in AssignSpoolModal (now i18n)
- Add backend unit tests for usage_tracker (11 tests)
- Add frontend tests for AssignSpoolModal filters (5 tests)
- Update CHANGELOG, README, wiki docs, and website
2026-02-12 15:42:27 +01:00
maziggy b423283ec3 Fix queued jobs incorrectly skipped after duplicate execution detection (#341)
When the same file was queued multiple times from the archive, all jobs
after the first were automatically skipped with "already printed X hours
ago". The scheduler's 4-hour safety check compared the shared archive's
completed_at timestamp, incorrectly treating legitimate repeat prints as
phantom reprints from crash recovery. This also affected single queue
items created from recently completed archives.

Remove the check entirely — the crash scenario it guarded against is
already prevented by committing item.status="printing" before sending
the print command.
2026-02-12 12:04:41 +01:00
maziggy aa87e5598b Add clear plate confirmation between queued prints
The print scheduler previously treated FINISH/FAILED as idle states
and would auto-start the next queued print before the user cleared
the build plate. Now requires explicit user confirmation via a
"Clear Plate & Start Next" button on the printer card. Uses an
in-memory plate-cleared flag — no MQTT command needed since the
scheduler's start_print overrides the printer state.
2026-02-12 08:45:05 +01:00
maziggy 1b5683b3a3 Add "Open in new tab" toggle for external sidebar links (#338)
External links behind reverse proxies (Traefik, nginx) block iframe
embedding via X-Frame-Options/CSP headers. Add a per-link boolean
toggle so users can choose between iframe (default) and new-tab
behavior. Keyboard shortcuts also respect the setting.
2026-02-12 07:51:55 +01:00
maziggy 38a1d6f5e9 Fix Telegram bold title broken by underscores in message body (#332)
Underscores in job names or error codes caused parse_mode to be
disabled entirely, rendering *bold* markers as literal asterisks.
Now escapes underscores in the body with \_ so Markdown stays enabled.
2026-02-12 07:28:54 +01:00
maziggy 5787c61f34 Fix printer card cover image not updating between prints
The cover image URL was always /printers/{id}/cover regardless of the
active print, so the browser served its cached image. Now appends the
print name as a cache-busting query parameter and resets load state
when the print changes.
2026-02-12 07:25:22 +01:00
maziggy 13de96127e Fix file downloads showing generic filenames for special characters (#334)
Content-Disposition parser now handles RFC 5987 percent-encoded filenames
(filename*=utf-8''...) used by FastAPI for non-ASCII characters. Applied
to all 7 download endpoints.

Closes #334
2026-02-12 07:19:03 +01:00
maziggy 1ac487254d Add RUB (₽) currency option (#333)
Closes #333
2026-02-12 07:12:02 +01:00
maziggy 778d1966af Move email settings under Authentication tab in settings
Rename the "Users" settings tab to "Authentication" and move the
standalone "Global Email" tab into it as an "Email Authentication"
sub-tab. This groups email/SMTP configuration with user management
where it logically belongs.

- Remove top-level email tab from settings tab bar
- Add sub-tab navigation (Users / Email Authentication) inside the
  Authentication tab
- Handle legacy ?tab=email URLs by mapping to users tab + email sub-tab
- Update i18n keys in en/de/ja locales
2026-02-11 08:43:40 +01:00
maziggy 8e09e4fb65 Add additional currency options for cost tracking (#329)
Added HKD, INR, KRW, SEK, NOK, DKK, PLN, BRL, TWD, SGD, NZD, MXN,
CZK, THB, and ZAR to the currency dropdown in settings.
2026-02-11 07:51:20 +01:00
maziggy 253bee18a1 Fix nozzle rack text unreadable on light filament colors (#300)
Nozzle rack slots use the filament color as background, but light
colors (white, beige) made the white "0.4" text nearly invisible.
Added a luminance check to switch to dark text on light backgrounds
and removed the dark text shadow when not needed.
2026-02-11 07:38:57 +01:00
maziggy 0fe2bb1342 Fix timelapse baseline race condition (#315)
The snapshot-diff timelapse detection captured its baseline of existing
MP4 files at print completion time inside a background task. Fast-
encoding printers could finish writing the timelapse before the baseline
was taken, so the new file appeared in the baseline and was never
detected as "new" — resulting in no timelapse attached.

Move baseline capture to print start time, when the timelapse file
cannot possibly exist yet. The baseline is stored in a module-level dict
keyed by printer_id and popped at completion time. Falls back to
completion-time baseline if the app was restarted mid-print.
2026-02-11 07:33:07 +01:00
maziggy 09d8e7d682 Fix external camera not used for snapshot + stream dropping (#325)
The snapshot endpoint always used the internal printer camera even when
an external camera was configured. Now checks for external camera first,
matching the stream endpoint pattern. Also added retry logic (3 attempts,
2s delay) to MJPEG and RTSP stream generators so they reconnect on
timeout instead of silently ending the stream.
2026-02-11 07:09:39 +01:00
maziggy edefce67fe Bumped version 2026-02-10 18:35:24 +01:00
maziggy 1d32efa239 Bumped version 2026-02-10 17:10:34 +01:00
maziggy 0282fbd442 Fix H2D Pro L/R nozzle hover card swap and hide left-only fields (#300)
Nozzle rack id 0 = extruder 0 = right nozzle, id 1 = extruder 1 = left.
The dual-nozzle hover card had them backwards, showing serial number and
max temp on the left column instead of the right. Serial and max temp
are now restricted to the right (removable) nozzle only. Single-nozzle
fallback (H2D, H2C) updated to use the primary nozzle (id 0).
2026-02-10 16:15:53 +01:00
maziggy 9484f263ab Add built-in filament name lookup table for nozzle rack and AMS tooltips (#300)
The Bambu Cloud API returns 400 for many filament IDs (e.g. GFB01,
GFU99, GFL99), causing nozzle rack hover cards to fall back to
abbreviated tray_type values ("ASA", "TPU", "PLA") instead of full
names.

Added a built-in lookup table of 86 known Bambu filament codes as a
Phase 4 fallback in get_filament_info. Resolution order is now:
cache → cloud API → local profiles → built-in table → empty fallback.

GFB01 → "Bambu ASA", GFU99 → "Generic TPU", GFL99 → "Generic PLA",
etc. Also benefits AMS tray tooltips for unresolvable filament IDs.
2026-02-10 15:39:16 +01:00
maziggy 2080665773 Updated CHANGELOG and README 2026-02-10 15:23:19 +01:00
maziggy f7cd173118 Fix H2C printer image and resolve nozzle rack filament names (#300)
1. H2C printer card was showing the H2D image — added dedicated
   h2c.png and updated getPrinterImage() mapping.

2. Nozzle rack hover card showed raw filament IDs (e.g. "GFU99")
   instead of human-readable names. Now resolves names via 3-tier
   fallback: Bambu Cloud → local slicer profiles → raw ID.
   - Frontend: nozzle rack filament_id values included in cloud
     lookup query; NozzleSlotHoverCard displays resolved name.
   - Backend: get_filament_info endpoint refactored from cloud-only
     to cache → cloud → local profiles. Matches local presets by
     setting_id in the imported OrcaSlicer JSON blob.
2026-02-10 15:12:14 +01:00
maziggy 7f22260c90 Fix H2C nozzle rack wrong empty slot, missing filament colors, and medium card layout (#300)
Three fixes for the H2C nozzle rack reported by testers:

1. Empty slot position: rack nozzles were mapped by sequential array
   index instead of by actual nozzle ID (16-21), so a missing slot
   always appeared at position 6 instead of its real position. Now
   maps each nozzle to its correct rack position via id offset.

2. Missing filament colors/materials: the H2C MQTT payload uses
   different field names than H2D — color_m (not filament_colour),
   fila_id (not filament_id), sn (not serial_number), tm (not
   max_temp). Added fallback field name resolution in the backend
   MQTT parser.

3. Medium card layout: the temperature row with nozzle rack overflowed
   on medium-sized cards. Added flex-wrap and a 190px flex-basis on
   the rack card so it wraps to a second line when space is tight.
2026-02-10 14:05:55 +01:00
maziggy c5fff9bc15 Fix AMS-HT mapping for left nozzle on H2D Pro (#318)
AMS-HT global tray ID was calculated as ams_id * 4 (= 512 for unit 128)
but AMS-HT uses the raw ams_id directly since it has a single tray.
The backend misidentified 512 as an external spool, producing wrong
ams_mapping2. Fixed in 4 locations: frontend getGlobalTrayId(), backend
start_print() ams_mapping2 builder, print scheduler, and Spoolman tracking.
2026-02-10 13:54:21 +01:00
maziggy 94ba65e3bc Fix H2C nozzle rack display and add L/R hover cards (#300)
Rack card now shows 6 rack-only slots (IDs 16-21) with empty placeholders,
excluding L/R hotend nozzles. New DualNozzleHoverCard shows both nozzle
details side-by-side with Active/Idle status. Single-nozzle H2 printers
get extended hover details. Backend stores nozzle_info for all H2 series.
Added i18n keys (en/de/ja) and 10 tests (7 backend + 3 frontend).
2026-02-10 13:40:03 +01:00
maziggy 19289855de Fix firmware badge shown for models without API data (#311)
Printers whose model has no firmware data in Bambu Lab's API (e.g. H2C
on public beta firmware) displayed a misleading green "up to date" badge.
The API returns latest_version=null for these models, so there is nothing
to compare against. Hide the badge when latest_version is not available.

- Add latest_version check to badge render condition in PrintersPage
- Add test for badge hidden when API returns no firmware data
2026-02-10 12:31:14 +01:00
maziggy a5706fe20a Fix support bundle reporting 0 AMS units
raw_data["ams"] is stored as a list by the MQTT handler, but the
support info code only checked for a nested dict format. AMS unit
and tray counts were always 0.
2026-02-10 12:17:53 +01:00
maziggy 31c8334538 Fix misleading GitHub backup description
App Settings card said "excludes sensitive data" but the complete
database is pushed. Changed to "complete database."
2026-02-10 12:00:16 +01:00
maziggy cc7bbd6fbe Firmware version badge on printer cards (#311)
- Show firmware version badge on printer card (green=up to date,
  orange=update available) with click to view release notes or
  start update workflow
- Badge only shown when firmware checking is enabled in settings
- Modal adapts: info-only view when up to date (release notes
  auto-expanded), update workflow when update available
- Gate badge query with firmware:read permission, upload button
  with firmware:update permission
2026-02-10 11:53:42 +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 37158b0600 Update supported printers docs — all models including H2C
Remove outdated "Testers Needed" messaging and Tested/Needs Testing
distinctions from README, website, and wiki. All 12 Bambu Lab printer
models (X1, X1C, X1E, P1P, P1S, P2S, A1, A1 Mini, H2D, H2D Pro,
H2C, H2S) are now uniformly listed as supported.
2026-02-10 11:15:28 +01:00
maziggy 39da463c90 Show 0% wear in nozzle rack hover card (#300)
The wear field was hidden when wear was 0 because the condition
`slot.wear > 0` treated 0 the same as null/unavailable. New nozzles
showed no wear info at all. Removed the > 0 check so "Wear: 0%"
displays correctly while still hiding the field when wear is null
(non-H2C printers).
2026-02-10 11:00:54 +01:00
maziggy c3018f06b2 Fix wrong timelapse attached to archive and skip calibration prints (#315)
Timelapse: Replace mtime-based "most recent file" approach with a
snapshot-diff strategy — baseline existing MP4 filenames before waiting,
then detect the new file that appears after encoding. Fixes wrong
timelapse in LAN-only mode where printer clock is unsynchronized.
Falls back to print-name matching if no new file appears after retries.

Calibration: Skip archiving internal printer files under /usr/ (e.g.
/usr/etc/print/auto_cali_for_user.gcode) so calibration prints don't
appear in the archive.
2026-02-10 10:53:05 +01:00
maziggy acd2aa825e Fix wrong thumbnail when reprinting same project name (#314)
The cover image cache was keyed by subtask_name and never invalidated
between prints. When a user printed the same project name twice with a
different bed layout, the cached thumbnail from the first print was
returned instead of fetching the new one from the printer.

Clear the cover cache for the printer on every print start so the next
cover request downloads a fresh 3MF and extracts the current thumbnail.
2026-02-10 10:13:11 +01:00
maziggy 516841a7f5 Fix virtual printer IP override ignored in server mode (#52)
The remote_interface_ip setting only worked in proxy mode but was
completely ignored in server modes (immediate/review/print_queue).
Users with multiple NICs (LAN + Tailscale, Docker bridges) got wrong
auto-detected IP in SSDP broadcasts and TLS certificates.
2026-02-10 10:05:35 +01:00
maziggy 8d42b05f62 Add configurable slicer preference — Bambu Studio or OrcaSlicer (#313)
Users can now choose their preferred slicer (Bambu Studio or OrcaSlicer)
in Settings → General. All "Open in Slicer" buttons across Archives,
3D Preview, and context menus use the correct protocol for the selected
slicer. OrcaSlicer uses orcaslicer://open?file=<URL> on all platforms;
Bambu Studio uses bambustudio:// (Windows) or bambustudioopen:// (macOS/
Linux). Default remains Bambu Studio for backward compatibility.
2026-02-10 09:38:06 +01:00
maziggy ae63ec8a91 Fix H2C firmware update downloading wrong (H2D) firmware (#311)
H2C was mapped to the "h2d" firmware API key, causing it to check
the H2D firmware page and offer H2D firmware for download. H2C has
its own separate firmware track (01.01.x.x vs H2D's 01.02.x.x).

Changed H2C to use its own "h2c" API key and added the reverse
mapping. Also added missing H2C (O1C) and H2S (O1S) entries to
the printer model ID and 3MF model name maps.
2026-02-10 08:51:44 +01:00
maziggy 53e13ecbd2 Fix H2C nozzle rack showing wrong slots and redesign to compact layout (#300)
The nozzle_info MQTT array contains L/R nozzle heads (IDs 0, 1) and
  rack slots (IDs 16-21). Backend was dumping all entries into nozzle_rack,
  and frontend slice(0,6) grabbed L, R, plus only 4 rack slots — cutting
  off the last position (e.g. the 0.6mm nozzle) and showing the mounted
  nozzle as docked.

  Backend: filter nozzle_rack to id >= 2 (rack-only), sort by ID.
  Frontend: compact single-row layout with bottom accent bars for
  mounted/docked status, wider rack card (flex-[2]), vertically
  centered temp cards.
2026-02-10 08:44:58 +01:00
maziggy edf244c469 Add local profiles — import OrcaSlicer presets without Bambu Cloud (#310)
Users who use OrcaSlicer without Bambu Cloud can now import slicer
presets directly into Bambuddy. Supports .orca_filament, .bbscfg,
.bbsflmt, .zip, and .json exports with automatic inheritance resolution
via OrcaSlicer's GitHub base profiles (cached with 7-day TTL).
2026-02-09 17:00:02 +01:00
maziggy 8449e1c2e2 Extend support bundle with comprehensive diagnostics
Add 10 new diagnostic sections to _collect_support_info(): printer
connectivity/firmware, integration status (Spoolman, MQTT, HA),
network interfaces (subnets only), Python package versions, database
health, Docker environment, WebSocket connections, and log file info.
All data properly anonymized — no IPs, names, or serials included.
2026-02-09 10:19:48 +01:00
maziggy 30fba33dcf Fix sidebar link custom icons displaying with inverted colors (#308)
CSS invert() filter was applied to all custom uploaded icons in dark
mode, causing full-color logos to render with wrong colors. Removed
the invert filter from custom icon <img> tags in Layout.tsx and
AddExternalLinkModal.tsx. Preset Lucide icons are unaffected.
2026-02-09 08:35:07 +01:00
maziggy 3f7d2bf619 Fix virtual printer FTP transfer failure with connection reset (#58)
Large 3MF uploads intermittently failed with [Errno 104] Connection
reset by peer while the 16-byte verify_job always succeeded. The
_handle_data_connection callback returned immediately, letting the
asyncio task complete while cmd_STOR was still reading from the data
connection. The passive port listener also stayed open during transfers.

- Keep _handle_data_connection alive via _transfer_done event so the
  asyncio task holds strong references throughout the transfer
- Close passive port listener after accepting the data connection
- Reject duplicate data connections with a warning log
- Add drain timeout (5s) to MQTT status pushes to prevent blocking
  when the slicer is busy with FTP upload
- Improve error logging with bytes received and exception type
2026-02-08 14:37:50 +01:00
maziggy d73da5e0ef Auto-detect subnet for printer discovery
The Add Printer dialog previously required users to manually enter their
network subnet for scanning (defaulting to 192.168.1.0/24). Now the
backend detects available network interfaces and returns their subnets
via the /discovery/info endpoint. The frontend auto-selects the first
detected subnet and shows a dropdown when multiple subnets are available,
falling back to a text input if none are detected.
2026-02-08 12:17:20 +01:00
maziggy 7e5f494304 Complete Japanese locale overhaul: restructure and translate all keys
Restructured ja.ts from a divergent format (different nesting, 12
structural conflicts, 48 namespaces) to match EN/DE structure exactly
(41 namespaces, 0 conflicts). Translated all 2,083 EN keys into
Japanese — previously 1,366 keys were missing and silently falling
back to English via i18next fallbackLng.
2026-02-08 09:29:48 +01:00
maziggy 9cbd66593e Show Spoolman fill level for AMS Lite and external spools (#293)
AMS Lite units (A1 series) have no weight sensor and always report 0%
fill level. When a spool is linked to Spoolman with weight data, use
Spoolman's remaining weight as a fallback. External spools also show
fill level from Spoolman data instead of always showing unknown.

Backend: Enrich GET /spoolman/spools/linked response with
remaining_weight and filament_weight alongside spool ID.

Frontend: Add getSpoolmanFillLevel() helper. Update regular AMS, HT
AMS, and external spool fill computations to use Spoolman fallback
when AMS reports 0%. Show "(Spoolman)" indicator in hover card when
fill data comes from Spoolman.
2026-02-08 08:47:18 +01:00