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.
- flashforge/__init__.py +70 -29
- flashforge/api/__init__.py +1 -0
- flashforge/api/constants/__init__.py +1 -0
- flashforge/api/constants/commands.py +1 -0
- flashforge/api/constants/endpoints.py +7 -0
- flashforge/api/controls/__init__.py +1 -0
- flashforge/api/controls/control.py +64 -61
- flashforge/api/controls/files.py +28 -26
- flashforge/api/controls/info.py +84 -85
- flashforge/api/controls/job_control.py +120 -82
- flashforge/api/controls/temp_control.py +14 -11
- flashforge/api/misc/__init__.py +1 -1
- flashforge/api/network/__init__.py +2 -1
- flashforge/api/network/utils.py +7 -8
- flashforge/client.py +185 -64
- flashforge/discovery/__init__.py +30 -3
- flashforge/discovery/discovery.py +641 -308
- flashforge/models/__init__.py +11 -10
- flashforge/models/machine_info.py +225 -100
- flashforge/models/responses.py +103 -43
- flashforge/tcp/__init__.py +30 -17
- flashforge/tcp/a3_client.py +408 -0
- flashforge/tcp/a4_client.py +300 -0
- flashforge/tcp/ff_client.py +93 -90
- flashforge/tcp/gcode/__init__.py +4 -2
- flashforge/tcp/gcode/a3_gcode_controller.py +109 -0
- flashforge/tcp/gcode/gcode_controller.py +48 -36
- flashforge/tcp/gcode/gcodes.py +7 -1
- flashforge/tcp/parsers/__init__.py +11 -11
- flashforge/tcp/parsers/endstop_status.py +103 -132
- flashforge/tcp/parsers/location_info.py +26 -13
- flashforge/tcp/parsers/print_status.py +49 -56
- flashforge/tcp/parsers/printer_info.py +38 -48
- flashforge/tcp/parsers/temp_info.py +46 -47
- flashforge/tcp/parsers/thumbnail_info.py +65 -40
- flashforge/tcp/tcp_client.py +296 -293
- flashforge_python_api-1.2.0.dist-info/METADATA +133 -0
- flashforge_python_api-1.2.0.dist-info/RECORD +46 -0
- {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/WHEEL +1 -1
- flashforge_python_api-1.0.2.dist-info/METADATA +0 -284
- flashforge_python_api-1.0.2.dist-info/RECORD +0 -43
- {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/entry_points.txt +0 -0
- {flashforge_python_api-1.0.2.dist-info → flashforge_python_api-1.2.0.dist-info}/licenses/LICENSE +0 -0
flashforge/client.py
CHANGED
|
@@ -4,33 +4,50 @@ FlashForge Python API - Main Unified Client
|
|
|
4
4
|
This module provides the main FlashForgeClient class that orchestrates both HTTP and TCP
|
|
5
5
|
communication layers for controlling FlashForge 3D printers.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import asyncio
|
|
8
|
-
from
|
|
9
|
+
from dataclasses import dataclass
|
|
9
10
|
|
|
10
11
|
import aiohttp
|
|
11
12
|
|
|
12
|
-
from .api.constants.endpoints import Endpoints
|
|
13
|
+
from .api.constants.endpoints import CAMERA_STREAM_PORT, Endpoints
|
|
13
14
|
from .api.controls import Control, Files, Info, JobControl, TempControl
|
|
14
15
|
from .api.controls.info import MachineInfoParser
|
|
15
16
|
from .api.network.utils import NetworkUtils
|
|
16
|
-
from .models import FFMachineInfo, ProductResponse
|
|
17
|
+
from .models import FFMachineInfo, Product, ProductResponse
|
|
17
18
|
from .tcp import FlashForgeClient as TcpClient
|
|
18
|
-
from .tcp import PrinterInfo
|
|
19
|
+
from .tcp import FlashForgeTcpClientOptions, PrinterInfo
|
|
20
|
+
from .tcp.parsers.temp_info import TempInfo
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(slots=True)
|
|
24
|
+
class FiveMClientConnectionOptions:
|
|
25
|
+
"""Optional connection and feature overrides for FiveMClient-compatible clients."""
|
|
26
|
+
|
|
27
|
+
http_port: int | None = None
|
|
28
|
+
tcp_port: int | None = None
|
|
29
|
+
led_control_override: bool | None = None
|
|
19
30
|
|
|
20
31
|
|
|
21
32
|
class FlashForgeClient:
|
|
22
33
|
"""
|
|
23
34
|
Main client for interacting with a FlashForge 3D printer.
|
|
24
|
-
|
|
35
|
+
|
|
25
36
|
This class provides methods for controlling the printer, managing print jobs,
|
|
26
37
|
retrieving information, and handling file operations. It orchestrates both
|
|
27
38
|
HTTP and TCP communication layers to provide a unified interface.
|
|
28
39
|
"""
|
|
29
40
|
|
|
30
|
-
def __init__(
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
ip_address: str,
|
|
44
|
+
serial_number: str,
|
|
45
|
+
check_code: str,
|
|
46
|
+
options: FiveMClientConnectionOptions | None = None,
|
|
47
|
+
):
|
|
31
48
|
"""
|
|
32
49
|
Creates an instance of FlashForgeClient.
|
|
33
|
-
|
|
50
|
+
|
|
34
51
|
Args:
|
|
35
52
|
ip_address: The IP address of the printer
|
|
36
53
|
serial_number: The serial number of the printer
|
|
@@ -42,15 +59,19 @@ class FlashForgeClient:
|
|
|
42
59
|
self.check_code = check_code
|
|
43
60
|
|
|
44
61
|
# Constants
|
|
45
|
-
self._PORT = 8898
|
|
62
|
+
self._PORT = options.http_port if options and options.http_port is not None else 8898
|
|
46
63
|
self._HTTP_TIMEOUT = 5.0
|
|
47
64
|
|
|
48
65
|
# HTTP client state
|
|
49
|
-
self._http_session:
|
|
50
|
-
self.
|
|
66
|
+
self._http_session: aiohttp.ClientSession | None = None
|
|
67
|
+
self._http_client_event = asyncio.Event()
|
|
68
|
+
self._http_client_event.set() # Not busy initially
|
|
51
69
|
|
|
52
70
|
# TCP client setup
|
|
53
|
-
|
|
71
|
+
tcp_options = None
|
|
72
|
+
if options and options.tcp_port is not None:
|
|
73
|
+
tcp_options = FlashForgeTcpClientOptions(port=options.tcp_port)
|
|
74
|
+
self.tcp_client = TcpClient(ip_address, tcp_options)
|
|
54
75
|
|
|
55
76
|
# Control instances
|
|
56
77
|
self.control = Control(self)
|
|
@@ -68,12 +89,22 @@ class FlashForgeClient:
|
|
|
68
89
|
self.mac_address: str = ""
|
|
69
90
|
self.flash_cloud_code: str = ""
|
|
70
91
|
self.polar_cloud_code: str = ""
|
|
92
|
+
self.camera_stream_url: str = ""
|
|
71
93
|
self.lifetime_print_time: str = ""
|
|
72
94
|
self.lifetime_filament_meters: str = ""
|
|
95
|
+
self.product_info: Product | None = None
|
|
73
96
|
|
|
74
97
|
# Control states
|
|
75
98
|
self.led_control: bool = False
|
|
76
99
|
self.filtration_control: bool = False
|
|
100
|
+
self._detected_led_control: bool = False
|
|
101
|
+
self._detected_filtration_control: bool = False
|
|
102
|
+
self._led_control_override = (
|
|
103
|
+
options.led_control_override
|
|
104
|
+
if options and options.led_control_override is not None
|
|
105
|
+
else None
|
|
106
|
+
)
|
|
107
|
+
self._apply_feature_overrides()
|
|
77
108
|
|
|
78
109
|
@property
|
|
79
110
|
def is_ad5x(self) -> bool:
|
|
@@ -85,35 +116,37 @@ class FlashForgeClient:
|
|
|
85
116
|
"""
|
|
86
117
|
return self._is_ad5x
|
|
87
118
|
|
|
88
|
-
async def __aenter__(self):
|
|
119
|
+
async def __aenter__(self) -> "FlashForgeClient":
|
|
89
120
|
"""Async context manager entry."""
|
|
90
121
|
await self._ensure_http_session()
|
|
91
122
|
await self.initialize()
|
|
92
123
|
return self
|
|
93
124
|
|
|
94
|
-
async def __aexit__(
|
|
125
|
+
async def __aexit__(
|
|
126
|
+
self,
|
|
127
|
+
exc_type: type[BaseException] | None,
|
|
128
|
+
exc_val: BaseException | None,
|
|
129
|
+
exc_tb: object,
|
|
130
|
+
) -> None:
|
|
95
131
|
"""Async context manager exit."""
|
|
96
132
|
await self.dispose()
|
|
97
133
|
|
|
98
134
|
async def _ensure_http_session(self) -> aiohttp.ClientSession:
|
|
99
135
|
"""
|
|
100
136
|
Ensures that an HTTP session is available.
|
|
101
|
-
|
|
137
|
+
|
|
102
138
|
Returns:
|
|
103
139
|
The HTTP session instance
|
|
104
140
|
"""
|
|
105
141
|
if self._http_session is None or self._http_session.closed:
|
|
106
142
|
timeout = aiohttp.ClientTimeout(total=self._HTTP_TIMEOUT)
|
|
107
|
-
self._http_session = aiohttp.ClientSession(
|
|
108
|
-
timeout=timeout,
|
|
109
|
-
headers={"Accept": "*/*"}
|
|
110
|
-
)
|
|
143
|
+
self._http_session = aiohttp.ClientSession(timeout=timeout, headers={"Accept": "*/*"})
|
|
111
144
|
return self._http_session
|
|
112
145
|
|
|
113
146
|
async def initialize(self) -> bool:
|
|
114
147
|
"""
|
|
115
148
|
Initializes the FlashForgeClient and verifies the connection to the printer.
|
|
116
|
-
|
|
149
|
+
|
|
117
150
|
Returns:
|
|
118
151
|
True if initialization is successful, False otherwise
|
|
119
152
|
"""
|
|
@@ -123,17 +156,35 @@ class FlashForgeClient:
|
|
|
123
156
|
print("Failed to connect to printer")
|
|
124
157
|
return False
|
|
125
158
|
|
|
159
|
+
@property
|
|
160
|
+
def _http_client_busy(self) -> bool:
|
|
161
|
+
"""
|
|
162
|
+
Legacy property to maintain internal API compatibility.
|
|
163
|
+
Returns True if the client is busy (event is not set), False otherwise.
|
|
164
|
+
"""
|
|
165
|
+
return not self._http_client_event.is_set()
|
|
166
|
+
|
|
167
|
+
@_http_client_busy.setter
|
|
168
|
+
def _http_client_busy(self, value: bool) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Sets the busy state using the event.
|
|
171
|
+
True means busy (clear event), False means not busy (set event).
|
|
172
|
+
"""
|
|
173
|
+
if value:
|
|
174
|
+
self._http_client_event.clear()
|
|
175
|
+
else:
|
|
176
|
+
self._http_client_event.set()
|
|
177
|
+
|
|
126
178
|
async def is_http_client_busy(self) -> bool:
|
|
127
179
|
"""
|
|
128
180
|
Checks if the HTTP client is currently busy.
|
|
129
|
-
|
|
181
|
+
Waits until the client is not busy before returning.
|
|
182
|
+
|
|
130
183
|
Returns:
|
|
131
|
-
|
|
184
|
+
False (always returns False after waiting, for compatibility)
|
|
132
185
|
"""
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
await asyncio.sleep(0.01)
|
|
136
|
-
return self._http_client_busy
|
|
186
|
+
await self._http_client_event.wait()
|
|
187
|
+
return False
|
|
137
188
|
|
|
138
189
|
def release_http_client(self) -> None:
|
|
139
190
|
"""Releases the HTTP client, allowing it to be used for new requests."""
|
|
@@ -142,9 +193,9 @@ class FlashForgeClient:
|
|
|
142
193
|
async def init_control(self) -> bool:
|
|
143
194
|
"""
|
|
144
195
|
Initializes the control interface with the printer.
|
|
145
|
-
|
|
196
|
+
|
|
146
197
|
This involves sending a product command and initializing TCP control.
|
|
147
|
-
|
|
198
|
+
|
|
148
199
|
Returns:
|
|
149
200
|
True if control initialization is successful, False otherwise
|
|
150
201
|
"""
|
|
@@ -155,20 +206,75 @@ class FlashForgeClient:
|
|
|
155
206
|
|
|
156
207
|
async def dispose(self) -> None:
|
|
157
208
|
"""
|
|
158
|
-
Disposes of the FlashForgeClient instance, stopping keep-alive messages
|
|
209
|
+
Disposes of the FlashForgeClient instance, stopping keep-alive messages
|
|
159
210
|
and cleaning up resources.
|
|
160
211
|
"""
|
|
161
212
|
# Stop TCP keep-alive and dispose
|
|
162
|
-
if hasattr(self.tcp_client,
|
|
213
|
+
if hasattr(self.tcp_client, "stop_keep_alive"):
|
|
163
214
|
await self.tcp_client.stop_keep_alive(True)
|
|
164
|
-
if hasattr(self.tcp_client,
|
|
215
|
+
if hasattr(self.tcp_client, "dispose"):
|
|
165
216
|
await self.tcp_client.dispose()
|
|
166
217
|
|
|
167
218
|
# Close HTTP session
|
|
168
219
|
if self._http_session and not self._http_session.closed:
|
|
169
220
|
await self._http_session.close()
|
|
170
221
|
|
|
171
|
-
|
|
222
|
+
self.camera_stream_url = ""
|
|
223
|
+
|
|
224
|
+
async def detect_camera_stream(self, timeout_ms: int = 3000) -> str:
|
|
225
|
+
"""
|
|
226
|
+
Probes the printer's known OEM camera endpoint.
|
|
227
|
+
|
|
228
|
+
Falls back to a short GET when HEAD is unsupported so MJPEG servers
|
|
229
|
+
that reject HEAD requests are still detected.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
timeout_ms: Timeout for each probe attempt in milliseconds.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
The working camera stream URL, or an empty string if not detected.
|
|
236
|
+
"""
|
|
237
|
+
probe_url = f"http://{self.ip_address}:{CAMERA_STREAM_PORT}/?action=stream"
|
|
238
|
+
timeout = aiohttp.ClientTimeout(total=timeout_ms / 1000)
|
|
239
|
+
|
|
240
|
+
if await self._probe_camera_stream_with_method("head", probe_url, timeout):
|
|
241
|
+
return probe_url
|
|
242
|
+
|
|
243
|
+
if await self._probe_camera_stream_with_method("get", probe_url, timeout):
|
|
244
|
+
return probe_url
|
|
245
|
+
|
|
246
|
+
return ""
|
|
247
|
+
|
|
248
|
+
async def _probe_camera_stream_with_method(
|
|
249
|
+
self,
|
|
250
|
+
method: str,
|
|
251
|
+
probe_url: str,
|
|
252
|
+
timeout: aiohttp.ClientTimeout,
|
|
253
|
+
) -> bool:
|
|
254
|
+
try:
|
|
255
|
+
session = await self._ensure_http_session()
|
|
256
|
+
request = session.head if method == "head" else session.get
|
|
257
|
+
|
|
258
|
+
async with request(probe_url, timeout=timeout) as response:
|
|
259
|
+
return self._is_valid_camera_probe_response(
|
|
260
|
+
response.status, response.headers.get("Content-Type", "")
|
|
261
|
+
)
|
|
262
|
+
except Exception:
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
@staticmethod
|
|
266
|
+
def _is_valid_camera_probe_response(status: int, content_type: str | None) -> bool:
|
|
267
|
+
if status != 200:
|
|
268
|
+
return False
|
|
269
|
+
|
|
270
|
+
normalized_content_type = (content_type or "").lower()
|
|
271
|
+
return (
|
|
272
|
+
normalized_content_type == ""
|
|
273
|
+
or "multipart" in normalized_content_type
|
|
274
|
+
or "video/x-mjpeg" in normalized_content_type
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def cache_details(self, info: FFMachineInfo | None) -> bool:
|
|
172
278
|
"""
|
|
173
279
|
Caches machine details from the provided FFMachineInfo object.
|
|
174
280
|
|
|
@@ -183,29 +289,30 @@ class FlashForgeClient:
|
|
|
183
289
|
|
|
184
290
|
# Cache all printer information
|
|
185
291
|
self.printer_name = info.name or ""
|
|
292
|
+
self.is_pro = info.is_pro
|
|
186
293
|
self.firmware_version = info.firmware_version or ""
|
|
187
|
-
self.firmware_ver = info.firmware_version.split(
|
|
294
|
+
self.firmware_ver = info.firmware_version.split("-")[0] if info.firmware_version else ""
|
|
188
295
|
self.mac_address = info.mac_address or ""
|
|
189
296
|
self.flash_cloud_code = info.flash_cloud_register_code or ""
|
|
190
297
|
self.polar_cloud_code = info.polar_cloud_register_code or ""
|
|
298
|
+
self.camera_stream_url = info.camera_stream_url or ""
|
|
191
299
|
self.lifetime_print_time = info.formatted_total_run_time or ""
|
|
192
300
|
self._is_ad5x = info.is_ad5x
|
|
193
301
|
|
|
194
302
|
# Format filament usage
|
|
195
|
-
if info.cumulative_filament is not None
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
self.lifetime_filament_meters = "0.00m"
|
|
303
|
+
filament_value = info.cumulative_filament if info.cumulative_filament is not None else 0.0
|
|
304
|
+
self.lifetime_filament_meters = f"{filament_value:.2f}m"
|
|
305
|
+
self._apply_feature_overrides()
|
|
199
306
|
|
|
200
307
|
return True
|
|
201
308
|
|
|
202
309
|
def get_endpoint(self, endpoint: str) -> str:
|
|
203
310
|
"""
|
|
204
311
|
Constructs the full API endpoint URL.
|
|
205
|
-
|
|
312
|
+
|
|
206
313
|
Args:
|
|
207
314
|
endpoint: The specific API endpoint path
|
|
208
|
-
|
|
315
|
+
|
|
209
316
|
Returns:
|
|
210
317
|
The full URL for the API endpoint
|
|
211
318
|
"""
|
|
@@ -214,7 +321,7 @@ class FlashForgeClient:
|
|
|
214
321
|
async def verify_connection(self) -> bool:
|
|
215
322
|
"""
|
|
216
323
|
Verifies the connection to the printer by retrieving machine details and TCP information.
|
|
217
|
-
|
|
324
|
+
|
|
218
325
|
Returns:
|
|
219
326
|
True if the connection is verified, False otherwise
|
|
220
327
|
"""
|
|
@@ -232,12 +339,14 @@ class FlashForgeClient:
|
|
|
232
339
|
return False
|
|
233
340
|
|
|
234
341
|
# Get TCP printer information to check for Pro model
|
|
235
|
-
tcp_info:
|
|
342
|
+
tcp_info: PrinterInfo | None = await self.tcp_client.get_printer_info()
|
|
236
343
|
if tcp_info:
|
|
237
|
-
if "Pro" in tcp_info.type_name:
|
|
344
|
+
if "Pro" in tcp_info.type_name and not machine_info.is_pro and not machine_info.is_ad5x:
|
|
238
345
|
self.is_pro = True
|
|
239
346
|
else:
|
|
240
|
-
print(
|
|
347
|
+
print(
|
|
348
|
+
"Warning: Unable to get PrinterInfo from TCP API, some features might not work"
|
|
349
|
+
)
|
|
241
350
|
|
|
242
351
|
# Cache the details
|
|
243
352
|
return self.cache_details(machine_info)
|
|
@@ -249,28 +358,24 @@ class FlashForgeClient:
|
|
|
249
358
|
async def send_product_command(self) -> bool:
|
|
250
359
|
"""
|
|
251
360
|
Sends a product command to the printer to retrieve control states.
|
|
252
|
-
|
|
361
|
+
|
|
253
362
|
This method sets the http_client_busy flag while the request is in progress.
|
|
254
|
-
|
|
363
|
+
|
|
255
364
|
Returns:
|
|
256
|
-
True if the product command is sent successfully and valid data is received,
|
|
365
|
+
True if the product command is sent successfully and valid data is received,
|
|
257
366
|
False otherwise
|
|
258
367
|
"""
|
|
259
368
|
self._http_client_busy = True
|
|
260
369
|
|
|
261
|
-
payload = {
|
|
262
|
-
"serialNumber": self.serial_number,
|
|
263
|
-
"checkCode": self.check_code
|
|
264
|
-
}
|
|
370
|
+
payload = {"serialNumber": self.serial_number, "checkCode": self.check_code}
|
|
265
371
|
|
|
266
372
|
try:
|
|
267
373
|
session = await self._ensure_http_session()
|
|
268
374
|
async with session.post(
|
|
269
375
|
self.get_endpoint(Endpoints.PRODUCT),
|
|
270
376
|
json=payload,
|
|
271
|
-
headers={"Content-Type": "application/json"}
|
|
377
|
+
headers={"Content-Type": "application/json"},
|
|
272
378
|
) as response:
|
|
273
|
-
|
|
274
379
|
if response.status != 200:
|
|
275
380
|
return False
|
|
276
381
|
|
|
@@ -282,6 +387,7 @@ class FlashForgeClient:
|
|
|
282
387
|
# Fallback: manually parse as JSON if Content-Type is malformed
|
|
283
388
|
text = await response.text()
|
|
284
389
|
import json
|
|
390
|
+
|
|
285
391
|
data = json.loads(text)
|
|
286
392
|
|
|
287
393
|
# Validate response structure
|
|
@@ -292,11 +398,12 @@ class FlashForgeClient:
|
|
|
292
398
|
product_response = ProductResponse(**data)
|
|
293
399
|
if product_response and product_response.product:
|
|
294
400
|
product = product_response.product
|
|
295
|
-
self.
|
|
296
|
-
self.
|
|
297
|
-
|
|
298
|
-
product.externalFanCtrlState == 0
|
|
401
|
+
self.product_info = product
|
|
402
|
+
self._detected_led_control = product.lightCtrlState != 0
|
|
403
|
+
self._detected_filtration_control = not (
|
|
404
|
+
product.internalFanCtrlState == 0 or product.externalFanCtrlState == 0
|
|
299
405
|
)
|
|
406
|
+
self._apply_feature_overrides()
|
|
300
407
|
return True
|
|
301
408
|
|
|
302
409
|
except Exception as error:
|
|
@@ -310,7 +417,7 @@ class FlashForgeClient:
|
|
|
310
417
|
async def get_http_session(self) -> aiohttp.ClientSession:
|
|
311
418
|
"""
|
|
312
419
|
Gets the HTTP session for making requests.
|
|
313
|
-
|
|
420
|
+
|
|
314
421
|
Returns:
|
|
315
422
|
The HTTP session instance
|
|
316
423
|
"""
|
|
@@ -318,19 +425,19 @@ class FlashForgeClient:
|
|
|
318
425
|
|
|
319
426
|
# Additional convenience methods for direct access to common operations
|
|
320
427
|
|
|
321
|
-
async def get_printer_status(self) ->
|
|
428
|
+
async def get_printer_status(self) -> FFMachineInfo | None:
|
|
322
429
|
"""
|
|
323
430
|
Gets the current printer status and information.
|
|
324
|
-
|
|
431
|
+
|
|
325
432
|
Returns:
|
|
326
433
|
FFMachineInfo object with current printer status, or None if failed
|
|
327
434
|
"""
|
|
328
435
|
return await self.info.get()
|
|
329
436
|
|
|
330
|
-
async def get_temperatures(self):
|
|
437
|
+
async def get_temperatures(self) -> TempInfo | None:
|
|
331
438
|
"""
|
|
332
439
|
Gets current temperature readings from the printer.
|
|
333
|
-
|
|
440
|
+
|
|
334
441
|
Returns:
|
|
335
442
|
Temperature information from the TCP client
|
|
336
443
|
"""
|
|
@@ -339,7 +446,7 @@ class FlashForgeClient:
|
|
|
339
446
|
async def home_all_axes(self) -> bool:
|
|
340
447
|
"""
|
|
341
448
|
Homes all axes (X, Y, Z) of the printer.
|
|
342
|
-
|
|
449
|
+
|
|
343
450
|
Returns:
|
|
344
451
|
True if successful, False otherwise
|
|
345
452
|
"""
|
|
@@ -348,7 +455,7 @@ class FlashForgeClient:
|
|
|
348
455
|
async def emergency_stop(self) -> bool:
|
|
349
456
|
"""
|
|
350
457
|
Performs an emergency stop of the printer.
|
|
351
|
-
|
|
458
|
+
|
|
352
459
|
Returns:
|
|
353
460
|
True if successful, False otherwise
|
|
354
461
|
"""
|
|
@@ -357,7 +464,7 @@ class FlashForgeClient:
|
|
|
357
464
|
async def pause_print(self) -> bool:
|
|
358
465
|
"""
|
|
359
466
|
Pauses the current print job.
|
|
360
|
-
|
|
467
|
+
|
|
361
468
|
Returns:
|
|
362
469
|
True if successful, False otherwise
|
|
363
470
|
"""
|
|
@@ -366,12 +473,26 @@ class FlashForgeClient:
|
|
|
366
473
|
async def resume_print(self) -> bool:
|
|
367
474
|
"""
|
|
368
475
|
Resumes a paused print job.
|
|
369
|
-
|
|
476
|
+
|
|
370
477
|
Returns:
|
|
371
478
|
True if successful, False otherwise
|
|
372
479
|
"""
|
|
373
480
|
return await self.job_control.resume_print_job()
|
|
374
481
|
|
|
482
|
+
def set_feature_overrides(self, *, led_control: bool | None = None) -> None:
|
|
483
|
+
"""Apply manual feature overrides for integrations that need UI-level capability control."""
|
|
484
|
+
self._led_control_override = led_control
|
|
485
|
+
self._apply_feature_overrides()
|
|
486
|
+
|
|
487
|
+
def _apply_feature_overrides(self) -> None:
|
|
488
|
+
"""Apply any configured manual capability overrides to the cached client state."""
|
|
489
|
+
self.led_control = (
|
|
490
|
+
self._led_control_override
|
|
491
|
+
if self._led_control_override is not None
|
|
492
|
+
else self._detected_led_control
|
|
493
|
+
)
|
|
494
|
+
self.filtration_control = self._detected_filtration_control
|
|
495
|
+
|
|
375
496
|
def __repr__(self) -> str:
|
|
376
497
|
"""String representation of the client."""
|
|
377
498
|
return (
|
flashforge/discovery/__init__.py
CHANGED
|
@@ -4,9 +4,36 @@ FlashForge Python API - Discovery Package
|
|
|
4
4
|
UDP-based printer discovery for finding FlashForge printers on the local network.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .discovery import
|
|
7
|
+
from .discovery import (
|
|
8
|
+
DiscoveredPrinter,
|
|
9
|
+
DiscoveryError,
|
|
10
|
+
DiscoveryMonitor,
|
|
11
|
+
DiscoveryOptions,
|
|
12
|
+
DiscoveryProtocol,
|
|
13
|
+
DiscoveryTimeoutError,
|
|
14
|
+
FlashForgePrinter,
|
|
15
|
+
FlashForgePrinterDiscovery,
|
|
16
|
+
InvalidResponseError,
|
|
17
|
+
PrinterDiscovery,
|
|
18
|
+
PrinterModel,
|
|
19
|
+
PrinterStatus,
|
|
20
|
+
SocketCreationError,
|
|
21
|
+
main,
|
|
22
|
+
)
|
|
8
23
|
|
|
9
24
|
__all__ = [
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
"PrinterDiscovery",
|
|
26
|
+
"DiscoveredPrinter",
|
|
27
|
+
"DiscoveryOptions",
|
|
28
|
+
"PrinterModel",
|
|
29
|
+
"DiscoveryProtocol",
|
|
30
|
+
"PrinterStatus",
|
|
31
|
+
"DiscoveryMonitor",
|
|
32
|
+
"DiscoveryError",
|
|
33
|
+
"InvalidResponseError",
|
|
34
|
+
"SocketCreationError",
|
|
35
|
+
"DiscoveryTimeoutError",
|
|
36
|
+
"FlashForgePrinter",
|
|
37
|
+
"FlashForgePrinterDiscovery",
|
|
38
|
+
"main",
|
|
12
39
|
]
|