flashforge-python-api 1.3.4__py3-none-any.whl → 1.4.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 +1 -1
- flashforge/api/controls/files.py +15 -3
- flashforge/api/controls/info.py +48 -5
- flashforge/models/machine_info.py +6 -1
- {flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/METADATA +1 -1
- {flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/RECORD +9 -9
- {flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/WHEEL +0 -0
- {flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/entry_points.txt +0 -0
- {flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/licenses/LICENSE +0 -0
flashforge/__init__.py
CHANGED
|
@@ -128,7 +128,7 @@ from .tcp import (
|
|
|
128
128
|
)
|
|
129
129
|
|
|
130
130
|
FiveMClient = FlashForgeClient
|
|
131
|
-
__version__ = "1.
|
|
131
|
+
__version__ = "1.4.0"
|
|
132
132
|
__author__ = "FlashForge Python API Contributors"
|
|
133
133
|
__email__ = "notghosttypes@gmail.com"
|
|
134
134
|
__description__ = "Python library for controlling FlashForge 3D printers"
|
flashforge/api/controls/files.py
CHANGED
|
@@ -69,8 +69,19 @@ class Files:
|
|
|
69
69
|
async def get_recent_file_list(self) -> list[FFGcodeFileEntry]:
|
|
70
70
|
"""
|
|
71
71
|
Retrieves a list of the 10 most recently printed files from the printer's API.
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
|
|
73
|
+
Only the **AD5X** answers with `gcodeListDetail`, the per-file block carrying
|
|
74
|
+
print time, filament weight, and the per-tool material data that material
|
|
75
|
+
matching is built from. Every other model - the 5M, the 5M Pro, **and the
|
|
76
|
+
Creator 5 / Creator 5 Pro** - returns bare file names, and their entries come
|
|
77
|
+
back with `gcode_tool_datas=None` and `printing_time=0`.
|
|
78
|
+
|
|
79
|
+
The Creator 5 is the surprise there, and it is firmware, not a parsing gap:
|
|
80
|
+
it is a newer printer than the AD5X but reports less (confirmed against a
|
|
81
|
+
Creator 5 Pro, 2026-08-05). Do not describe this method as "AD5X and newer" -
|
|
82
|
+
that phrasing cost a downstream integration three releases of chasing a bug
|
|
83
|
+
that was never in the code. Callers that need per-tool data on a Creator 5
|
|
84
|
+
must parse the 3mf themselves at upload time.
|
|
74
85
|
|
|
75
86
|
Returns:
|
|
76
87
|
A list of FFGcodeFileEntry objects. Returns an empty list if the request fails or an error occurs.
|
|
@@ -125,7 +136,8 @@ class Files:
|
|
|
125
136
|
return entries
|
|
126
137
|
return []
|
|
127
138
|
|
|
128
|
-
#
|
|
139
|
+
# Only the AD5X provides detailed info in gcodeListDetail. The
|
|
140
|
+
# Creator 5 series does not, despite being the newer hardware.
|
|
129
141
|
if result.gcode_list_detail and len(result.gcode_list_detail) > 0:
|
|
130
142
|
return result.gcode_list_detail
|
|
131
143
|
|
flashforge/api/controls/info.py
CHANGED
|
@@ -46,6 +46,19 @@ PID_MODEL_NAMES: dict[int, str] = {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
# The one state in which the firmware actually counts `estimatedTime` down.
|
|
50
|
+
# Outside it the field freezes at its last value while the wall clock keeps
|
|
51
|
+
# moving, so `now() + estimatedTime` walks forward one minute per minute instead
|
|
52
|
+
# of holding still - a paused print appears to recede forever. The duration
|
|
53
|
+
# stays correct throughout; only its conversion to an absolute timestamp is
|
|
54
|
+
# invalid, which is why `print_eta` is ungated and `completion_time` is not.
|
|
55
|
+
#
|
|
56
|
+
# HEATING is deliberately excluded. The pre-print warmup does not advance the
|
|
57
|
+
# job either, so the same drift applies - it just lasts minutes rather than
|
|
58
|
+
# hours, which is why it is easy to miss.
|
|
59
|
+
_ADVANCING_STATES = frozenset({MachineState.PRINTING})
|
|
60
|
+
|
|
61
|
+
|
|
49
62
|
class MachineInfoParser:
|
|
50
63
|
"""
|
|
51
64
|
Transforms printer detail data from the API response format into a structured FFMachineInfo object.
|
|
@@ -75,7 +88,14 @@ class MachineInfoParser:
|
|
|
75
88
|
formatted_run_time = MachineInfoParser._format_time_from_seconds(
|
|
76
89
|
getattr(detail, "print_duration", 0) or 0
|
|
77
90
|
)
|
|
78
|
-
|
|
91
|
+
machine_state = MachineInfoParser._get_machine_state(
|
|
92
|
+
getattr(detail, "status", "") or ""
|
|
93
|
+
)
|
|
94
|
+
completion_time = (
|
|
95
|
+
datetime.now() + timedelta(seconds=estimated_time)
|
|
96
|
+
if machine_state in _ADVANCING_STATES
|
|
97
|
+
else None
|
|
98
|
+
)
|
|
79
99
|
|
|
80
100
|
total_minutes = getattr(detail, "cumulative_print_time", 0) or 0
|
|
81
101
|
hours = total_minutes // 60
|
|
@@ -255,9 +275,7 @@ class MachineInfoParser:
|
|
|
255
275
|
print_speed_adjust=getattr(detail, "print_speed_adjust", 0) or 0,
|
|
256
276
|
filament_type=getattr(detail, "right_filament_type", "") or "",
|
|
257
277
|
# Machine state
|
|
258
|
-
machine_state=
|
|
259
|
-
getattr(detail, "status", "") or ""
|
|
260
|
-
),
|
|
278
|
+
machine_state=machine_state,
|
|
261
279
|
status=getattr(detail, "status", "") or "",
|
|
262
280
|
total_print_layers=getattr(detail, "target_print_layer", 0) or 0,
|
|
263
281
|
tvoc=getattr(detail, "tvoc", 0) or 0,
|
|
@@ -302,7 +320,21 @@ class MachineInfoParser:
|
|
|
302
320
|
|
|
303
321
|
@staticmethod
|
|
304
322
|
def _get_machine_state(status: str) -> MachineState:
|
|
305
|
-
"""Map raw status strings into the public machine state enum.
|
|
323
|
+
"""Map raw status strings into the public machine state enum.
|
|
324
|
+
|
|
325
|
+
An unmapped value costs the consumer everything the field is for: it
|
|
326
|
+
becomes ``UNKNOWN``, which in Home Assistant renders as "unknown" - the
|
|
327
|
+
one state that says nothing, at the moment the user most needs to know
|
|
328
|
+
what the printer is doing. Both additions below were found that way, in
|
|
329
|
+
a log full of `Unknown machine status received` while a Creator 5 Pro sat
|
|
330
|
+
paused on a detected clog.
|
|
331
|
+
|
|
332
|
+
Consumers may map this enum onto a fixed set of values - the Home
|
|
333
|
+
Assistant integration's Machine Status sensor is a `device_class=ENUM`
|
|
334
|
+
with an explicit `options` list - so a *new* member is a breaking change
|
|
335
|
+
for them, while mapping onto an existing one is not. Prefer the closest
|
|
336
|
+
existing state unless a new one is worth coordinating.
|
|
337
|
+
"""
|
|
306
338
|
valid_status = status.lower() if isinstance(status, str) else ""
|
|
307
339
|
state_mapping = {
|
|
308
340
|
"ready": MachineState.READY,
|
|
@@ -312,9 +344,20 @@ class MachineInfoParser:
|
|
|
312
344
|
"heating": MachineState.HEATING,
|
|
313
345
|
"printing": MachineState.PRINTING,
|
|
314
346
|
"pausing": MachineState.PAUSING,
|
|
347
|
+
# The Creator 5 Pro reports "pause" for a print that is paused,
|
|
348
|
+
# where the documented value is "paused" - both are mapped, because
|
|
349
|
+
# firmware that reports one is not a reason to drop the other.
|
|
350
|
+
# Observed on pid 41, firmware 1.9.4, whenever the printer paused
|
|
351
|
+
# itself on a detected clog.
|
|
352
|
+
"pause": MachineState.PAUSED,
|
|
315
353
|
"paused": MachineState.PAUSED,
|
|
316
354
|
"cancel": MachineState.CANCELLED,
|
|
317
355
|
"completed": MachineState.COMPLETED,
|
|
356
|
+
# Reported while a file is being transferred to the printer. Not a
|
|
357
|
+
# print, but not idle either, so BUSY is the honest existing fit; a
|
|
358
|
+
# dedicated state would need consumers to add it to their option
|
|
359
|
+
# lists first.
|
|
360
|
+
"downloading": MachineState.BUSY,
|
|
318
361
|
}
|
|
319
362
|
|
|
320
363
|
if valid_status in state_mapping:
|
|
@@ -514,7 +514,12 @@ class FFMachineInfo(BaseModel):
|
|
|
514
514
|
|
|
515
515
|
# Extras
|
|
516
516
|
print_eta: str = "00:00"
|
|
517
|
-
|
|
517
|
+
# None whenever the print is not advancing (paused, heating, ready, error,
|
|
518
|
+
# ...). The firmware freezes `estimatedTime` outside the PRINTING state, so
|
|
519
|
+
# an absolute timestamp derived from it would recede in real time rather
|
|
520
|
+
# than hold. Use `print_eta` / `estimated_time` for the remaining duration,
|
|
521
|
+
# which stays valid in every state.
|
|
522
|
+
completion_time: datetime | None = None
|
|
518
523
|
formatted_run_time: str = "00:00"
|
|
519
524
|
formatted_total_run_time: str = "0h:0m"
|
|
520
525
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashforge-python-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A comprehensive Python library for controlling FlashForge 3D printers
|
|
5
5
|
Project-URL: Homepage, https://github.com/GhostTypes/ff-5mp-api-py
|
|
6
6
|
Project-URL: Documentation, https://github.com/GhostTypes/ff-5mp-api-py#readme
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
flashforge/__init__.py,sha256=
|
|
1
|
+
flashforge/__init__.py,sha256=cYJmX39_FZxN6yyHkwepT_g9mnU2hHuVYuv3Lpg6seY,5472
|
|
2
2
|
flashforge/client.py,sha256=nDtnv3y9XlEQknIkgIUiuOYAiyTEGbQ7tHBdCs2fg1I,21432
|
|
3
3
|
flashforge/exceptions.py,sha256=rZwBJHyqqtoI6cgd8iGOvPH8EDcytA3lyU3FoVMJgyU,1732
|
|
4
4
|
flashforge/api/__init__.py,sha256=vQz-DkG6LTH39bI4fyiUc0D9jQzemLh45pORLptSOlg,335
|
|
@@ -8,8 +8,8 @@ flashforge/api/constants/endpoints.py,sha256=oZtOFfkU64THDeCMUIVv6_0L-BOUZzgLKAU
|
|
|
8
8
|
flashforge/api/controls/__init__.py,sha256=53s-H25Pjwr0kvPk8MZ2Twy8VHGqGcO6Q6uBphhEOh4,293
|
|
9
9
|
flashforge/api/controls/control.py,sha256=OgE7K6NkFLwFN1r_q5g1RPwYC8MCt0f6L5CZxDW44d8,15151
|
|
10
10
|
flashforge/api/controls/creator5_palette.py,sha256=zdD3PIXx7hCVj2Fmv1CSFOaqogRUPeBaymgY2DFiMxk,8835
|
|
11
|
-
flashforge/api/controls/files.py,sha256=
|
|
12
|
-
flashforge/api/controls/info.py,sha256=
|
|
11
|
+
flashforge/api/controls/files.py,sha256=Jk2ySrJdw2QDk1CJlGfY4bTmZJocx4GXztkEqE4rRaI,8830
|
|
12
|
+
flashforge/api/controls/info.py,sha256=7E2TtfFgT79m9lI3nBIvO-536dhl4xzwe1L9XCWVZ78,24836
|
|
13
13
|
flashforge/api/controls/job_control.py,sha256=NnACm4LPUz4n8lnRiUl33jilWekS1b6HQTbP--nDzZc,33225
|
|
14
14
|
flashforge/api/controls/temp_control.py,sha256=EOO5TkODirjFcnQ7rZrirxAweHBdVin032AWj-Z8VQs,13040
|
|
15
15
|
flashforge/api/filament/__init__.py,sha256=isT2dl0hzUS0xMTXMm4Tip0GTG1gCsm8LKWa9xPu4Y8,104
|
|
@@ -24,7 +24,7 @@ flashforge/api/network/utils.py,sha256=Q5-Vj_1VN611QV_TGpDzrrZ2X8PqaGT0j00BhSHv5
|
|
|
24
24
|
flashforge/discovery/__init__.py,sha256=G0WiP70EhfHdjXR1RNlv6xjhyHOdo-HWyOm9J0ds4SM,828
|
|
25
25
|
flashforge/discovery/discovery.py,sha256=-hOmVIpn8gu-rPYo8FRBfhKb_tBqwamLfmoPVYaDzLI,27614
|
|
26
26
|
flashforge/models/__init__.py,sha256=hdcK0E4KeDfv_A8Y2aPOHgHziwTjP1RPb_VP-89dOzQ,1086
|
|
27
|
-
flashforge/models/machine_info.py,sha256=
|
|
27
|
+
flashforge/models/machine_info.py,sha256=OvvpjjUtmOHyogYADFfRJRQnwwPQnjiGVCkXRNdka7Y,24397
|
|
28
28
|
flashforge/models/responses.py,sha256=10-8yFfZtLuDmH1TK2xVYl-Uf-zT2WEJnpb2H5ZK3ZU,10637
|
|
29
29
|
flashforge/tcp/__init__.py,sha256=hpnqoWHeRtTwJPzdsVlwGt1njKbAkrgIHKADkLSIRec,1317
|
|
30
30
|
flashforge/tcp/a3_client.py,sha256=Aqb6MuKkjnNUVdE2LbZKd7bPlFK8XPIm17C_BG_PuNU,15867
|
|
@@ -42,8 +42,8 @@ flashforge/tcp/parsers/print_status.py,sha256=C-KzukhK0TR86AP3t3XrMH4dief6pa3I-y
|
|
|
42
42
|
flashforge/tcp/parsers/printer_info.py,sha256=CHPs6nJfEByXyG3co7Nb8_ygggxkYuc7A9O9bpQEqXE,5441
|
|
43
43
|
flashforge/tcp/parsers/temp_info.py,sha256=9wRGUM9cKvDiZD3SIxBx_qVLZbeXoYsoY3-R3vN3H4g,7938
|
|
44
44
|
flashforge/tcp/parsers/thumbnail_info.py,sha256=1U1S_gcIZ2lDpEA4622ywUew6TVw0BnzjlBAiihQwzk,10478
|
|
45
|
-
flashforge_python_api-1.
|
|
46
|
-
flashforge_python_api-1.
|
|
47
|
-
flashforge_python_api-1.
|
|
48
|
-
flashforge_python_api-1.
|
|
49
|
-
flashforge_python_api-1.
|
|
45
|
+
flashforge_python_api-1.4.0.dist-info/METADATA,sha256=VQsv_msrpBzurF7zMQUadt7jpyy1ljomJQxV_XhALIg,4970
|
|
46
|
+
flashforge_python_api-1.4.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
47
|
+
flashforge_python_api-1.4.0.dist-info/entry_points.txt,sha256=AkOxlsLvQ7cvMLxn7tlzfKp_DCH2hXhbVceHIXxawpU,66
|
|
48
|
+
flashforge_python_api-1.4.0.dist-info/licenses/LICENSE,sha256=-cTA-hrmvlb3pqlQrBZQXUnKayhUjsLJMPb7TD91frM,1067
|
|
49
|
+
flashforge_python_api-1.4.0.dist-info/RECORD,,
|
|
File without changes
|
{flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{flashforge_python_api-1.3.4.dist-info → flashforge_python_api-1.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|