Coverage for src / zooc / zooc_klipper.py: 0%
76 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 21:45 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 21:45 +0000
1"""ZOOC module for the Klipper."""
2import logging
3import pprint
5from klipper_utils.klipper_module import KlipperModule
6from klipper_utils.klipper_utils import ConfigWrapper, Gcmd, PrinterConfig
7from project_meta.project_info_pkg import ProjectInfoPkg
9from .data.calibration_data import CalibrationData
10from .data.fixed_offset import load_fixed_offsets
11from .data.temps import Temps
12from .run.run import Run
13from .run.run_config import RunConfig
14from .zooc_core import ZoocCore, ZoocError
16logger = logging.getLogger(__name__)
18class ZoocKlipper(KlipperModule):
19 """ZOOC - Klipper plugin."""
21 def __init__(self, config: ConfigWrapper):
22 """Initialize the ZOOC Klipper module.
24 Called by the Klipper when the module is loaded.
26 :param config: Klipper config object.
27 """
28 super().__init__(config=config, cmd_prefix="ZOOC_")
30 self.adjust_wait_target_temperature: bool = True
31 self.z_offset_zooc: float = 0.0 # Currently applied Z-offset calibration
33 self._register_command(self._cmd_run, "RUN Z-calibration in various bed and extruder temperatures")
34 self._register_command(self._cmd_show, "SHOW the profile's results")
35 self._register_command(self._cmd_adjust, "ADJUST the Z-offset to given bed and extruder temperature")
37 # Plugin configuration
38 self.run_config: RunConfig = Run.klipper_config(config)
39 self.zooc_core: ZoocCore = ZoocCore(
40 temps_ref=self.run_config.temps_ref,
41 allowed_offset_range=config.getfloatlist("allowed_offset_range"),
42 fixed_offsets=load_fixed_offsets(config),
43 calibration_data=CalibrationData.klipper_config(config),
44 )
46 logger.info(f"Loaded {ProjectInfoPkg()}")
48 def _cmd_run(self, gcmd: Gcmd) -> None:
49 """Perform the calibration.
51 The RUN command routine.
53 :param gcmd: G-code command.
54 """
55 run = Run(gcmd=gcmd, run_config=self.run_config, klipper_module=self)
56 gcmd.respond_info("Calibration can take several minutes, and during this time, Klipper's temperature readings may not update.")
57 calibration_data = run.run()
58 self.zooc_core.set_calibration_data(calibration_data)
60 configfile: PrinterConfig = self.printer.lookup_object('configfile')
61 calibration_data.to_config(configfile, self.name)
63 gcmd.respond_info(f"RESULTS=\n{calibration_data}")
64 gcmd.respond_info(f"Z calibration complete. Run SAVE_CONFIG to store the results to {self.printer.get_start_args().get('config_file')}")
66 for gcode in self.run_config.post_run_gcode:
67 self.gcode.run_script_from_command(gcode)
69 def _cmd_show(self, gcmd: Gcmd) -> None:
70 """Show the current calibration data.
72 The SHOW command routine.
74 :param gcmd: G-code command.
75 :raises gcmd.error: If the current calibration data is not valid.
76 """
77 pp = pprint.PrettyPrinter(depth=4)
78 gcmd.respond_info(f"Fixed profiles:\n{pp.pformat(self.zooc_core.fixed_offsets)}")
80 if not self.zooc_core.calibration_data.is_valid():
81 raise gcmd.error("Calibration was not run or loaded. Run ZOOC_RUN first")
83 gcmd.respond_info(f"\nCalibration:\n{self.zooc_core.calibration_data}")
85 # pylint: disable=too-many-positional-arguments, too-many-arguments
86 def _cmd_adjust(self, gcmd: Gcmd, fixed_offsets: str | None = None, bed_temp: float | None = None, extruder_temp: float | None = None, dry_run: bool = False) -> None:
87 """Adjust the Z-offset to the given bed and extruder temperatures.
89 The ADJUST command routine.
91 :param gcmd: G-code command.
92 :param fixed_offsets: Fixed offsets to apply.
93 :param bed_temp: Bed temperature to use.
94 :param extruder_temp: Extruder temperature to use.
95 :param dry_run: If True, do not apply the offset.
96 :raises gcmd.error: If the calibration is not run or valid, or if the temperatures are not set.
97 :raises ZoocError: (for doc tool only. ZoocError is actually converted to gcmd.error).
98 """
99 if fixed_offsets is None:
100 fixed_offsets = ""
101 try:
102 if not self.zooc_core.calibration_data.is_valid():
103 raise ZoocError("Temperature calibration is empty or invalid. Run ZOOC_RUN or check '[zooc] offset_data:' in printer.cfg")
105 # Get current temperatures
106 temps_curr, temps_target = self.get_temperatures()
107 if not temps_curr.both_on():
108 raise ZoocError("Cannot read bed or extruder temperatures")
110 temps_offset = self.zooc_core.get_aggregated_temps(temps_pri=Temps.create(bed_temp, extruder_temp), temps_sec=temps_target)
111 z_fixed_offsets = sum(self.zooc_core.get_fixed_offsets(fixed_offsets).values())
112 z_offset_temp = self.zooc_core.get_temp_z_offset(temps=temps_offset)
113 z_offset_final = z_offset_temp + z_fixed_offsets
115 # When the Z-offset increases, i.e. gap between bed and nozzle reduces, apply (increase) Z-offset immediately before heating
116 move_now = z_offset_temp > self.zooc_core.get_temp_z_offset(temps_curr)
118 gcmd.respond_info(f"Adjusting: Z-offset={z_offset_final:.3f} mm (temp={z_offset_temp:.3f} + fixed={z_fixed_offsets:.3f} mm)")
119 self._set_offset(gcmd=gcmd, z_offset=z_offset_final, move_now=move_now, dry_run=dry_run)
120 except ZoocError as err:
121 if dry_run:
122 gcmd.respond_info(f"DRY MODE: {err}")
123 else:
124 raise gcmd.error(f"{err}") from err
126 def _set_offset(self, gcmd: Gcmd, z_offset: float, move_now: bool, dry_run: bool) -> None:
127 """Set the Z-offset to the given value.
129 :param gcmd: G-code command.
130 :param z_offset: The Z-offset to apply [mm].
131 :param move_now: Move the toolhead the given offset immediately.
132 :param dry_run: If True, do not apply the offset, just log it.
133 """
134 # Get the Z-offset adjusted by the user or other plugins.
135 # I.e, the difference between the previously applied Z-offset and ZOOC's Z-offset
136 z_offset_curr = self._get_curr_offset_positions()[2]
137 z_offset_user = z_offset_curr - self.z_offset_zooc
139 absolute = True # Using absolute offset by default
140 move_cmd = 1 if move_now else 0
141 # https://www.klipper3d.org/G-Codes.html#set_gcode_offset
142 if absolute:
143 # New absolute Z-offset value is the ZOOC's Z-offset plus the user's Z-offset
144 z_offset_zooc_abs = z_offset + z_offset_user
145 gcmd_offset = self.gcode.create_gcode_command("SET_GCODE_OFFSET",
146 "SET_GCODE_OFFSET",
147 {'Z': z_offset_zooc_abs, 'MOVE': move_cmd})
148 logger.info(f"Setting Z-offset from {z_offset_curr:.3f} to zooc={z_offset:.3f} + user={z_offset_user:.3f} = {z_offset_zooc_abs:.3f} mm")
149 else:
150 # Relative value to adjust the Z-offset to gain the desired Z-offset
151 z_offset_zooc_rel = z_offset - self.z_offset_zooc
153 # Use Z_ADJUST to allow user to adjust offset. relative Z-offset
154 gcmd_offset = self.gcode.create_gcode_command("SET_GCODE_OFFSET",
155 "SET_GCODE_OFFSET",
156 {'Z_ADJUST': z_offset_zooc_rel, 'MOVE': move_cmd})
157 logger.info(f"Adjust Z-offset from {z_offset_curr:.3f} to zooc={z_offset:.3f} + user={z_offset_user:.3f} = {z_offset_zooc_rel:+.3f} mm")
159 self.z_offset_zooc = z_offset
160 if dry_run:
161 gcmd.respond_info("DRY MODE: Nothing was modified in dry run")
162 else:
163 self.gcode_move.cmd_SET_GCODE_OFFSET(gcmd_offset)