Browse Source

Added Spoolman support to add unknown Bambu Lab spools and track them

Martin Ziegler 5 months ago
parent
commit
8bc263ad02
3 changed files with 44 additions and 11 deletions
  1. 7 5
      README.md
  2. 18 1
      backend/app/main.py
  3. 19 5
      backend/app/services/spoolman.py

+ 7 - 5
README.md

@@ -91,6 +91,7 @@ v∆v
 - **Spoolman Integration** - Sync AMS filament data with your Spoolman server
   - Automatic or manual sync modes
   - Per-printer or all-printer sync
+  - Auto-creates spools and filaments in Spoolman
   - Matches Bambu Lab spools by unique tray UUID
   - Tracks filament usage during prints
   - Third-party spools (SpoolEase, etc.) gracefully skipped
@@ -644,17 +645,18 @@ Bambusy matches AMS spools to Spoolman spools using the **tray UUID** - a unique
 - Remaining filament weight (from AMS sensor)
 - Filament usage during prints (deducted from Spoolman inventory)
 
+**Auto-Creation:**
+- When a Bambu Lab spool is detected that doesn't exist in Spoolman, it's automatically created
+- Filament types are matched by material and color, or created if needed
+- The "Bambu Lab" vendor is auto-created if it doesn't exist
+- New spools include a comment noting they were auto-created
+
 **Limitations:**
 - Only **Bambu Lab original spools** can be synced (they have valid tray UUIDs)
 - Third-party spools (SpoolEase, refilled spools, etc.) are gracefully skipped - they won't cause errors
-- Spools must exist in Spoolman before syncing - Bambusy doesn't create new spools automatically
 
 #### Troubleshooting Spoolman
 
-**"Spool not found in Spoolman" errors:**
-- The Bambu Lab spool exists in your AMS but hasn't been added to Spoolman yet
-- Add the spool in Spoolman's web interface, matching the filament type and color
-
 **Third-party spools showing in AMS:**
 - SpoolEase and other third-party spools are automatically skipped during sync
 - This is normal behavior - they don't have Bambu Lab tray UUIDs

+ 18 - 1
backend/app/main.py

@@ -69,7 +69,7 @@ from backend.app.services.bambu_ftp import download_file_async
 from backend.app.services.smart_plug_manager import smart_plug_manager
 from backend.app.services.tasmota import tasmota_service
 from backend.app.models.smart_plug import SmartPlug
-from backend.app.services.spoolman import get_spoolman_client, init_spoolman_client
+from backend.app.services.spoolman import get_spoolman_client, init_spoolman_client, close_spoolman_client
 
 
 # Track active prints: {(printer_id, filename): archive_id}
@@ -933,6 +933,22 @@ async def lifespan(app: FastAPI):
     async with async_session() as db:
         await init_printer_connections(db)
 
+    # Auto-connect to Spoolman if enabled
+    async with async_session() as db:
+        from backend.app.api.routes.settings import get_setting
+        spoolman_enabled = await get_setting(db, "spoolman_enabled")
+        spoolman_url = await get_setting(db, "spoolman_url")
+
+        if spoolman_enabled and spoolman_enabled.lower() == "true" and spoolman_url:
+            try:
+                client = await init_spoolman_client(spoolman_url)
+                if await client.health_check():
+                    logging.info(f"Auto-connected to Spoolman at {spoolman_url}")
+                else:
+                    logging.warning(f"Spoolman at {spoolman_url} is not reachable")
+            except Exception as e:
+                logging.warning(f"Failed to auto-connect to Spoolman: {e}")
+
     # Start the print scheduler
     asyncio.create_task(print_scheduler.run())
 
@@ -941,6 +957,7 @@ async def lifespan(app: FastAPI):
     # Shutdown
     print_scheduler.stop()
     printer_manager.disconnect_all()
+    await close_spoolman_client()
 
 
 app = FastAPI(

+ 19 - 5
backend/app/services/spoolman.py

@@ -586,12 +586,26 @@ class SpoolmanClient:
                 location=location,
             )
 
-        # Spool not found in Spoolman - log warning but don't create new
-        logger.warning(
-            f"Bambu Lab spool with tray_uuid {tray.tray_uuid} not found in Spoolman. "
-            f"Add this spool to Spoolman first to enable syncing."
+        # Spool not found - auto-create it
+        logger.info(
+            f"Creating new spool in Spoolman for {tray.tray_sub_brands} "
+            f"(tray_uuid: {tray.tray_uuid[:16]}...)"
+        )
+
+        # First find or create the filament type
+        filament = await self._find_or_create_filament(tray)
+        if not filament:
+            logger.error(f"Failed to find or create filament for {tray.tray_sub_brands}")
+            return None
+
+        # Create the spool with tray_uuid stored as "tag" in extra field
+        return await self.create_spool(
+            filament_id=filament["id"],
+            remaining_weight=remaining,
+            location=location,
+            comment=f"Auto-created from {printer_name} AMS",
+            extra={"tag": tray.tray_uuid},
         )
-        return None
 
     async def _find_or_create_filament(self, tray: AMSTray) -> dict | None:
         """Find existing filament or create new one.