From c8b1c24b5baa5e5e6351430dfda11912cf8105e3 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Thu, 2 Apr 2026 12:17:55 +0100 Subject: [PATCH 01/17] Formatting, add G92 E0 on end tool --- klippy/extras/load_filament.py | 118 ++++++++++--------------------- klippy/extras/unload_filament.py | 87 ++++++----------------- 2 files changed, 59 insertions(+), 146 deletions(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index 31846e756a49..c5639c580d33 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -22,39 +22,27 @@ def __init__(self, config): self.cutter_object = self.cutter_name = None self.bucket_object = None self.custom_boundary_object = None - self.filament_switch_sensor_name = ( - self.filament_switch_sensor_object - ) = None - self.filament_flow_sensor_name = self.filament_flow_sensor_object = ( - None - ) + self.filament_switch_sensor_name = self.filament_switch_sensor_object = None + self.filament_flow_sensor_name = self.filament_flow_sensor_object = None self.load_started: bool = False self.current_purge_index: int = 0 self.travel_speed = None self._old_extrude_distance: float | None = None self.extrude_count: int = 0 - self.printer.register_event_handler( - "klippy:connect", self.handle_connect - ) + self.printer.register_event_handler("klippy:connect", self.handle_connect) self.printer.register_event_handler("klippy:ready", self.handle_ready) self.idex = config.getboolean("idex", False) - self.has_custom_boundary = config.getboolean( - "has_custom_boundary", False - ) + self.has_custom_boundary = config.getboolean("has_custom_boundary", False) if config.get("filament_flow_sensor_name", None): - self.filament_flow_sensor_name = config.get( - "filament_flow_sensor_name" - ) + self.filament_flow_sensor_name = config.get("filament_flow_sensor_name") if ( config.get("filament_switch_sensor_name", None) and self.filament_flow_sensor_object is None ): - self.filament_switch_sensor_name = config.get( - "filament_switch_sensor_name" - ) + self.filament_switch_sensor_name = config.get("filament_switch_sensor_name") if ( config.get("cutter_sensor_name", None) and not self.filament_flow_sensor_object @@ -174,9 +162,7 @@ def handle_ready(self): self.custom_boundary_object = self.printer.lookup_object( "bed_custom_bound", None ) - logging.info( - "[LOAD FILAMENT EXTRA] Bed custom boundary extra recognized" - ) + logging.info("[LOAD FILAMENT EXTRA] Bed custom boundary extra recognized") def handle_cutter_filament_present(self, eventtime=None) -> None: if not self.load_started or not self.cutter_object: @@ -186,9 +172,7 @@ def handle_cutter_filament_present(self, eventtime=None) -> None: logging.info( "[LOAD FILAMENT EXTRA] 'filament_present' event received from cutter sensor" ) - self.reactor.update_timer( - self.extrude_to_sensor_timer, self.reactor.NEVER - ) + self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return @@ -199,33 +183,23 @@ def verify_switch_sensor_state(self, eventtime): if self.filament_switch_sensor_object.get_status(eventtime)[ "filament_detected" ]: - self.reactor.update_timer( - self.extrude_to_sensor_timer, self.reactor.NEVER - ) - self.move_extruder_mm( - self.extruder_to_nozzle_dist, speed=30, wait=True - ) - self.reactor.update_timer( - self.extrude_purge_timer, self.reactor.NOW - ) + self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) + self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) + self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return self.reactor.NEVER else: return eventtime + 1.50 def verify_flow_sensor_state(self, eventtime): if self.load_started and self.filament_flow_sensor_object: - if self.filament_flow_sensor_object.runout_helper.get_status( - eventtime - )["filament_detected"]: + if self.filament_flow_sensor_object.runout_helper.get_status(eventtime)[ + "filament_detected" + ]: self.reactor.update_timer( self.extrude_to_sensor_timer, self.reactor.NEVER ) - self.move_extruder_mm( - self.extruder_to_nozzle_dist, speed=30, wait=True - ) - self.reactor.update_timer( - self.extrude_purge_timer, self.reactor.NOW - ) + self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) + self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return self.reactor.NEVER return eventtime + 0.775 @@ -238,9 +212,7 @@ def load(self, eventtime): self.move_extruder_mm( self.extruder_to_nozzle_dist, speed=30, wait=True ) # Extrude to the nozzle - self.reactor.update_timer( - self.extrude_purge_timer, self.reactor.NOW - ) + self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return self.reactor.NEVER self.extrude_count += 1 @@ -259,9 +231,7 @@ def purge_extrude(self, eventtime): self.gcode.respond_info( f"[LOAD FILAMENT] Purging....{self.current_purge_index + 1}" ) - self.move_extruder_mm( - distance=self.purge_distance, speed=self.purge_speed - ) + self.move_extruder_mm(distance=self.purge_distance, speed=self.purge_speed) self.current_purge_index += 1 return eventtime + float(self.purge_interval) @@ -269,36 +239,30 @@ def _purge_end(self, eventtime): self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NEVER) if self.park is not None: - self.toolhead.manual_move( - [self.park[0], self.park[1]], self.travel_speed - ) + self.toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) self.toolhead.wait_moves() - if ( - self.has_custom_boundary - and self.custom_boundary_object is not None - ): + if self.has_custom_boundary and self.custom_boundary_object is not None: if self.custom_boundary_object.get_status()["status"] == "default": self.custom_boundary_object.set_custom_boundary() self._old_extrude_distance = None self.current_purge_index = 0 self.load_started = False - self.printer.send_event("load_filament:end") - self.gcode.run_script_from_command("G91\nM400") - self.gcode.run_script_from_command("M83\nM400") - self.gcode.run_script_from_command( - "G92 E0.0\nM400" - ) # Restore extruder position - self.gcode.run_script_from_command("M82\nM400") - self.toolhead.wait_moves() - self.gcode.respond_info("Restoring gcode positions.") + + self.gcode.run_script_from_command("G91") + self.gcode.run_script_from_command("M83") # relative + self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position + self.gcode.run_script_from_command("M82\nM400") # Absolute self.restore_state() + + self.gcode.respond_info("Restoring gcode positions.") self.toolhead.wait_moves() self.heat_and_wait(0, wait=False) if self.idex: self.gcode.run_script_from_command("T0 PARK\nM400") + self.printer.send_event("load_filament:end") def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: """Move the extruder @@ -327,9 +291,7 @@ def move_home(self): """Move the toolhead to the home position (To the park position)""" if self.toolhead is None: return - self.toolhead.manual_move( - [self.park[0], self.park[1]], self.travel_speed - ) + self.toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) self.toolhead.wait_moves() def home_needed(self): @@ -346,9 +308,7 @@ def home_needed(self): self.gcode.respond_info("Homing") self.gcode.run_script_from_command("G28") except Exception as e: - logging.error( - f"Unable to perform home on load filament, error: {e}" - ) + logging.error(f"Unable to perform home on load filament, error: {e}") def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): """Heats the extruder and wait. @@ -395,9 +355,7 @@ def save_state(self): self.gcode.run_script_from_command( f"SAVE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}\nM400" ) - self.gcode.run_script_from_command( - "SAVE_GCODE_STATE NAME=_LOAD_STATE\nM400" - ) + self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_LOAD_STATE\nM400") return True def restore_state(self): @@ -423,9 +381,7 @@ def cmd_PURGE_STOP(self, gcmd): cmd_LOAD_FILAMENT_HELPER = """Load Filament to the toolhead routine""" def cmd_LOAD_FILAMENT(self, gcmd): - temp = gcmd.get( - "TEMPERATURE", 220.0, parser=float, minval=210, maxval=250 - ) + temp = gcmd.get("TEMPERATURE", 220.0, parser=float, minval=210, maxval=250) try: if self.load_started: self.gcode.respond_info("Already loading filament") @@ -446,9 +402,9 @@ def cmd_LOAD_FILAMENT(self, gcmd): else: self.gcode.run_script_from_command("T1 LOAD") - self.gcode.run_script_from_command("G91\nM400") - self.gcode.run_script_from_command("M83\nM400") - + self.gcode.run_script_from_command("G91") + self.gcode.run_script_from_command("M83") + self.gcode.run_script_from_command("G92 E0.0") self.extrude_count = 0 if self.custom_boundary_object is not None: @@ -473,9 +429,7 @@ def cmd_LOAD_FILAMENT(self, gcmd): self.move_extruder_mm(distance=30, speed=40, wait=True) # * Actually start loading the machine - self.reactor.update_timer( - self.extrude_to_sensor_timer, self.reactor.NOW - ) + self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NOW) if self.filament_flow_sensor_object is not None: self.reactor.update_timer( diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index 22211da6952a..275b42b482a5 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -20,29 +20,19 @@ def __init__(self, config): self.min_event_systime = None self.bucket_object = None self.cutter_object = None - self.filament_flow_sensor_object = self.filament_flow_sensor_name = ( - None - ) - self.filament_switch_sensor_object = ( - self.filament_switch_sensor_name - ) = None + self.filament_flow_sensor_object = self.filament_flow_sensor_name = None + self.filament_switch_sensor_object = self.filament_switch_sensor_name = None self.unload_started = False self.unextrude_count: int = 0 self.travel_speed = None - self.printer.register_event_handler( - "klippy:connect", self.handle_connect - ) + self.printer.register_event_handler("klippy:connect", self.handle_connect) self.printer.register_event_handler("klippy:ready", self.handle_ready) self.printer.register_event_handler( "unload_filament:end", - lambda: self.gcode.respond_info( - "[UNLOAD FILAMENT] Unload Finished" - ), + lambda: self.gcode.respond_info("[UNLOAD FILAMENT] Unload Finished"), ) self.idex = config.getboolean("idex", False) - self.has_custom_boundary = config.getboolean( - "has_custom_boundary", False - ) + self.has_custom_boundary = config.getboolean("has_custom_boundary", False) self.travel_speed = config.getfloat( "travel_speed", 100.0, minval=50.0, maxval=500.0 ) @@ -59,9 +49,7 @@ def __init__(self, config): "unload_speed", default=10.0, minval=2.0, maxval=200.0 ) self.cutter_name = config.get("cutter_sensor_name", None) - self.timeout = config.getint( - "timeout", default=None, minval=10, maxval=1000 - ) + self.timeout = config.getint("timeout", default=None, minval=10, maxval=1000) self.unextrude_timer = self.reactor.register_timer( self.unextrude, self.reactor.NEVER ) @@ -84,18 +72,14 @@ def handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 if self.has_custom_boundary: - self.custom_boundary_object = self.printer.lookup_object( - "bed_custom_bound" - ) + self.custom_boundary_object = self.printer.lookup_object("bed_custom_bound") if self.bucket: self.bucket_object = self.printer.lookup_object("bucket") if self.cutter_name: if ( - self.printer.lookup_object( - f"cutter_sensor {self.cutter_name}", None - ) + self.printer.lookup_object(f"cutter_sensor {self.cutter_name}", None) is not None ): self.cutter_object = self.printer.lookup_object( @@ -159,9 +143,7 @@ def unload_end(self, eventtime=None): self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") - self.gcode.run_script_from_command( - "G92 E0.0" - ) # Restore extruder position + self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position self.gcode.run_script_from_command("M82\nM400") self.restore_state() @@ -169,8 +151,6 @@ def unload_end(self, eventtime=None): self.custom_boundary_object.set_custom_boundary() self.custom_boundary_object.move_to_park() - - self.gcode.respond_info("Cooling down extruder") self.heat_extruder(0, wait=False) @@ -193,44 +173,30 @@ def unextrude(self, eventtime): self.reactor.update_timer( self.verify_switch_sensor_timer, self.reactor.NEVER ) - completion = self.reactor.register_callback( - self.unload_end - ) + completion = self.reactor.register_callback(self.unload_end) return completion.wait() self.unextrude_count += 1 - self.move_extruder_mm( - distance=-10, speed=self.unload_speed, wait=False - ) + self.move_extruder_mm(distance=-10, speed=self.unload_speed, wait=False) return float(eventtime + float(10 / self.unload_speed)) except Exception as e: - raise UnloadFilamentError( - f"[UNLOAD FILAMENT] Error while unloading: {e}" - ) + raise UnloadFilamentError(f"[UNLOAD FILAMENT] Error while unloading: {e}") def disable_sensors(self): if self.filament_flow_sensor_object: - self.filament_flow_sensor_object.runout_helper.sensor_enabled = ( - False - ) + self.filament_flow_sensor_object.runout_helper.sensor_enabled = False if self.filament_switch_sensor_object: - self.filament_switch_sensor_object.runout_helper.sensor_enabled = ( - False - ) + self.filament_switch_sensor_object.runout_helper.sensor_enabled = False self.gcode.respond_info("filament switch sensor is not enabled") def enable_sensors(self): if self.filament_flow_sensor_object: - self.filament_flow_sensor_object.runout_helper.sensor_enabled = ( - True - ) + self.filament_flow_sensor_object.runout_helper.sensor_enabled = True if self.filament_switch_sensor_object: self.gcode.respond_info("filament switch sensor is now enabled") - self.filament_switch_sensor_object.runout_helper.sensor_enabled = ( - True - ) + self.filament_switch_sensor_object.runout_helper.sensor_enabled = True def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: """Move the extruder @@ -253,9 +219,7 @@ def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: if wait: self.toolhead.wait_moves() except Exception as e: - raise UnloadFilamentError( - f"[UNLOAD FILAMENT] Error moving extruder {e}" - ) + raise UnloadFilamentError(f"[UNLOAD FILAMENT] Error moving extruder {e}") def home_needed(self) -> None: if not self.toolhead: @@ -301,9 +265,7 @@ def save_state(self) -> None: self.gcode.run_script_from_command( "SAVE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state\nM400" ) - self.gcode.run_script_from_command( - "SAVE_GCODE_STATE NAME=_UNLOAD_STATE\nM400" - ) + self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_UNLOAD_STATE\nM400") def restore_state(self) -> None: """Restore gcode state and dual carriage state if the system is in IDEX configuration""" @@ -316,9 +278,7 @@ def restore_state(self) -> None: ) def cmd_UNLOAD_FILAMENT(self, gcmd): - temp = gcmd.get( - "TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0 - ) + temp = gcmd.get("TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0) if not self.toolhead: return try: @@ -339,8 +299,9 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): self.printer.send_event("unload_filament:start") self.gcode.respond_info("[UNLOAD FILAMENT] Start") - self.gcode.run_script_from_command("G91\nM400") - self.gcode.run_script_from_command("M83\nM400") + self.gcode.run_script_from_command("G91") + self.gcode.run_script_from_command("M83") + self.gcode.run_script_from_command("G92 E0.0") if self.timeout: self.unextrude_count = 0 @@ -364,9 +325,7 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): ) ) if not self.cutter_object and self.timeout: - self.reactor.update_timer( - self.unextrude_timer, self.reactor.NOW - ) + self.reactor.update_timer(self.unextrude_timer, self.reactor.NOW) if self.filament_flow_sensor_object: self.reactor.update_timer( self.verify_flow_sensor_timer, self.reactor.NOW From 3e0dc7b076c6270afa03ff06f64671008131bb0a Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Fri, 10 Apr 2026 12:40:47 +0100 Subject: [PATCH 02/17] Refactor logic --- klippy/extras/unload_filament.py | 85 +++++++++++++------------------- 1 file changed, 34 insertions(+), 51 deletions(-) diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index 275b42b482a5..76f74e1559ed 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -6,7 +6,7 @@ class UnloadFilamentError(Exception): """Raised when there is an error unloading filament""" - def __init__(self, message, errors: typing.Optional[list] = None): + def __init__(self, message, errors: list[str] | None = None): super(UnloadFilamentError, self).__init__(message) self.errors = errors @@ -22,10 +22,10 @@ def __init__(self, config): self.cutter_object = None self.filament_flow_sensor_object = self.filament_flow_sensor_name = None self.filament_switch_sensor_object = self.filament_switch_sensor_name = None + self.idex_object = None self.unload_started = False self.unextrude_count: int = 0 self.travel_speed = None - self.printer.register_event_handler("klippy:connect", self.handle_connect) self.printer.register_event_handler("klippy:ready", self.handle_ready) self.printer.register_event_handler( "unload_filament:end", @@ -65,18 +65,12 @@ def __init__(self, config): "GCODE Macro to unload filament, takes into account if there is a belay and or a filament cutter with a sensor", ) - def handle_connect(self): - self.toolhead = self.printer.lookup_object("toolhead") - def handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 - if self.has_custom_boundary: self.custom_boundary_object = self.printer.lookup_object("bed_custom_bound") - if self.bucket: self.bucket_object = self.printer.lookup_object("bucket") - if self.cutter_name: if ( self.printer.lookup_object(f"cutter_sensor {self.cutter_name}", None) @@ -85,21 +79,17 @@ def handle_ready(self): self.cutter_object = self.printer.lookup_object( f"cutter_sensor {self.cutter_name}", None ) - self.printer.register_event_handler( "cutter_sensor:no_filament", self.handle_cutter_fnp, ) - if self.idex: self.idex_object = self.printer.lookup_object("dual_carriage") - if self.filament_flow_sensor_name: self.filament_flow_sensor_object = self.printer.lookup_object( f"filament_motion_sensor {self.filament_flow_sensor_name}", None, ) - if self.filament_switch_sensor_name: self.filament_switch_sensor_object = self.printer.lookup_object( f"filament_switch_sensor {self.filament_switch_sensor_name}", @@ -114,6 +104,9 @@ def handle_cutter_fnp(self) -> None: logging.info( "Cutter reported not having filament, pulling filament out of the printer" ) + self.reactor.update_timer( + self.verify_switch_sensor_timer, float(self.reactor.NOW + 10.0) + ) self.reactor.update_timer(self.unextrude_timer, self.reactor.NOW) def verify_switch_sensor_state(self, eventtime): @@ -128,7 +121,8 @@ def verify_switch_sensor_state(self, eventtime): ): self.reactor.update_timer(self.unextrude_timer, self.reactor.NEVER) completion = self.reactor.register_callback(self.unload_end) - return completion.wait() + completion.wait() + return self.reactor.NEVER return eventtime + 1.50 def verify_flow_sensor_state(self, eventtime): @@ -138,27 +132,21 @@ def verify_flow_sensor_state(self, eventtime): ... def unload_end(self, eventtime=None): - if not self.unload_started or not self.toolhead: + toolhead = self.printer.lookup_object("toolhead") + if not self.unload_started or not toolhead: return self.reactor.NEVER - self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position - self.gcode.run_script_from_command("M82\nM400") self.restore_state() - if self.custom_boundary_object: self.custom_boundary_object.set_custom_boundary() self.custom_boundary_object.move_to_park() - self.gcode.respond_info("Cooling down extruder") self.heat_extruder(0, wait=False) - if self.idex: self.gcode.respond_info("Parking toolhead 0") - self.gcode.run_script_from_command("T0 PARK\nM400") - - self.gcode.respond_info("[UNLOAD FILAMENT] Finished") + self.gcode.run_script_from_command("T0 PARK") self.unload_started = False self.printer.send_event("unload_filament:end") return self.reactor.NEVER @@ -204,29 +192,27 @@ def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: Args: distance (float): The distance in mm to move the extruder. """ - if not self.toolhead: - return + toolhead = self.printer.lookup_object("toolhead") try: eventtime = self.reactor.monotonic() gcode_move = self.printer.lookup_object("gcode_move") - prev_pos = self.toolhead.get_position() + prev_pos = toolhead.get_position() gcode_move.absolute_coord = False # G91 v = distance * gcode_move.get_status(eventtime)["extrude_factor"] new_distance = v + prev_pos[3] - self.toolhead.manual_move( + toolhead.manual_move( [prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed ) if wait: - self.toolhead.wait_moves() + toolhead.wait_moves() except Exception as e: raise UnloadFilamentError(f"[UNLOAD FILAMENT] Error moving extruder {e}") def home_needed(self) -> None: - if not self.toolhead: - return try: + toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() - kin = self.toolhead.get_kinematics() + kin = toolhead.get_kinematics() _homed_axes = kin.get_status(eventtime)["homed_axes"] if "xyz" in _homed_axes.lower(): return @@ -245,10 +231,9 @@ def heat_extruder(self, temp, wait: bool = False) -> None: wait (bool, optional): Whether to wait or not for the temperature to reach the interval . Defaults to True """ - # if not self.toolhead: - # return + toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() - extruder = self.toolhead.get_extruder() + extruder = toolhead.get_extruder() pheaters = self.printer.lookup_object("heaters") extruder_heater = extruder.get_heater() pheaters.set_temperature(extruder_heater, temp, False) @@ -263,58 +248,56 @@ def save_state(self) -> None: """Save gcode state and dual carriage state if the system is in IDEX configuration""" if self.idex: self.gcode.run_script_from_command( - "SAVE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state\nM400" + "SAVE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state" ) - self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_UNLOAD_STATE\nM400") + self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_UNLOAD_STATE") def restore_state(self) -> None: """Restore gcode state and dual carriage state if the system is in IDEX configuration""" self.gcode.run_script_from_command( - "RESTORE_GCODE_STATE NAME=_UNLOAD_STATE MOVE=1 MOVE_SPEED=100\nM400" + "RESTORE_GCODE_STATE NAME=_UNLOAD_STATE MOVE=1 MOVE_SPEED=100" ) if self.idex: self.gcode.run_script_from_command( - "RESTORE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state MOVE=0\nM400" + "RESTORE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state MOVE=0" ) def cmd_UNLOAD_FILAMENT(self, gcmd): temp = gcmd.get("TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0) - if not self.toolhead: - return try: + toolhead = self.printer.lookup_object("toolhead") + idle_timeout = self.printer.lookup_object("idle_timeout") + is_printing = ( + idle_timeout.get_status(self.reactor.monotonic())["state"] == "Printing" + ) + if is_printing: + self.gcode.respond_info("Cannot unload while printing") + return + if self.unload_started: self.gcode.respond_info("Printer already unloading filament") return self.home_needed() self.save_state() self.disable_sensors() # So not to pause the filament switch sensor when filament is taken out - if self.idex: if gcmd.get("TOOLHEAD") == "Load_T0": self.gcode.run_script_from_command("T0 UNLOAD") else: self.gcode.run_script_from_command("T1 UNLOAD") - self.unload_started = True self.printer.send_event("unload_filament:start") self.gcode.respond_info("[UNLOAD FILAMENT] Start") - self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") self.gcode.run_script_from_command("G92 E0.0") - if self.timeout: self.unextrude_count = 0 - self.heat_extruder(temp, wait=False) - if self.bucket_object: self.bucket_object.move_to_bucket() - self.heat_extruder(temp, wait=True) - - self.toolhead.wait_moves() - + toolhead.wait_moves() if self.cutter_object: self.reactor.register_callback( partial( @@ -324,19 +307,19 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): off_heaters=False, ) ) + return if not self.cutter_object and self.timeout: self.reactor.update_timer(self.unextrude_timer, self.reactor.NOW) if self.filament_flow_sensor_object: self.reactor.update_timer( self.verify_flow_sensor_timer, self.reactor.NOW ) - if self.filament_switch_sensor_object: self.gcode.respond_info( "[UNLOAD] Starting filament switch sensor verification in 10 seconds" ) self.reactor.update_timer( - self.verify_switch_sensor_timer, self.reactor.NOW + 5.0 + self.verify_switch_sensor_timer, float(self.reactor.NOW + 15.0) ) except Exception as e: From 24217b9b2d85f629761085a74b1d935592f8be30 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Fri, 10 Apr 2026 12:41:25 +0100 Subject: [PATCH 03/17] Fix bugs on dispatching callbacks and timed callbacks, refactor and cleanup --- klippy/extras/load_filament.py | 154 ++++++++++++++------------------- 1 file changed, 64 insertions(+), 90 deletions(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index c5639c580d33..dbae30cb5fa3 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -3,14 +3,6 @@ from functools import partial -class LoadFilamentError(Exception): - """Raised when there is an error loading filament""" - - def __init__(self, message, errors): - super(LoadFilamentError, self).__init__(message) - self.errors = errors - - class LoadFilament: def __init__(self, config): self.printer = config.get_printer() @@ -29,10 +21,8 @@ def __init__(self, config): self.travel_speed = None self._old_extrude_distance: float | None = None self.extrude_count: int = 0 - - self.printer.register_event_handler("klippy:connect", self.handle_connect) self.printer.register_event_handler("klippy:ready", self.handle_ready) - + self.min_event_systime = self.reactor.NEVER self.idex = config.getboolean("idex", False) self.has_custom_boundary = config.getboolean("has_custom_boundary", False) @@ -110,9 +100,6 @@ def __init__(self, config): "Helper gcode command that stop filament purging", ) - def handle_connect(self): - self.toolhead = self.printer.lookup_object("toolhead") - def handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 if self.bucket: @@ -133,12 +120,6 @@ def handle_ready(self): logging.info( "[LOAD FILAMENT EXTRA] Cutter Sensor recognized, cutter sensor state event handlers set" ) - # if self.printer.lookup_object("unload_filament", None) is not None: - # self.printer.register_event_handler( - # "unload_filament", self.handle_filament_unload - # ) - # logging.info("[LOAD FILAMENT EXTRA] Unload Extra recognized") - if self.idex: self.idex_object = self.printer.lookup_object("dual_carriage") logging.info("[LOAD FILAMENT EXTRA] Idex machine recognized") @@ -165,103 +146,97 @@ def handle_ready(self): logging.info("[LOAD FILAMENT EXTRA] Bed custom boundary extra recognized") def handle_cutter_filament_present(self, eventtime=None) -> None: - if not self.load_started or not self.cutter_object: + if not self.load_started: + return + if not self.cutter_object: return - self.gcode.respond_info("Filament reached toolhead") - logging.info( - "[LOAD FILAMENT EXTRA] 'filament_present' event received from cutter sensor" - ) self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) - return def verify_switch_sensor_state(self, eventtime): if not self.load_started or not self.filament_switch_sensor_object: return self.reactor.NEVER - if self.filament_switch_sensor_object.get_status(eventtime)[ "filament_detected" ]: + self.gcode.respond("Filament reached sensor") self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) + self.reactor.update_timer( + self.verify_switch_sensor_timer, self.reactor.NEVER + ) self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return self.reactor.NEVER - else: - return eventtime + 1.50 + return eventtime + 0.80 def verify_flow_sensor_state(self, eventtime): - if self.load_started and self.filament_flow_sensor_object: - if self.filament_flow_sensor_object.runout_helper.get_status(eventtime)[ - "filament_detected" - ]: - self.reactor.update_timer( - self.extrude_to_sensor_timer, self.reactor.NEVER - ) - self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) - self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) - return self.reactor.NEVER - return eventtime + 0.775 + if not self.load_started or not self.filament_flow_sensor_object: + return self.reactor.NEVER + if self.filament_flow_sensor_object.runout_helper.get_status(eventtime)[ + "filament_detected" + ]: + self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) + self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) + self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) + return self.reactor.NEVER + return eventtime + 0.8 def load(self, eventtime): if not self.load_started: return self.reactor.NEVER - if self.extrude_timeout: if self.extrude_count >= self.extrude_timeout: + self.reactor.update_timer( + self.extrude_to_sensor_timer, self.reactor.NEVER + ) self.move_extruder_mm( self.extruder_to_nozzle_dist, speed=30, wait=True ) # Extrude to the nozzle self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) return self.reactor.NEVER self.extrude_count += 1 - self.move_extruder_mm(distance=10, speed=self.load_speed, wait=False) return eventtime + float(10 / self.load_speed) def purge_extrude(self, eventtime): if not self.load_started: + self.gcode.respond_info( + "Filament loading has not started on purging loading" + ) return self.reactor.NEVER - if self.current_purge_index >= self.purge_max_retries: - self.gcode.respond_info("[LOAD FILAMENT] End.") + self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NEVER) + self.gcode.respond_info("Filament Loaded") completion = self.reactor.register_callback(self._purge_end) completion.wait() return self.reactor.NEVER - self.gcode.respond_info( - f"[LOAD FILAMENT] Purging....{self.current_purge_index + 1}" - ) + self.gcode.respond_info(f"Purging....{self.current_purge_index + 1}") self.move_extruder_mm(distance=self.purge_distance, speed=self.purge_speed) self.current_purge_index += 1 return eventtime + float(self.purge_interval) def _purge_end(self, eventtime): self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NEVER) - + toolhead = self.printer.lookup_object("toolhead") if self.park is not None: - self.toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) - self.toolhead.wait_moves() - + toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) + toolhead.wait_moves() if self.has_custom_boundary and self.custom_boundary_object is not None: if self.custom_boundary_object.get_status()["status"] == "default": self.custom_boundary_object.set_custom_boundary() - self._old_extrude_distance = None self.current_purge_index = 0 self.load_started = False - self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") # relative self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position - self.gcode.run_script_from_command("M82\nM400") # Absolute self.restore_state() - self.gcode.respond_info("Restoring gcode positions.") - self.toolhead.wait_moves() + toolhead.wait_moves() self.heat_and_wait(0, wait=False) - if self.idex: - self.gcode.run_script_from_command("T0 PARK\nM400") + self.gcode.run_script_from_command("T0 PARK") self.printer.send_event("load_filament:end") def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: @@ -271,36 +246,38 @@ def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: distance (float): The distance in mm to move the extruder. """ try: + toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() gcode_move = self.printer.lookup_object("gcode_move") - prev_pos = self.toolhead.get_position() + prev_pos = toolhead.get_position() v = distance * gcode_move.get_status(eventtime)["extrude_factor"] new_distance = v + prev_pos[3] - self.toolhead.manual_move( + toolhead.manual_move( [prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed ) if wait: - self.toolhead.wait_moves() + toolhead.wait_moves() return except Exception: logging.error("Unexpected error while trying to move extruder.") - return def move_home(self): """Move the toolhead to the home position (To the park position)""" - if self.toolhead is None: + toolhead = self.printer.lookup_object("toolhead", None) + if toolhead is None: return - self.toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) - self.toolhead.wait_moves() + toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) + toolhead.wait_moves() def home_needed(self): """Perform home if needed""" - if self.toolhead is None: + toolhead = self.printer.lookup_object("toolhead", None) + if toolhead is None: return try: eventtime = self.reactor.monotonic() - kin = self.toolhead.get_kinematics() + kin = toolhead.get_kinematics() _homed_axes = kin.get_status(eventtime)["homed_axes"] if "xyz" in _homed_axes.lower(): return @@ -321,7 +298,8 @@ def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): Weather to wait or not for the temperature to reach the interval . Defaults to True """ eventtime = self.reactor.monotonic() - extruder = self.toolhead.get_extruder() + toolhead = self.printer.lookup_object("toolhead") + extruder = toolhead.get_extruder() pheaters = self.printer.lookup_object("heaters") extruder_heater = extruder.get_heater() pheaters.set_temperature(extruder.get_heater(), temp, False) @@ -336,16 +314,13 @@ def disable_sensors(self): return if self.filament_flow_sensor_object is not None: self.filament_flow_sensor_object.runout_helper.sensor_enabled = 0 - if self.filament_switch_sensor_object is not None: self.filament_switch_sensor_object.runout_helper.sensor_enabled = 0 - return def enable_sensors(self): if self.filament_flow_sensor_object is not None: self.filament_flow_sensor_object.runout_helper.sensor_enabled = 1 - if self.filament_switch_sensor_object is not None: self.filament_switch_sensor_object.runout_helper.sensor_enabled = 1 @@ -353,19 +328,19 @@ def save_state(self): """Save gcode state and dual carriage state if the system is in IDEX configuration""" if self.idex: self.gcode.run_script_from_command( - f"SAVE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}\nM400" + f"SAVE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}" ) - self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_LOAD_STATE\nM400") + self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_LOAD_STATE") return True def restore_state(self): """Restore gcode state and dual carriage state if the system is in IDEX configuration""" self.gcode.run_script_from_command( - "RESTORE_GCODE_STATE NAME=_LOAD_STATE MOVE=0\nM400" + "RESTORE_GCODE_STATE NAME=_LOAD_STATE MOVE=0" ) if self.idex: self.gcode.run_script_from_command( - f"RESTORE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}\nM400" + f"RESTORE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}" ) return True @@ -383,6 +358,15 @@ def cmd_PURGE_STOP(self, gcmd): def cmd_LOAD_FILAMENT(self, gcmd): temp = gcmd.get("TEMPERATURE", 220.0, parser=float, minval=210, maxval=250) try: + idle_timeout = self.printer.lookup_object("idle_timeout") + is_printing = ( + idle_timeout.get_status(self.reactor.monotonic())["state"] == "Printing" + ) + toolhead = self.printer.lookup_object("toolhead") + if is_printing: + self.printer.command_error("Cannot load while printing") + return + if self.load_started: self.gcode.respond_info("Already loading filament") return @@ -390,10 +374,9 @@ def cmd_LOAD_FILAMENT(self, gcmd): self.home_needed() self.save_state() self.disable_sensors() - self.load_started = True self.printer.send_event("load_filament:start") - self.gcode.respond_info("[LOAD FILAMENT] Start") + self.gcode.respond_info("Filament Loading start") # * Select the head -> Meant for Idex systems if self.idex: @@ -401,23 +384,18 @@ def cmd_LOAD_FILAMENT(self, gcmd): self.gcode.run_script_from_command("T0 LOAD") else: self.gcode.run_script_from_command("T1 LOAD") - self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") self.gcode.run_script_from_command("G92 E0.0") - self.extrude_count = 0 - + if self.extrude_timeout: + self.extrude_count = 0 if self.custom_boundary_object is not None: self.custom_boundary_object.restore_default_boundary() - self.heat_and_wait(temp, wait=False) - if self.bucket_object is not None: self.bucket_object.move_to_bucket() - self.heat_and_wait(temp, wait=True) - self.toolhead.wait_moves() - + # toolhead.wait_moves() # * Force the motion sensor to "No Filament" state if self.filament_flow_sensor_object is not None: self.reactor.register_callback( @@ -427,21 +405,17 @@ def cmd_LOAD_FILAMENT(self, gcmd): ) ) self.move_extruder_mm(distance=30, speed=40, wait=True) - # * Actually start loading the machine self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NOW) - if self.filament_flow_sensor_object is not None: self.reactor.update_timer( self.verify_flow_sensor_timer, self.reactor.NOW ) - if self.filament_switch_sensor_object is not None: self.reactor.update_timer( self.verify_switch_sensor_timer, self.reactor.NOW ) - - except LoadFilamentError as e: + except Exception as e: logging.error(f"[LOADING FILAMENT ERROR]: {e}") def get_status(self, eventtime): From f7aee1cff22f90114c46a4abd8e0e644dec40977 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Fri, 10 Apr 2026 12:42:04 +0100 Subject: [PATCH 04/17] Cleanup bugs on event dispatch, refactor code --- klippy/extras/cutter_sensor.py | 205 ++++++++++----------------------- 1 file changed, 60 insertions(+), 145 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index a36f368e093f..3a2dc1e24da9 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -31,10 +31,7 @@ def __init__(self, config): self.printer = config.get_printer() self.reactor = self.printer.get_reactor() self.gcode = self.printer.lookup_object("gcode") - self.toolhead = None - self.printer.register_event_handler("klippy:connect", self._handle_connect) self.printer.register_event_handler("klippy:ready", self._handle_ready) - self.min_event_systime = self.reactor.NEVER self.filament_present = False self.sensor_enabled = True @@ -42,11 +39,12 @@ def __init__(self, config): self.prev_pos = None self._extrude_timeout = 30 self._timeout = 0 - + self.custom_boundary_object = None + self.load_filament_object = None + self.unload_filament_object = None self.pause_delay = config.getfloat("pause_delay", 0.5, above=0.0) self.event_delay = config.getfloat("event_delay", 3.0, above=0.0) self.runout_pause = config.getboolean("pause_on_runout", False) - self.extrude_length_mm = config.getfloat( "extrude_length_mm", 5.0, minval=1.0, maxval=100.0 ) @@ -88,13 +86,11 @@ def __init__(self, config): self.pre_cutter_position_x, self.pre_cutter_position_y = ( self.pre_cutter_position ) - cutter_sensor_pin = config.get("cutter_sensor_pin") buttons = self.printer.load_object(config, "buttons") buttons.register_debounce_button( cutter_sensor_pin, self._cutter_sensor_callback, config ) - self.unextrude_to_sensor_timer = self.reactor.register_timer( self.unextrude, self.reactor.NEVER ) @@ -116,9 +112,6 @@ def __init__(self, config): self.cmd_SET_FILAMENT_SENSOR_helper, ) - def _handle_connect(self) -> None: - self.toolhead = self.printer.lookup_object("toolhead") - def _handle_ready(self) -> None: self.min_event_systime = self.reactor.monotonic() + 2.0 self.custom_boundary_object = self.printer.lookup_object( @@ -157,7 +150,6 @@ def cmd_CUT(self, gcmd) -> None: temperature = gcmd.get( "TEMPERATURE", default=220.0, parser=float, minval=200, maxval=250 ) - self.reactor.register_callback( partial( self.cut, @@ -175,28 +167,23 @@ def cut( temp: int = 220, ) -> None: """Perform cut""" - if not self.toolhead: - return - + toolhead = self.printer.lookup_object("toolhead") if self.is_cutting: self.gcode.respond_info( f"[CUTTER {self.name} sensor] Already cutting filament" ) return - self.is_cutting = True self.home_needed() - self.toolhead.wait_moves() eventtime = self.reactor.monotonic() - kin_status = self.toolhead.get_kinematics().get_status(eventtime) - + kin_status = toolhead.get_kinematics().get_status(eventtime) if "xyz" not in kin_status["homed_axes"]: self.gcode.respond_info( f"[CUTTER {self.name} sensor] Printer needs to be homed for filament cutting.", log=True, ) return - self.prev_pos = self.toolhead.get_position() + self.prev_pos = toolhead.get_position() self.gcode.run_script_from_command("G90\nM400") self.gcode.run_script_from_command("M83\nM400") self.gcode.run_script_from_command("G92 E0.0\nM400") @@ -218,12 +205,12 @@ def cut( self.move_extruder_mm( distance=self.extrude_length_mm + 10, speed=self.extrude_speed ) - self.toolhead.wait_moves() + toolhead.wait_moves() self.reactor.update_timer(self.unextrude_to_sensor_timer, self.reactor.NOW) if self.prev_pos and return_last_pos: self.move_back() - self.toolhead.wait_moves() + toolhead.wait_moves() if self.custom_boundary_object: self.custom_boundary_object.set_custom_boundary() if off_heaters: @@ -234,56 +221,46 @@ def unextrude(self, eventtime): """Unextrude""" if not self.is_cutting: return self.reactor.NEVER - try: - self.move_extruder_mm(distance=-10, speed=self.extrude_speed, wait=False) - logging.info("[CUTTER] unextruding") - return float(eventtime + float(10 / self.extrude_speed)) - except Exception as e: - raise CutterSensorError( - f"[CUTTER {self.name} sensor] Error Unextruding: {e}" - ) + self.move_extruder_mm(distance=-10, speed=self.extrude_speed, wait=False) + return eventtime + float(10 / self.extrude_speed) def move_extruder_mm(self, distance=10.0, speed=10, wait=True) -> None: """Move the extruder. Args: dist (float in mm): The distance in a certain amount. """ - if not self.toolhead: - return + toolhead = self.printer.lookup_object("toolhead") try: + toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() gcode_move = self.printer.lookup_object("gcode_move") - prev_pos = self.toolhead.get_position() + prev_pos = toolhead.get_position() v = distance * gcode_move.get_status(eventtime)["extrude_factor"] new_distance = v + prev_pos[3] - self.toolhead.move( - [prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed - ) + toolhead.move([prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed) if wait: - self.toolhead.wait_moves() + toolhead.wait_moves() except Exception as e: - raise CutterSensorError( + logging.info( f"[CUTTER {self.name} sensor] Unable to move extruder error: {e}." ) def home_needed(self) -> None: - if not self.toolhead: + toolhead = self.printer.lookup_object("toolhead") + if not toolhead: raise CutterSensorError( "Toolhead object is missing, called on home_needed." ) try: eventtime = self.reactor.monotonic() - - kin = self.toolhead.get_kinematics() + kin = toolhead.get_kinematics() _homed_axes = kin.get_status(eventtime)["homed_axes"] - if "xyz" in _homed_axes.lower(): self.gcode.respond_info("Printer already homed.") return else: self.gcode.respond_info("Homing.") self.gcode.run_script_from_command("G28\nM400") - except Exception as e: raise CutterSensorError(f"Unable to home: {e}") @@ -298,14 +275,12 @@ def heat_extruder(self, temp, wait: typing.Optional["bool"] = True) -> None: Weather to wait or not for the temperature to reach the interval . Defaults to True """ try: - if self.toolhead is None: - return + toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() - extruder = self.toolhead.get_extruder() + extruder = toolhead.get_extruder() pheaters = self.printer.lookup_object("heaters") pheaters.set_temperature(extruder.get_heater(), temp, False) extruder_heater = extruder.get_heater() - while not (self.printer.is_shutdown() and wait): self.gcode.respond_info("Waiting for temperature to stabilize.") heater_temp, target = extruder_heater.get_temp(eventtime) @@ -318,176 +293,115 @@ def heat_extruder(self, temp, wait: typing.Optional["bool"] = True) -> None: def cut_move(self) -> None: """Performs the cut movement""" try: - if not self.toolhead: - return - self.toolhead.manual_move( + toolhead = self.printer.lookup_object("toolhead") + toolhead.manual_move( [self.cutter_position_x, self.cutter_position_y], self.travel_speed, ) - self.toolhead.manual_move( + toolhead.manual_move( [self.pre_cutter_position_x, self.pre_cutter_position_y], self.cut_speed, ) - self.toolhead.wait_moves() + toolhead.wait_moves() except Exception as e: raise CutterSensorError(f"Error performing performing cut move: {e}.") def move_to_cutter_pos(self) -> None: """Moves the toolhead to the pre cutting position""" try: - if not self.toolhead: - return + toolhead = self.printer.lookup_object("toolhead") curtime = self.reactor.monotonic() - kin_status = self.toolhead.get_kinematics().get_status(curtime) + kin_status = toolhead.get_kinematics().get_status(curtime) if "xyz" not in kin_status["homed_axes"]: return - self.toolhead.manual_move( + toolhead.manual_move( [self.pre_cutter_position_x, self.pre_cutter_position_y], self.travel_speed, ) - self.toolhead.wait_moves() + toolhead.wait_moves() except Exception as e: raise CutterSensorError(f"Error moving to cutter position: {e}.") def move_home(self) -> None: try: - if not self.toolhead: - return + toolhead = self.printer.lookup_object("toolhead") """Moves to the homing position""" gcode_move = self.printer.lookup_object("gcode_move") homing_origin = gcode_move.get_status()["homing_origin"] - self.toolhead.manual_move(homing_origin, self.travel_speed) + toolhead.manual_move(homing_origin, self.travel_speed) except Exception as e: raise CutterSensorError(f"Error moving to home position: {e}.") def move_to_bucket(self, split=False) -> None: """Moves to the bucket position""" try: - if not self.toolhead: - return + toolhead = self.printer.lookup_object("toolhead") if self.custom_boundary_object: self.gcode.respond_info("Restoring original printer Boundaries.") self.custom_boundary_object.restore_default_boundary() - if not split: - self.toolhead.manual_move( + toolhead.manual_move( [self.bucked_position_x, self.bucked_position_y], self.travel_speed, ) else: - self.toolhead.manual_move([self.bucked_position_x], self.travel_speed) - self.toolhead.wait_moves() - self.toolhead.manual_move([self.bucked_position_y], self.travel_speed) - - self.toolhead.wait_moves() + toolhead.manual_move([self.bucked_position_x], self.travel_speed) + toolhead.wait_moves() + toolhead.manual_move([self.bucked_position_y], self.travel_speed) + toolhead.wait_moves() except Exception as e: raise CutterSensorError(f"Error moving to bucket position: {e}.") def move_back(self): """Moves back to the original position where the CUT gcode command was called""" try: - if not self.prev_pos or not self.toolhead: + toolhead = self.printer.lookup_object("toolhead") + if not self.prev_pos: return - - self.toolhead.manual_move( + toolhead.manual_move( [self.prev_pos[0], self.prev_pos[1], self.prev_pos[2]], self.travel_speed, ) - self.toolhead.wait_moves() + toolhead.wait_moves() except Exception as e: - raise CutterSensorError(f"Error moving to the original position: {e}.") + raise CutterSensorError(f"Error moving to the original position: {e}") def _cutter_sensor_callback(self, eventtime, state): """Callback for the sensor state change""" if self.filament_present == state: return self.filament_present = state - logging.info(f"[CUTTER {self.name} Sensor] state changed to {str(state)}") - eventtime = self.reactor.monotonic() if ( eventtime < self.min_event_systime ): # Can also add self.enabled, if not enabled does nothing return - + self.min_event_systime = self.reactor.NEVER idle_timeout = self.printer.lookup_object("idle_timeout") is_printing = ( idle_timeout.get_status(eventtime)["state"] == "Printing" ) # state can be either ["Ready", "Printing", "Idle"] - - if ( - self.load_filament_object - and self.load_filament_object.get_status(eventtime).get("state", False) - ) or ( - self.unload_filament_object - and self.unload_filament_object.get_status(eventtime).get("state", False) - ): - if state: - self.printer.send_event("cutter_sensor:filament_present") - self.gcode.respond_info( - f"[CUTTER {self.name}] detected filament while loading filament" - ) - logging.info( - f"[CUTTER {self.name}] detected filament while loading filament" - ) - else: - if self.is_cutting: - self.reactor.update_timer( - self.unextrude_to_sensor_timer, self.reactor.NEVER - ) - self.is_cutting = False - self.gcode.run_script_from_command("G90\nM400") - self.gcode.run_script_from_command("M83\nM400") - self.gcode.run_script_from_command("G92 E0.0\nM400") - self.gcode.respond_info(f"[CUTTER {self.name} sensor] Cut done.") - self.printer.send_event("cutter_sensor:no_filament") - self.min_event_systime = self.reactor.monotonic() + 2.0 - - elif state: - if not is_printing and self.insert_gcode is not None: - self.printer.send_event("cutter_sensor:filament_present") - # self.min_event_systime = self.reactor.NEVER - self.min_event_systime = self.reactor.monotonic() + 2.0 - logging.info( - f"[CUTTER {self.name} sensor] Filament Detected | Time: {eventtime}" + if state: + self.printer.send_event("cutter_sensor:filament_present") + logging.info("Cutter sensor reports filament present") + self.gcode.respond_info("cutter sensor reports filament present") + else: + self.gcode.respond_info("Cutter sensor reports no filament") + if self.is_cutting: + self.reactor.update_timer( + self.unextrude_to_sensor_timer, self.reactor.NEVER ) - # self.reactor.register_callback(self._insert_event_handler) - - elif not is_printing and self.runout_gcode is not None: - # runout detected - - # self.min_event_systime = self.reactor.NEVER - self.min_event_systime = self.reactor.monotonic() + 2.0 - # if self.is_cutting: - # self.reactor.update_timer( - # self.unextrude_to_sensor_timer, self.reactor.NEVER - # ) - # self.is_cutting = False + self.is_cutting = False + self.gcode.run_script_from_command("G90") + self.gcode.run_script_from_command("M83") + self.gcode.run_script_from_command("G92 E0.0") + logging.info("Cut done") + self.gcode.respond_info("Cut Done!") self.printer.send_event("cutter_sensor:no_filament") - logging.info( - f"[CUTTER {self.name} sensor] Filament Not Detected | Time: {eventtime}" - ) - self.gcode.run_script_from_command("G90\nM400") - self.gcode.run_script_from_command("M83\nM400") - self.gcode.run_script_from_command("G92 E0.0\nM400") - elif ( - is_printing and self.runout_gcode is not None - ): # Runout detected while printing - # self.min_event_systime = self.reactor.NEVER - self.min_event_systime = self.reactor.monotonic() + 2.0 - - self.printer.send_event("cutter_sensor:no_filament") - logging.info( - f"[CUTTER {self.name} sensor] Filament Not Detected | Time: {eventtime}" - ) - # self.reactor.register_callback(self._runout_event_handler) - self.gcode.run_script_from_command("G90\nM400") - self.gcode.run_script_from_command("M83\nM400") - self.gcode.run_script_from_command("G92 E0.0\nM400") - return + self.min_event_systime = self.reactor.monotonic() + 2.0 def _insert_event_handler(self, eventtime): self._exec_gcode("", self.insert_gcode) @@ -508,6 +422,7 @@ def _exec_gcode(self, prefix, template): self.gcode.run_script(prefix + template.render() + "\nM400") except Exception: logging.exception("Script running error") + self.min_event_systime = self.reactor.monotonic() + self.event_delay def get_status(self, eventtime): From 7d7204c63b86dd51c402eeff598f84ad260faf2f Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Fri, 10 Apr 2026 15:18:31 +0100 Subject: [PATCH 05/17] Change klipper dist timeout variable name --- klippy/extras/load_filament.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index dbae30cb5fa3..ae398f0f6f5a 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -67,7 +67,7 @@ def __init__(self, config): "purge_interval", default=3.0, minval=0.5, maxval=10.0 ) self.extrude_timeout = config.getint( - "extrude_max_count", default=None, minval=2, maxval=1000 + "dist_timeout", default=None, minval=2, maxval=1000 ) # * Callback Timers From 17715c6cbc38d2efa3ccc54663b1f58b75b4ab05 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Tue, 14 Apr 2026 11:02:42 +0100 Subject: [PATCH 06/17] Add stage routine stage var --- klippy/extras/cutter_sensor.py | 2 +- klippy/extras/load_filament.py | 28 ++++++++++++++++------------ klippy/extras/unload_filament.py | 25 ++++++++++++++++++------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index 3a2dc1e24da9..8d6a8942af36 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -61,7 +61,7 @@ def __init__(self, config): "travel_speed", 100.0, above=0.0, minval=30.0, maxval=600.0 ) self.cut_speed = config.getfloat( - "cut_speed", 100.0, above=50.0, minval=50.0, maxval=300.0 + "cut_speed", 100.0, above=0.0, minval=2.0, maxval=300.0 ) self.cutter_position = config.getfloatlist("cutter_position_xy", count=2) self.pre_cutter_position = config.getfloatlist( diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index ae398f0f6f5a..d3c6c4b1d65a 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -4,6 +4,8 @@ class LoadFilament: + steps = {"None", "heating", "pulling", "purging"} + def __init__(self, config): self.printer = config.get_printer() self.reactor = self.printer.get_reactor() @@ -25,7 +27,7 @@ def __init__(self, config): self.min_event_systime = self.reactor.NEVER self.idex = config.getboolean("idex", False) self.has_custom_boundary = config.getboolean("has_custom_boundary", False) - + self.step = "None" if config.get("filament_flow_sensor_name", None): self.filament_flow_sensor_name = config.get("filament_flow_sensor_name") if ( @@ -197,9 +199,11 @@ def load(self, eventtime): return self.reactor.NEVER self.extrude_count += 1 self.move_extruder_mm(distance=10, speed=self.load_speed, wait=False) + self.step = "pulling" return eventtime + float(10 / self.load_speed) def purge_extrude(self, eventtime): + self.step = "purging" if not self.load_started: self.gcode.respond_info( "Filament loading has not started on purging loading" @@ -237,6 +241,7 @@ def _purge_end(self, eventtime): self.heat_and_wait(0, wait=False) if self.idex: self.gcode.run_script_from_command("T0 PARK") + self.step = "None" self.printer.send_event("load_filament:end") def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: @@ -303,6 +308,7 @@ def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): pheaters = self.printer.lookup_object("heaters") extruder_heater = extruder.get_heater() pheaters.set_temperature(extruder.get_heater(), temp, False) + self.step = "heating" while not self.printer.is_shutdown() and wait: heater_temp, _ = extruder_heater.get_temp(eventtime) if heater_temp >= (temp - 5) and heater_temp <= (temp + 5): @@ -358,26 +364,21 @@ def cmd_PURGE_STOP(self, gcmd): def cmd_LOAD_FILAMENT(self, gcmd): temp = gcmd.get("TEMPERATURE", 220.0, parser=float, minval=210, maxval=250) try: - idle_timeout = self.printer.lookup_object("idle_timeout") - is_printing = ( - idle_timeout.get_status(self.reactor.monotonic())["state"] == "Printing" - ) - toolhead = self.printer.lookup_object("toolhead") + pstat = self.printer.lookup_object("print_stats") + pstat_state = pstat.get_status(self.reactor.monotonic())["state"] + is_printing = pstat_state == "printing" if is_printing: self.printer.command_error("Cannot load while printing") return - if self.load_started: - self.gcode.respond_info("Already loading filament") + self.gcode.respond_info("Filament already loading") return - self.home_needed() self.save_state() self.disable_sensors() self.load_started = True self.printer.send_event("load_filament:start") self.gcode.respond_info("Filament Loading start") - # * Select the head -> Meant for Idex systems if self.idex: if gcmd.get("TOOLHEAD") == "Load_T0": @@ -395,7 +396,6 @@ def cmd_LOAD_FILAMENT(self, gcmd): if self.bucket_object is not None: self.bucket_object.move_to_bucket() self.heat_and_wait(temp, wait=True) - # toolhead.wait_moves() # * Force the motion sensor to "No Filament" state if self.filament_flow_sensor_object is not None: self.reactor.register_callback( @@ -419,7 +419,11 @@ def cmd_LOAD_FILAMENT(self, gcmd): logging.error(f"[LOADING FILAMENT ERROR]: {e}") def get_status(self, eventtime): - return {"state": bool(self.load_started)} + return { + "state": bool(self.load_started), + "step": self.step, + "avail_steps": list(self.steps), + } def load_config_prefix(config): diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index 76f74e1559ed..2f7883e24a34 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -12,6 +12,8 @@ def __init__(self, message, errors: list[str] | None = None): class UnloadFilament: + steps = {"None", "heating", "pulling", "cutting"} + def __init__(self, config): self.printer = config.get_printer() self.reactor = self.printer.get_reactor() @@ -26,6 +28,7 @@ def __init__(self, config): self.unload_started = False self.unextrude_count: int = 0 self.travel_speed = None + self.step = "None" self.printer.register_event_handler("klippy:ready", self.handle_ready) self.printer.register_event_handler( "unload_filament:end", @@ -148,6 +151,7 @@ def unload_end(self, eventtime=None): self.gcode.respond_info("Parking toolhead 0") self.gcode.run_script_from_command("T0 PARK") self.unload_started = False + self.step = None self.printer.send_event("unload_filament:end") return self.reactor.NEVER @@ -164,6 +168,7 @@ def unextrude(self, eventtime): completion = self.reactor.register_callback(self.unload_end) return completion.wait() self.unextrude_count += 1 + self.step = "pulling" self.move_extruder_mm(distance=-10, speed=self.unload_speed, wait=False) return float(eventtime + float(10 / self.unload_speed)) @@ -237,6 +242,7 @@ def heat_extruder(self, temp, wait: bool = False) -> None: pheaters = self.printer.lookup_object("heaters") extruder_heater = extruder.get_heater() pheaters.set_temperature(extruder_heater, temp, False) + self.step = "heating" while not self.printer.is_shutdown() and wait: heater_temp, _ = extruder_heater.get_temp(eventtime) if heater_temp >= (temp - 5) and heater_temp <= (temp + 5): @@ -265,15 +271,15 @@ def restore_state(self) -> None: def cmd_UNLOAD_FILAMENT(self, gcmd): temp = gcmd.get("TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0) try: - toolhead = self.printer.lookup_object("toolhead") - idle_timeout = self.printer.lookup_object("idle_timeout") - is_printing = ( - idle_timeout.get_status(self.reactor.monotonic())["state"] == "Printing" + pstat = self.printer.lookup_object("print_stats") + pstat_state = pstat.get_status(self.reactor.monotonic())["state"] + is_printing = pstat_state == "printing" + self.gcode.respond_info( + f"Current idle timeout {pstat_state} state {is_printing}" ) if is_printing: self.gcode.respond_info("Cannot unload while printing") return - if self.unload_started: self.gcode.respond_info("Printer already unloading filament") return @@ -285,6 +291,7 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): self.gcode.run_script_from_command("T0 UNLOAD") else: self.gcode.run_script_from_command("T1 UNLOAD") + self.step = "None" self.unload_started = True self.printer.send_event("unload_filament:start") self.gcode.respond_info("[UNLOAD FILAMENT] Start") @@ -297,8 +304,8 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): if self.bucket_object: self.bucket_object.move_to_bucket() self.heat_extruder(temp, wait=True) - toolhead.wait_moves() if self.cutter_object: + self.step = "cutting" self.reactor.register_callback( partial( self.cutter_object.cut, @@ -328,7 +335,11 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): ) def get_status(self, eventtime): - return {"state": bool(self.unload_started)} + return { + "state": bool(self.unload_started), + "step": self.step, + "steps": list(self.steps), + } def load_config(config): From 77ea212037ce33ff14202e1365c9ad1299af5eef Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 10:14:28 +0100 Subject: [PATCH 07/17] Fix formatting --- klippy/extras/bucket.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/klippy/extras/bucket.py b/klippy/extras/bucket.py index c6f6dd724bc6..153057d37d6d 100644 --- a/klippy/extras/bucket.py +++ b/klippy/extras/bucket.py @@ -1,4 +1,3 @@ -import logging import typing @@ -10,16 +9,10 @@ def __init__(self, config): self.toolhead = None self.printer.register_event_handler("klippy:ready", self.handle_ready) - self.printer.register_event_handler( - "klippy:connect", self.handle_connect - ) + self.printer.register_event_handler("klippy:connect", self.handle_connect) - self.has_custom_bed_bound = config.getboolean( - "has_custom_boundary", False - ) - self.bucket_position = config.getfloatlist( - "bucket_position", None, count=2 - ) + self.has_custom_bed_bound = config.getboolean("has_custom_boundary", False) + self.bucket_position = config.getfloatlist("bucket_position", None, count=2) self.travel_speed = config.getfloat( "travel_speed", default=50.0, minval=20.0, maxval=10000.0 ) @@ -47,12 +40,10 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): return try: if self.custom_bed_bound_object: - _conf_bound = ( - self.custom_bed_bound_object.check_boundary_limits( - position=( - self.bucket_position[0], - self.bucket_position[1], - ) + _conf_bound = self.custom_bed_bound_object.check_boundary_limits( + position=( + self.bucket_position[0], + self.bucket_position[1], ) ) if ( @@ -61,7 +52,6 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): "status", "" ) == "custom": self.custom_bed_bound_object.restore_default_boundary() - if not split: self.toolhead.manual_move( @@ -69,9 +59,7 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): self.travel_speed, ) else: - self.toolhead.manual_move( - [self.bucket_position[0]], self.travel_speed - ) + self.toolhead.manual_move([self.bucket_position[0]], self.travel_speed) self.toolhead.wait_moves() self.toolhead.manual_move( [self.bucket_position[0], self.bucket_position[1]], From 5fc703b7f931e9a889217ccd534b899fd82861d9 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 11:45:28 +0100 Subject: [PATCH 08/17] Replace cut callback with direct cut method call --- klippy/extras/cutter_sensor.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index 8d6a8942af36..e18c3e8ff665 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -150,13 +150,10 @@ def cmd_CUT(self, gcmd) -> None: temperature = gcmd.get( "TEMPERATURE", default=220.0, parser=float, minval=200, maxval=250 ) - self.reactor.register_callback( - partial( - self.cut, - return_last_pos=return_last_position, - off_heaters=turn_off_heaters, - temp=temperature, - ) + self.cut( + return_last_pos=return_last_position, + off_heaters=turn_off_heaters, + temp=temperature, ) def cut( @@ -184,7 +181,7 @@ def cut( ) return self.prev_pos = toolhead.get_position() - self.gcode.run_script_from_command("G90\nM400") + self.gcode.run_script_from_command("G91\nM400") self.gcode.run_script_from_command("M83\nM400") self.gcode.run_script_from_command("G92 E0.0\nM400") self.gcode.respond_info(f"[CUTTER {self.name} sensor ] Heating extruder.") @@ -264,7 +261,7 @@ def home_needed(self) -> None: except Exception as e: raise CutterSensorError(f"Unable to home: {e}") - def heat_extruder(self, temp, wait: typing.Optional["bool"] = True) -> None: + def heat_extruder(self, temp, wait: bool = True) -> None: """Heats the extruder and wait. Method returns when temperature is [temp - 5 ; temp + 5]. From 1eba220e46c569e75fea5b65af486fa49e8bc336 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 11:53:01 +0100 Subject: [PATCH 09/17] Remove unused imports --- klippy/extras/cutter_sensor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index e18c3e8ff665..ad6008be36e6 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -12,8 +12,6 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import logging -import typing -from functools import partial class CutterSensorError(Exception): From e2bfd73e6f08ab3e85919141fffde335bc95b733 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 14:40:22 +0100 Subject: [PATCH 10/17] Formatting --- klippy/extras/cutter_sensor.py | 68 ++++++++++++++++++++------- klippy/extras/load_filament.py | 80 ++++++++++++++++++++++++-------- klippy/extras/unload_filament.py | 67 +++++++++++++++++++------- 3 files changed, 161 insertions(+), 54 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index ad6008be36e6..69209b2de758 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -61,7 +61,9 @@ def __init__(self, config): self.cut_speed = config.getfloat( "cut_speed", 100.0, above=0.0, minval=2.0, maxval=300.0 ) - self.cutter_position = config.getfloatlist("cutter_position_xy", count=2) + self.cutter_position = config.getfloatlist( + "cutter_position_xy", count=2 + ) self.pre_cutter_position = config.getfloatlist( "pre_cutter_position_xy", count=2 ) @@ -73,12 +75,18 @@ def __init__(self, config): gcode_macro = self.printer.load_object(config, "gcode_macro") if config.get("runout_gcode", None) is not None: - self.runout_gcode = gcode_macro.load_template(config, "runout_gcode", "") + self.runout_gcode = gcode_macro.load_template( + config, "runout_gcode", "" + ) if config.get("insert_gcode", None) is not None: - self.insert_gcode = gcode_macro.load_template(config, "insert_gcode") + self.insert_gcode = gcode_macro.load_template( + config, "insert_gcode" + ) if self.bucked_position_xy: - self.bucked_position_x, self.bucked_position_y = self.bucked_position_xy + self.bucked_position_x, self.bucked_position_y = ( + self.bucked_position_xy + ) self.cutter_position_x, self.cutter_position_y = self.cutter_position self.pre_cutter_position_x, self.pre_cutter_position_y = ( @@ -143,8 +151,12 @@ def cmd_CUT(self, gcmd) -> None: Call CUT gcode command to perform the filament cutting """ - return_last_position = gcmd.get("MOVE_TO_LAST_POS", default=False, parser=bool) - turn_off_heaters = gcmd.get("TURN_OFF_HEATER", default=False, parser=bool) + return_last_position = gcmd.get( + "MOVE_TO_LAST_POS", default=False, parser=bool + ) + turn_off_heaters = gcmd.get( + "TURN_OFF_HEATER", default=False, parser=bool + ) temperature = gcmd.get( "TEMPERATURE", default=220.0, parser=float, minval=200, maxval=250 ) @@ -182,12 +194,16 @@ def cut( self.gcode.run_script_from_command("G91\nM400") self.gcode.run_script_from_command("M83\nM400") self.gcode.run_script_from_command("G92 E0.0\nM400") - self.gcode.respond_info(f"[CUTTER {self.name} sensor ] Heating extruder.") + self.gcode.respond_info( + f"[CUTTER {self.name} sensor ] Heating extruder." + ) self.heat_extruder(temp, wait=True) if self.bucked_position_xy: self.move_to_bucket() self.move_extruder_mm(distance=10, speed=self.extrude_speed) - self.move_extruder_mm(distance=self.retract_length_mm, speed=self.extrude_speed) + self.move_extruder_mm( + distance=self.retract_length_mm, speed=self.extrude_speed + ) if self.custom_boundary_object: self.custom_boundary_object.restore_default_boundary() self.move_to_cutter_pos() @@ -201,7 +217,9 @@ def cut( distance=self.extrude_length_mm + 10, speed=self.extrude_speed ) toolhead.wait_moves() - self.reactor.update_timer(self.unextrude_to_sensor_timer, self.reactor.NOW) + self.reactor.update_timer( + self.unextrude_to_sensor_timer, self.reactor.NOW + ) if self.prev_pos and return_last_pos: self.move_back() @@ -216,7 +234,9 @@ def unextrude(self, eventtime): """Unextrude""" if not self.is_cutting: return self.reactor.NEVER - self.move_extruder_mm(distance=-10, speed=self.extrude_speed, wait=False) + self.move_extruder_mm( + distance=-10, speed=self.extrude_speed, wait=False + ) return eventtime + float(10 / self.extrude_speed) def move_extruder_mm(self, distance=10.0, speed=10, wait=True) -> None: @@ -232,7 +252,9 @@ def move_extruder_mm(self, distance=10.0, speed=10, wait=True) -> None: prev_pos = toolhead.get_position() v = distance * gcode_move.get_status(eventtime)["extrude_factor"] new_distance = v + prev_pos[3] - toolhead.move([prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed) + toolhead.move( + [prev_pos[0], prev_pos[1], prev_pos[2], new_distance], speed + ) if wait: toolhead.wait_moves() except Exception as e: @@ -299,7 +321,9 @@ def cut_move(self) -> None: ) toolhead.wait_moves() except Exception as e: - raise CutterSensorError(f"Error performing performing cut move: {e}.") + raise CutterSensorError( + f"Error performing performing cut move: {e}." + ) def move_to_cutter_pos(self) -> None: """Moves the toolhead to the pre cutting position""" @@ -333,7 +357,9 @@ def move_to_bucket(self, split=False) -> None: try: toolhead = self.printer.lookup_object("toolhead") if self.custom_boundary_object: - self.gcode.respond_info("Restoring original printer Boundaries.") + self.gcode.respond_info( + "Restoring original printer Boundaries." + ) self.custom_boundary_object.restore_default_boundary() if not split: toolhead.manual_move( @@ -341,9 +367,13 @@ def move_to_bucket(self, split=False) -> None: self.travel_speed, ) else: - toolhead.manual_move([self.bucked_position_x], self.travel_speed) + toolhead.manual_move( + [self.bucked_position_x], self.travel_speed + ) toolhead.wait_moves() - toolhead.manual_move([self.bucked_position_y], self.travel_speed) + toolhead.manual_move( + [self.bucked_position_y], self.travel_speed + ) toolhead.wait_moves() except Exception as e: raise CutterSensorError(f"Error moving to bucket position: {e}.") @@ -360,14 +390,18 @@ def move_back(self): ) toolhead.wait_moves() except Exception as e: - raise CutterSensorError(f"Error moving to the original position: {e}") + raise CutterSensorError( + f"Error moving to the original position: {e}" + ) def _cutter_sensor_callback(self, eventtime, state): """Callback for the sensor state change""" if self.filament_present == state: return self.filament_present = state - logging.info(f"[CUTTER {self.name} Sensor] state changed to {str(state)}") + logging.info( + f"[CUTTER {self.name} Sensor] state changed to {str(state)}" + ) eventtime = self.reactor.monotonic() if ( eventtime < self.min_event_systime diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index d3c6c4b1d65a..039fcd77eaca 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -16,7 +16,9 @@ def __init__(self, config): self.cutter_object = self.cutter_name = None self.bucket_object = None self.custom_boundary_object = None - self.filament_switch_sensor_name = self.filament_switch_sensor_object = None + self.filament_switch_sensor_name = ( + self.filament_switch_sensor_object + ) = None self.filament_flow_sensor_name = self.filament_flow_sensor_object = None self.load_started: bool = False self.current_purge_index: int = 0 @@ -26,15 +28,21 @@ def __init__(self, config): self.printer.register_event_handler("klippy:ready", self.handle_ready) self.min_event_systime = self.reactor.NEVER self.idex = config.getboolean("idex", False) - self.has_custom_boundary = config.getboolean("has_custom_boundary", False) + self.has_custom_boundary = config.getboolean( + "has_custom_boundary", False + ) self.step = "None" if config.get("filament_flow_sensor_name", None): - self.filament_flow_sensor_name = config.get("filament_flow_sensor_name") + self.filament_flow_sensor_name = config.get( + "filament_flow_sensor_name" + ) if ( config.get("filament_switch_sensor_name", None) and self.filament_flow_sensor_object is None ): - self.filament_switch_sensor_name = config.get("filament_switch_sensor_name") + self.filament_switch_sensor_name = config.get( + "filament_switch_sensor_name" + ) if ( config.get("cutter_sensor_name", None) and not self.filament_flow_sensor_object @@ -145,7 +153,9 @@ def handle_ready(self): self.custom_boundary_object = self.printer.lookup_object( "bed_custom_bound", None ) - logging.info("[LOAD FILAMENT EXTRA] Bed custom boundary extra recognized") + logging.info( + "[LOAD FILAMENT EXTRA] Bed custom boundary extra recognized" + ) def handle_cutter_filament_present(self, eventtime=None) -> None: if not self.load_started: @@ -153,7 +163,9 @@ def handle_cutter_filament_present(self, eventtime=None) -> None: if not self.cutter_object: return self.gcode.respond_info("Filament reached toolhead") - self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) + self.reactor.update_timer( + self.extrude_to_sensor_timer, self.reactor.NEVER + ) self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) def verify_switch_sensor_state(self, eventtime): @@ -163,12 +175,18 @@ def verify_switch_sensor_state(self, eventtime): "filament_detected" ]: self.gcode.respond("Filament reached sensor") - self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) + self.reactor.update_timer( + self.extrude_to_sensor_timer, self.reactor.NEVER + ) self.reactor.update_timer( self.verify_switch_sensor_timer, self.reactor.NEVER ) - self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) - self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) + self.move_extruder_mm( + self.extruder_to_nozzle_dist, speed=30, wait=True + ) + self.reactor.update_timer( + self.extrude_purge_timer, self.reactor.NOW + ) return self.reactor.NEVER return eventtime + 0.80 @@ -178,9 +196,15 @@ def verify_flow_sensor_state(self, eventtime): if self.filament_flow_sensor_object.runout_helper.get_status(eventtime)[ "filament_detected" ]: - self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NEVER) - self.move_extruder_mm(self.extruder_to_nozzle_dist, speed=30, wait=True) - self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) + self.reactor.update_timer( + self.extrude_to_sensor_timer, self.reactor.NEVER + ) + self.move_extruder_mm( + self.extruder_to_nozzle_dist, speed=30, wait=True + ) + self.reactor.update_timer( + self.extrude_purge_timer, self.reactor.NOW + ) return self.reactor.NEVER return eventtime + 0.8 @@ -195,7 +219,9 @@ def load(self, eventtime): self.move_extruder_mm( self.extruder_to_nozzle_dist, speed=30, wait=True ) # Extrude to the nozzle - self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NOW) + self.reactor.update_timer( + self.extrude_purge_timer, self.reactor.NOW + ) return self.reactor.NEVER self.extrude_count += 1 self.move_extruder_mm(distance=10, speed=self.load_speed, wait=False) @@ -210,13 +236,17 @@ def purge_extrude(self, eventtime): ) return self.reactor.NEVER if self.current_purge_index >= self.purge_max_retries: - self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NEVER) + self.reactor.update_timer( + self.extrude_purge_timer, self.reactor.NEVER + ) self.gcode.respond_info("Filament Loaded") completion = self.reactor.register_callback(self._purge_end) completion.wait() return self.reactor.NEVER self.gcode.respond_info(f"Purging....{self.current_purge_index + 1}") - self.move_extruder_mm(distance=self.purge_distance, speed=self.purge_speed) + self.move_extruder_mm( + distance=self.purge_distance, speed=self.purge_speed + ) self.current_purge_index += 1 return eventtime + float(self.purge_interval) @@ -224,7 +254,9 @@ def _purge_end(self, eventtime): self.reactor.update_timer(self.extrude_purge_timer, self.reactor.NEVER) toolhead = self.printer.lookup_object("toolhead") if self.park is not None: - toolhead.manual_move([self.park[0], self.park[1]], self.travel_speed) + toolhead.manual_move( + [self.park[0], self.park[1]], self.travel_speed + ) toolhead.wait_moves() if self.has_custom_boundary and self.custom_boundary_object is not None: if self.custom_boundary_object.get_status()["status"] == "default": @@ -234,7 +266,9 @@ def _purge_end(self, eventtime): self.load_started = False self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") # relative - self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position + self.gcode.run_script_from_command( + "G92 E0.0" + ) # Restore extruder position self.restore_state() self.gcode.respond_info("Restoring gcode positions.") toolhead.wait_moves() @@ -290,7 +324,9 @@ def home_needed(self): self.gcode.respond_info("Homing") self.gcode.run_script_from_command("G28") except Exception as e: - logging.error(f"Unable to perform home on load filament, error: {e}") + logging.error( + f"Unable to perform home on load filament, error: {e}" + ) def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): """Heats the extruder and wait. @@ -362,7 +398,9 @@ def cmd_PURGE_STOP(self, gcmd): cmd_LOAD_FILAMENT_HELPER = """Load Filament to the toolhead routine""" def cmd_LOAD_FILAMENT(self, gcmd): - temp = gcmd.get("TEMPERATURE", 220.0, parser=float, minval=210, maxval=250) + temp = gcmd.get( + "TEMPERATURE", 220.0, parser=float, minval=210, maxval=250 + ) try: pstat = self.printer.lookup_object("print_stats") pstat_state = pstat.get_status(self.reactor.monotonic())["state"] @@ -406,7 +444,9 @@ def cmd_LOAD_FILAMENT(self, gcmd): ) self.move_extruder_mm(distance=30, speed=40, wait=True) # * Actually start loading the machine - self.reactor.update_timer(self.extrude_to_sensor_timer, self.reactor.NOW) + self.reactor.update_timer( + self.extrude_to_sensor_timer, self.reactor.NOW + ) if self.filament_flow_sensor_object is not None: self.reactor.update_timer( self.verify_flow_sensor_timer, self.reactor.NOW diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index 2f7883e24a34..423ae0de32a6 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -23,7 +23,9 @@ def __init__(self, config): self.bucket_object = None self.cutter_object = None self.filament_flow_sensor_object = self.filament_flow_sensor_name = None - self.filament_switch_sensor_object = self.filament_switch_sensor_name = None + self.filament_switch_sensor_object = ( + self.filament_switch_sensor_name + ) = None self.idex_object = None self.unload_started = False self.unextrude_count: int = 0 @@ -32,10 +34,14 @@ def __init__(self, config): self.printer.register_event_handler("klippy:ready", self.handle_ready) self.printer.register_event_handler( "unload_filament:end", - lambda: self.gcode.respond_info("[UNLOAD FILAMENT] Unload Finished"), + lambda: self.gcode.respond_info( + "[UNLOAD FILAMENT] Unload Finished" + ), ) self.idex = config.getboolean("idex", False) - self.has_custom_boundary = config.getboolean("has_custom_boundary", False) + self.has_custom_boundary = config.getboolean( + "has_custom_boundary", False + ) self.travel_speed = config.getfloat( "travel_speed", 100.0, minval=50.0, maxval=500.0 ) @@ -52,7 +58,9 @@ def __init__(self, config): "unload_speed", default=10.0, minval=2.0, maxval=200.0 ) self.cutter_name = config.get("cutter_sensor_name", None) - self.timeout = config.getint("timeout", default=None, minval=10, maxval=1000) + self.timeout = config.getint( + "timeout", default=None, minval=10, maxval=1000 + ) self.unextrude_timer = self.reactor.register_timer( self.unextrude, self.reactor.NEVER ) @@ -71,12 +79,16 @@ def __init__(self, config): def handle_ready(self): self.min_event_systime = self.reactor.monotonic() + 2.0 if self.has_custom_boundary: - self.custom_boundary_object = self.printer.lookup_object("bed_custom_bound") + self.custom_boundary_object = self.printer.lookup_object( + "bed_custom_bound" + ) if self.bucket: self.bucket_object = self.printer.lookup_object("bucket") if self.cutter_name: if ( - self.printer.lookup_object(f"cutter_sensor {self.cutter_name}", None) + self.printer.lookup_object( + f"cutter_sensor {self.cutter_name}", None + ) is not None ): self.cutter_object = self.printer.lookup_object( @@ -140,7 +152,9 @@ def unload_end(self, eventtime=None): return self.reactor.NEVER self.gcode.run_script_from_command("G91") self.gcode.run_script_from_command("M83") - self.gcode.run_script_from_command("G92 E0.0") # Restore extruder position + self.gcode.run_script_from_command( + "G92 E0.0" + ) # Restore extruder position self.restore_state() if self.custom_boundary_object: self.custom_boundary_object.set_custom_boundary() @@ -169,18 +183,26 @@ def unextrude(self, eventtime): return completion.wait() self.unextrude_count += 1 self.step = "pulling" - self.move_extruder_mm(distance=-10, speed=self.unload_speed, wait=False) + self.move_extruder_mm( + distance=-10, speed=self.unload_speed, wait=False + ) return float(eventtime + float(10 / self.unload_speed)) except Exception as e: - raise UnloadFilamentError(f"[UNLOAD FILAMENT] Error while unloading: {e}") + raise UnloadFilamentError( + f"[UNLOAD FILAMENT] Error while unloading: {e}" + ) def disable_sensors(self): if self.filament_flow_sensor_object: - self.filament_flow_sensor_object.runout_helper.sensor_enabled = False + self.filament_flow_sensor_object.runout_helper.sensor_enabled = ( + False + ) if self.filament_switch_sensor_object: - self.filament_switch_sensor_object.runout_helper.sensor_enabled = False + self.filament_switch_sensor_object.runout_helper.sensor_enabled = ( + False + ) self.gcode.respond_info("filament switch sensor is not enabled") def enable_sensors(self): @@ -189,7 +211,9 @@ def enable_sensors(self): if self.filament_switch_sensor_object: self.gcode.respond_info("filament switch sensor is now enabled") - self.filament_switch_sensor_object.runout_helper.sensor_enabled = True + self.filament_switch_sensor_object.runout_helper.sensor_enabled = ( + True + ) def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: """Move the extruder @@ -211,7 +235,9 @@ def move_extruder_mm(self, distance=10.0, speed=30.0, wait=True) -> None: if wait: toolhead.wait_moves() except Exception as e: - raise UnloadFilamentError(f"[UNLOAD FILAMENT] Error moving extruder {e}") + raise UnloadFilamentError( + f"[UNLOAD FILAMENT] Error moving extruder {e}" + ) def home_needed(self) -> None: try: @@ -256,7 +282,9 @@ def save_state(self) -> None: self.gcode.run_script_from_command( "SAVE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state" ) - self.gcode.run_script_from_command("SAVE_GCODE_STATE NAME=_UNLOAD_STATE") + self.gcode.run_script_from_command( + "SAVE_GCODE_STATE NAME=_UNLOAD_STATE" + ) def restore_state(self) -> None: """Restore gcode state and dual carriage state if the system is in IDEX configuration""" @@ -269,7 +297,9 @@ def restore_state(self) -> None: ) def cmd_UNLOAD_FILAMENT(self, gcmd): - temp = gcmd.get("TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0) + temp = gcmd.get( + "TEMPERATURE", 250.0, parser=float, minval=220.0, maxval=500.0 + ) try: pstat = self.printer.lookup_object("print_stats") pstat_state = pstat.get_status(self.reactor.monotonic())["state"] @@ -316,7 +346,9 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): ) return if not self.cutter_object and self.timeout: - self.reactor.update_timer(self.unextrude_timer, self.reactor.NOW) + self.reactor.update_timer( + self.unextrude_timer, self.reactor.NOW + ) if self.filament_flow_sensor_object: self.reactor.update_timer( self.verify_flow_sensor_timer, self.reactor.NOW @@ -326,7 +358,8 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): "[UNLOAD] Starting filament switch sensor verification in 10 seconds" ) self.reactor.update_timer( - self.verify_switch_sensor_timer, float(self.reactor.NOW + 15.0) + self.verify_switch_sensor_timer, + float(self.reactor.NOW + 15.0), ) except Exception as e: From 49503d30e4644d10999c7bc9afa958c5d46f975d Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 14:45:04 +0100 Subject: [PATCH 11/17] Del: unused modules that never got finished --- .../extras/bo_idex_xy_offset_calibration.py | 379 ------------------ klippy/extras/repetability.py | 176 -------- 2 files changed, 555 deletions(-) delete mode 100644 klippy/extras/bo_idex_xy_offset_calibration.py delete mode 100644 klippy/extras/repetability.py diff --git a/klippy/extras/bo_idex_xy_offset_calibration.py b/klippy/extras/bo_idex_xy_offset_calibration.py deleted file mode 100644 index 6cbcb0ff6133..000000000000 --- a/klippy/extras/bo_idex_xy_offset_calibration.py +++ /dev/null @@ -1,379 +0,0 @@ -from __future__ import annotations - - -class XYOffsetCalibrationTool: - """Tool that helps the calibration of the XY offsets for Idex printers.""" - - def __init__(self, config): - self.printer = config.get_printer() - self.reactor = self.printer.get_reactor() - - # self.name = config.get_name().split()[-1] - - # * Register event handlers - self.printer.register_event_handler("klippy:connect", self.handle_connect) - self.printer.register_event_handler("klippy:ready", self.handle_ready) - - self.gcode = self.printer.lookup_object("gcode") - self.enable = False - self.last_state = False - - # * Control variables - self.prime: bool = False - self.line_number: int = 0 - self.line_spacing: float = 0.0 - self.line_height: float = 0.0 - self.line_top: float = 0.0 - self.initial_x_h: float = 0.0 - self.initial_y_h: float = 0.0 - self.initial_x_h_2: float = 0.0 - self.initial_y_h_2: float = 0.0 - self.initial_x_v: float = 0.0 - self.initial_y_v: float = 0.0 - self.initial_x_v_2: float = 0.0 - self.initial_y_v_2: float = 0.0 - self.extrude_per_mm: float = 0.0 - - self.horizontal_line_spacing: float = 0.0 - self.horizontal_line_height: float = 0.0 - self.horizontal_line_top: float = 0.0 - - self.vertical_line_spacing: float = 0.0 - self.vertical_line_height: float = 0.0 - self.vertical_line_top: float = 0.0 - - self.tool_2_x_offset: float = 0.0 - self.tool_2_y_offset: float = 0.0 - - self.filament_temperature: int = 190 - # * Register gcode commands - self.gcode.register_command( - "XYCALIBRATION", - self.cmd_XYCALIBRATION, - desc=self.cmd_XYCALIBRATION_help, - ) - - # self.gcode.register_mux_command( - # "XYNOZZLEPRIME", - # "XYOffsetCalibrationTool", - # self.name, - # self.cmd_XYNOZZLEPRIME, - # self.cmd_XYNOZZLEPRIME_help, - # ) - - def handle_connect(self): - """Event handler method for when klippy connects""" - self.toolhead = self.printer.lookup_object("toolhead") - # self.configfile = self.printer.lookup_object("configfile") - # self.manual_probe = self.printer.lookup_object("manual_probe") - # self.stepper_enable = self.printer.lookup_object("stepper_enable") - # self.mcu = self.printer.lookup_object("mcu") - # printer.register_event_handler("toolhead:set_position", - # self.reset_last_position) - # printer.register_event_handler("toolhead:manual_move", - # self.reset_last_position) - # printer.register_event_handler("gcode:command_error", - # self.reset_last_position) - # printer.register_event_handler("extruder:activate_extruder", - # self._handle_activate_extruder) - # printer.register_event_handler("homing:home_rails_end", - # self._handle_home_rails_end) - # printer.register_event_handler("gcode:request_restart", self._handle_request_restart) - - # self.printer.send_event("stepper_enable:motor_off", print_time) - - # gcmd.get_int("ENABLE", 1) - - # cur_time = self.printer.get_reactor().monotonic() - # kin_status = toolhead.get_kinematics().get_status(curtime) - - # toolhead.set_position(pos, homing_axes=[2]) - # toolhead.manual_move([None, None, self.z_hop], self.z_hop_speed) - # toolhead.get_kinematics().note_z_homed() - # need_x, need_y, need_z = [gcmd.get(axis, None) is not None for axis in "XYZ"] - # g28_cmd = self.gcode.create_gcode_command("G28", "G28", new_params) - # self.prev_G28(g28_cmd) - - # toolhead.get_position() - - def handle_ready(self): - """Event handler method for when klippy is enabled""" - self.toolhead = self.printer.lookup_object("toolhead") - self.kin = self.printer.lookup_object("toolhead").get_kinematics() - - - - # self.enable = True - # self.reactor.update_timer( - # self.update_direction_timer, self.reactor.Never - # ) - # self.enable = False - - cmd_XYCALIBRATION_help = "Handles the calibration of xy axes on Idex printers." - def cmd_XYCALIBRATION(self, gcmd): - # * First home axes - self.printer.send_event( - "stepper_enable:motor_on", self.printer.get_reactor().monotonic() - ) - gcmd.respond_info("XY calibration tool for Idex printers loaded.") - gcmd.respond_info(f"{self.toolhead.get_position()}") - - gcmd.respond_info(f"{self.get_homed_axes()}") - if self.get_homed_axes() is None or self.get_homed_axes() != "xy": - gcmd.respond_info("Must home axes before running the XY offset calibration for Idex printers.") - # return # I cannot continue if there is no homing - - - - _dual_carriage_module = self.kin.dc_module - - if _dual_carriage_module is None: - gcmd.respond_info("No dual carriage config section defined. Cannot run tool") - return - - - # Set extruder 0 - _dual_carriage_module.activate_dc_mode( - index= 0, - mode= "PRIMARY" #This is the default - ) - - _cur_extruder = self.toolhead.get_extruder() - _cur_extruder_name = _cur_extruder.get_name() - - - _available_printer_objects = self.printer.objects - gcmd.respond_info(f"Available Objects : {_available_printer_objects}") - gcmd.respond_info(f"Current extruder name : {_cur_extruder_name,}") - # Check that the kin type is cartesian - - # * Maybe home axes - # _homexy = "G28" - # # * Force usage of toolhead 0 T0 - # * Get extruder names, and activate the main toolhead - # gcmd.run_script_from_command("G28") - - # # * Horizontal Print in tool 0 - _horizontal_print_tool_0 = [ - "G90", - "M83", - f"G1 X{self.initial_x_h_2} Y{self.initial_y_h - 2} E{-0.1} F{12000}", # Brim - "G1 Z0.2 E0.5 F2000", # Brim - f"G1 X{self.initial_x_h} E{(self.initial_x_h_2 - self.initial_x_h) * self.extrude_per_mm} F3000", # U turn of the brim - f"G1 Y{self.initial_y_h} E{2 * self.extrude_per_mm}", # Start of the calibration - "G91", - ] - - # # self.print_horizontal_line - # # Raise the nozze G1 Z2 E-0.1 - - # # * Vertical Print in tool 0 - # _horizontal_print_tool_0 = [ - # "G90", - # "M83 ", - # f"G1 X{self.initial_x_v - 2} Y{self.initial_y_v} E{-0.1} F{12000}", # Brim - # "G1 Z0.2 E0.5 F2000", # Brim - # f"G1 X{self.initial_y_v} E{(self.initial_y_h_2 - self.initial_x_h) * self.extrude_per_mm} F3000", # U turn of the brim - # f"G1 Y{self.initial_y_h} E{2 * self.extrude_per_mm}", # Start of the calibration - # "G91", - # ] - - # # self.print_vertical_line - - # # Park the nozzle - # # G91 - # # park_nozzle - - # # Change to tool 1 - # # prime - # # * Horizontal print in tool 1 - - # _horizontal_print_tool_1 = [ - # "G90", - # "M83", - # f"G1 X{self.initial_x_h} Y{self.initial_y_h_2 - 2 + 2} E{-0.1} F{12000}", # Brim - # "G1 Z0.2 E0.5 F2000", # Lower nozzle for brim - # f"G1 X{self.initial_x_h_2} E{(self.initial_x_h_2 - self.initial_x_h) * self.extrude_per_mm} F3000", # U turn of the brim - # f"G1 Y{self.initial_y_h_2} E{2 * self.extrude_per_mm}", # Start of the calibration - # "G91", - # ] - # # self.print_horizontal_line - - # # Raise nozzle G1 Z2 E-0.1 - - # # * Vertical Print in tool 1 - # _horizontal_print_tool_0 = [ - # "G90", - # "M83 ", - # f"G1 X{self.initial_x_v_2 + 2} Y{self.initial_y_v} E{-0.1} F{12000}", # Brim - # "G1 Z0.2 E0.5 F2000", # Brim - # f"G1 X{self.initial_y_v_2} E{(self.initial_y_h_2 - self.initial_x_h) * self.extrude_per_mm} F3000", # U turn of the brim - # f"G1 Y{self.initial_x_v_2} E{2 * self.extrude_per_mm}", # Start of the calibration - # "G91", - # ] - # # self.print_vertical_line - - # # * Park the nozzle - # # G90 - # # G1 Y150 # So the user can see the result and select the line - - # # Change to tool 0 - # # T0 - - # # Permit the user to choose the line - # pass - - # cmd_OFFSETCALIBRATION_helpe = ( - # "calls a ui to select the ranges or something i don't know" - # ) - - # def cmd_OFFSETCALIBRATION(self, gcmd): - # pass - - # cmd_XYNOZZLEPRIME_help = "Primes the nozzle that it's going to be used" - - # def cmd_XYNOZZLEPRIME(self, gcmd): - # commands = [ - # f"M109 S{self.filament_temperature}", - # "G92 E0", # Specify that Extruder is at position 0 - # "M83", # Relative coordinates - # "G1 E50 F300", # Prime the nozzle - # "G92 E0", # Specify that Extruder is at position 0 - # ] - # for command_line in commands: - # gcmd.run_script_as_command(command_line) - - def get_status(self, eventtime): - return {"last_state": self.last_state, "enabled": self.enable} - - ################################################################################ - # Helper function that are useful for performing the XY calibration - ################################################################################ - - - def print_horizontal_line(self, gcmd, callback): - gcode_lines = [ - f"G1 X{self.horizontal_line_spacing} E{ abs(self.horizontal_line_spacing * self.extrude_per_mm)}", - f"G1 Y{self.horizontal_line_height} E{abs(self.horizontal_line_height * self.extrude_per_mm)}", - f"G1 X{self.horizontal_line_top} E{ abs(self.horizontal_line_top * self.extrude_per_mm)}", - f"G1 Y{int(self.horizontal_line_height) * -1} E{abs(self.horizontal_line_height * self.extrude_per_mm)}", - ] - for line in range(self.line_number + 1): - gcmd.run_command( - next(iter(gcode_lines)) - ) # Something like this to run all the lines - - def print_vertical_lines(self, gcmd, callback): - gcode_lines = [ - f"G1 Y{self.vertical_line_spacing} E{abs(self.vertical_line_spacing * self.extrude_per_mm)}", - f"G1 X{self.vertical_line_height} E{abs(self.vertical_line_height * self.extrude_per_mm)}", - f"G1 Y{self.vertical_line_top} E{abs(self.vertical_line_top * self.extrude_per_mm)}", - f"G1 X{int(self.vertical_line_height * -1)} E{abs(self.vertical_line_height * self.extrude_per_mm)}", - ] - for line in range(self.line_number + 1): - gcmd.run_command( - next(iter(gcode_lines)) - ) # Something like this to run all the lines - - def update_offsets(self): - pass - - def get_homed_axes(self) -> str | None: - cur_time = self.reactor.monotonic() - _kin_status = self.kin.get_status(cur_time) - - if not isinstance(_kin_status, dict): - return None - - if "homed_axes" in _kin_status.keys(): - return _kin_status["homed_axes"] - - return None - - # gcmd respond_info #Displays stuff on the console - # gcode run_script #Don't know what this one does Same as run_script_from_command but with mutex - # gcode run_script_from_command #Runs a command from a string - # toolhead move #Moves the toolhead - # toohead wait_moves() #Waits for the toolhead to finish its movements - - # When i have the tool head i can use the move command move(newpos, speed) - # There is also on toolhead a method called check_busy - # also a get_kinematics - # - - -def load_config(config): - return XYOffsetCalibrationTool(config) - - -# def load_config_prefix(config): -# return XYOffsetCalibrationTool(config) - - - -""" -From the kinmatics i can run - -get_steppers() -calc_position(self, stepper_positions) -set_position(self, newpos, homign_axes) -note_z_not_homed(self) -home(homign_state) -_motor_off(slef, pritne_time) -_check_positions(slef, move) -check_move(self, move) -get_status - - -variables: -self.rails -max_velocity, max_accel -max_z_velocity -max_z_accel -limits -ranges -axes_min -axes_max - - -From the main mcu class - - -canbus_uuid -setup_pin -create_oid(self) -get_printer -get_name -register_response(self, cb, msg, oid=None) -get_query_slot(self, oid) -seconds_to_clock(self, time) -get_max_stepper_error(self) -alloc_command_queue(self) - -lookup_command(self, msgformat, cp=None) -lookup_query_command(sef, msgformat, respformat, oid=None, cp=None, is_async=None) -estimate_print_time -get_contatns() -get_enumerations() -get_constants_float() -register_stepqueue(self, stepqueue) -request_move_queue_slot(self) -register_flush_callback(self, callback) -flush_move(self, printe_time, clear_history_time) -check_active() -is_fileoutput(self) -is_shutdown() -stats() - - -can also go get the extruder object so i can run some specific methods - - - - - - - -No final quando tiver-mos os valores, colocamos um gcode offset na segunda cabeça com o valor obtido -cada vez que cada cabeca e activada colocamos um offse, na cabela um e 0,0 e a segunda cabeca e o valor obtido -""" \ No newline at end of file diff --git a/klippy/extras/repetability.py b/klippy/extras/repetability.py deleted file mode 100644 index b1c560d42296..000000000000 --- a/klippy/extras/repetability.py +++ /dev/null @@ -1,176 +0,0 @@ -import pandas as pd -import logging -import os -import stepper - -class Repetability: - DATAFRAME_SETUP = [ - "timestamp", - "iteration", - "motion_report_live_position", - "motion_report_velocity", - "gcode_move_absolute_coordinates", - "gcode_move_homing_origin", - "gcode_move_position", - "toolhead_position", - "kin_homed_axes", - "kin_axis_minimum", - "kin_axis_maximum", - "limits", - "kin_pos" - ] - def __init__(self, config): - - self.printer = config.get_printer() - self.reactor = self.printer.get_reactor() - self.toolhead = None - self.gcode = self.printer.lookup_object("gcode") - - self.start_position= config.getfloatlist("start_position",None, count=2) - self.end_position= config.getfloatlist("end_position",None, count=2) - self.bed_boundary = config.getboolean("bed_boundary", False) - - self.movement = config.getboolean("movement", False) - self.z_raise = config.getfloat("z_raise") - self.travel_speed = config.getfloat("travel_speed", minval=50.0, maxval=500.0) - self.iterations = config.getint("iterations", minval=1, maxval=1000) - self.save_path = config.get("save_path") - - self.gcode.register_command( - "TEST_ENDSTOP", - self.cmd_TEST_ENDSTOP, - "Tests an z enstop" - ) - - self.data_save: pd.DataFrame = pd.DataFrame(columns=self.DATAFRAME_SETUP) - - self.printer.register_event_handler("klippy:connect", self.handle_connect) - - - def cmd_TEST_ENDSTOP(self, gcmd): - if self.toolhead is None: - return - if self.bed_boundary: - if self.printer.lookup_object("bed_custom_bound") is not None: - self.bed_boundary_object = self.printer.lookup_object("bed_custom_bound") - - for iteration in range(0, self.iterations): - if self.bed_boundary: - self.bed_boundary_object.restore_default_boundary() - - gcmd.respond_info(f"[TEST REPEATABILITY] Iteration: {iteration}") - - # self.move(x=self.start_position[0],y= self.start_position[1]) - - self.gcode.run_script_from_command("G28 \nM400") - - self.save_info(iteration) - - - self.reactor.register_callback(self.save_to_file) - - def handle_connect(self): - self.toolhead = self.printer.lookup_object("toolhead") - - - - def move(self, x, y): - if self.toolhead is None: - return - self.toolhead.manual_move([x, y], self.travel_speed) - - def get_kinematics_pos(self): - self.toolhead.flush_step_generation() - kin = self.toolhead.flush_step_generation() - kin_spos = {s.get_name(): s.get_commanded_position() for s in kin.get_steppers()} - kin_pos = kin.calc_position(kin_spos) - return kin_pos - - - def save_info(self, iteration): - eventtime = self.reactor.monotonic() - _gcode_move_status = self.get_gcode_move_status(eventtime) - _toolhead_status = self.get_toolhead_status(eventtime) - _extruder_status = self.get_extruder_status(eventtime) - _motion_report_status = self.get_motion_report_status(eventtime) - _kin = self.toolhead.get_kinematics() - _kin_status = _kin.get_status(eventtime) - - _row_data = [ - self.reactor.monotonic(), - iteration, - _motion_report_status["live_position"], - _motion_report_status["live_velocity"], - _gcode_move_status["absolute_coordinates"], - _gcode_move_status["homing_origin"], - _gcode_move_status["position"], - _toolhead_status["position"], - _kin_status["homed_axes"], - _kin_status["axis_minimum"], - _kin_status["axis_maximum"], - _kin.limits, - self.get_kinematics_pos() - ] - - _new_row_entry: pd.DataFrame = pd.DataFrame( - [_row_data], columns=self.data_save.columns - ) - - # * Update the csv file. - self.data_save = pd.concat( - [ - self.data_save, - _new_row_entry, - ], - ignore_index=True, - ) - - return - - def save_to_file(self, eventtime): - """Saves the print data dataframe into a csv file - - Returns: - bool: True if saved, False if an exception occurred - """ - try: - self.data_save.to_csv( - os.path.join(self.save_path, "repetability_test.csv"), - index=False, - ) - return True - except Exception as e: - logging.error( - f"Exception occurred when saving print data to csv file. MSG= {e}" - ) - # raise EdnaException("Error saving dataframe to directory") - return False - - - -################################################################################ - def get_gcode_move_status(self, eventtime): - gcode_move = self.printer.lookup_object("gcode_move") - return gcode_move.get_status(eventtime) - - def get_toolhead_status(self, eventtime): - toolhead = self.printer.lookup_object("toolhead") - return toolhead.get_status(eventtime) - - def get_extruder_status(self, eventtime): - toolhead = self.printer.lookup_object("toolhead") - extruder = toolhead.get_extruder() - return extruder.get_status(eventtime) - - def get_motion_report_status(self, eventtime): - motion_report = self.printer.lookup_object("motion_report") - return motion_report.get_status(eventtime) - - def get_kin_status(self, eventtime): - toolhead = self.printer.lookup_object("toolhead") - kin = toolhead.get_kinematics() - return kin.get_status(eventtime) - - -def load_config(config): - return Repetability(config) \ No newline at end of file From b47ddf5b32faa305dea3684825cb70b571fd27c5 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 14:56:28 +0100 Subject: [PATCH 12/17] Format line length --- klippy/extras/load_filament.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index 039fcd77eaca..5eb598e26a0d 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -100,7 +100,8 @@ def __init__(self, config): "TOOLHEAD", self.name, self.cmd_LOAD_FILAMENT, - "GCODE MACRO to load filament, takes into account if there is a belay and or a filament cutter with a sensor.", + "GCODE MACRO to load filament, takes into account" + "if there is a belay and or a filament cutter with a sensor.", ) self.gcode.register_mux_command( "PURGE_STOP", @@ -128,7 +129,8 @@ def handle_ready(self): self.handle_cutter_filament_present, ) logging.info( - "[LOAD FILAMENT EXTRA] Cutter Sensor recognized, cutter sensor state event handlers set" + "[LOAD FILAMENT EXTRA] Cutter Sensor recognized," + "cutter sensor state event handlers set" ) if self.idex: self.idex_object = self.printer.lookup_object("dual_carriage") @@ -139,7 +141,8 @@ def handle_ready(self): default=None, ) logging.info( - f"[LOAD FILAMENT EXTRA] Filament flow sensor {self.filament_flow_sensor_name} recognized" + "[LOAD FILAMENT EXTRA] Filament flow sensor " + f"{self.filament_flow_sensor_name} recognized" ) if self.filament_switch_sensor_name: self.filament_switch_sensor_object = self.printer.lookup_object( @@ -147,7 +150,8 @@ def handle_ready(self): default=None, ) logging.info( - f"[LOAD FILAMENT EXTRA] Filament switch sensor {self.filament_switch_sensor_name} recognized" + f"[LOAD FILAMENT EXTRA] Filament switch sensor" + f" {self.filament_switch_sensor_name} recognized" ) if self.has_custom_boundary: self.custom_boundary_object = self.printer.lookup_object( @@ -336,7 +340,8 @@ def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): temp (float): Target temperature in Celsius. wait (bool, optional): - Weather to wait or not for the temperature to reach the interval . Defaults to True + Weather to wait or not for the temperature to reach the interval. + Defaults to True """ eventtime = self.reactor.monotonic() toolhead = self.printer.lookup_object("toolhead") @@ -367,7 +372,8 @@ def enable_sensors(self): self.filament_switch_sensor_object.runout_helper.sensor_enabled = 1 def save_state(self): - """Save gcode state and dual carriage state if the system is in IDEX configuration""" + """Save gcode state and dual carriage state if the system + is in IDEX configuration""" if self.idex: self.gcode.run_script_from_command( f"SAVE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}" @@ -376,7 +382,8 @@ def save_state(self): return True def restore_state(self): - """Restore gcode state and dual carriage state if the system is in IDEX configuration""" + """Restore gcode state and dual carriage state if the system + is in IDEX configuration""" self.gcode.run_script_from_command( "RESTORE_GCODE_STATE NAME=_LOAD_STATE MOVE=0" ) @@ -387,7 +394,8 @@ def restore_state(self): return True - cmd_PURGE_STOP_HELPER = """Helper gcode command to stop filament purging when in the Load filament routine""" + cmd_PURGE_STOP_HELPER = """Helper gcode command to stop filament purging"\ + "when in the Load filament routine""" def cmd_PURGE_STOP(self, gcmd): if self.load_started: From 855385e949cb0468e92e78711eddc183ccc703a4 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 15:13:46 +0100 Subject: [PATCH 13/17] Del: unused fan duct module --- klippy/extras/toolhead_fan_duct_sensor.py | 29 ----------------------- 1 file changed, 29 deletions(-) delete mode 100644 klippy/extras/toolhead_fan_duct_sensor.py diff --git a/klippy/extras/toolhead_fan_duct_sensor.py b/klippy/extras/toolhead_fan_duct_sensor.py deleted file mode 100644 index a92d465675a8..000000000000 --- a/klippy/extras/toolhead_fan_duct_sensor.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -import typing - - -class ToolheadFanDuctSensor: - def __init__(self, config): - self.printer = config.get_printer() - self.name = config.get_name().split()[-1] - self.reactor = self.printer.get_reactor() - self.gcode = self.printer.lookup_object("gcode") - - # * Register fan duct sensor - duct_sensor_pin = config.get("sensor_pin") - buttons = self.printer.load_object(config, "buttons") - buttons.register_buttons([duct_sensor_pin], self.fan_duct_sensor_handler) - - def fan_duct_sensor_handler(self, eventtime, state): - if not state: - return - - if state: - # * Stop the printer - self.printer.invoke_shutdown( - msg="Toolhead Fan duct is currently open, close the fan duct and restart" - ) - - -def load_config_prefix(config): - return ToolheadFanDuctSensor(config) From f88838348335026c211e734664b01dcdf140cb9b Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 15:20:53 +0100 Subject: [PATCH 14/17] Fix trailing whitespaces and +80 char lines --- klippy/extras/belay.py | 11 ++++------ klippy/extras/bucket.py | 35 ++++++++++++++++++++++---------- klippy/extras/cutter_sensor.py | 16 ++++++++++----- klippy/extras/load_filament.py | 10 +++++---- klippy/extras/unload_filament.py | 31 +++++++++++++++++++--------- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/klippy/extras/belay.py b/klippy/extras/belay.py index aefa0a4ea34b..1c91a3ce0e0b 100644 --- a/klippy/extras/belay.py +++ b/klippy/extras/belay.py @@ -151,9 +151,7 @@ def handle_enable(self): if not condition(): return self.enabled = True - self.reactor.update_timer( - self.update_direction_timer, self.reactor.NOW - ) + self.reactor.update_timer(self.update_direction_timer, self.reactor.NOW) self.update_multiplier() def handle_disable(self): @@ -170,9 +168,7 @@ def sensor_callback(self, eventtime, state): self.last_state = state if self.enabled: virtual_sdcard = self.printer.lookup_object("virtual_sdcard") - file_pos = virtual_sdcard.get_status(eventtime).get( - "file_position" - ) + file_pos = virtual_sdcard.get_status(eventtime).get("file_position") if file_pos > 1: self.timeout = self.reactor.monotonic() + self.stuck_timeout if self.debug_level >= 1: @@ -233,7 +229,8 @@ def update_verify_stuck(self, eventtime) -> float: if state.lower() == "printing": if self.debug_level >= 1: self.gcode.respond_info( - f"Belay stuck with timeout {self.timeout} timer: {self.reactor.monotonic()}" + f"Belay stuck with timeout {self.timeout} "\ + f"timer: {self.reactor.monotonic()}" ) if self.timeout > self.reactor.monotonic(): diff --git a/klippy/extras/bucket.py b/klippy/extras/bucket.py index 153057d37d6d..4f152cea97f7 100644 --- a/klippy/extras/bucket.py +++ b/klippy/extras/bucket.py @@ -9,10 +9,16 @@ def __init__(self, config): self.toolhead = None self.printer.register_event_handler("klippy:ready", self.handle_ready) - self.printer.register_event_handler("klippy:connect", self.handle_connect) + self.printer.register_event_handler( + "klippy:connect", self.handle_connect + ) - self.has_custom_bed_bound = config.getboolean("has_custom_boundary", False) - self.bucket_position = config.getfloatlist("bucket_position", None, count=2) + self.has_custom_bed_bound = config.getboolean( + "has_custom_boundary", False + ) + self.bucket_position = config.getfloatlist( + "bucket_position", None, count=2 + ) self.travel_speed = config.getfloat( "travel_speed", default=50.0, minval=20.0, maxval=10000.0 ) @@ -22,7 +28,8 @@ def __init__(self, config): self.gcode.register_command( "MOVE_TO_BUCKET", self.cmd_MOVE_TO_BUCKET, - "Gcode for moving the toolhead to the bucket position. Takes into account if there is a custom bed boundary", + "Gcode for moving the toolhead to the bucket position." + "Takes into account if there is a custom bed boundary", ) def handle_ready(self): @@ -40,10 +47,12 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): return try: if self.custom_bed_bound_object: - _conf_bound = self.custom_bed_bound_object.check_boundary_limits( - position=( - self.bucket_position[0], - self.bucket_position[1], + _conf_bound = ( + self.custom_bed_bound_object.check_boundary_limits( + position=( + self.bucket_position[0], + self.bucket_position[1], + ) ) ) if ( @@ -59,7 +68,9 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): self.travel_speed, ) else: - self.toolhead.manual_move([self.bucket_position[0]], self.travel_speed) + self.toolhead.manual_move( + [self.bucket_position[0]], self.travel_speed + ) self.toolhead.wait_moves() self.toolhead.manual_move( [self.bucket_position[0], self.bucket_position[1]], @@ -76,7 +87,8 @@ def move_to_bucket(self, split: typing.Optional["bool"] = False): self.custom_bed_bound_object.set_custom_boundary() except Exception as e: raise BucketMoveError( - f"Exception occurred when moving toolhead to bucket position: {e}" + "Exception occurred when moving " + f"toolhead to bucket position: {e}" ) def cmd_MOVE_TO_BUCKET(self, gcmd): @@ -85,7 +97,8 @@ def cmd_MOVE_TO_BUCKET(self, gcmd): class BucketMoveError(Exception): - "Raised when there is an exception moving the toolhead to the bucket" + """Raised when there is an exception + moving the toolhead to the bucket""" def __init__(self, message, errors=None): super(BucketMoveError, self).__init__(message) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index 69209b2de758..3056fa5a5a29 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -186,7 +186,8 @@ def cut( kin_status = toolhead.get_kinematics().get_status(eventtime) if "xyz" not in kin_status["homed_axes"]: self.gcode.respond_info( - f"[CUTTER {self.name} sensor] Printer needs to be homed for filament cutting.", + f"[CUTTER {self.name} sensor] Printer " + "needs to be homed for filament cutting.", log=True, ) return @@ -259,7 +260,8 @@ def move_extruder_mm(self, distance=10.0, speed=10, wait=True) -> None: toolhead.wait_moves() except Exception as e: logging.info( - f"[CUTTER {self.name} sensor] Unable to move extruder error: {e}." + f"[CUTTER {self.name} sensor]" + f"Unable to move extruder error: {e}." ) def home_needed(self) -> None: @@ -289,7 +291,9 @@ def heat_extruder(self, temp, wait: bool = True) -> None: temp (float): Target temperature in Celsius. wait (bool, optional): - Weather to wait or not for the temperature to reach the interval . Defaults to True + Weather to wait or not for the + temperature to reach the interval. + Defaults to True """ try: toolhead = self.printer.lookup_object("toolhead") @@ -379,7 +383,8 @@ def move_to_bucket(self, split=False) -> None: raise CutterSensorError(f"Error moving to bucket position: {e}.") def move_back(self): - """Moves back to the original position where the CUT gcode command was called""" + """Moves back to the original position + where the CUT gcode command was called""" try: toolhead = self.printer.lookup_object("toolhead") if not self.prev_pos: @@ -445,7 +450,8 @@ def _runout_event_handler(self, eventtime): self._exec_gcode(pause_prefix, self.runout_gcode) def _exec_gcode(self, prefix, template): - """Internal Executes a gcode just like what's in the klipper filament_switch_sensor.py""" + """Internal Executes a gcode just like + what's in the klipper filament_switch_sensor.py""" try: self.gcode.run_script(prefix + template.render() + "\nM400") diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index 5eb598e26a0d..19d5e26857a7 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -340,7 +340,8 @@ def heat_and_wait(self, temp, wait: typing.Optional["bool"] = False): temp (float): Target temperature in Celsius. wait (bool, optional): - Weather to wait or not for the temperature to reach the interval. + Weather to wait or not for the + temperature to reach the interval. Defaults to True """ eventtime = self.reactor.monotonic() @@ -372,7 +373,7 @@ def enable_sensors(self): self.filament_switch_sensor_object.runout_helper.sensor_enabled = 1 def save_state(self): - """Save gcode state and dual carriage state if the system + """Save gcode state and dual carriage state if the system is in IDEX configuration""" if self.idex: self.gcode.run_script_from_command( @@ -382,14 +383,15 @@ def save_state(self): return True def restore_state(self): - """Restore gcode state and dual carriage state if the system + """Restore gcode state and dual carriage state if the system is in IDEX configuration""" self.gcode.run_script_from_command( "RESTORE_GCODE_STATE NAME=_LOAD_STATE MOVE=0" ) if self.idex: self.gcode.run_script_from_command( - f"RESTORE_DUAL_CARRIAGE_STATE NAME=load_carriage_state_{self.name}" + "RESTORE_DUAL_CARRIAGE_STATE " \ + f"NAME=load_carriage_state_{self.name}" ) return True diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index 423ae0de32a6..ec10df732661 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -73,7 +73,8 @@ def __init__(self, config): self.gcode.register_command( "UNLOAD_FILAMENT", self.cmd_UNLOAD_FILAMENT, - "GCODE Macro to unload filament, takes into account if there is a belay and or a filament cutter with a sensor", + "GCODE Macro to unload filament, takes into account if" + "there is a belay and or a filament cutter with a sensor", ) def handle_ready(self): @@ -117,7 +118,8 @@ def handle_cutter_fnp(self) -> None: return self.gcode.respond_info("Pulling filament out of the printer wait....") logging.info( - "Cutter reported not having filament, pulling filament out of the printer" + "Cutter reported not having filament," + " pulling filament out of the printer" ) self.reactor.update_timer( self.verify_switch_sensor_timer, float(self.reactor.NOW + 10.0) @@ -126,7 +128,8 @@ def handle_cutter_fnp(self) -> None: def verify_switch_sensor_state(self, eventtime): """Verifies if the filament is actually unloaded or not - The switch sensor here is assumed to be at the end of the filament pathway. + The switch sensor here is assumed to be at + the end of the filament pathway. """ if not self.unload_started or not self.filament_switch_sensor_object: return self.reactor.NEVER @@ -143,7 +146,8 @@ def verify_switch_sensor_state(self, eventtime): def verify_flow_sensor_state(self, eventtime): """TODO: - Verifies the presence of filament on the **flow sensor** and reacts to this.""" + Verifies the presence of filament on + the **flow sensor** and reacts to this.""" ... def unload_end(self, eventtime=None): @@ -260,7 +264,8 @@ def heat_extruder(self, temp, wait: bool = False) -> None: temp (float): Target temperature in Celsius. wait (bool, optional): - Whether to wait or not for the temperature to reach the interval . Defaults to True + Whether to wait or not for the + temperature to reach the interval . Defaults to True """ toolhead = self.printer.lookup_object("toolhead") eventtime = self.reactor.monotonic() @@ -277,7 +282,8 @@ def heat_extruder(self, temp, wait: bool = False) -> None: return def save_state(self) -> None: - """Save gcode state and dual carriage state if the system is in IDEX configuration""" + """Save gcode state and dual carriage state + if the system is in IDEX configuration""" if self.idex: self.gcode.run_script_from_command( "SAVE_DUAL_CARRIAGE_STATE NAME=unload_carriage_state" @@ -287,7 +293,8 @@ def save_state(self) -> None: ) def restore_state(self) -> None: - """Restore gcode state and dual carriage state if the system is in IDEX configuration""" + """Restore gcode state and dual carriage state + if the system is in IDEX configuration""" self.gcode.run_script_from_command( "RESTORE_GCODE_STATE NAME=_UNLOAD_STATE MOVE=1 MOVE_SPEED=100" ) @@ -315,7 +322,9 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): return self.home_needed() self.save_state() - self.disable_sensors() # So not to pause the filament switch sensor when filament is taken out + self.disable_sensors() + # So not to pause the filament switch + # sensor when filament is taken out if self.idex: if gcmd.get("TOOLHEAD") == "Load_T0": self.gcode.run_script_from_command("T0 UNLOAD") @@ -355,7 +364,8 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): ) if self.filament_switch_sensor_object: self.gcode.respond_info( - "[UNLOAD] Starting filament switch sensor verification in 10 seconds" + "[UNLOAD] Starting filament switch sensor" + " verification in 10 seconds" ) self.reactor.update_timer( self.verify_switch_sensor_timer, @@ -364,7 +374,8 @@ def cmd_UNLOAD_FILAMENT(self, gcmd): except Exception as e: raise UnloadFilamentError( - f"[UNLOAD] Unexpected error while trying to unload filament: {e}" + f"[UNLOAD] Unexpected error while trying "\ + f"to unload filament: {e}" ) def get_status(self, eventtime): From f32eb0db1bcb5eed6d0beab88a6789cb296efacf Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 15:39:52 +0100 Subject: [PATCH 15/17] Remove explicit type from control var --- klippy/extras/load_filament.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index 19d5e26857a7..1121229057eb 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -20,7 +20,7 @@ def __init__(self, config): self.filament_switch_sensor_object ) = None self.filament_flow_sensor_name = self.filament_flow_sensor_object = None - self.load_started: bool = False + self.load_started= False self.current_purge_index: int = 0 self.travel_speed = None self._old_extrude_distance: float | None = None From 52d1022c886c711cb61e1965e8e165e0bb1f0591 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 15:50:36 +0100 Subject: [PATCH 16/17] Remove explicitly setting the type for variables --- klippy/extras/cutter_sensor.py | 2 +- klippy/extras/load_filament.py | 4 ++-- klippy/extras/unload_filament.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/klippy/extras/cutter_sensor.py b/klippy/extras/cutter_sensor.py index 3056fa5a5a29..070ffb5e7a1f 100644 --- a/klippy/extras/cutter_sensor.py +++ b/klippy/extras/cutter_sensor.py @@ -33,7 +33,7 @@ def __init__(self, config): self.min_event_systime = self.reactor.NEVER self.filament_present = False self.sensor_enabled = True - self.is_cutting: bool = False + self.is_cutting = False self.prev_pos = None self._extrude_timeout = 30 self._timeout = 0 diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index 1121229057eb..e46d16f561b0 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -21,10 +21,10 @@ def __init__(self, config): ) = None self.filament_flow_sensor_name = self.filament_flow_sensor_object = None self.load_started= False - self.current_purge_index: int = 0 + self.current_purge_index = 0 self.travel_speed = None self._old_extrude_distance: float | None = None - self.extrude_count: int = 0 + self.extrude_count = 0 self.printer.register_event_handler("klippy:ready", self.handle_ready) self.min_event_systime = self.reactor.NEVER self.idex = config.getboolean("idex", False) diff --git a/klippy/extras/unload_filament.py b/klippy/extras/unload_filament.py index ec10df732661..ee170983b3dd 100644 --- a/klippy/extras/unload_filament.py +++ b/klippy/extras/unload_filament.py @@ -28,7 +28,7 @@ def __init__(self, config): ) = None self.idex_object = None self.unload_started = False - self.unextrude_count: int = 0 + self.unextrude_count = 0 self.travel_speed = None self.step = "None" self.printer.register_event_handler("klippy:ready", self.handle_ready) From 53758c7f35e4c00ae433dd475f3c8746ed4e4401 Mon Sep 17 00:00:00 2001 From: HugoCLSC Date: Wed, 15 Apr 2026 16:04:28 +0100 Subject: [PATCH 17/17] Remove explicitly setting the type for variables --- klippy/extras/load_filament.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/klippy/extras/load_filament.py b/klippy/extras/load_filament.py index e46d16f561b0..34cc24885d38 100644 --- a/klippy/extras/load_filament.py +++ b/klippy/extras/load_filament.py @@ -23,7 +23,7 @@ def __init__(self, config): self.load_started= False self.current_purge_index = 0 self.travel_speed = None - self._old_extrude_distance: float | None = None + self._old_extrude_distance = None self.extrude_count = 0 self.printer.register_event_handler("klippy:ready", self.handle_ready) self.min_event_systime = self.reactor.NEVER