mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
Fix Bambu Lab spool detection in Spoolman sync and AMS cards
The spool detection only checked tray_uuid and tag_uid, but some Bambu Lab
spools have RFID read issues causing these to be missing while still having
valid tray_info_idx (Bambu filament preset ID like "GFA00").
Backend changes (backend/app/services/spoolman.py):
- Add tray_info_idx to AMSTray dataclass
- Parse tray_info_idx from raw MQTT data in parse_ams_tray()
- Check tray_info_idx first in is_bambu_lab_spool() - most reliable indicator
- Bambu Lab presets start with "GF" (GFA00, GFB00, GFL00, etc.)
- Fall back to tray_uuid then tag_uid for unique spool identification
- Add warning when Bambu spool detected but no unique ID for Spoolman
Backend changes (backend/app/api/routes/spoolman.py):
- Update /sync/{printer_id} and /sync-all to pass tray_info_idx
- Guard against empty spool_tag when adding to tracking set
Frontend changes (frontend/src/pages/PrintersPage.tsx):
- Add isBambuLabSpool() helper with same detection logic as backend
- Update all 4 vendor detection locations in AMS cards to use new helper
Detection priority (same on frontend and backend):
1. tray_info_idx starts with "GF" (Bambu preset ID)
2. tray_uuid is valid 32-char hex (non-zero)
3. tag_uid is valid 16-char hex (non-zero)
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, tray.tag_uid):
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
|
||||
skipped.append(
|
||||
SkippedSpool(
|
||||
location=location,
|
||||
@@ -230,7 +230,8 @@ async def sync_printer_ams(
|
||||
if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
|
||||
else tray.tag_uid
|
||||
)
|
||||
current_tray_uuids.add(spool_tag.upper())
|
||||
if spool_tag:
|
||||
current_tray_uuids.add(spool_tag.upper())
|
||||
|
||||
try:
|
||||
sync_result = await client.sync_ams_tray(tray, printer.name)
|
||||
@@ -342,7 +343,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, tray.tag_uid):
|
||||
if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
|
||||
all_skipped.append(
|
||||
SkippedSpool(
|
||||
location=location,
|
||||
@@ -359,7 +360,8 @@ async def sync_all_printers(db: AsyncSession = Depends(get_db)):
|
||||
if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
|
||||
else tray.tag_uid
|
||||
)
|
||||
printer_tray_uuids[printer.name].add(spool_tag.upper())
|
||||
if spool_tag:
|
||||
printer_tray_uuids[printer.name].add(spool_tag.upper())
|
||||
|
||||
try:
|
||||
sync_result = await client.sync_ams_tray(tray, printer.name)
|
||||
|
||||
@@ -49,6 +49,7 @@ class AMSTray:
|
||||
remain: int # Remaining percentage (0-100)
|
||||
tag_uid: str # RFID tag UID
|
||||
tray_uuid: str # Spool UUID
|
||||
tray_info_idx: str # Bambu filament preset ID like "GFA00"
|
||||
tray_weight: int # Spool weight in grams (usually 1000)
|
||||
|
||||
|
||||
@@ -521,6 +522,9 @@ class SpoolmanClient:
|
||||
if tray_uuid in ("", "00000000000000000000000000000000"):
|
||||
tray_uuid = ""
|
||||
|
||||
# Get tray_info_idx (Bambu filament preset ID like "GFA00")
|
||||
tray_info_idx = tray_data.get("tray_info_idx", "") or ""
|
||||
|
||||
# Get remaining percentage, ensure non-negative
|
||||
remain = max(0, int(tray_data.get("remain", 0)))
|
||||
|
||||
@@ -533,6 +537,7 @@ class SpoolmanClient:
|
||||
remain=remain,
|
||||
tag_uid=tag_uid,
|
||||
tray_uuid=tray_uuid,
|
||||
tray_info_idx=tray_info_idx.strip(),
|
||||
tray_weight=int(tray_data.get("tray_weight", 1000)),
|
||||
)
|
||||
|
||||
@@ -552,25 +557,35 @@ 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, tag_uid: str = "") -> bool:
|
||||
def is_bambu_lab_spool(self, tray_uuid: str, tag_uid: str = "", tray_info_idx: str = "") -> bool:
|
||||
"""Check if a tray has a valid Bambu Lab spool.
|
||||
|
||||
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.
|
||||
Bambu Lab spools can be identified by:
|
||||
1. tray_uuid: 32-character hex string (preferred, consistent across printers)
|
||||
2. tag_uid: 16-character hex string (RFID tag, varies between readers)
|
||||
3. tray_info_idx: Bambu filament preset ID like "GFA00" (most reliable)
|
||||
|
||||
Non-Bambu Lab spools (SpoolEase, third-party) won't have valid
|
||||
tray_uuid or tag_uid.
|
||||
Non-Bambu Lab spools (SpoolEase, third-party) won't have these identifiers.
|
||||
|
||||
Args:
|
||||
tray_uuid: The tray UUID to check (32 hex chars)
|
||||
tag_uid: The RFID tag UID to check as fallback (16 hex chars)
|
||||
tray_info_idx: Bambu filament preset ID like "GFA00", "GFB00"
|
||||
|
||||
Returns:
|
||||
True if the spool has valid Bambu Lab identifiers, False otherwise.
|
||||
"""
|
||||
# First check tray_uuid (preferred - consistent across printer models)
|
||||
# Check tray_info_idx first - Bambu filament preset IDs like "GFA00", "GFB00", etc.
|
||||
# This is the most reliable indicator as it's set when the spool is recognized
|
||||
if tray_info_idx:
|
||||
idx = tray_info_idx.strip()
|
||||
# Bambu Lab preset IDs start with "GF" followed by letter and digits
|
||||
# e.g., GFA00, GFB00, GFL00, GFN00, GFG00, GFS00, GFU00
|
||||
if idx and len(idx) >= 3 and idx.startswith("GF"):
|
||||
logger.debug(f"Identified Bambu Lab spool via tray_info_idx: {idx}")
|
||||
return True
|
||||
|
||||
# Check tray_uuid (preferred - consistent across printer models)
|
||||
if tray_uuid:
|
||||
uuid = tray_uuid.strip()
|
||||
if len(uuid) == 32 and uuid != "00000000000000000000000000000000":
|
||||
@@ -624,16 +639,17 @@ 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}, idx={tray.tray_info_idx or 'none'}, "
|
||||
f"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 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:
|
||||
# Only sync trays with valid Bambu Lab identifiers
|
||||
if not self.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
|
||||
if tray.tray_uuid or tray.tag_uid or tray.tray_info_idx:
|
||||
logger.info(
|
||||
f"Skipping non-Bambu Lab spool: {printer_name} AMS {tray.ams_id} tray {tray.tray_id} "
|
||||
f"(tray_uuid={tray.tray_uuid}, tag_uid={tray.tag_uid})"
|
||||
f"(tray_info_idx={tray.tray_info_idx}, tray_uuid={tray.tray_uuid}, tag_uid={tray.tag_uid})"
|
||||
)
|
||||
else:
|
||||
logger.debug(f"Skipping tray without RFID tag: AMS {tray.ams_id} tray {tray.tray_id}")
|
||||
@@ -644,6 +660,14 @@ class SpoolmanClient:
|
||||
tray.tray_uuid if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000" else tray.tag_uid
|
||||
)
|
||||
|
||||
# If no unique identifier available, we can't sync even if it's a Bambu Lab spool
|
||||
if not spool_tag:
|
||||
logger.warning(
|
||||
f"Bambu Lab spool detected but no unique identifier for Spoolman: "
|
||||
f"{printer_name} AMS {tray.ams_id} tray {tray.tray_id} (tray_info_idx={tray.tray_info_idx})"
|
||||
)
|
||||
return None
|
||||
|
||||
# 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)}"
|
||||
|
||||
@@ -703,6 +703,35 @@ function getWifiStrength(rssi: number | null | undefined): { label: string; colo
|
||||
return { label: 'Very weak', color: 'text-red-400', bars: 1 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tray contains a Bambu Lab spool.
|
||||
* Uses same logic as backend: tray_info_idx (GF*), tray_uuid, or tag_uid.
|
||||
*/
|
||||
function isBambuLabSpool(tray: {
|
||||
tray_uuid?: string | null;
|
||||
tag_uid?: string | null;
|
||||
tray_info_idx?: string | null;
|
||||
} | null | undefined): boolean {
|
||||
if (!tray) return false;
|
||||
|
||||
// Check tray_info_idx first (most reliable - Bambu preset IDs start with "GF")
|
||||
if (tray.tray_info_idx && tray.tray_info_idx.startsWith('GF')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check tray_uuid (32 hex chars, non-zero)
|
||||
if (tray.tray_uuid && tray.tray_uuid !== '00000000000000000000000000000000') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check tag_uid (16 hex chars, non-zero)
|
||||
if (tray.tag_uid && tray.tag_uid !== '0000000000000000') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function CoverImage({ url, printName }: { url: string | null; printName?: string }) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
@@ -1921,7 +1950,7 @@ function PrinterCard({
|
||||
|
||||
// Build filament data for hover card
|
||||
const filamentData = tray?.tray_type ? {
|
||||
vendor: (tray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
profile: cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
|
||||
colorName: getBambuColorName(tray.tray_id_name) || hexToBasicColorName(tray.tray_color),
|
||||
colorHex: tray.tray_color || null,
|
||||
@@ -2068,7 +2097,7 @@ function PrinterCard({
|
||||
|
||||
// Build filament data for hover card
|
||||
const filamentData = tray?.tray_type ? {
|
||||
vendor: (tray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
profile: cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
|
||||
colorName: getBambuColorName(tray.tray_id_name) || hexToBasicColorName(tray.tray_color),
|
||||
colorHex: tray.tray_color || null,
|
||||
@@ -2242,7 +2271,7 @@ function PrinterCard({
|
||||
|
||||
// Build filament data for hover card
|
||||
const extFilamentData = {
|
||||
vendor: (extTray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
vendor: (isBambuLabSpool(extTray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
|
||||
profile: extCloudInfo?.name || extTray.tray_sub_brands || extTray.tray_type || 'Unknown',
|
||||
colorName: getBambuColorName(extTray.tray_id_name) || hexToBasicColorName(extTray.tray_color),
|
||||
colorHex: extTray.tray_color || null,
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-D7518JU5.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-COShNpeg.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DAZKHkJ3.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user