فهرست منبع

Fix AMS fill level showing 0% for non-Viewer users (#676)

  When inventory spool assignments had stale weight_used values, the fill
  level fallback chain (Spoolman → Inventory → AMS remain) used nullish
  coalescing (??), which doesn't fall through on 0. A stale inventory
  fill of 0% permanently shadowed the correct real-time AMS remain value.
  Viewer users were unaffected because their group lacked the
  inventory:view_assignments permission, so the query never fired.

  Now when inventory says 0% but AMS hardware reports positive remain,
  the inventory value is bypassed in favor of the live AMS data.
maziggy 2 ماه پیش
والد
کامیت
adb0500e67

+ 1 - 0
CHANGELOG.md

@@ -26,6 +26,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Prometheus Build Info Metric** ([#633](https://github.com/maziggy/bambuddy/pull/633)) — Added a `bambuddy_build_info` gauge metric to the Prometheus metrics endpoint, exposing the application version, Python version, platform, and architecture as labels. Follows the standard Prometheus `_build_info` convention for dashboards and version-change alerting. Contributed by @sw1nn.
 
 ### Fixed
+- **AMS Fill Level Shows 0% for Non-Viewer Users** ([#676](https://github.com/maziggy/bambuddy/issues/676)) — When authentication was enabled with advanced permissions, users with `inventory:view_assignments` permission saw 0% fill level on AMS slots where inventory spool data had stale `weight_used` values. The fill level fallback chain (Spoolman → Inventory → AMS remain) used nullish coalescing (`??`), which doesn't fall through on `0` — so a stale inventory fill of 0% permanently shadowed the correct real-time AMS remain value from the printer. Now, when inventory says 0% but the AMS hardware reports a positive remain, the inventory value is bypassed in favor of the live AMS data. Viewer users were unaffected because their group lacked `inventory:view_assignments`, so the inventory query never fired and the AMS remain was used directly. Reported by @cadtoolbox.
 - **Virtual Printer Proxy Mode Always Shows X1C Model** — Creating a virtual printer in Proxy mode always set the model to X1C regardless of the destination printer, because the frontend hides the model dropdown in proxy mode and the backend defaulted to X1C. Now auto-inherits the model from the target printer when creating or updating a proxy virtual printer (e.g. a proxy pointing at a P1S correctly presents itself as P1S to the slicer). The model also auto-updates when changing the target printer or switching to proxy mode.
 - **Cloud Profiles Shared Across All Users** ([#665](https://github.com/maziggy/bambuddy/issues/665)) — When authentication was enabled, Bambu Cloud credentials were stored globally — one account per Bambuddy instance. If User A logged into Cloud, every other user saw User A's account and profiles. User B logging in would overwrite User A's credentials. Cloud credentials are now stored per-user: each user logs into their own Bambu Cloud account independently. When auth is disabled (single-user mode), behavior is unchanged. Also fixed cloud data endpoints (`/cloud/settings`, `/cloud/fields`, preset CRUD) requiring `settings:read` / `settings:update` permissions instead of `cloud:auth` — users who had "Cloud Auth" enabled but "Settings" disabled couldn't load profiles after logging in. Reported by @cadtoolbox.
 - **Local Profiles Not Shown in AMS Slot Configuration** — Imported local filament profiles were hidden in the AMS slot configure modal when a printer model was set. The `compatible_printers` filter parsed the stored JSON array as a semicolon-delimited string, so the matching always failed and every local preset was silently skipped. Removed the filter entirely — user-imported profiles should be available on any printer.

+ 3 - 1
frontend/src/components/spoolbuddy/AmsUnitCard.tsx

@@ -147,7 +147,9 @@ function SpoolSlot({ tray, slotIndex, isActive, fillOverride, onClick }: SpoolSl
   const isEmpty = isTrayEmpty(tray);
   const color = trayColorToCSS(tray.tray_color);
   const amsFill = tray.remain !== null && tray.remain !== undefined && tray.remain >= 0 ? tray.remain : null;
-  const effectiveFill = fillOverride ?? amsFill;
+  // If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
+  const resolvedOverride = (fillOverride === 0 && amsFill !== null && amsFill > 0) ? null : fillOverride;
+  const effectiveFill = resolvedOverride ?? amsFill;
 
   return (
     <div

+ 16 - 6
frontend/src/pages/PrintersPage.tsx

@@ -3182,9 +3182,13 @@ function PrinterCard({
                                   }
                                   return null;
                                 })();
-                                const effectiveFill = spoolmanFill ?? inventoryFill ?? (hasFillLevel ? tray.remain : null);
+                                // If inventory says 0% but AMS reports positive remain, prefer AMS
+                                // (inventory weight_used may be stale or over-counted — #676)
+                                const resolvedInventoryFill = (inventoryFill === 0 && hasFillLevel && tray.remain > 0)
+                                  ? null : inventoryFill;
+                                const effectiveFill = spoolmanFill ?? resolvedInventoryFill ?? (hasFillLevel ? tray.remain : null);
                                 const fillSource = spoolmanFill !== null ? 'spoolman' as const
-                                  : inventoryFill !== null ? 'inventory' as const
+                                  : resolvedInventoryFill !== null ? 'inventory' as const
                                   : hasFillLevel ? 'ams' as const
                                   : undefined;
 
@@ -3406,9 +3410,12 @@ function PrinterCard({
                           }
                           return null;
                         })();
-                        const htEffectiveFill = htSpoolmanFill ?? htInventoryFill ?? (hasFillLevel ? tray.remain : null);
+                        // If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
+                        const htResolvedInventoryFill = (htInventoryFill === 0 && hasFillLevel && tray.remain > 0)
+                          ? null : htInventoryFill;
+                        const htEffectiveFill = htSpoolmanFill ?? htResolvedInventoryFill ?? (hasFillLevel ? tray.remain : null);
                         const htFillSource = htSpoolmanFill !== null ? 'spoolman' as const
-                          : htInventoryFill !== null ? 'inventory' as const
+                          : htResolvedInventoryFill !== null ? 'inventory' as const
                           : hasFillLevel ? 'ams' as const
                           : undefined;
 
@@ -3732,9 +3739,12 @@ function PrinterCard({
                                 return null;
                               })();
                               const extHasFillLevel = extTray.tray_type && extTray.remain >= 0;
-                              const extEffectiveFill = extSpoolmanFill ?? extInventoryFill ?? (extHasFillLevel ? extTray.remain : null);
+                              // If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
+                              const extResolvedInventoryFill = (extInventoryFill === 0 && extHasFillLevel && extTray.remain > 0)
+                                ? null : extInventoryFill;
+                              const extEffectiveFill = extSpoolmanFill ?? extResolvedInventoryFill ?? (extHasFillLevel ? extTray.remain : null);
                               const extFillSource = extSpoolmanFill !== null ? 'spoolman' as const
-                                : extInventoryFill !== null ? 'inventory' as const
+                                : extResolvedInventoryFill !== null ? 'inventory' as const
                                 : extHasFillLevel ? 'ams' as const
                                 : undefined;
 

+ 6 - 2
frontend/src/pages/spoolbuddy/SpoolBuddyAmsPage.tsx

@@ -228,6 +228,8 @@ export function SpoolBuddyAmsPage() {
       };
       const invFill = fillOverrides[`${unit.id}-0`] ?? null;
       const amsFill = tray.remain != null && tray.remain >= 0 ? tray.remain : null;
+      // If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
+      const resolvedInvFill = (invFill === 0 && amsFill !== null && amsFill > 0) ? null : invFill;
       items.push({
         key: `ht-${unit.id}`,
         label: getAmsName(unit.id),
@@ -237,7 +239,7 @@ export function SpoolBuddyAmsPage() {
         temp: unit.temp,
         humidity: unit.humidity,
         nozzleSide: getNozzleSide(unit.id),
-        effectiveFill: invFill ?? amsFill,
+        effectiveFill: resolvedInvFill ?? amsFill,
         onClick: () => handleAmsSlotClick(unit.id, 0, isTrayEmpty(tray) ? null : tray),
       });
     }
@@ -253,6 +255,8 @@ export function SpoolBuddyAmsPage() {
       const extSlotTrayId = extTrayId - 254;
       const extInvFill = fillOverrides[`255-${extSlotTrayId}`] ?? null;
       const extAmsFill = extTray.remain != null && extTray.remain >= 0 ? extTray.remain : null;
+      // If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
+      const extResolvedInvFill = (extInvFill === 0 && extAmsFill !== null && extAmsFill > 0) ? null : extInvFill;
       items.push({
         key: `ext-${extTrayId}`,
         label: isDualNozzle
@@ -262,7 +266,7 @@ export function SpoolBuddyAmsPage() {
         isEmpty: isTrayEmpty(extTray),
         isActive: isExtActive,
         nozzleSide: null,
-        effectiveFill: extInvFill ?? extAmsFill,
+        effectiveFill: extResolvedInvFill ?? extAmsFill,
         onClick: () => handleExtSlotClick(extTray),
       });
     }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
static/assets/index-C7vUGUA4.js


+ 1 - 1
static/index.html

@@ -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-COExSGYQ.js"></script>
+    <script type="module" crossorigin src="/assets/index-C7vUGUA4.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index--YKaUCwD.css">
   </head>
   <body>

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است