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

1"""ZOOC module for the Klipper.""" 

2import logging 

3import pprint 

4 

5from klipper_utils.klipper_module import KlipperModule 

6from klipper_utils.klipper_utils import ConfigWrapper, Gcmd, PrinterConfig 

7from project_meta.project_info_pkg import ProjectInfoPkg 

8 

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 

15 

16logger = logging.getLogger(__name__) 

17 

18class ZoocKlipper(KlipperModule): 

19 """ZOOC - Klipper plugin.""" 

20 

21 def __init__(self, config: ConfigWrapper): 

22 """Initialize the ZOOC Klipper module. 

23 

24 Called by the Klipper when the module is loaded. 

25 

26 :param config: Klipper config object. 

27 """ 

28 super().__init__(config=config, cmd_prefix="ZOOC_") 

29 

30 self.adjust_wait_target_temperature: bool = True 

31 self.z_offset_zooc: float = 0.0 # Currently applied Z-offset calibration 

32 

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") 

36 

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 ) 

45 

46 logger.info(f"Loaded {ProjectInfoPkg()}") 

47 

48 def _cmd_run(self, gcmd: Gcmd) -> None: 

49 """Perform the calibration. 

50 

51 The RUN command routine. 

52 

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) 

59 

60 configfile: PrinterConfig = self.printer.lookup_object('configfile') 

61 calibration_data.to_config(configfile, self.name) 

62 

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')}") 

65 

66 for gcode in self.run_config.post_run_gcode: 

67 self.gcode.run_script_from_command(gcode) 

68 

69 def _cmd_show(self, gcmd: Gcmd) -> None: 

70 """Show the current calibration data. 

71 

72 The SHOW command routine. 

73 

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)}") 

79 

80 if not self.zooc_core.calibration_data.is_valid(): 

81 raise gcmd.error("Calibration was not run or loaded. Run ZOOC_RUN first") 

82 

83 gcmd.respond_info(f"\nCalibration:\n{self.zooc_core.calibration_data}") 

84 

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. 

88 

89 The ADJUST command routine. 

90 

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") 

104 

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") 

109 

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 

114 

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) 

117 

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 

125 

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. 

128 

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 

138 

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 

152 

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") 

158 

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)