flashforge-python-api 1.0.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 +188 -0
- flashforge/api/__init__.py +17 -0
- flashforge/api/constants/__init__.py +10 -0
- flashforge/api/constants/commands.py +10 -0
- flashforge/api/constants/endpoints.py +11 -0
- flashforge/api/controls/__init__.py +16 -0
- flashforge/api/controls/control.py +357 -0
- flashforge/api/controls/files.py +171 -0
- flashforge/api/controls/info.py +292 -0
- flashforge/api/controls/job_control.py +561 -0
- flashforge/api/controls/temp_control.py +89 -0
- flashforge/api/filament/__init__.py +7 -0
- flashforge/api/filament/filament.py +39 -0
- flashforge/api/misc/__init__.py +8 -0
- flashforge/api/misc/scientific_notation.py +28 -0
- flashforge/api/misc/temperature.py +42 -0
- flashforge/api/network/__init__.py +10 -0
- flashforge/api/network/fnet_code.py +15 -0
- flashforge/api/network/utils.py +55 -0
- flashforge/client.py +381 -0
- flashforge/discovery/__init__.py +12 -0
- flashforge/discovery/discovery.py +388 -0
- flashforge/models/__init__.py +50 -0
- flashforge/models/machine_info.py +247 -0
- flashforge/models/responses.py +123 -0
- flashforge/tcp/__init__.py +41 -0
- flashforge/tcp/ff_client.py +587 -0
- flashforge/tcp/gcode/__init__.py +11 -0
- flashforge/tcp/gcode/gcode_controller.py +296 -0
- flashforge/tcp/gcode/gcodes.py +93 -0
- flashforge/tcp/parsers/__init__.py +27 -0
- flashforge/tcp/parsers/endstop_status.py +273 -0
- flashforge/tcp/parsers/location_info.py +68 -0
- flashforge/tcp/parsers/print_status.py +182 -0
- flashforge/tcp/parsers/printer_info.py +154 -0
- flashforge/tcp/parsers/temp_info.py +217 -0
- flashforge/tcp/parsers/thumbnail_info.py +242 -0
- flashforge/tcp/tcp_client.py +448 -0
- flashforge_python_api-1.0.0.dist-info/METADATA +123 -0
- flashforge_python_api-1.0.0.dist-info/RECORD +43 -0
- flashforge_python_api-1.0.0.dist-info/WHEEL +4 -0
- flashforge_python_api-1.0.0.dist-info/entry_points.txt +2 -0
- flashforge_python_api-1.0.0.dist-info/licenses/LICENSE +21 -0
flashforge/__init__.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FlashForge Python API
|
|
3
|
+
|
|
4
|
+
A Python library for controlling FlashForge 3D printers via HTTP and TCP APIs.
|
|
5
|
+
Provides comprehensive async support for printer control, job management, file operations,
|
|
6
|
+
temperature control, and real-time communication.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
Basic usage example:
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import asyncio
|
|
13
|
+
from flashforge import FlashForgeClient
|
|
14
|
+
|
|
15
|
+
async def main():
|
|
16
|
+
async with FlashForgeClient("192.168.1.100", "serial123", "check456") as client:
|
|
17
|
+
if await client.initialize():
|
|
18
|
+
print(f"Connected to {client.printer_name}")
|
|
19
|
+
|
|
20
|
+
# Get printer status
|
|
21
|
+
status = await client.get_printer_status()
|
|
22
|
+
print(f"Status: {status.machine_state}")
|
|
23
|
+
|
|
24
|
+
# Control operations
|
|
25
|
+
await client.control.set_led_on()
|
|
26
|
+
await client.control.home_axes()
|
|
27
|
+
|
|
28
|
+
# Temperature monitoring
|
|
29
|
+
temps = await client.get_temperatures()
|
|
30
|
+
print(f"Extruder: {temps.extruder_temp}°C")
|
|
31
|
+
|
|
32
|
+
asyncio.run(main())
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from .api.constants.commands import Commands
|
|
37
|
+
from .api.constants.endpoints import Endpoints
|
|
38
|
+
|
|
39
|
+
# Import control classes for advanced usage
|
|
40
|
+
from .api.controls import (
|
|
41
|
+
Control,
|
|
42
|
+
Files,
|
|
43
|
+
Info,
|
|
44
|
+
JobControl,
|
|
45
|
+
TempControl,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Import utility classes
|
|
49
|
+
from .api.network.utils import NetworkUtils
|
|
50
|
+
from .api.network.fnet_code import FNetCode
|
|
51
|
+
from .api.filament import Filament
|
|
52
|
+
from .api.misc import Temperature as TempWrapper, format_scientific_notation
|
|
53
|
+
from .client import FlashForgeClient
|
|
54
|
+
|
|
55
|
+
# Import discovery classes
|
|
56
|
+
from .discovery import (
|
|
57
|
+
FlashForgePrinter,
|
|
58
|
+
FlashForgePrinterDiscovery,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Import key models for convenience
|
|
62
|
+
from .models import (
|
|
63
|
+
DetailResponse,
|
|
64
|
+
FFMachineInfo,
|
|
65
|
+
FFPrinterDetail,
|
|
66
|
+
FFGcodeFileEntry,
|
|
67
|
+
FFGcodeToolData,
|
|
68
|
+
FilamentArgs,
|
|
69
|
+
GenericResponse,
|
|
70
|
+
MachineState,
|
|
71
|
+
Product,
|
|
72
|
+
ProductResponse,
|
|
73
|
+
Temperature,
|
|
74
|
+
SlotInfo,
|
|
75
|
+
MatlStationInfo,
|
|
76
|
+
IndepMatlInfo,
|
|
77
|
+
AD5XMaterialMapping,
|
|
78
|
+
AD5XLocalJobParams,
|
|
79
|
+
AD5XSingleColorJobParams,
|
|
80
|
+
AD5XUploadParams,
|
|
81
|
+
GCodeListResponse,
|
|
82
|
+
ThumbnailResponse,
|
|
83
|
+
)
|
|
84
|
+
from .tcp import (
|
|
85
|
+
Endstop,
|
|
86
|
+
EndstopStatus,
|
|
87
|
+
FlashForgeTcpClient,
|
|
88
|
+
GCodeController,
|
|
89
|
+
GCodes,
|
|
90
|
+
LocationInfo,
|
|
91
|
+
MachineStatus,
|
|
92
|
+
MoveMode,
|
|
93
|
+
PrinterInfo,
|
|
94
|
+
PrintStatus,
|
|
95
|
+
Status,
|
|
96
|
+
TempData,
|
|
97
|
+
TempInfo,
|
|
98
|
+
ThumbnailInfo,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Import TCP classes for low-level access
|
|
102
|
+
from .tcp import (
|
|
103
|
+
FlashForgeTcpClient as TcpClient,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
__version__ = "1.0.0"
|
|
107
|
+
__author__ = "FlashForge Python API Contributors"
|
|
108
|
+
__email__ = "notghosttypes@gmail.com"
|
|
109
|
+
__description__ = "Python library for controlling FlashForge 3D printers"
|
|
110
|
+
|
|
111
|
+
# Public API - Main classes most users will need
|
|
112
|
+
__all__ = [
|
|
113
|
+
# Main client class
|
|
114
|
+
"FlashForgeClient",
|
|
115
|
+
|
|
116
|
+
# Data models
|
|
117
|
+
"FFMachineInfo",
|
|
118
|
+
"FFPrinterDetail",
|
|
119
|
+
"FFGcodeFileEntry",
|
|
120
|
+
"FFGcodeToolData",
|
|
121
|
+
"MachineState",
|
|
122
|
+
"Temperature",
|
|
123
|
+
"GenericResponse",
|
|
124
|
+
"DetailResponse",
|
|
125
|
+
"ProductResponse",
|
|
126
|
+
"Product",
|
|
127
|
+
"FilamentArgs",
|
|
128
|
+
|
|
129
|
+
# AD5X models
|
|
130
|
+
"SlotInfo",
|
|
131
|
+
"MatlStationInfo",
|
|
132
|
+
"IndepMatlInfo",
|
|
133
|
+
"AD5XMaterialMapping",
|
|
134
|
+
"AD5XLocalJobParams",
|
|
135
|
+
"AD5XSingleColorJobParams",
|
|
136
|
+
"AD5XUploadParams",
|
|
137
|
+
"GCodeListResponse",
|
|
138
|
+
"ThumbnailResponse",
|
|
139
|
+
|
|
140
|
+
# Control classes for advanced usage
|
|
141
|
+
"Control",
|
|
142
|
+
"JobControl",
|
|
143
|
+
"Info",
|
|
144
|
+
"Files",
|
|
145
|
+
"TempControl",
|
|
146
|
+
|
|
147
|
+
# TCP classes for low-level operations
|
|
148
|
+
"TcpClient",
|
|
149
|
+
"FlashForgeTcpClient",
|
|
150
|
+
"PrinterInfo",
|
|
151
|
+
"TempInfo",
|
|
152
|
+
"TempData",
|
|
153
|
+
"LocationInfo",
|
|
154
|
+
"EndstopStatus",
|
|
155
|
+
"MachineStatus",
|
|
156
|
+
"MoveMode",
|
|
157
|
+
"Status",
|
|
158
|
+
"Endstop",
|
|
159
|
+
"PrintStatus",
|
|
160
|
+
"ThumbnailInfo",
|
|
161
|
+
"GCodes",
|
|
162
|
+
"GCodeController",
|
|
163
|
+
|
|
164
|
+
# Discovery classes
|
|
165
|
+
"FlashForgePrinter",
|
|
166
|
+
"FlashForgePrinterDiscovery",
|
|
167
|
+
|
|
168
|
+
# Utilities
|
|
169
|
+
"NetworkUtils",
|
|
170
|
+
"FNetCode",
|
|
171
|
+
"Filament",
|
|
172
|
+
"TempWrapper",
|
|
173
|
+
"format_scientific_notation",
|
|
174
|
+
"Endpoints",
|
|
175
|
+
"Commands",
|
|
176
|
+
|
|
177
|
+
# Package metadata
|
|
178
|
+
"__version__",
|
|
179
|
+
"__author__",
|
|
180
|
+
"__email__",
|
|
181
|
+
"__description__",
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
# Convenience imports for common patterns
|
|
185
|
+
from .models.machine_info import MachineState as State
|
|
186
|
+
|
|
187
|
+
# Add version info accessible as flashforge.version
|
|
188
|
+
version = __version__
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FlashForge Python API - API Package
|
|
3
|
+
"""
|
|
4
|
+
from .constants import Commands, Endpoints
|
|
5
|
+
from .controls import Control, Files, Info, JobControl, TempControl
|
|
6
|
+
from .network import NetworkUtils
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Commands",
|
|
10
|
+
"Endpoints",
|
|
11
|
+
"Control",
|
|
12
|
+
"Files",
|
|
13
|
+
"Info",
|
|
14
|
+
"JobControl",
|
|
15
|
+
"TempControl",
|
|
16
|
+
"NetworkUtils",
|
|
17
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class Commands:
|
|
2
|
+
"""
|
|
3
|
+
Commands used in the "new" HTTP API
|
|
4
|
+
"""
|
|
5
|
+
LIGHT_CONTROL_CMD = "lightControl_cmd"
|
|
6
|
+
PRINTER_CONTROL_CMD = "printerCtl_cmd"
|
|
7
|
+
JOB_CONTROL_CMD = "jobCtl_cmd"
|
|
8
|
+
CIRCULATION_CONTROL_CMD = "circulateCtl_cmd"
|
|
9
|
+
CAMERA_CONTROL_CMD = "streamCtrl_cmd"
|
|
10
|
+
TEMP_CONTROL_CMD = "temperatureCtl_cmd"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FlashForge Python API - Controls Package
|
|
3
|
+
"""
|
|
4
|
+
from .control import Control
|
|
5
|
+
from .files import Files
|
|
6
|
+
from .info import Info
|
|
7
|
+
from .job_control import JobControl
|
|
8
|
+
from .temp_control import TempControl
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Control",
|
|
12
|
+
"Files",
|
|
13
|
+
"Info",
|
|
14
|
+
"JobControl",
|
|
15
|
+
"TempControl",
|
|
16
|
+
]
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FlashForge Python API - Control Module
|
|
3
|
+
"""
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from ...models.responses import FilamentArgs
|
|
9
|
+
from ..constants.commands import Commands
|
|
10
|
+
from ..constants.endpoints import Endpoints
|
|
11
|
+
from ..network.utils import NetworkUtils
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ...client import FlashForgeClient
|
|
15
|
+
from ...tcp.ff_client import FlashForgeClient as TcpClient
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Control:
|
|
19
|
+
"""
|
|
20
|
+
Provides methods for controlling various aspects of the FlashForge 3D printer.
|
|
21
|
+
This includes homing axes, controlling filtration, camera, speed, Z-axis offset,
|
|
22
|
+
fans, LEDs, and filament operations.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, client: "FlashForgeClient"):
|
|
26
|
+
"""
|
|
27
|
+
Creates an instance of the Control class.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
client: The FlashForgeClient instance used for communication with the printer.
|
|
31
|
+
"""
|
|
32
|
+
self.client = client
|
|
33
|
+
self._tcp_client: Optional[TcpClient] = None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def tcp_client(self) -> "TcpClient":
|
|
37
|
+
"""Get the TCP client instance."""
|
|
38
|
+
if self._tcp_client is None:
|
|
39
|
+
self._tcp_client = self.client.tcp_client
|
|
40
|
+
return self._tcp_client
|
|
41
|
+
|
|
42
|
+
async def home_axes(self) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Homes the X, Y, and Z axes of the printer.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
True if the command is successful, False otherwise.
|
|
48
|
+
"""
|
|
49
|
+
return await self.tcp_client.home_axes()
|
|
50
|
+
|
|
51
|
+
async def home_axes_rapid(self) -> bool:
|
|
52
|
+
"""
|
|
53
|
+
Performs a rapid homing of the X, Y, and Z axes.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if the command is successful, False otherwise.
|
|
57
|
+
"""
|
|
58
|
+
return await self.tcp_client.rapid_home()
|
|
59
|
+
|
|
60
|
+
async def set_external_filtration_on(self) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Turns on the external filtration system.
|
|
63
|
+
Requires the printer to have filtration control.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if the command is successful, False otherwise.
|
|
67
|
+
"""
|
|
68
|
+
if self.client.filtration_control:
|
|
69
|
+
return await self._send_filtration_command(FilamentArgs(False, True))
|
|
70
|
+
print("SetExternalFiltrationOn() error, filtration not equipped.")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
async def set_internal_filtration_on(self) -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Turns on the internal filtration system.
|
|
76
|
+
Requires the printer to have filtration control.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
True if the command is successful, False otherwise.
|
|
80
|
+
"""
|
|
81
|
+
if self.client.filtration_control:
|
|
82
|
+
return await self._send_filtration_command(FilamentArgs(True, False))
|
|
83
|
+
print("SetInternalFiltrationOn() error, filtration not equipped.")
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
async def set_filtration_off(self) -> bool:
|
|
87
|
+
"""
|
|
88
|
+
Turns off both internal and external filtration systems.
|
|
89
|
+
Requires the printer to have filtration control.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
True if the command is successful, False otherwise.
|
|
93
|
+
"""
|
|
94
|
+
if self.client.filtration_control:
|
|
95
|
+
return await self._send_filtration_command(FilamentArgs(False, False))
|
|
96
|
+
print("SetFiltrationOff() error, filtration not equipped.")
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
async def turn_camera_on(self) -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Turns on the printer's camera.
|
|
102
|
+
Only applicable for Pro models.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
True if the command is successful, False otherwise.
|
|
106
|
+
"""
|
|
107
|
+
if not self.client.is_pro:
|
|
108
|
+
return False
|
|
109
|
+
return await self._send_camera_command(True)
|
|
110
|
+
|
|
111
|
+
async def turn_camera_off(self) -> bool:
|
|
112
|
+
"""
|
|
113
|
+
Turns off the printer's camera.
|
|
114
|
+
Only applicable for Pro models.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
True if the command is successful, False otherwise.
|
|
118
|
+
"""
|
|
119
|
+
if not self.client.is_pro:
|
|
120
|
+
return False
|
|
121
|
+
return await self._send_camera_command(False)
|
|
122
|
+
|
|
123
|
+
async def set_speed_override(self, speed: int) -> bool:
|
|
124
|
+
"""
|
|
125
|
+
Sets the print speed override.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
speed: The desired print speed percentage (e.g., 100 for normal speed).
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if the command is successful, False otherwise.
|
|
132
|
+
"""
|
|
133
|
+
return await self._send_printer_control_cmd(print_speed=speed)
|
|
134
|
+
|
|
135
|
+
async def set_z_axis_override(self, offset: float) -> bool:
|
|
136
|
+
"""
|
|
137
|
+
Sets the Z-axis offset override.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
offset: The Z-axis offset value.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if the command is successful, False otherwise.
|
|
144
|
+
"""
|
|
145
|
+
return await self._send_printer_control_cmd(z_offset=offset)
|
|
146
|
+
|
|
147
|
+
async def set_chamber_fan_speed(self, speed: int) -> bool:
|
|
148
|
+
"""
|
|
149
|
+
Sets the chamber fan speed.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
speed: The desired chamber fan speed percentage.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if the command is successful, False otherwise.
|
|
156
|
+
"""
|
|
157
|
+
return await self._send_printer_control_cmd(chamber_fan_speed=speed)
|
|
158
|
+
|
|
159
|
+
async def set_cooling_fan_speed(self, speed: int) -> bool:
|
|
160
|
+
"""
|
|
161
|
+
Sets the cooling fan speed.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
speed: The desired cooling fan speed percentage.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
True if the command is successful, False otherwise.
|
|
168
|
+
"""
|
|
169
|
+
return await self._send_printer_control_cmd(cooling_fan_speed=speed)
|
|
170
|
+
|
|
171
|
+
async def set_led_on(self) -> bool:
|
|
172
|
+
"""
|
|
173
|
+
Turns on the printer's LED lights.
|
|
174
|
+
Requires the printer to have LED control.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
True if the command is successful, False otherwise.
|
|
178
|
+
"""
|
|
179
|
+
if self.client.led_control:
|
|
180
|
+
return await self.send_control_command(Commands.LIGHT_CONTROL_CMD, {"status": "open"})
|
|
181
|
+
print("SetLedOn() error, LEDs not equipped.")
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
async def set_led_off(self) -> bool:
|
|
185
|
+
"""
|
|
186
|
+
Turns off the printer's LED lights.
|
|
187
|
+
Requires the printer to have LED control.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if the command is successful, False otherwise.
|
|
191
|
+
"""
|
|
192
|
+
if self.client.led_control:
|
|
193
|
+
return await self.send_control_command(Commands.LIGHT_CONTROL_CMD, {"status": "close"})
|
|
194
|
+
print("SetLedOff() error, LEDs not equipped.")
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
async def turn_runout_sensor_on(self) -> bool:
|
|
198
|
+
"""
|
|
199
|
+
Turns on the filament runout sensor.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
True if the command is successful, False otherwise.
|
|
203
|
+
"""
|
|
204
|
+
return await self.tcp_client.turn_runout_sensor_on()
|
|
205
|
+
|
|
206
|
+
async def turn_runout_sensor_off(self) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
Turns off the filament runout sensor.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
True if the command is successful, False otherwise.
|
|
212
|
+
"""
|
|
213
|
+
return await self.tcp_client.turn_runout_sensor_off()
|
|
214
|
+
|
|
215
|
+
async def send_control_command(self, command: str, args: Dict[str, Any]) -> bool:
|
|
216
|
+
"""
|
|
217
|
+
Sends a generic control command to the printer via HTTP POST.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
command: The specific command string to send.
|
|
221
|
+
args: The arguments or payload specific to the command.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
True if the command is acknowledged with a success code, False otherwise.
|
|
225
|
+
"""
|
|
226
|
+
payload = {
|
|
227
|
+
"serialNumber": self.client.serial_number,
|
|
228
|
+
"checkCode": self.client.check_code,
|
|
229
|
+
"payload": {
|
|
230
|
+
"cmd": command,
|
|
231
|
+
"args": args
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
print(f"SendControlCommand:\n{payload}")
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
await self.client.is_http_client_busy()
|
|
239
|
+
|
|
240
|
+
async with aiohttp.ClientSession() as session:
|
|
241
|
+
async with session.post(
|
|
242
|
+
self.client.get_endpoint(Endpoints.CONTROL),
|
|
243
|
+
json=payload,
|
|
244
|
+
headers={"Content-Type": "application/json"}
|
|
245
|
+
) as response:
|
|
246
|
+
# Fix for FlashForge printer's malformed Content-Type header
|
|
247
|
+
# Some printers return "appliation/json" instead of "application/json"
|
|
248
|
+
try:
|
|
249
|
+
data = await response.json()
|
|
250
|
+
except aiohttp.ContentTypeError:
|
|
251
|
+
# Fallback: manually parse as JSON if Content-Type is malformed
|
|
252
|
+
text = await response.text()
|
|
253
|
+
import json
|
|
254
|
+
data = json.loads(text)
|
|
255
|
+
|
|
256
|
+
print(f"Command reply: {data}")
|
|
257
|
+
|
|
258
|
+
return NetworkUtils.is_ok(data)
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
print(f"Error in send_control_command: {e}")
|
|
262
|
+
return False
|
|
263
|
+
finally:
|
|
264
|
+
self.client.release_http_client()
|
|
265
|
+
|
|
266
|
+
async def send_job_control_cmd(self, command: str) -> bool:
|
|
267
|
+
"""
|
|
268
|
+
Sends a job control command.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
command: The job control command to send.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
True if the command is successful, False otherwise.
|
|
275
|
+
"""
|
|
276
|
+
payload = {
|
|
277
|
+
"jobID": "", # jobID seems to be optional or not strictly enforced
|
|
278
|
+
"action": command
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return await self.send_control_command(Commands.JOB_CONTROL_CMD, payload)
|
|
282
|
+
|
|
283
|
+
async def _send_printer_control_cmd(
|
|
284
|
+
self,
|
|
285
|
+
z_offset: float = 0.0,
|
|
286
|
+
print_speed: int = 100,
|
|
287
|
+
chamber_fan_speed: int = 100,
|
|
288
|
+
cooling_fan_speed: int = 100
|
|
289
|
+
) -> bool:
|
|
290
|
+
"""
|
|
291
|
+
Sends a command to control various printer settings during a print.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
z_offset: The Z-axis compensation offset.
|
|
295
|
+
print_speed: The print speed percentage.
|
|
296
|
+
chamber_fan_speed: The chamber fan speed percentage.
|
|
297
|
+
cooling_fan_speed: The cooling fan speed percentage.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
True if the command is successful, False otherwise.
|
|
301
|
+
"""
|
|
302
|
+
info = await self.client.info.get()
|
|
303
|
+
|
|
304
|
+
if info and info.current_print_layer < 2:
|
|
305
|
+
# Don't accidentally turn on the fans in the initial layers
|
|
306
|
+
chamber_fan_speed = 0
|
|
307
|
+
cooling_fan_speed = 0
|
|
308
|
+
|
|
309
|
+
if not self._is_printing(info):
|
|
310
|
+
raise Exception("Attempted to send printerCtl_cmd with no active job")
|
|
311
|
+
|
|
312
|
+
payload = {
|
|
313
|
+
"zAxisCompensation": z_offset,
|
|
314
|
+
"speed": print_speed,
|
|
315
|
+
"chamberFan": chamber_fan_speed,
|
|
316
|
+
"coolingFan": cooling_fan_speed,
|
|
317
|
+
"coolingLeftFan": 0 # This is unused
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return await self.send_control_command(Commands.PRINTER_CONTROL_CMD, payload)
|
|
321
|
+
|
|
322
|
+
async def _send_filtration_command(self, args: FilamentArgs) -> bool:
|
|
323
|
+
"""
|
|
324
|
+
Sends a command to control the printer's filtration system.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
args: The filtration arguments specifying internal and external fan states.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
True if the command is successful, False otherwise.
|
|
331
|
+
"""
|
|
332
|
+
return await self.send_control_command(Commands.CIRCULATION_CONTROL_CMD, args.dict())
|
|
333
|
+
|
|
334
|
+
async def _send_camera_command(self, enabled: bool) -> bool:
|
|
335
|
+
"""
|
|
336
|
+
Sends a command to control the printer's camera.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
enabled: True to turn the camera on ("open"), false to turn it off ("close").
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
True if the command is successful, False otherwise.
|
|
343
|
+
"""
|
|
344
|
+
payload = {"action": "open" if enabled else "close"}
|
|
345
|
+
return await self.send_control_command(Commands.CAMERA_CONTROL_CMD, payload)
|
|
346
|
+
|
|
347
|
+
def _is_printing(self, info: Any) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
Checks if the printer is currently printing based on its status information.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
info: The printer information object.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
True if the printer status is "printing", False otherwise.
|
|
356
|
+
"""
|
|
357
|
+
return info and getattr(info, "status", "") == "printing"
|