flashforge-python-api 1.0.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. flashforge/__init__.py +70 -29
  2. flashforge/api/__init__.py +1 -0
  3. flashforge/api/constants/__init__.py +1 -0
  4. flashforge/api/constants/commands.py +1 -0
  5. flashforge/api/constants/endpoints.py +7 -0
  6. flashforge/api/controls/__init__.py +1 -0
  7. flashforge/api/controls/control.py +64 -61
  8. flashforge/api/controls/files.py +28 -26
  9. flashforge/api/controls/info.py +84 -85
  10. flashforge/api/controls/job_control.py +120 -82
  11. flashforge/api/controls/temp_control.py +14 -11
  12. flashforge/api/misc/__init__.py +1 -1
  13. flashforge/api/network/__init__.py +2 -1
  14. flashforge/api/network/utils.py +7 -8
  15. flashforge/client.py +185 -64
  16. flashforge/discovery/__init__.py +30 -3
  17. flashforge/discovery/discovery.py +641 -308
  18. flashforge/models/__init__.py +11 -10
  19. flashforge/models/machine_info.py +225 -100
  20. flashforge/models/responses.py +103 -43
  21. flashforge/tcp/__init__.py +30 -17
  22. flashforge/tcp/a3_client.py +408 -0
  23. flashforge/tcp/a4_client.py +300 -0
  24. flashforge/tcp/ff_client.py +93 -90
  25. flashforge/tcp/gcode/__init__.py +4 -2
  26. flashforge/tcp/gcode/a3_gcode_controller.py +109 -0
  27. flashforge/tcp/gcode/gcode_controller.py +48 -36
  28. flashforge/tcp/gcode/gcodes.py +7 -1
  29. flashforge/tcp/parsers/__init__.py +11 -11
  30. flashforge/tcp/parsers/endstop_status.py +103 -132
  31. flashforge/tcp/parsers/location_info.py +26 -13
  32. flashforge/tcp/parsers/print_status.py +49 -56
  33. flashforge/tcp/parsers/printer_info.py +38 -48
  34. flashforge/tcp/parsers/temp_info.py +46 -47
  35. flashforge/tcp/parsers/thumbnail_info.py +65 -40
  36. flashforge/tcp/tcp_client.py +296 -293
  37. flashforge_python_api-1.2.0.dist-info/METADATA +133 -0
  38. flashforge_python_api-1.2.0.dist-info/RECORD +46 -0
  39. {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/WHEEL +1 -1
  40. flashforge_python_api-1.0.2.dist-info/METADATA +0 -284
  41. flashforge_python_api-1.0.2.dist-info/RECORD +0 -43
  42. {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/entry_points.txt +0 -0
  43. {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,11 +7,10 @@ building upon the basic TCP communication provided by FlashForgeTcpClient.
7
7
 
8
8
  import asyncio
9
9
  import logging
10
- from typing import Optional
11
10
 
12
11
  from .gcode import GCodeController, GCodes
13
12
  from .parsers import EndstopStatus, LocationInfo, PrinterInfo, PrintStatus, TempInfo, ThumbnailInfo
14
- from .tcp_client import FlashForgeTcpClient
13
+ from .tcp_client import FlashForgeTcpClient, FlashForgeTcpClientOptions
15
14
 
16
15
  logger = logging.getLogger(__name__)
17
16
 
@@ -19,23 +18,23 @@ logger = logging.getLogger(__name__)
19
18
  class FlashForgeClient(FlashForgeTcpClient):
20
19
  """
21
20
  High-level client for interacting with FlashForge 3D printers via TCP/IP.
22
-
21
+
23
22
  This class implements specific G-code commands and workflows for printer control,
24
23
  such as initialization, LED control, job management, homing, temperature settings,
25
24
  filament operations, and retrieving various printer statuses.
26
-
25
+
27
26
  It uses a "legacy" API approach primarily based on sending G-code/M-code commands
28
27
  and parsing text-based responses.
29
28
  """
30
29
 
31
- def __init__(self, hostname: str) -> None:
30
+ def __init__(self, hostname: str, options: FlashForgeTcpClientOptions | None = None) -> None:
32
31
  """
33
32
  Create an instance of FlashForgeClient.
34
-
33
+
35
34
  Args:
36
35
  hostname: The IP address or hostname of the FlashForge printer
37
36
  """
38
- super().__init__(hostname)
37
+ super().__init__(hostname, options)
39
38
  self._control = GCodeController(self)
40
39
  self._is_5m_pro = False
41
40
  """Flag indicating if the connected printer is a 5M Pro model, which may have specific features."""
@@ -43,7 +42,7 @@ class FlashForgeClient(FlashForgeTcpClient):
43
42
  def get_ip(self) -> str:
44
43
  """
45
44
  Get the IP address or hostname of the connected printer.
46
-
45
+
47
46
  Returns:
48
47
  The printer's hostname or IP address
49
48
  """
@@ -52,9 +51,9 @@ class FlashForgeClient(FlashForgeTcpClient):
52
51
  def gcode(self) -> GCodeController:
53
52
  """
54
53
  Get the GCodeController instance associated with this client.
55
-
54
+
56
55
  Provides access to specific G-code command methods.
57
-
56
+
58
57
  Returns:
59
58
  The GCodeController instance
60
59
  """
@@ -63,10 +62,10 @@ class FlashForgeClient(FlashForgeTcpClient):
63
62
  async def init_control(self) -> bool:
64
63
  """
65
64
  Initialize the control connection with the printer.
66
-
65
+
67
66
  This typically involves sending a login command, retrieving printer info,
68
67
  and starting a keep-alive mechanism. Retries on failure.
69
-
68
+
70
69
  Returns:
71
70
  True if control is successfully initialized, False otherwise
72
71
  """
@@ -100,7 +99,7 @@ class FlashForgeClient(FlashForgeTcpClient):
100
99
  async def led_on(self) -> bool:
101
100
  """
102
101
  Turn the printer's LED lights on.
103
-
102
+
104
103
  Returns:
105
104
  True if the command is successful, False otherwise
106
105
  """
@@ -109,7 +108,7 @@ class FlashForgeClient(FlashForgeTcpClient):
109
108
  async def led_off(self) -> bool:
110
109
  """
111
110
  Turn the printer's LED lights off.
112
-
111
+
113
112
  Returns:
114
113
  True if the command is successful, False otherwise
115
114
  """
@@ -118,7 +117,7 @@ class FlashForgeClient(FlashForgeTcpClient):
118
117
  async def pause_job(self) -> bool:
119
118
  """
120
119
  Pause the current print job.
121
-
120
+
122
121
  Returns:
123
122
  True if the command is successful, False otherwise
124
123
  """
@@ -127,7 +126,7 @@ class FlashForgeClient(FlashForgeTcpClient):
127
126
  async def resume_job(self) -> bool:
128
127
  """
129
128
  Resume a paused print job.
130
-
129
+
131
130
  Returns:
132
131
  True if the command is successful, False otherwise
133
132
  """
@@ -136,7 +135,7 @@ class FlashForgeClient(FlashForgeTcpClient):
136
135
  async def stop_job(self) -> bool:
137
136
  """
138
137
  Stop the current print job.
139
-
138
+
140
139
  Returns:
141
140
  True if the command is successful, False otherwise
142
141
  """
@@ -145,10 +144,10 @@ class FlashForgeClient(FlashForgeTcpClient):
145
144
  async def start_job(self, name: str) -> bool:
146
145
  """
147
146
  Start a print job from a file stored on the printer.
148
-
147
+
149
148
  Args:
150
149
  name: The name of the file to print (typically without path)
151
-
150
+
152
151
  Returns:
153
152
  True if the command is successful, False otherwise
154
153
  """
@@ -157,7 +156,7 @@ class FlashForgeClient(FlashForgeTcpClient):
157
156
  async def home_axes(self) -> bool:
158
157
  """
159
158
  Home all axes (X, Y, Z) of the printer.
160
-
159
+
161
160
  Returns:
162
161
  True if the command is successful, False otherwise
163
162
  """
@@ -166,7 +165,7 @@ class FlashForgeClient(FlashForgeTcpClient):
166
165
  async def rapid_home(self) -> bool:
167
166
  """
168
167
  Perform a rapid homing of all axes.
169
-
168
+
170
169
  Returns:
171
170
  True if the command is successful, False otherwise
172
171
  """
@@ -175,9 +174,9 @@ class FlashForgeClient(FlashForgeTcpClient):
175
174
  async def turn_runout_sensor_on(self) -> bool:
176
175
  """
177
176
  Turn on the filament runout sensor.
178
-
177
+
179
178
  This functionality is only available on specific printer models (e.g., 5M Pro).
180
-
179
+
181
180
  Returns:
182
181
  True if the command is successful and applicable, False otherwise
183
182
  """
@@ -190,9 +189,9 @@ class FlashForgeClient(FlashForgeTcpClient):
190
189
  async def turn_runout_sensor_off(self) -> bool:
191
190
  """
192
191
  Turn off the filament runout sensor.
193
-
192
+
194
193
  This functionality is only available on specific printer models (e.g., 5M Pro).
195
-
194
+
196
195
  Returns:
197
196
  True if the command is successful and applicable, False otherwise
198
197
  """
@@ -205,11 +204,11 @@ class FlashForgeClient(FlashForgeTcpClient):
205
204
  async def set_extruder_temp(self, temp: int, wait_for: bool = False) -> bool:
206
205
  """
207
206
  Set the target temperature for the extruder.
208
-
207
+
209
208
  Args:
210
209
  temp: The target temperature in Celsius
211
210
  wait_for: If True, the method will wait until the target temperature is reached
212
-
211
+
213
212
  Returns:
214
213
  True if the command is successful, False otherwise
215
214
  """
@@ -218,7 +217,7 @@ class FlashForgeClient(FlashForgeTcpClient):
218
217
  async def cancel_extruder_temp(self) -> bool:
219
218
  """
220
219
  Cancel extruder heating and set its target temperature to 0.
221
-
220
+
222
221
  Returns:
223
222
  True if the command is successful, False otherwise
224
223
  """
@@ -227,11 +226,11 @@ class FlashForgeClient(FlashForgeTcpClient):
227
226
  async def set_bed_temp(self, temp: int, wait_for: bool = False) -> bool:
228
227
  """
229
228
  Set the target temperature for the print bed.
230
-
229
+
231
230
  Args:
232
231
  temp: The target temperature in Celsius
233
232
  wait_for: If True, the method will wait until the target temperature is reached
234
-
233
+
235
234
  Returns:
236
235
  True if the command is successful, False otherwise
237
236
  """
@@ -240,10 +239,10 @@ class FlashForgeClient(FlashForgeTcpClient):
240
239
  async def cancel_bed_temp(self, wait_for_cool: bool = False) -> bool:
241
240
  """
242
241
  Cancel print bed heating and set its target temperature to 0.
243
-
242
+
244
243
  Args:
245
244
  wait_for_cool: If True, waits for the bed to cool down after canceling
246
-
245
+
247
246
  Returns:
248
247
  True if the command is successful, False otherwise
249
248
  """
@@ -252,13 +251,13 @@ class FlashForgeClient(FlashForgeTcpClient):
252
251
  async def extrude(self, length: float, feedrate: int = 450) -> bool:
253
252
  """
254
253
  Command the extruder to extrude a specific length of filament.
255
-
254
+
256
255
  Uses G1 E[length] F[feedrate] command.
257
-
256
+
258
257
  Args:
259
258
  length: The length of filament to extrude in millimeters
260
259
  feedrate: The feedrate for extrusion in mm/min (default: 450)
261
-
260
+
262
261
  Returns:
263
262
  True if the command is successful, False otherwise
264
263
  """
@@ -267,14 +266,14 @@ class FlashForgeClient(FlashForgeTcpClient):
267
266
  async def move_extruder(self, x: float, y: float, feedrate: int) -> bool:
268
267
  """
269
268
  Move the extruder to a specified X, Y position.
270
-
269
+
271
270
  Uses G1 X[x] Y[y] F[feedrate] command.
272
-
271
+
273
272
  Args:
274
273
  x: The target X coordinate
275
274
  y: The target Y coordinate
276
275
  feedrate: The feedrate for the movement in mm/min
277
-
276
+
278
277
  Returns:
279
278
  True if the command is successful, False otherwise
280
279
  """
@@ -283,15 +282,15 @@ class FlashForgeClient(FlashForgeTcpClient):
283
282
  async def move(self, x: float, y: float, z: float, feedrate: int) -> bool:
284
283
  """
285
284
  Move the extruder to a specified X, Y, Z position.
286
-
285
+
287
286
  Uses G1 X[x] Y[y] Z[z] F[feedrate] command.
288
-
287
+
289
288
  Args:
290
289
  x: The target X coordinate
291
290
  y: The target Y coordinate
292
291
  z: The target Z coordinate
293
292
  feedrate: The feedrate for the movement in mm/min
294
-
293
+
295
294
  Returns:
296
295
  True if the command is successful, False otherwise
297
296
  """
@@ -300,12 +299,12 @@ class FlashForgeClient(FlashForgeTcpClient):
300
299
  async def send_cmd_ok(self, cmd: str) -> bool:
301
300
  """
302
301
  Send a G-code/M-code command to the printer and check for an "ok" response.
303
-
302
+
304
303
  Expects the printer's reply to include "Received." and "ok" to be considered successful.
305
-
304
+
306
305
  Args:
307
306
  cmd: The command string to send (e.g., "~M115")
308
-
307
+
309
308
  Returns:
310
309
  True if the command is acknowledged with "ok", False otherwise or on error
311
310
  """
@@ -321,29 +320,29 @@ class FlashForgeClient(FlashForgeTcpClient):
321
320
  async def send_raw_cmd(self, cmd: str) -> str:
322
321
  """
323
322
  Send a raw command string to the printer and return the raw response.
324
-
323
+
325
324
  Handles a special case for "M661" (list files), which is processed differently.
326
-
325
+
327
326
  Args:
328
327
  cmd: The raw command string to send
329
-
328
+
330
329
  Returns:
331
330
  The printer's raw string response, or an empty string on failure.
332
331
  For "M661", it returns a newline-separated list of files.
333
332
  """
334
333
  if "M661" not in cmd:
335
334
  result = await self.send_command_async(cmd)
336
- return result or ''
335
+ return result or ""
337
336
 
338
337
  file_list = await self.get_file_list_async()
339
338
  return "\n".join(file_list)
340
339
 
341
- async def get_printer_info(self) -> Optional[PrinterInfo]:
340
+ async def get_printer_info(self) -> PrinterInfo | None:
342
341
  """
343
342
  Retrieve general printer information (model, firmware, etc.).
344
-
343
+
345
344
  Sends CMD_INFO_STATUS and parses the response into a PrinterInfo object.
346
-
345
+
347
346
  Returns:
348
347
  A PrinterInfo object, or None if retrieval fails
349
348
  """
@@ -352,12 +351,12 @@ class FlashForgeClient(FlashForgeTcpClient):
352
351
  return PrinterInfo().from_replay(response)
353
352
  return None
354
353
 
355
- async def get_temp_info(self) -> Optional[TempInfo]:
354
+ async def get_temp_info(self) -> TempInfo | None:
356
355
  """
357
356
  Retrieve current temperature information (extruder, bed).
358
-
357
+
359
358
  Sends CMD_TEMP and parses the response into a TempInfo object.
360
-
359
+
361
360
  Returns:
362
361
  A TempInfo object, or None if retrieval fails
363
362
  """
@@ -366,12 +365,12 @@ class FlashForgeClient(FlashForgeTcpClient):
366
365
  return TempInfo().from_replay(response)
367
366
  return None
368
367
 
369
- async def get_location_info(self) -> Optional[LocationInfo]:
368
+ async def get_location_info(self) -> LocationInfo | None:
370
369
  """
371
370
  Retrieve the current XYZ coordinates of the print head.
372
-
371
+
373
372
  Sends CMD_INFO_XYZAB and parses the response into a LocationInfo object.
374
-
373
+
375
374
  Returns:
376
375
  A LocationInfo object, or None if retrieval fails
377
376
  """
@@ -383,37 +382,39 @@ class FlashForgeClient(FlashForgeTcpClient):
383
382
  async def _get_nozzle_temp(self) -> float:
384
383
  """
385
384
  Retrieve the current temperature of the nozzle (extruder).
386
-
385
+
387
386
  Returns:
388
387
  The current nozzle temperature in Celsius, or 0 if unavailable
389
388
  """
390
389
  temps = await self.get_temp_info()
391
- if temps and temps.get_extruder_temp():
392
- return temps.get_extruder_temp().get_current()
390
+ if temps:
391
+ extruder_temp = temps.get_extruder_temp()
392
+ if extruder_temp:
393
+ return extruder_temp.get_current()
393
394
  return 0.0
394
395
 
395
- async def get_endstop_status(self) -> Optional[EndstopStatus]:
396
+ async def get_endstop_status(self) -> EndstopStatus | None:
396
397
  """
397
398
  Retrieve the current endstop status and machine state information.
398
-
399
+
399
400
  Sends M119 command and parses the response into an EndstopStatus object.
400
401
  This includes endstop states, machine status, move mode, LED status, and current file.
401
-
402
+
402
403
  Returns:
403
404
  An EndstopStatus object, or None if retrieval fails
404
405
  """
405
- response = await self.send_command_async(GCodes.CMD_INFO_STATUS) # M119
406
+ response = await self.send_command_async(GCodes.CMD_ENDSTOP_INFO)
406
407
  if response:
407
408
  return EndstopStatus().from_replay(response)
408
409
  return None
409
410
 
410
- async def get_print_status(self) -> Optional[PrintStatus]:
411
+ async def get_print_status(self) -> PrintStatus | None:
411
412
  """
412
413
  Retrieve the current print status and progress information.
413
-
414
+
414
415
  Sends M27 command and parses the response into a PrintStatus object.
415
416
  This includes SD card byte progress and layer progress.
416
-
417
+
417
418
  Returns:
418
419
  A PrintStatus object, or None if retrieval fails
419
420
  """
@@ -422,15 +423,15 @@ class FlashForgeClient(FlashForgeTcpClient):
422
423
  return PrintStatus().from_replay(response)
423
424
  return None
424
425
 
425
- async def get_thumbnail(self, file_name: str) -> Optional[ThumbnailInfo]:
426
+ async def get_thumbnail(self, file_name: str) -> ThumbnailInfo | None:
426
427
  """
427
428
  Retrieve the thumbnail image for a specific file.
428
-
429
+
429
430
  Sends M662 command and parses the response to extract PNG thumbnail data.
430
-
431
+
431
432
  Args:
432
433
  file_name: The name of the file to get the thumbnail for
433
-
434
+
434
435
  Returns:
435
436
  A ThumbnailInfo object containing the PNG image data, or None if retrieval fails
436
437
  """
@@ -444,7 +445,7 @@ class FlashForgeClient(FlashForgeTcpClient):
444
445
  async def check_machine_state(self) -> str:
445
446
  """
446
447
  Get a simplified machine state string for quick status checking.
447
-
448
+
448
449
  Returns:
449
450
  A string indicating the machine state ("printing", "ready", "paused", "complete", "unknown")
450
451
  """
@@ -463,10 +464,10 @@ class FlashForgeClient(FlashForgeTcpClient):
463
464
  else:
464
465
  return "unknown"
465
466
 
466
- async def get_current_print_file(self) -> Optional[str]:
467
+ async def get_current_print_file(self) -> str | None:
467
468
  """
468
469
  Get the name of the currently loaded/printing file.
469
-
470
+
470
471
  Returns:
471
472
  The filename of the current print job, or None if no file is loaded
472
473
  """
@@ -478,7 +479,7 @@ class FlashForgeClient(FlashForgeTcpClient):
478
479
  async def is_printer_ready(self) -> bool:
479
480
  """
480
481
  Check if the printer is ready for new commands.
481
-
482
+
482
483
  Returns:
483
484
  True if the printer is in a ready state, False otherwise
484
485
  """
@@ -490,7 +491,7 @@ class FlashForgeClient(FlashForgeTcpClient):
490
491
  async def get_print_progress(self) -> tuple[int, int, int]:
491
492
  """
492
493
  Get comprehensive print progress information.
493
-
494
+
494
495
  Returns:
495
496
  A tuple of (layer_percent, sd_percent, current_layer) where:
496
497
  - layer_percent: Progress based on layers (0-100)
@@ -518,35 +519,37 @@ class FlashForgeClient(FlashForgeTcpClient):
518
519
 
519
520
  return (int(layer_percent), int(sd_percent), current_layer)
520
521
 
521
- async def wait_for_part_cool(self, target_temp: float = 50.0, timeout_seconds: int = 1800) -> bool:
522
+ async def wait_for_part_cool(
523
+ self, target_temp: float = 50.0, timeout_seconds: int = 1800
524
+ ) -> bool:
522
525
  """
523
526
  Wait for printer components (extruder and bed) to cool down to a safe temperature.
524
-
527
+
525
528
  This method cancels heating for both extruder and bed, then waits for them to cool down.
526
-
529
+
527
530
  Args:
528
531
  target_temp: The target temperature to wait for (default: 50°C)
529
532
  timeout_seconds: Maximum time to wait in seconds (default: 30 minutes)
530
-
533
+
531
534
  Returns:
532
535
  True if components cooled to target temperature, False if timeout or error
533
536
  """
534
537
  logger.info(f"Starting cooling process - waiting for parts to cool to {target_temp}°C")
535
-
538
+
536
539
  # Cancel heating for both extruder and bed
537
540
  extruder_cancel_ok = await self.cancel_extruder_temp()
538
541
  bed_cancel_ok = await self.cancel_bed_temp()
539
-
542
+
540
543
  if not (extruder_cancel_ok and bed_cancel_ok):
541
544
  logger.error("Failed to cancel heating - cannot wait for cooling")
542
545
  return False
543
-
546
+
544
547
  start_time = asyncio.get_event_loop().time()
545
548
  timeout_time = start_time + timeout_seconds
546
-
549
+
547
550
  extruder_cooled = False
548
551
  bed_cooled = False
549
-
552
+
550
553
  while asyncio.get_event_loop().time() < timeout_time:
551
554
  # Check current temperatures
552
555
  temp_info = await self.get_temp_info()
@@ -554,7 +557,7 @@ class FlashForgeClient(FlashForgeTcpClient):
554
557
  logger.warning("Could not get temperature info during cooling")
555
558
  await asyncio.sleep(10)
556
559
  continue
557
-
560
+
558
561
  # Check extruder temperature
559
562
  if not extruder_cooled:
560
563
  extruder_temp = temp_info.get_extruder_temp()
@@ -563,7 +566,7 @@ class FlashForgeClient(FlashForgeTcpClient):
563
566
  if current_extruder_temp <= target_temp:
564
567
  extruder_cooled = True
565
568
  logger.info(f"Extruder cooled to {current_extruder_temp}°C")
566
-
569
+
567
570
  # Check bed temperature
568
571
  if not bed_cooled:
569
572
  bed_temp = temp_info.get_bed_temp()
@@ -572,15 +575,15 @@ class FlashForgeClient(FlashForgeTcpClient):
572
575
  if current_bed_temp <= target_temp:
573
576
  bed_cooled = True
574
577
  logger.info(f"Bed cooled to {current_bed_temp}°C")
575
-
578
+
576
579
  # Check if both have cooled
577
580
  if extruder_cooled and bed_cooled:
578
581
  logger.info("All components have cooled to target temperature")
579
582
  return True
580
-
583
+
581
584
  # Wait before next check
582
585
  await asyncio.sleep(10) # Check every 10 seconds
583
-
586
+
584
587
  # Timeout reached
585
588
  logger.warning(f"Timeout after {timeout_seconds} seconds waiting for cooling")
586
589
  logger.warning(f"Extruder cooled: {extruder_cooled}, Bed cooled: {bed_cooled}")
@@ -2,10 +2,12 @@
2
2
  G-code related modules for FlashForge printer communication.
3
3
  """
4
4
 
5
+ from .a3_gcode_controller import A3GCodeController
5
6
  from .gcode_controller import GCodeController
6
7
  from .gcodes import GCodes
7
8
 
8
9
  __all__ = [
9
- 'GCodes',
10
- 'GCodeController',
10
+ "GCodes",
11
+ "GCodeController",
12
+ "A3GCodeController",
11
13
  ]
@@ -0,0 +1,109 @@
1
+ """
2
+ G-code controller for FlashForge Adventurer 3 printers.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ from .gcode_controller import GCodeController
10
+
11
+ if TYPE_CHECKING:
12
+ from ..a3_client import A3FileEntry, A3PrinterInfo, A3Thumbnail, FlashForgeA3Client
13
+ from ..parsers import EndstopStatus, LocationInfo, PrintStatus
14
+
15
+
16
+ class A3GCodeController(GCodeController):
17
+ """High-level G-code controller for Adventurer 3 printers."""
18
+
19
+ client: FlashForgeA3Client
20
+
21
+ def __init__(self, client: FlashForgeA3Client) -> None:
22
+ super().__init__(client)
23
+ self.client = client
24
+
25
+ async def set_printer_name(self, name: str) -> bool:
26
+ return await self.client.set_printer_name(name)
27
+
28
+ async def get_print_status(self) -> PrintStatus | None:
29
+ return await self.client.get_print_status()
30
+
31
+ async def list_files(self) -> list[A3FileEntry]:
32
+ return await self.client.list_files()
33
+
34
+ async def get_thumbnail(self, filename: str) -> A3Thumbnail | None:
35
+ return await self.client.get_thumbnail(filename)
36
+
37
+ async def select_file(self, filename: str) -> bool:
38
+ return await self.client.select_file(filename)
39
+
40
+ async def start_job(self, filename: str) -> bool:
41
+ if not await self.client.select_file(filename):
42
+ return False
43
+ return await self.client.start_print()
44
+
45
+ async def get_current_position(self) -> LocationInfo | None:
46
+ return await self.client.get_position()
47
+
48
+ async def get_endstop_status(self) -> EndstopStatus | None:
49
+ return await self.client.get_endstop_status()
50
+
51
+ async def enable_motors(self) -> bool:
52
+ return await self.client.enable_motors()
53
+
54
+ async def disable_motors(self) -> bool:
55
+ return await self.client.disable_motors()
56
+
57
+ async def emergency_stop(self) -> bool:
58
+ return await self.client.emergency_stop()
59
+
60
+ async def cancel_heat_wait(self) -> bool:
61
+ return await self.client.cancel_heat_wait()
62
+
63
+ async def led_control(self, params: str) -> bool:
64
+ return await self.client.led_control(params)
65
+
66
+ async def custom_m144(self, params: str | None = None) -> str | None:
67
+ return await self.client.custom_m144(params)
68
+
69
+ async def custom_m145(self, params: str | None = None) -> str | None:
70
+ return await self.client.custom_m145(params)
71
+
72
+ async def get_printer_model(self) -> str | None:
73
+ info = await self.client.get_printer_info()
74
+ return info.machine_type if info else None
75
+
76
+ async def custom_m651(self) -> str | None:
77
+ return await self.client.custom_m651()
78
+
79
+ async def custom_m652(self) -> str | None:
80
+ return await self.client.custom_m652()
81
+
82
+ async def custom_m653(self, params: str) -> bool:
83
+ return await self.client.custom_m653(params)
84
+
85
+ async def custom_m654(self, params: str) -> bool:
86
+ return await self.client.custom_m654(params)
87
+
88
+ async def custom_m611(self) -> str | None:
89
+ return await self.client.custom_m611()
90
+
91
+ async def custom_m612(self) -> str | None:
92
+ return await self.client.custom_m612()
93
+
94
+ async def get_printer_info(self) -> A3PrinterInfo | None:
95
+ return await self.client.get_printer_info()
96
+
97
+ async def is_adventurer_3(self) -> bool:
98
+ info = await self.get_printer_info()
99
+ if not info:
100
+ return False
101
+ machine_type = info.machine_type.lower()
102
+ return (
103
+ "adventurer3" in machine_type
104
+ or "adventurer 3" in machine_type
105
+ or "adventurer iii" in machine_type
106
+ )
107
+
108
+ async def get_detailed_position(self) -> LocationInfo | None:
109
+ return await self.client.get_position_xyze()