[PR #1063] [MERGED] feat(inventory): unified Spoolman inventory UI + Storage Location + AMS deep-link + SpoolBuddy NFC write support #1152

Closed
opened 2026-05-06 12:35:23 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/maziggy/bambuddy/pull/1063
Author: @netscout2001
Created: 4/21/2026
Status: Merged
Merged: 4/24/2026
Merged by: @maziggy

Base: devHead: feature/spoolman-inventory-ui


📝 Commits (10+)

  • afbb1f9 feat(inventory): replace Spoolman iframe with internal inventory UI
  • 87875be security: harden Spoolman inventory proxy endpoints
  • 2d59d78 security: harden Spoolman inventory helpers and input validation
  • f2fb707 feat(inventory): add editable Storage Location field to spools
  • c30384f feat(inventory): deep-link from AMS slot hover card to internal inventory
  • ea55790 perf(inventory): fetch deep-link spool by ID instead of waiting for full list
  • 479506c fix(inventory): prevent wrong-endpoint 404 and avoid redundant API call on deep-link
  • 301caf4 fix(inventory): hide usage history widget in Spoolman mode
  • 3144887 fix(security): guard Infinity/NaN price and invalid deep-link spool IDs
  • dae3f78 feat(spoolbuddy): Spoolman-aware NFC tag lookup and weight sync

📊 Changes

47 files changed (+7248 additions, -343 deletions)

View changed files

📝 CHANGELOG.md (+15 -2)
backend/app/api/routes/_spoolman_helpers.py (+222 -0)
📝 backend/app/api/routes/spoolbuddy.py (+372 -75)
backend/app/api/routes/spoolman_inventory.py (+531 -0)
📝 backend/app/core/auth.py (+44 -2)
📝 backend/app/core/database.py (+10 -0)
📝 backend/app/main.py (+2 -0)
📝 backend/app/models/spool.py (+3 -1)
📝 backend/app/models/spoolbuddy_device.py (+1 -0)
📝 backend/app/schemas/spool.py (+5 -3)
📝 backend/app/schemas/spoolbuddy.py (+47 -31)
📝 backend/app/services/opentag3d.py (+65 -23)
📝 backend/app/services/spoolbuddy_ssh.py (+88 -22)
📝 backend/app/services/spoolman.py (+321 -5)
📝 backend/app/services/spoolman_tracking.py (+10 -2)
backend/tests/integration/test_auth_apikey_rbac.py (+160 -0)
📝 backend/tests/integration/test_spoolbuddy.py (+1099 -2)
backend/tests/integration/test_spoolman_inventory_api.py (+1524 -0)
📝 backend/tests/unit/services/test_spoolman_service.py (+48 -1)
📝 backend/tests/unit/services/test_spoolman_tracking.py (+7 -3)

...and 27 more files

📄 Description

Description

This PR integrates Spoolman more deeply into Bambuddy so users never have to leave the app to manage their filament, regardless of which inventory backend is active.

Unified Inventory UI for Spoolman mode
When Spoolman is enabled, the Bambuddy inventory page proxies all spool data through a new backend API (/spoolman/inventory/*). Create, edit, archive, delete and bulk-create spools — all from the same table/card UI as the internal inventory. The frontend is fully inventory-backend-agnostic.

Storage Location field
A new "Storage Location" text field on every spool (e.g. "Shelf A, Box 3"). In internal inventory mode it is stored locally. In Spoolman mode it maps bidirectionally to Spoolman's native location field — reads on load, writes back on save, and can be explicitly cleared.

"Open in Inventory" deep-link from AMS slot hover card
The hover card shown when hovering over an AMS slot now contains an "Open in Inventory" button (for both Spoolman-linked and internally-assigned spools). Clicking navigates to /inventory?spool=, which immediately opens the edit modal for that exact spool. Falls back to a targeted single-spool fetch if the spool list is not yet cached.

SpoolBuddy NFC write support for Spoolman spools (bonus — not part of the original FR)
SpoolBuddy devices can now write OpenTag3D NDEF tags for Spoolman-managed spools, not just local DB spools. The nfc/write-tag endpoint falls back to Spoolman when a spool is not found locally and encodes the tag via a new encode_opentag3d_from_mapped() path. After a successful write, nfc/write-result stores the tag UID back into Spoolman's extra.tag field. Spoolman spools are also fully discoverable via NFC scan (nfc/tag-scanned) and weight sync (scale/update-spool-weight) uses Spoolman's own filament.spool_weight as core weight instead of a hardcoded 250 g fallback.

Fixes https://github.com/maziggy/bambuddy/issues/1038

Documentation

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test addition or update

Changes Made

  • New /spoolman/inventory/* proxy API (list, get, create, bulk-create, update, delete, archive, restore, weight sync)
  • SpoolBuddyInventoryPage — replaced Spoolman iframe with full internal inventory UI in Spoolman mode
  • InventoryPage?spool=<id> deep-link support and "Open in Inventory" button on AMS slot hover card
  • SpoolFormModal / AdditionalSection — new Storage Location field, bidirectional Spoolman location sync
  • _spoolman_helpers.py_map_spoolman_spool(), _safe_float(), _safe_int(), _safe_optional_float()
  • opentag3d.pyencode_opentag3d_from_mapped() for dict-based NDEF encoding without ORM object
  • spoolbuddy.py — Spoolman-aware nfc/tag-scanned, nfc/write-tag, nfc/write-result, scale/update-spool-weight
  • Security hardening: NaN/Inf guards, spool_id ≤ 0 guard, SSRF scheme check, uint16/byte overflow clamps, max_length on tag fields, removeprefix instead of lstrip for color hex

Screenshots

see Issue FR

Testing

  • Unit: 952 passed

  • Integration: 1004 passed

  • Frontend: 1447/1447 passed

  • Bandit security scan: 0 Medium/High findings

  • I have tested this on my local machine

Checklist

  • My code follows the project's coding style
  • I have commented my code where necessary
  • My changes generate no new warnings
  • I have tested my changes thoroughly

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/maziggy/bambuddy/pull/1063 **Author:** [@netscout2001](https://github.com/netscout2001) **Created:** 4/21/2026 **Status:** ✅ Merged **Merged:** 4/24/2026 **Merged by:** [@maziggy](https://github.com/maziggy) **Base:** `dev` ← **Head:** `feature/spoolman-inventory-ui` --- ### 📝 Commits (10+) - [`afbb1f9`](https://github.com/maziggy/bambuddy/commit/afbb1f9e4c0fd1b5b4a842b78031d1594f91bdd6) feat(inventory): replace Spoolman iframe with internal inventory UI - [`87875be`](https://github.com/maziggy/bambuddy/commit/87875be8fa2cfd0641dafa4351bdfc2c8542646c) security: harden Spoolman inventory proxy endpoints - [`2d59d78`](https://github.com/maziggy/bambuddy/commit/2d59d78dee246713a9ed8ae45f586876edbfa363) security: harden Spoolman inventory helpers and input validation - [`f2fb707`](https://github.com/maziggy/bambuddy/commit/f2fb707ee5e6a0f6283895713097f45bfd9ecbc6) feat(inventory): add editable Storage Location field to spools - [`c30384f`](https://github.com/maziggy/bambuddy/commit/c30384f81fdab6e0512732f367dd12d220dc983b) feat(inventory): deep-link from AMS slot hover card to internal inventory - [`ea55790`](https://github.com/maziggy/bambuddy/commit/ea55790ebc32f5ed5dc5081a6ab1d7bd0c3ad98e) perf(inventory): fetch deep-link spool by ID instead of waiting for full list - [`479506c`](https://github.com/maziggy/bambuddy/commit/479506ca0cdc26f658c489c2a7711baa372f0f35) fix(inventory): prevent wrong-endpoint 404 and avoid redundant API call on deep-link - [`301caf4`](https://github.com/maziggy/bambuddy/commit/301caf4350032666459c2fbc96d58027a4d2717b) fix(inventory): hide usage history widget in Spoolman mode - [`3144887`](https://github.com/maziggy/bambuddy/commit/3144887d610ee7a19e55b54defac01d0473fb275) fix(security): guard Infinity/NaN price and invalid deep-link spool IDs - [`dae3f78`](https://github.com/maziggy/bambuddy/commit/dae3f781ff4fea50d07655c90d600107d83dcbcf) feat(spoolbuddy): Spoolman-aware NFC tag lookup and weight sync ### 📊 Changes **47 files changed** (+7248 additions, -343 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+15 -2) ➕ `backend/app/api/routes/_spoolman_helpers.py` (+222 -0) 📝 `backend/app/api/routes/spoolbuddy.py` (+372 -75) ➕ `backend/app/api/routes/spoolman_inventory.py` (+531 -0) 📝 `backend/app/core/auth.py` (+44 -2) 📝 `backend/app/core/database.py` (+10 -0) 📝 `backend/app/main.py` (+2 -0) 📝 `backend/app/models/spool.py` (+3 -1) 📝 `backend/app/models/spoolbuddy_device.py` (+1 -0) 📝 `backend/app/schemas/spool.py` (+5 -3) 📝 `backend/app/schemas/spoolbuddy.py` (+47 -31) 📝 `backend/app/services/opentag3d.py` (+65 -23) 📝 `backend/app/services/spoolbuddy_ssh.py` (+88 -22) 📝 `backend/app/services/spoolman.py` (+321 -5) 📝 `backend/app/services/spoolman_tracking.py` (+10 -2) ➕ `backend/tests/integration/test_auth_apikey_rbac.py` (+160 -0) 📝 `backend/tests/integration/test_spoolbuddy.py` (+1099 -2) ➕ `backend/tests/integration/test_spoolman_inventory_api.py` (+1524 -0) 📝 `backend/tests/unit/services/test_spoolman_service.py` (+48 -1) 📝 `backend/tests/unit/services/test_spoolman_tracking.py` (+7 -3) _...and 27 more files_ </details> ### 📄 Description ## Description This PR integrates Spoolman more deeply into Bambuddy so users never have to leave the app to manage their filament, regardless of which inventory backend is active. Unified Inventory UI for Spoolman mode When Spoolman is enabled, the Bambuddy inventory page proxies all spool data through a new backend API (/spoolman/inventory/*). Create, edit, archive, delete and bulk-create spools — all from the same table/card UI as the internal inventory. The frontend is fully inventory-backend-agnostic. Storage Location field A new "Storage Location" text field on every spool (e.g. "Shelf A, Box 3"). In internal inventory mode it is stored locally. In Spoolman mode it maps bidirectionally to Spoolman's native location field — reads on load, writes back on save, and can be explicitly cleared. "Open in Inventory" deep-link from AMS slot hover card The hover card shown when hovering over an AMS slot now contains an "Open in Inventory" button (for both Spoolman-linked and internally-assigned spools). Clicking navigates to /inventory?spool=<id>, which immediately opens the edit modal for that exact spool. Falls back to a targeted single-spool fetch if the spool list is not yet cached. SpoolBuddy NFC write support for Spoolman spools (bonus — not part of the original FR) SpoolBuddy devices can now write OpenTag3D NDEF tags for Spoolman-managed spools, not just local DB spools. The nfc/write-tag endpoint falls back to Spoolman when a spool is not found locally and encodes the tag via a new encode_opentag3d_from_mapped() path. After a successful write, nfc/write-result stores the tag UID back into Spoolman's extra.tag field. Spoolman spools are also fully discoverable via NFC scan (nfc/tag-scanned) and weight sync (scale/update-spool-weight) uses Spoolman's own filament.spool_weight as core weight instead of a hardcoded 250 g fallback. ## Related Issue Fixes [https://github.com/maziggy/bambuddy/issues/1038](https://github.com/maziggy/bambuddy/issues/1038) ## Documentation - [x] Docs PR(s) [https://github.com/maziggy/bambuddy-wiki/pull/18](https://github.com/maziggy/bambuddy-wiki/pull/18) - [ ] No docs update required — reason: New feature (non-breaking change that adds functionality) ## Type of Change <!-- Mark the relevant option with an "x" --> - [ ] Bug fix (non-breaking change that fixes an issue) - [x] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Code refactoring - [ ] Performance improvement - [ ] Test addition or update ## Changes Made - New `/spoolman/inventory/*` proxy API (list, get, create, bulk-create, update, delete, archive, restore, weight sync) - `SpoolBuddyInventoryPage` — replaced Spoolman iframe with full internal inventory UI in Spoolman mode - `InventoryPage` — `?spool=<id>` deep-link support and "Open in Inventory" button on AMS slot hover card - `SpoolFormModal` / `AdditionalSection` — new Storage Location field, bidirectional Spoolman `location` sync - `_spoolman_helpers.py` — `_map_spoolman_spool()`, `_safe_float()`, `_safe_int()`, `_safe_optional_float()` - `opentag3d.py` — `encode_opentag3d_from_mapped()` for dict-based NDEF encoding without ORM object - `spoolbuddy.py` — Spoolman-aware `nfc/tag-scanned`, `nfc/write-tag`, `nfc/write-result`, `scale/update-spool-weight` - Security hardening: NaN/Inf guards, `spool_id ≤ 0` guard, SSRF scheme check, uint16/byte overflow clamps, `max_length` on tag fields, `removeprefix` instead of `lstrip` for color hex ## Screenshots see Issue FR ## Testing - Unit: 952 passed - Integration: 1004 passed - Frontend: 1447/1447 passed - Bandit security scan: 0 Medium/High findings - [x] I have tested this on my local machine ## Checklist - [x] My code follows the project's coding style - [x] I have commented my code where necessary - [x] My changes generate no new warnings - [x] I have tested my changes thoroughly --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 12:35:23 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/bambuddy#1152
No description provided.