Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e4ac90b
fix(moonraker): deliver ws callbacks on the GUI thread, fail them on …
gmmcosta15 Oct 13, 2026
70ea670
fix(moonraker): keep error replies from blanking AMU gates, unify the…
gmmcosta15 Oct 13, 2026
8cd5b58
fix(amu): rebuild the MMU state model, guard Happy-Hare v3 gcode and …
gmmcosta15 Oct 13, 2026
ed5f9c5
refactor(amu): drop the unused drying_state gate field
gmmcosta15 Oct 13, 2026
b99191e
fix(spoolman): route gate fill, spool assignment and basic panel load…
gmmcosta15 Oct 13, 2026
21e0bf1
test(amu): drop the fetch_spool error test, the backend rebuild remov…
gmmcosta15 Oct 13, 2026
585adcf
Merge branch 'fix/amu-2-backend' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 13, 2026
6af2139
fix(files): retry a history lookup only after a disconnect, a deleted…
gmmcosta15 Oct 13, 2026
bf10eec
Merge branch 'fix/amu-1-ws-transport' into fix/amu-2-backend
gmmcosta15 Oct 13, 2026
8c8d494
Merge branch 'fix/amu-2-backend' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 13, 2026
843e282
Merge remote-tracking branch 'origin/dev' into fix/amu-2-backend
gmmcosta15 Oct 13, 2026
2a31c4c
Merge branch 'fix/amu-2-backend' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 13, 2026
f887ce0
Merge remote-tracking branch 'origin/dev' into fix/amu-2-backend
gmmcosta15 Oct 13, 2026
75159ed
Merge branch 'fix/amu-2-backend' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 13, 2026
6acd261
fix(amu): ignore the None MMU state sent on Klipper restart in Filame…
gmmcosta15 Oct 14, 2026
2eee276
Merge branch 'fix/amu-2-backend' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 14, 2026
b42240e
Merge remote-tracking branch 'origin/dev' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 14, 2026
4c6bac9
Merge remote-tracking branch 'origin/dev' into fix/amu-3-spoolman-flows
gmmcosta15 Oct 14, 2026
43ff435
fix(amu): eject from the basic panel, show load/unload refusals, matc…
gmmcosta15 Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 39 additions & 21 deletions BlocksScreen/devices/amu/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,38 +187,56 @@ def reset_mmu(self) -> None:
"""Erase all persisted MMU state (gate map, TTG map, statistics) and re-init."""
self.run_gcode_signal.emit("MMU_RESET CONFIRM=1")

def load_refusal(self) -> str:
"""Return why the selected gate cannot load, or "" when it is idle-ready."""
state = self._mmu_state
if state is None:
return "MMU status not received yet."
if not state.enabled:
return "MMU is disabled."
if state.is_printing:
return "Not available while printing."
if not state.is_homed:
return "MMU is not homed."
if state.filament_pos != FilamentPos.UNLOADED:
return "Filament is not fully unloaded."
if state.current_gate_info is None:
return "No gate is selected."
return ""

def unload_refusal(self) -> str:
"""Return why the filament cannot be unloaded, or "" when it can."""
state = self._mmu_state
if state is None:
return "MMU status not received yet."
if not state.enabled:
return "MMU is disabled."
if state.is_printing:
return "Not available while printing."
if state.filament_pos == FilamentPos.UNLOADED:
return "Filament is already unloaded."
return ""

def _selected_gate_unloaded(self) -> GateInfo | None:
"""Return the selected gate when the MMU is idle-ready: enabled, homed, unloaded."""
state = self._mmu_state
if (
state is None
or not (state.enabled and state.is_homed)
or state.is_printing
or state.filament_pos != FilamentPos.UNLOADED
):
if self._mmu_state is None or self.load_refusal():
return None
return state.current_gate_info
return self._mmu_state.current_gate_info

def load_gate(self) -> bool:
"""Load filament from the selected gate by sending MMU_LOAD; True if sent."""
if self._selected_gate_unloaded() is None:
logger.warning("Ignoring MMU_LOAD: no homed, unloaded gate selected")
if reason := self.load_refusal():
logger.warning("Ignoring MMU_LOAD: %s", reason)
return False
self.run_gcode_signal.emit("MMU_LOAD")
return True

def unload(self) -> bool:
"""Unload the currently loaded filament by sending MMU_UNLOAD; True if sent."""
state = self._mmu_state
if (
state is None
or not state.enabled
or state.is_printing
or state.filament_pos == FilamentPos.UNLOADED
):
logger.warning("Ignoring MMU_UNLOAD: MMU disabled, printing or unloaded")
def unload(self, eject: bool = False) -> bool:
"""Unload via MMU_UNLOAD, or MMU_EJECT when *eject*; True if sent."""
if reason := self.unload_refusal():
logger.warning("Ignoring unload: %s", reason)
return False
self.run_gcode_signal.emit("MMU_UNLOAD")
self.run_gcode_signal.emit("MMU_EJECT" if eject else "MMU_UNLOAD")
return True

def select_gate(self, gate: int) -> None:
Expand Down
Loading