Fix Bambu Cloud preset lookup by transforming filament_id to setting_id

Printers report filament_id (e.g., GFA00) but the Bambu Cloud API expects
setting_id format which has an "S" inserted after "GF" (e.g., GFSA00).

- Add _filament_id_to_setting_id() helper function
- Transform IDs before API calls: GFx## -> GFSx##
- User presets (P-prefix) and already-correct IDs unchanged
- Improved warning message to show both original and transformed IDs
This commit is contained in:
maziggy
2026-01-28 09:34:56 +01:00
parent 180752f7e6
commit 65a6e7791b
2 changed files with 118 additions and 2 deletions
+36 -2
View File
@@ -267,6 +267,34 @@ _filament_cache_time: float = 0
FILAMENT_CACHE_TTL = 300 # 5 minutes
def _filament_id_to_setting_id(filament_id: str) -> str:
"""
Convert filament_id to setting_id format for Bambu Cloud API.
Printers report filament_id (e.g., GFA00, GFG02) but the API expects
setting_id format which has an "S" inserted after "GF" (e.g., GFSA00, GFSG02).
User presets (starting with "P") and already-correct IDs are returned unchanged.
"""
if not filament_id:
return filament_id
# User presets start with "P" - leave unchanged
if filament_id.startswith("P"):
return filament_id
# Official Bambu presets: GFx## -> GFSx##
# Check if it matches the filament_id pattern (GF followed by letter and digits)
if filament_id.startswith("GF") and len(filament_id) >= 4:
# Check if it's already a setting_id (has S after GF)
if filament_id[2] == "S":
return filament_id
# Insert "S" after "GF": GFA00 -> GFSA00
return f"GFS{filament_id[2:]}"
return filament_id
@router.post("/filament-info")
async def get_filament_info(setting_ids: list[str] = Body(...), db: AsyncSession = Depends(get_db)):
"""
@@ -308,7 +336,10 @@ async def get_filament_info(setting_ids: list[str] = Body(...), db: AsyncSession
continue
try:
data = await cloud.get_setting_detail(setting_id)
# Transform filament_id to setting_id format (GFA00 -> GFSA00)
api_setting_id = _filament_id_to_setting_id(setting_id)
data = await cloud.get_setting_detail(api_setting_id)
setting = data.get("setting", {})
# Extract name (e.g., "Bambu PLA Basic Jade White")
@@ -323,11 +354,14 @@ async def get_filament_info(setting_ids: list[str] = Body(...), db: AsyncSession
k_value = None
info = {"name": name, "k": k_value}
# Cache using original ID so frontend gets expected response
_filament_cache[setting_id] = info
result[setting_id] = info
except Exception as e:
logger.warning(f"Failed to get cloud preset {setting_id}: {e}")
logger.warning(
f"Failed to get cloud preset {setting_id} (API ID: {_filament_id_to_setting_id(setting_id)}): {e}"
)
# Cache the failure to avoid repeated requests
_filament_cache[setting_id] = {"name": "", "k": None}
result[setting_id] = {"name": "", "k": None}
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Debug script to investigate Bambu Cloud preset API responses."""
import asyncio
import json
import sys
from pathlib import Path
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))
import httpx
from sqlalchemy import create_engine, text
from backend.app.core.config import settings
# Test preset IDs (from the warning logs)
TEST_IDS = ["GFG02", "GFL05", "GFA00", "GFA02", "GFA06"]
def get_token_from_db() -> str | None:
"""Get the stored token from the database."""
db_path = settings.base_dir / "bambuddy.db"
engine = create_engine(f"sqlite:///{db_path}")
with engine.connect() as conn:
result = conn.execute(text("SELECT value FROM settings WHERE key = 'bambu_cloud_token'"))
row = result.fetchone()
if row and row[0]:
return row[0]
return None
async def test_preset(setting_id: str, token: str, base_url: str = "https://api.bambulab.com"):
"""Test fetching a single preset and show full response."""
url = f"{base_url}/v1/iot-service/api/slicer/setting/{setting_id}"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
print(f"\n{'=' * 60}")
print(f"Testing preset: {setting_id}")
print(f"URL: {url}")
print(f"{'=' * 60}")
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers)
print(f"Status: {response.status_code}")
print("\nResponse body:")
try:
data = response.json()
print(json.dumps(data, indent=2))
except Exception:
print(response.text)
return response.status_code
async def main():
# Get token from DB
token = get_token_from_db()
if not token:
print("Could not find token in database.")
print("Make sure you're logged into Bambu Cloud in Bambuddy.")
sys.exit(1)
print(f"Found token in database (length: {len(token)})")
# Allow testing specific preset IDs from command line
test_ids = sys.argv[1:] if len(sys.argv) > 1 else TEST_IDS
# Test each preset
for preset_id in test_ids:
await test_preset(preset_id, token)
if __name__ == "__main__":
asyncio.run(main())