feat(printer): support Filament Track Switch (FTS) accessory in print modal (#1162)

The FTS routes any AMS slot to either extruder, so AMS info reports
  bits 8-11 = 0xE (uninitialized) and ams_extruder_map ends up empty.
  The print modal's per-nozzle dropdown filter then hides every loaded
  slot, leaving the user with an empty filament dropdown.

  Detection: parse print.device.fila_switch from MQTT push_status into a
  new FilaSwitchState dataclass on PrinterState; surface it through the
  GET /printers/{id}/status response as a nullable FilaSwitchResponse.

  Frontend: useFilamentMapping and FilamentMapping skip the per-extruder
  filter when fila_switch.installed is true. Slots currently fed into a
  track display an [L]/[R] routing badge in the dropdown so the user
  can see where the FTS is currently routing them.

  Tests: 4 backend unit (TestFilamentTrackSwitchDetection), 2 backend
  integration (status route), 2 hook regression, 2 component regression.
This commit is contained in:
maziggy
2026-04-29 16:18:45 +02:00
parent f45f6a131d
commit b45ca2a662
14 changed files with 596 additions and 117 deletions
+14
View File
@@ -238,6 +238,16 @@ export interface PrintOptions {
filament_tangle_detect: boolean;
}
export interface FilaSwitchState {
installed: boolean;
// in[track] = currently loaded slot for that track (-1 = empty)
in_slots: number[];
// out[track] = extruder this track terminates at (0 = right, 1 = left)
out_extruders: number[];
stat: number;
info: number;
}
export interface PrinterStatus {
id: number;
name: string;
@@ -299,6 +309,10 @@ export interface PrinterStatus {
// Format: {ams_id: extruder_id} where extruder 0=right, 1=left
// Note: JSON keys are always strings
ams_extruder_map: Record<string, number>;
// Filament Track Switch accessory — null when not installed. When present,
// AMS slots aren't tied to a specific extruder; the FTS routes any slot to
// either extruder, so per-extruder slot filtering must be skipped.
fila_switch: FilaSwitchState | null;
// Currently loaded tray (global tray ID, 255 = no filament loaded, 254 = external spool)
tray_now: number;
// AMS status for filament change tracking (0=idle, 1=filament_change, 2=rfid_identifying, 3=assist, 4=calibration)