Commit Graph
426 Commits
Author SHA1 Message Date
maziggy f943420ea2 Add SpoolBuddy NFC tag writing with OpenTag3D format
Write NTAG213/215/216 tags for third-party spools via the SpoolBuddy
  kiosk UI. New "Write" page with three workflows: existing spool, new
  spool creation, and tag replacement. Backend encodes 133-byte OpenTag3D
  NDEF payloads (material, color, brand, weight, temp). Daemon writes
  page-by-page via PN5180 NTAG WRITE command with read-back verification.
  Write commands flow through heartbeat polling with WebSocket status
  updates. Includes 39 new tests and translations for all 6 languages.
2026-03-02 13:56:15 +01:00
maziggy 628a814271 Redesign SpoolBuddy settings page, add language/time format support
- Redesign settings page with tabbed layout (Device, Display, Scale, Updates)
  - Add screen blank timeout: blanks after touch inactivity, tap to wake
  - Add CSS brightness filter for HDMI displays (no sysfs on HDMI)
  - Add backend `language` field to app settings for server-side persistence
  - Sync UI language from backend on kiosk load (separate Chromium instance)
  - Top bar clock respects user's time format setting (system/12h/24h)
  - Add SpoolBuddy settings translations for all 6 languages (en/de/fr/ja/it/pt-BR)
  - Disable Chromium swipe-to-navigate in kiosk install script
  - Add `video` group for DSI backlight access
2026-03-02 12:44:45 +01:00
maziggy f2d28a0ef8 Spoolbuddy frontend fixes 2026-03-02 11:22:52 +01:00
maziggy a5016432df Fix SpoolBuddy tag_type for linked spools + add inventory weight check column
SpoolBuddy's "Link to Spool" used the generic updateSpool API which only
  set tag_uid, leaving tag_type and data_origin empty. Now uses linkTagToSpool
  with tag_type='generic' and data_origin='nfc_link'.

  Added a "Weight Check" inventory column (hidden by default) that compares
  each spool's last scale measurement against calculated gross weight with
  ±50g tolerance. Shows green check for match, yellow warning + sync button
  for mismatch. Backend stores last_scale_weight and last_weighed_at on each
  spool when weight is synced via SpoolBuddy. Includes edge case handling
  when scale weight < core weight. i18n keys added for all 6 locales.
2026-03-02 08:29:22 +01:00
AneoPsy ff3d3a82a7 feat(stats): add hourly heatmap for short timeframes and fix timezone bugs
Print Activity now shows an hourly heatmap (hours x days) for timeframes
≤7 days and dynamically adjusts calendar months for longer ranges.
Adds hourly granularity to Filament Trends, persists timeframe selection,
fixes optional chaining in QuickStatsWidget, and fixes UTC/local timezone
mismatches in date key generation. Also hardens the /archives/slim limit
param and fixes empty query string handling in API client.
2026-03-01 03:43:40 -10:00
AneoPsy 168c00f47d feat(stats): add /archives/slim endpoint and fix dashboard bugs
- Add lightweight GET /archives/slim endpoint with column-level SELECT
  (13 fields vs 46), skipping duplicate detection for ~70-80% payload
  reduction on the stats dashboard
- Add ArchiveSlim Pydantic schema and TypeScript type
- Switch StatsPage and FilamentTrends to use ArchiveSlim
- Fix critical bug in failure_analysis.py: use effective_days for week
  count, separate non-date filters, build fresh week_filter per loop
- Fix busiestDay timezone bug: parse YYYY-MM-DD with split() + local
  Date constructor instead of new Date() which creates UTC midnight
- Fix success streak ordering: sort by completed_at || created_at
  instead of created_at alone
- Add 6 integration tests for /archives/slim endpoint
2026-03-01 03:11:28 -10:00
AneoPsy 792a86f1e5 feat(stats): add new widgets, shared MetricToggle, and timeframe filtering
Add Print Time of Day, Color Distribution, and Records widgets to the
Stats page. Extract shared MetricToggle component for consistent
weight/prints/time toggles. Add date range filtering to archives and
stats API endpoints. Reorganize widgets into parent cards (Printer Stats
and Filament Trends) and fix Dashboard layout persistence for new widgets.
2026-03-01 03:11:28 -10:00
maziggy 65c904b231 Fix sidebar navigation not respecting user permissions
Sidebar nav items (Archives, Queue, Stats, Profiles, Maintenance,
  Projects, Inventory, Files) were visible to all users regardless of
  role permissions — only Settings was gated. Now each item is hidden
  when the user lacks the corresponding read permission. Printers
  remains always visible as the home page.

  Also adds missing inventory:read|create|update|delete to the frontend
  Permission type (existed in backend but was absent from the frontend
  type definition).
2026-02-28 13:07:54 +01:00
maziggy 04e64ffde1 Fix SpoolBuddy scale tare & calibration not being applied
The tare and calibrate buttons on the Settings page queued commands
  but never executed them due to five broken links:

  1. Daemon received tare command but never called scale.tare()
  2. No endpoint to persist tare offset back to backend DB
  3. Heartbeat never called scale.update_calibration()
  4. Heartbeat response with stale values overwrote new tare to zero
  5. set-factor used DB tare_offset (stale/zero), producing wrong
     calibration factor — empty scale showed ~5000g

  Fixed daemon to execute tare, persist result, and propagate
  calibration. Calibration step now captures raw ADC at tare time
  and sends it with step 2, making factor computation self-contained.

  Replaced calibration weight input with compact touch numpad for
  the RPi kiosk's 1024x600 touchscreen (no physical keyboard).
2026-02-28 09:51:51 +01:00
maziggy d92803b557 Fix "Open in Slicer" failing for filenames with special characters
Filenames containing / \ ? or # (e.g. "Abzweigdose/Verteilerdose 70mm")
broke the slicer protocol handler. BambuStudio/OrcaSlicer url_decode()
the entire protocol URL before downloading, which decoded %2F back to /,
creating extra path segments that returned 404. Sanitize these characters
to _ in slicer download URLs — the filename is cosmetic (backend resolves
files by archive ID).
2026-02-24 12:32:46 +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 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
Keybored 85645ce184 Merge branch '0.2.1b2' into feature/cost_tracker 2026-02-20 15:44:34 +01:00
Ryan Ewen 4a625c1ef2 Add background print dispatching + tests 2026-02-20 08:51:13 +01:00
MartinNYHC c72366c9d3 Merge branch '0.2.1' into feature/cost_tracker 2026-02-19 16:30:28 +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
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
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 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
MartinNYHC 72f5bd445f Merge branch '0.2.1b' into feature/filament-fix 2026-02-18 12:23:21 +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 b02e9da943 WIP: multi virtual printer support 2026-02-18 06:37:27 +01:00
Matteo Parenti 233f50c38d fix(inventory): spool catalog entry ID matching 2026-02-17 16:07:15 +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 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
MartinNYHC 0774130994 Merge branch '0.2.0b' into 0.2.0b 2026-02-16 08:36:38 +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
MartinNYHC fa3372f402 Merge branch '0.2.0b' into 0.2.0b 2026-02-15 14:53:21 +01:00
Keybored 22114dee42 Merge branch '0.2.0b' into feature/storage_meter 2026-02-15 14:02:26 +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
Keybored bcea9654fe Merge branch '0.2.0b' into feature/storage_meter 2026-02-15 11:13:25 +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 259c7eaa43 fix: respect disabled update check setting (#367)
Backend /updates/check endpoint now returns early without calling
GitHub API when check_updates is disabled. Settings page no longer
auto-fetches update status when the setting is off. Printer card
firmware badge falls back to showing the current version from MQTT
instead of disappearing when firmware update checks are disabled.
2026-02-15 10:09:22 +01:00
maziggy ce1b3b2741 Revert "Merge pull request #361 from Keybored02/0.2.0b"
This reverts commit 0743bc2df1, reversing
changes made to 292c5e855c.
2026-02-15 09:55:05 +01:00
MartinNYHC 9fb0c59dba Merge branch '0.2.0b' into 0.2.0b 2026-02-15 09:13:19 +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
AneoPsy aa684b4b36 feat(queue): add filament weight display
- Show total filament weight on Queue page
- Display weight per queue item
- Add translations for queue weight labels
2026-02-14 14:32:49 -10:00
Matteo Parenti 26557f6038 Add storgae usage metering and breakdown 2026-02-14 18:33:04 +01:00
Matteo Parenti 12e758e68e Add insufficient filament warning 2026-02-13 22:37:06 +01:00
Matteo Parenti 8b0895f63f Add soft-delete for system maintenance tasks 2026-02-13 15:39:52 +01:00
MartinNYHC 44df253c67 Merge branch '0.2.0b' into feature/inventory 2026-02-12 16:38:03 +01:00
maziggy 1f0931e00c Sync 2026-02-12 11:54:50 +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