fix(filament-mapping): X2D/H2D dual-nozzle without AMS lost

external-spool extruder routing (#1257)

  X2D with 0 AMS units and two external spools (Ext-L feeding left
  extruder, Ext-R feeding right) showed "Required filament type not
  found in printer" even when the matching filament was physically
  loaded. Cause: useFilamentMapping derived dual-nozzle status from
  ams_extruder_map being non-empty -- that map is populated from AMS
  info bits, so dual-nozzle printers without AMS got an empty map
  and hasDualNozzle=false. External spools then fell through to
  extruderId=undefined, and the nozzle-aware filter rejected every
  candidate because undefined !== 0/1.

  Prefer the hardware-reported printerStatus.nozzles array length as
  the dual-nozzle signal -- populated regardless of AMS configuration
  -- and keep the ams_extruder_map branch as fallback for older
  firmware that might not surface nozzles. Affects all dual-nozzle
  printers running without AMS: X2D, H2D, X2 Pro.

  Regression test pins both layers the bug straddled --
  buildLoadedFilaments extruderId assignment per external spool, and
  computeAmsMapping picking the correct external for a per-nozzle
  requirement -- so a future change that re-breaks either fails CI.
This commit is contained in:
maziggy
2026-05-10 10:15:07 +02:00
parent 79d54a8d53
commit 080176c6e5
5 changed files with 80 additions and 3 deletions
+13 -1
View File
@@ -16,7 +16,19 @@ import type { PrinterStatus } from '../api/client';
export function buildLoadedFilaments(printerStatus: PrinterStatus | undefined): LoadedFilament[] {
const filaments: LoadedFilament[] = [];
const amsExtruderMap = printerStatus?.ams_extruder_map;
const hasDualNozzle = amsExtruderMap && Object.keys(amsExtruderMap).length > 0;
// Dual-nozzle detection. The backend always emits a 2-entry nozzles array
// (default-stub second entry for single-nozzle printers), so length is not
// a reliable signal. Real second-nozzle hardware sets `nozzle_diameter` from
// the MQTT `right_nozzle_diameter` field (bambu_mqtt.py:2619-2621); without
// that field, nozzles[1] stays at its empty default. Belt-and-braces: a
// populated ams_extruder_map (dual-nozzle with AMS) and >1 vt_tray (only
// dual-nozzle hardware exposes multiple external feeds) each independently
// imply dual-nozzle — keep them as fallbacks for any firmware rev that
// surfaces one signal but not the other. (#1257)
const hasDualNozzle =
Boolean(printerStatus?.nozzles?.[1]?.nozzle_diameter)
|| (amsExtruderMap && Object.keys(amsExtruderMap).length > 0)
|| (printerStatus?.vt_tray?.length ?? 0) > 1;
// Add filaments from all AMS units (regular and HT)
printerStatus?.ams?.forEach((amsUnit) => {