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
@@ -2,122 +2,182 @@
2
2
  FlashForge Python API - Response Models
3
3
  """
4
4
 
5
- from typing import Optional, Union
6
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
7
6
 
8
- from .machine_info import FFPrinterDetail, FFGcodeFileEntry
7
+ from .machine_info import FFGcodeFileEntry, FFPrinterDetail
9
8
 
10
9
 
11
10
  class GenericResponse(BaseModel):
12
11
  """Represents a generic response from the printer's API."""
12
+
13
+ model_config = ConfigDict(extra="forbid")
14
+
13
15
  code: int
14
16
  message: str = ""
15
17
 
16
18
 
17
19
  class DetailResponse(GenericResponse):
18
20
  """Represents the structure of the response from the printer's detail endpoint."""
21
+
22
+ model_config = ConfigDict(extra="forbid")
23
+
19
24
  detail: FFPrinterDetail
20
25
 
21
26
 
22
27
  class Product(BaseModel):
23
28
  """
24
29
  Defines the structure of the `product` object nested within a `ProductResponse`.
25
-
30
+
26
31
  This contains various control state flags reported by the printer,
27
32
  indicating the status or availability of certain features like temperature controls,
28
33
  fan controls, and light controls. A state of 0 often means off/unavailable,
29
34
  while other numbers (typically 1) mean on/available or a specific mode.
30
-
35
+
31
36
  Field names match the actual camelCase format returned by the printer.
32
37
  """
33
- chamberTempCtrlState: int
34
- externalFanCtrlState: int
35
- internalFanCtrlState: int
36
- lightCtrlState: int
37
- nozzleTempCtrlState: int
38
- platformTempCtrlState: int
38
+
39
+ model_config = ConfigDict(extra="forbid")
40
+
41
+ chamberTempCtrlState: int # noqa: N815 - field must match API camelCase response format
42
+ externalFanCtrlState: int # noqa: N815 - field must match API camelCase response format
43
+ internalFanCtrlState: int # noqa: N815 - field must match API camelCase response format
44
+ lightCtrlState: int # noqa: N815 - field must match API camelCase response format
45
+ nozzleTempCtrlState: int # noqa: N815 - field must match API camelCase response format
46
+ platformTempCtrlState: int # noqa: N815 - field must match API camelCase response format
39
47
 
40
48
 
41
49
  class ProductResponse(GenericResponse):
42
50
  """
43
51
  Represents the expected structure of the response from the "product command"
44
52
  sent to the printer (typically to the `/product` endpoint).
45
-
53
+
46
54
  This response includes general status information (via `GenericResponse`)
47
55
  and a nested `product` object containing specific control states.
48
56
  """
57
+
58
+ model_config = ConfigDict(extra="forbid")
59
+
49
60
  product: Product
50
61
 
51
62
 
52
63
  class FilamentArgs(BaseModel):
53
64
  """Represents the arguments for controlling the printer's filtration system."""
54
- internal: str
55
- external: str
56
65
 
57
- def __init__(self, internal_on: bool, external_on: bool):
58
- super().__init__(
59
- internal="open" if internal_on else "close",
60
- external="open" if external_on else "close"
66
+ model_config = ConfigDict(extra="forbid")
67
+
68
+ internal: str = Field(description="Internal filtration state ('open' or 'close')")
69
+ external: str = Field(description="External filtration state ('open' or 'close')")
70
+
71
+ @field_validator("internal", "external")
72
+ @classmethod
73
+ def validate_filtration_state(cls, v: str) -> str:
74
+ """Validate that the state is either 'open' or 'close'."""
75
+ if v not in {"open", "close"}:
76
+ raise ValueError(f"Filtration state must be 'open' or 'close', got: {v}")
77
+ return v
78
+
79
+ @classmethod
80
+ def create(cls, internal_on: bool, external_on: bool) -> "FilamentArgs":
81
+ """
82
+ Create FilamentArgs from boolean states.
83
+
84
+ Args:
85
+ internal_on: Whether internal filtration should be open (True) or closed (False)
86
+ external_on: Whether external filtration should be open (True) or closed (False)
87
+
88
+ Returns:
89
+ FilamentArgs instance with proper string states
90
+ """
91
+ return cls(
92
+ internal="open" if internal_on else "close", external="open" if external_on else "close"
61
93
  )
62
94
 
63
95
 
64
96
  class AD5XMaterialMapping(BaseModel):
65
97
  """Represents a material mapping for AD5X multi-color printing. Maps a tool (extruder) to a specific material station slot."""
98
+
99
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
100
+
66
101
  tool_id: int = Field(ge=0, le=3, description="Tool ID (0-based: 0, 1, 2, 3)")
67
102
  slot_id: int = Field(ge=1, le=4, description="Slot ID (1-based: 1, 2, 3, 4)")
68
103
  material_name: str = Field(description="Name of the material (e.g., 'PLA', 'SILK')")
69
- tool_material_color: str = Field(description="Hex color code for the tool material (e.g., '#FFFFFF')")
70
- slot_material_color: str = Field(description="Hex color code for the slot material (e.g., '#46328E')")
104
+ tool_material_color: str = Field(
105
+ description="Hex color code for the tool material (e.g., '#FFFFFF')"
106
+ )
107
+ slot_material_color: str = Field(
108
+ description="Hex color code for the slot material (e.g., '#46328E')"
109
+ )
71
110
 
72
- class Config:
73
- populate_by_name = True
111
+ @field_validator("tool_material_color", "slot_material_color")
112
+ @classmethod
113
+ def validate_hex_color(cls, v: str) -> str:
114
+ """Validate that the color is a valid hex color code."""
115
+ import re
116
+
117
+ if not re.match(r"^#[0-9A-Fa-f]{6}$", v):
118
+ raise ValueError(f"Invalid hex color format: {v}")
119
+ return v
74
120
 
75
121
 
76
122
  class AD5XLocalJobParams(BaseModel):
77
123
  """Parameters for starting an AD5X local job with material mappings. Used for multi-color prints that utilize the material station."""
78
- file_name: str = Field(description="Name of the file on the printer to start")
79
- leveling_before_print: bool = Field(description="Whether to perform bed leveling before printing")
80
- material_mappings: list[AD5XMaterialMapping] = Field(description="Array of material mappings (1-4 items)")
81
124
 
82
- class Config:
83
- populate_by_name = True
125
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
126
+
127
+ file_name: str = Field(description="Name of the file on the printer to start")
128
+ leveling_before_print: bool = Field(
129
+ description="Whether to perform bed leveling before printing"
130
+ )
131
+ material_mappings: list[AD5XMaterialMapping] = Field(
132
+ min_length=1, max_length=4, description="Array of material mappings (1-4 items)"
133
+ )
84
134
 
85
135
 
86
136
  class AD5XSingleColorJobParams(BaseModel):
87
137
  """Parameters for starting an AD5X single-color local job. Used for single-color prints that do not require the material station."""
88
- file_name: str = Field(description="Name of the file on the printer to start")
89
- leveling_before_print: bool = Field(description="Whether to perform bed leveling before printing")
90
138
 
91
- class Config:
92
- populate_by_name = True
139
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
140
+
141
+ file_name: str = Field(description="Name of the file on the printer to start")
142
+ leveling_before_print: bool = Field(
143
+ description="Whether to perform bed leveling before printing"
144
+ )
93
145
 
94
146
 
95
147
  class AD5XUploadParams(BaseModel):
96
148
  """Parameters for uploading a file to AD5X printer with material station support. Extends basic upload functionality with AD5X-specific features."""
149
+
150
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
151
+
97
152
  file_path: str = Field(description="Local file path to upload")
98
153
  start_print: bool = Field(description="Whether to start printing immediately after upload")
99
- leveling_before_print: bool = Field(description="Whether to perform bed leveling before printing")
154
+ leveling_before_print: bool = Field(
155
+ description="Whether to perform bed leveling before printing"
156
+ )
100
157
  flow_calibration: bool = Field(description="Whether to enable flow calibration")
101
158
  first_layer_inspection: bool = Field(description="Whether to enable first layer inspection")
102
159
  time_lapse_video: bool = Field(description="Whether to enable time lapse video recording")
103
- material_mappings: list[AD5XMaterialMapping] = Field(description="Array of material mappings for the material station (1-4 items)")
104
-
105
- class Config:
106
- populate_by_name = True
160
+ material_mappings: list[AD5XMaterialMapping] = Field(
161
+ min_length=1,
162
+ max_length=4,
163
+ description="Array of material mappings for the material station (1-4 items)",
164
+ )
107
165
 
108
166
 
109
167
  class GCodeListResponse(GenericResponse):
110
168
  """Represents the response structure for a G-code file list request."""
111
- gcode_list: Optional[Union[list[str], list[FFGcodeFileEntry]]] = Field(default=None, alias="gcodeList")
112
- gcode_list_detail: Optional[list[FFGcodeFileEntry]] = Field(default=None, alias="gcodeListDetail")
113
169
 
114
- class Config:
115
- populate_by_name = True
170
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
171
+
172
+ gcode_list: list[str] | list[FFGcodeFileEntry] | None = Field(default=None, alias="gcodeList")
173
+ gcode_list_detail: list[FFGcodeFileEntry] | None = Field(default=None, alias="gcodeListDetail")
116
174
 
117
175
 
118
176
  class ThumbnailResponse(GenericResponse):
119
177
  """Represents the response structure for a G-code thumbnail request."""
120
- image_data: str = Field(alias="imageData", description="The thumbnail image data encoded as a base64 string")
121
178
 
122
- class Config:
123
- populate_by_name = True
179
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
180
+
181
+ image_data: str = Field(
182
+ alias="imageData", description="The thumbnail image data encoded as a base64 string"
183
+ )
@@ -5,8 +5,10 @@ This module provides low-level TCP socket communication and high-level G-code in
5
5
  for controlling FlashForge printers via their TCP API.
6
6
  """
7
7
 
8
+ from .a3_client import A3BuildVolume, A3FileEntry, A3PrinterInfo, A3Thumbnail, FlashForgeA3Client
9
+ from .a4_client import A4BuildVolume, A4FileEntry, A4PrinterInfo, FlashForgeA4Client
8
10
  from .ff_client import FlashForgeClient
9
- from .gcode import GCodeController, GCodes
11
+ from .gcode import A3GCodeController, GCodeController, GCodes
10
12
  from .parsers import (
11
13
  Endstop,
12
14
  EndstopStatus,
@@ -20,22 +22,33 @@ from .parsers import (
20
22
  TempInfo,
21
23
  ThumbnailInfo,
22
24
  )
23
- from .tcp_client import FlashForgeTcpClient
25
+ from .tcp_client import FlashForgeTcpClient, FlashForgeTcpClientOptions
24
26
 
25
27
  __all__ = [
26
- 'FlashForgeTcpClient',
27
- 'FlashForgeClient',
28
- 'GCodes',
29
- 'GCodeController',
30
- 'PrinterInfo',
31
- 'TempInfo',
32
- 'TempData',
33
- 'LocationInfo',
34
- 'EndstopStatus',
35
- 'MachineStatus',
36
- 'MoveMode',
37
- 'Status',
38
- 'Endstop',
39
- 'PrintStatus',
40
- 'ThumbnailInfo',
28
+ "FlashForgeTcpClient",
29
+ "FlashForgeTcpClientOptions",
30
+ "FlashForgeClient",
31
+ "FlashForgeA3Client",
32
+ "FlashForgeA4Client",
33
+ "GCodes",
34
+ "GCodeController",
35
+ "A3GCodeController",
36
+ "A3BuildVolume",
37
+ "A3PrinterInfo",
38
+ "A3FileEntry",
39
+ "A3Thumbnail",
40
+ "A4BuildVolume",
41
+ "A4PrinterInfo",
42
+ "A4FileEntry",
43
+ "PrinterInfo",
44
+ "TempInfo",
45
+ "TempData",
46
+ "LocationInfo",
47
+ "EndstopStatus",
48
+ "MachineStatus",
49
+ "MoveMode",
50
+ "Status",
51
+ "Endstop",
52
+ "PrintStatus",
53
+ "ThumbnailInfo",
41
54
  ]