flashforge-python-api 1.2.1__py3-none-any.whl → 1.2.3__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/info.py +20 -2
- flashforge/discovery/discovery.py +18 -17
- flashforge/models/machine_info.py +1 -0
- {flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/METADATA +24 -7
- {flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/RECORD +9 -9
- {flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/WHEEL +0 -0
- {flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/entry_points.txt +0 -0
- {flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/licenses/LICENSE +0 -0
flashforge/__init__.py
CHANGED
|
@@ -127,7 +127,7 @@ from .tcp import (
|
|
|
127
127
|
)
|
|
128
128
|
|
|
129
129
|
FiveMClient = FlashForgeClient
|
|
130
|
-
__version__ = "1.
|
|
130
|
+
__version__ = "1.2.3"
|
|
131
131
|
__author__ = "FlashForge Python API Contributors"
|
|
132
132
|
__email__ = "notghosttypes@gmail.com"
|
|
133
133
|
__description__ = "Python library for controlling FlashForge 3D printers"
|
flashforge/api/controls/info.py
CHANGED
|
@@ -14,6 +14,14 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from ...client import FlashForgeClient
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
# Firmware-reported PIDs from FlashForge's /detail endpoint. These are stable
|
|
18
|
+
# identifiers set by firmware, unlike the user-mutable `name` field.
|
|
19
|
+
PID_5M = 35
|
|
20
|
+
PID_5M_PRO = 36
|
|
21
|
+
PID_AD5X = 38
|
|
22
|
+
KNOWN_HTTP_PIDS = {PID_5M, PID_5M_PRO, PID_AD5X}
|
|
23
|
+
|
|
24
|
+
|
|
17
25
|
class MachineInfoParser:
|
|
18
26
|
"""
|
|
19
27
|
Transforms printer detail data from the API response format into a structured FFMachineInfo object.
|
|
@@ -68,8 +76,17 @@ class MachineInfoParser:
|
|
|
68
76
|
or len(getattr(getattr(detail, "matl_station_info", None), "slot_infos", []) or []) > 0
|
|
69
77
|
)
|
|
70
78
|
printer_name = getattr(detail, "name", "") or ""
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
pid = getattr(detail, "pid", None)
|
|
80
|
+
|
|
81
|
+
if pid in KNOWN_HTTP_PIDS:
|
|
82
|
+
is_ad5x = pid == PID_AD5X
|
|
83
|
+
is_pro = pid == PID_5M_PRO
|
|
84
|
+
else:
|
|
85
|
+
# Fallback for firmware that doesn't report pid: legacy
|
|
86
|
+
# name+capability heuristic. Vulnerable to user renames, which
|
|
87
|
+
# is why pid-based detection is preferred when available.
|
|
88
|
+
is_ad5x = printer_name.upper() == "AD5X" or has_material_station
|
|
89
|
+
is_pro = "Pro" in printer_name and not is_ad5x
|
|
73
90
|
|
|
74
91
|
# Build the FFMachineInfo object
|
|
75
92
|
machine_info = FFMachineInfo(
|
|
@@ -107,6 +124,7 @@ class MachineInfoParser:
|
|
|
107
124
|
fill_amount=getattr(detail, "fill_amount", 0) or 0,
|
|
108
125
|
firmware_version=getattr(detail, "firmware_version", "") or "",
|
|
109
126
|
name=printer_name,
|
|
127
|
+
pid=pid,
|
|
110
128
|
is_pro=is_pro,
|
|
111
129
|
is_ad5x=is_ad5x,
|
|
112
130
|
nozzle_size=getattr(detail, "nozzle_model", "") or "",
|
|
@@ -12,7 +12,7 @@ from dataclasses import dataclass, field
|
|
|
12
12
|
from enum import IntEnum, StrEnum
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
|
-
import
|
|
15
|
+
import ifaddr
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
@@ -535,26 +535,27 @@ class PrinterDiscovery:
|
|
|
535
535
|
broadcast_addresses: list[str] = []
|
|
536
536
|
|
|
537
537
|
try:
|
|
538
|
-
for
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
538
|
+
for adapter in ifaddr.get_adapters():
|
|
539
|
+
for ip in adapter.ips:
|
|
540
|
+
if not ip.is_IPv4:
|
|
541
|
+
continue
|
|
542
|
+
|
|
543
|
+
ip_addr = ip.ip
|
|
544
|
+
if not isinstance(ip_addr, str) or ip_addr.startswith("127."):
|
|
542
545
|
continue
|
|
543
546
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
+
prefix = ip.network_prefix
|
|
548
|
+
if not isinstance(prefix, int) or not 0 < prefix <= 32:
|
|
549
|
+
continue
|
|
547
550
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
551
|
+
mask_int = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF
|
|
552
|
+
netmask = ".".join(
|
|
553
|
+
str((mask_int >> (8 * (3 - i))) & 0xFF) for i in range(4)
|
|
554
|
+
)
|
|
552
555
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
except Exception as error:
|
|
557
|
-
logger.warning("Error processing interface %s: %s", interface_name, error)
|
|
556
|
+
broadcast = self.calculate_broadcast_address(ip_addr, netmask)
|
|
557
|
+
if broadcast and broadcast not in broadcast_addresses:
|
|
558
|
+
broadcast_addresses.append(broadcast)
|
|
558
559
|
except Exception as error:
|
|
559
560
|
logger.error("Error getting network interfaces: %s", error)
|
|
560
561
|
broadcast_addresses = ["255.255.255.255", "192.168.1.255", "192.168.0.255"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashforge-python-api
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
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
|
|
@@ -16,12 +16,13 @@ Classifier: Programming Language :: Python :: 3
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
20
|
Classifier: Topic :: Scientific/Engineering
|
|
20
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
22
|
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
22
23
|
Requires-Python: >=3.11
|
|
23
24
|
Requires-Dist: aiohttp>=3.8.0
|
|
24
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: ifaddr>=0.2.0
|
|
25
26
|
Requires-Dist: pydantic>=2.0.0
|
|
26
27
|
Requires-Dist: requests>=2.31.0
|
|
27
28
|
Provides-Extra: all
|
|
@@ -70,14 +71,20 @@ Modern LAN-mode HTTP printers require:
|
|
|
70
71
|
|
|
71
72
|
- printer IP address
|
|
72
73
|
- serial number
|
|
73
|
-
- check code
|
|
74
|
+
- check code (per-printer credential, not returned by discovery)
|
|
74
75
|
|
|
75
76
|
```python
|
|
76
77
|
import asyncio
|
|
77
|
-
|
|
78
|
+
import os
|
|
79
|
+
from flashforge import FlashForgeClient, FiveMClientConnectionOptions, PrinterDiscovery
|
|
78
80
|
|
|
79
81
|
|
|
80
82
|
async def main():
|
|
83
|
+
check_code = os.getenv("FLASHFORGE_CHECK_CODE", "").strip()
|
|
84
|
+
if not check_code:
|
|
85
|
+
print("Set FLASHFORGE_CHECK_CODE before running this example")
|
|
86
|
+
return
|
|
87
|
+
|
|
81
88
|
discovery = PrinterDiscovery()
|
|
82
89
|
printers = await discovery.discover()
|
|
83
90
|
|
|
@@ -86,13 +93,23 @@ async def main():
|
|
|
86
93
|
return
|
|
87
94
|
|
|
88
95
|
printer = printers[0]
|
|
96
|
+
if not printer.serial_number:
|
|
97
|
+
print("Discovered printer did not report a serial number")
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
options = FiveMClientConnectionOptions(
|
|
101
|
+
http_port=printer.event_port,
|
|
102
|
+
tcp_port=printer.command_port,
|
|
103
|
+
)
|
|
89
104
|
|
|
90
105
|
async with FlashForgeClient(
|
|
91
106
|
printer.ip_address,
|
|
92
|
-
printer.serial_number
|
|
93
|
-
|
|
107
|
+
printer.serial_number,
|
|
108
|
+
check_code,
|
|
109
|
+
options=options,
|
|
94
110
|
) as client:
|
|
95
|
-
|
|
111
|
+
status = await client.get_printer_status()
|
|
112
|
+
if not status:
|
|
96
113
|
return
|
|
97
114
|
|
|
98
115
|
await client.init_control()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
flashforge/__init__.py,sha256=
|
|
1
|
+
flashforge/__init__.py,sha256=TkwDdU0toTFKhbDE6-9P85X8o4_GKwkdFTp4CcdFMt4,5336
|
|
2
2
|
flashforge/client.py,sha256=lplKJxNOLhUJMusm025cj7rZvWRAiv2j5qENdBIctuA,16920
|
|
3
3
|
flashforge/api/__init__.py,sha256=vQz-DkG6LTH39bI4fyiUc0D9jQzemLh45pORLptSOlg,335
|
|
4
4
|
flashforge/api/constants/__init__.py,sha256=Q0HL2tqSBYPd4Oz49VHLS3qUvRuv__GCvTGecaLrQ-Y,163
|
|
@@ -7,7 +7,7 @@ flashforge/api/constants/endpoints.py,sha256=zU_ONPyLUATr1KPlk0x8SLrK4ojs4QiiWr1
|
|
|
7
7
|
flashforge/api/controls/__init__.py,sha256=53s-H25Pjwr0kvPk8MZ2Twy8VHGqGcO6Q6uBphhEOh4,293
|
|
8
8
|
flashforge/api/controls/control.py,sha256=8wK2YOGf9NphWXsjIDapyQEfx-qGZgytXZFsjKeWvSA,11291
|
|
9
9
|
flashforge/api/controls/files.py,sha256=8T9kzgss_2DYFL4WVuEFj8W9sOPPPMIMq3dTq1d551w,6560
|
|
10
|
-
flashforge/api/controls/info.py,sha256=
|
|
10
|
+
flashforge/api/controls/info.py,sha256=CesJXAQZkmF_o9dNKeZmeu_44ysDK4Lj8uHRwAVcv4w,13086
|
|
11
11
|
flashforge/api/controls/job_control.py,sha256=-DglR97DWBzl45mmqGkauYpUM_I-C0tXrbp9SKPYOVs,21814
|
|
12
12
|
flashforge/api/controls/temp_control.py,sha256=TGkX63WHygVA9DeYfLARbktZ0o-bYX6MNyYOX6V_RB4,3034
|
|
13
13
|
flashforge/api/filament/__init__.py,sha256=isT2dl0hzUS0xMTXMm4Tip0GTG1gCsm8LKWa9xPu4Y8,104
|
|
@@ -19,9 +19,9 @@ flashforge/api/network/__init__.py,sha256=G5fBoAlq-NQPDkWp579mFIqsj0v7B1Lb2TAzO6
|
|
|
19
19
|
flashforge/api/network/fnet_code.py,sha256=BR2niVKKk4ZfXtizUDIMXzDhlCxPHzqWg5AQQIcoj3g,402
|
|
20
20
|
flashforge/api/network/utils.py,sha256=hh-9SGI61ii0CRkLVnAp-Eq2E0a9PAKPyNLMGaYPNsI,2065
|
|
21
21
|
flashforge/discovery/__init__.py,sha256=G0WiP70EhfHdjXR1RNlv6xjhyHOdo-HWyOm9J0ds4SM,828
|
|
22
|
-
flashforge/discovery/discovery.py,sha256=
|
|
22
|
+
flashforge/discovery/discovery.py,sha256=ka75bIXnn2a14DEpzpmJxUGajF-tpLHUR2EHA88NIGc,26073
|
|
23
23
|
flashforge/models/__init__.py,sha256=MNpnXdS6Yah5kSwBB7cbA2Hd-8azffE62pgUrrKSZk4,984
|
|
24
|
-
flashforge/models/machine_info.py,sha256=
|
|
24
|
+
flashforge/models/machine_info.py,sha256=HDOzXi9RaYgFPkqtYe8dLyM5c8ZUCFfZ_uefGIj1VYM,15003
|
|
25
25
|
flashforge/models/responses.py,sha256=befMeXAc7XXiK7emF8Ep2cd1GuhZ-zyYPbbPqS2lcQI,7214
|
|
26
26
|
flashforge/tcp/__init__.py,sha256=hpnqoWHeRtTwJPzdsVlwGt1njKbAkrgIHKADkLSIRec,1317
|
|
27
27
|
flashforge/tcp/a3_client.py,sha256=a1jdDcBnTSNrzJFKTuVV13fSLbZmxKEZ94n3cGf3wOA,15603
|
|
@@ -39,8 +39,8 @@ flashforge/tcp/parsers/print_status.py,sha256=4q9ZlJfO0c7_t1o15tgxbguMxpDjEW9pMy
|
|
|
39
39
|
flashforge/tcp/parsers/printer_info.py,sha256=QY6LECfcOhVHSIdsvQZwJago0OjXroUwiX0H-uQaH2A,5411
|
|
40
40
|
flashforge/tcp/parsers/temp_info.py,sha256=9Waf68tt-4QBcChyUwfMHDwWdIzM4mmDpZAKeAzklgs,7908
|
|
41
41
|
flashforge/tcp/parsers/thumbnail_info.py,sha256=ZmxdEbVFOzqR1AfhXXZyjENVI66BdRdE7Q8LQ8an9FY,10201
|
|
42
|
-
flashforge_python_api-1.2.
|
|
43
|
-
flashforge_python_api-1.2.
|
|
44
|
-
flashforge_python_api-1.2.
|
|
45
|
-
flashforge_python_api-1.2.
|
|
46
|
-
flashforge_python_api-1.2.
|
|
42
|
+
flashforge_python_api-1.2.3.dist-info/METADATA,sha256=8Gji1l3nedh4E53ZM5AE7wkP0JoPBGOucAoBIK_yEO4,4787
|
|
43
|
+
flashforge_python_api-1.2.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
44
|
+
flashforge_python_api-1.2.3.dist-info/entry_points.txt,sha256=AkOxlsLvQ7cvMLxn7tlzfKp_DCH2hXhbVceHIXxawpU,66
|
|
45
|
+
flashforge_python_api-1.2.3.dist-info/licenses/LICENSE,sha256=-cTA-hrmvlb3pqlQrBZQXUnKayhUjsLJMPb7TD91frM,1067
|
|
46
|
+
flashforge_python_api-1.2.3.dist-info/RECORD,,
|
|
File without changes
|
{flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{flashforge_python_api-1.2.1.dist-info → flashforge_python_api-1.2.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|