mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
Fix Spoolman sync skipping Bambu Lab spools with missing tray_uuid
The spool detection logic only checked tray_uuid (32 hex chars) to identify
Bambu Lab spools. Some spools have RFID read issues causing missing or
invalid tray_uuid while still having valid tag_uid (16 hex chars).
Changes:
- Update is_bambu_lab_spool() to accept tag_uid as fallback parameter
- Prefer tray_uuid for identification, fall back to tag_uid if invalid
- Use whichever valid identifier for Spoolman tag storage and matching
- Update both /sync/{printer_id} and /sync-all endpoints
This fixes spools being incorrectly marked as "Non-Bambu Lab spool (no RFID
tag)" when they have valid tag_uid but missing/zero tray_uuid.
This commit is contained in:
@@ -213,7 +213,7 @@ async def sync_printer_ams(
|
||||
location = client.convert_ams_slot_to_location(ams_id, tray.tray_id)
|
||||
|
||||
# Skip non-Bambu Lab spools (SpoolEase/third-party) - track as skipped
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid):
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
|
||||
skipped.append(
|
||||
SkippedSpool(
|
||||
location=location,
|
||||
@@ -224,8 +224,13 @@ async def sync_printer_ams(
|
||||
)
|
||||
continue
|
||||
|
||||
# Track this tray UUID as currently present in the AMS
|
||||
current_tray_uuids.add(tray.tray_uuid.upper())
|
||||
# Track this spool tag as currently present in the AMS (prefer tray_uuid, fallback to tag_uid)
|
||||
spool_tag = (
|
||||
tray.tray_uuid
|
||||
if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
|
||||
else tray.tag_uid
|
||||
)
|
||||
current_tray_uuids.add(spool_tag.upper())
|
||||
|
||||
try:
|
||||
sync_result = await client.sync_ams_tray(tray, printer.name)
|
||||
@@ -337,7 +342,7 @@ async def sync_all_printers(db: AsyncSession = Depends(get_db)):
|
||||
location = f"{printer.name} - {client.convert_ams_slot_to_location(ams_id, tray.tray_id)}"
|
||||
|
||||
# Skip non-Bambu Lab spools (SpoolEase/third-party) - track as skipped
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid):
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
|
||||
all_skipped.append(
|
||||
SkippedSpool(
|
||||
location=location,
|
||||
@@ -348,8 +353,13 @@ async def sync_all_printers(db: AsyncSession = Depends(get_db)):
|
||||
)
|
||||
continue
|
||||
|
||||
# Track this tray UUID as currently present in the AMS
|
||||
printer_tray_uuids[printer.name].add(tray.tray_uuid.upper())
|
||||
# Track this spool tag as currently present in the AMS (prefer tray_uuid, fallback to tag_uid)
|
||||
spool_tag = (
|
||||
tray.tray_uuid
|
||||
if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
|
||||
else tray.tag_uid
|
||||
)
|
||||
printer_tray_uuids[printer.name].add(spool_tag.upper())
|
||||
|
||||
try:
|
||||
sync_result = await client.sync_ams_tray(tray, printer.name)
|
||||
|
||||
@@ -552,35 +552,47 @@ class SpoolmanClient:
|
||||
ams_letter = chr(ord("A") + ams_id)
|
||||
return f"AMS {ams_letter}{tray_id + 1}"
|
||||
|
||||
def is_bambu_lab_spool(self, tray_uuid: str) -> bool:
|
||||
"""Check if a tray has a valid Bambu Lab spool UUID.
|
||||
def is_bambu_lab_spool(self, tray_uuid: str, tag_uid: str = "") -> bool:
|
||||
"""Check if a tray has a valid Bambu Lab spool.
|
||||
|
||||
Bambu Lab spools have a tray_uuid which is a 32-character hex string.
|
||||
This UUID is consistent across all printer models (unlike tag_uid which
|
||||
varies between X1C/H2D readers).
|
||||
Bambu Lab spools have a tray_uuid (32-character hex string) and/or
|
||||
a tag_uid (16-character hex string). The tray_uuid is preferred as
|
||||
it's consistent across printer models, but tag_uid is accepted as
|
||||
a fallback since some spools may have RFID read issues.
|
||||
|
||||
Non-Bambu Lab spools (SpoolEase, third-party) won't have a valid tray_uuid.
|
||||
Non-Bambu Lab spools (SpoolEase, third-party) won't have valid
|
||||
tray_uuid or tag_uid.
|
||||
|
||||
Args:
|
||||
tray_uuid: The tray UUID to check
|
||||
tray_uuid: The tray UUID to check (32 hex chars)
|
||||
tag_uid: The RFID tag UID to check as fallback (16 hex chars)
|
||||
|
||||
Returns:
|
||||
True if the spool has a valid Bambu Lab UUID, False otherwise.
|
||||
True if the spool has valid Bambu Lab identifiers, False otherwise.
|
||||
"""
|
||||
if not tray_uuid:
|
||||
return False
|
||||
# Bambu Lab tray_uuid is always 32 hex characters
|
||||
uuid = tray_uuid.strip()
|
||||
if len(uuid) != 32:
|
||||
return False
|
||||
# Verify it's all hex characters and not empty/zero
|
||||
if uuid == "00000000000000000000000000000000":
|
||||
return False
|
||||
try:
|
||||
int(uuid, 16)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
# First check tray_uuid (preferred - consistent across printer models)
|
||||
if tray_uuid:
|
||||
uuid = tray_uuid.strip()
|
||||
if len(uuid) == 32 and uuid != "00000000000000000000000000000000":
|
||||
try:
|
||||
int(uuid, 16)
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Fallback: check tag_uid (RFID tag - varies between printer readers)
|
||||
# Bambu Lab RFID tags are 16 hex characters (8 bytes)
|
||||
if tag_uid:
|
||||
tag = tag_uid.strip()
|
||||
if len(tag) == 16 and tag != "0000000000000000":
|
||||
try:
|
||||
int(tag, 16)
|
||||
logger.debug(f"Identified Bambu Lab spool via tag_uid fallback: {tag}")
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def calculate_remaining_weight(self, remain_percent: int, spool_weight: int) -> float:
|
||||
"""Calculate remaining weight from percentage.
|
||||
@@ -612,11 +624,12 @@ class SpoolmanClient:
|
||||
"""
|
||||
logger.debug(
|
||||
f"Processing {printer_name} AMS {tray.ams_id} tray {tray.tray_id}: "
|
||||
f"type={tray.tray_type}, uuid={tray.tray_uuid[:16] if tray.tray_uuid else 'none'}..."
|
||||
f"type={tray.tray_type}, uuid={tray.tray_uuid[:16] if tray.tray_uuid else 'none'}, "
|
||||
f"tag={tray.tag_uid[:8] if tray.tag_uid else 'none'}..."
|
||||
)
|
||||
|
||||
# Only sync trays with valid Bambu Lab tray_uuid
|
||||
if not self.is_bambu_lab_spool(tray.tray_uuid):
|
||||
# Only sync trays with valid Bambu Lab identifiers (tray_uuid or tag_uid)
|
||||
if not self.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
|
||||
if tray.tray_uuid or tray.tag_uid:
|
||||
logger.info(
|
||||
f"Skipping non-Bambu Lab spool: {printer_name} AMS {tray.ams_id} tray {tray.tray_id} "
|
||||
@@ -626,15 +639,20 @@ class SpoolmanClient:
|
||||
logger.debug(f"Skipping tray without RFID tag: AMS {tray.ams_id} tray {tray.tray_id}")
|
||||
return None
|
||||
|
||||
# Determine which identifier to use for Spoolman (prefer tray_uuid, fallback to tag_uid)
|
||||
spool_tag = (
|
||||
tray.tray_uuid if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000" else tray.tag_uid
|
||||
)
|
||||
|
||||
# Calculate remaining weight
|
||||
remaining = self.calculate_remaining_weight(tray.remain, tray.tray_weight)
|
||||
location = f"{printer_name} - {self.convert_ams_slot_to_location(tray.ams_id, tray.tray_id)}"
|
||||
|
||||
# Find existing spool by tray_uuid (stored as "tag" in Spoolman)
|
||||
existing = await self.find_spool_by_tag(tray.tray_uuid)
|
||||
# Find existing spool by tag (tray_uuid or tag_uid, stored as "tag" in Spoolman)
|
||||
existing = await self.find_spool_by_tag(spool_tag)
|
||||
if existing:
|
||||
# Update existing spool
|
||||
logger.info(f"Updating existing spool {existing['id']} for tray_uuid {tray.tray_uuid}")
|
||||
logger.info(f"Updating existing spool {existing['id']} for tag {spool_tag[:16]}...")
|
||||
return await self.update_spool(
|
||||
spool_id=existing["id"],
|
||||
remaining_weight=remaining,
|
||||
@@ -642,7 +660,7 @@ class SpoolmanClient:
|
||||
)
|
||||
|
||||
# Spool not found - auto-create it
|
||||
logger.info(f"Creating new spool in Spoolman for {tray.tray_sub_brands} (tray_uuid: {tray.tray_uuid[:16]}...)")
|
||||
logger.info(f"Creating new spool in Spoolman for {tray.tray_sub_brands} (tag: {spool_tag[:16]}...)")
|
||||
|
||||
# First find or create the filament type
|
||||
filament = await self._find_or_create_filament(tray)
|
||||
@@ -650,7 +668,7 @@ class SpoolmanClient:
|
||||
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
|
||||
# Create the spool with identifier stored as "tag" in extra field
|
||||
# Note: Spoolman extra field values must be valid JSON, so we encode the string
|
||||
import json
|
||||
|
||||
@@ -659,7 +677,7 @@ class SpoolmanClient:
|
||||
remaining_weight=remaining,
|
||||
location=location,
|
||||
comment="Created by Bambuddy",
|
||||
extra={"tag": json.dumps(tray.tray_uuid)},
|
||||
extra={"tag": json.dumps(spool_tag)},
|
||||
)
|
||||
|
||||
async def _find_or_create_filament(self, tray: AMSTray) -> dict | None:
|
||||
|
||||
Reference in New Issue
Block a user