flashforge-python-api 1.0.0__tar.gz → 1.0.1__tar.gz
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_python_api-1.0.1/.github/workflows/publish.yml +141 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/.gitignore +3 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/CHANGELOG.md +5 -0
- flashforge_python_api-1.0.1/CLAUDE.md +25 -0
- flashforge_python_api-1.0.1/PKG-INFO +211 -0
- flashforge_python_api-1.0.1/README.md +162 -0
- flashforge_python_api-1.0.1/docs/README.md +87 -0
- flashforge_python_api-1.0.1/docs/advanced.md +93 -0
- flashforge_python_api-1.0.1/docs/api_reference.md +75 -0
- flashforge_python_api-1.0.1/docs/client.md +123 -0
- flashforge_python_api-1.0.1/docs/models.md +91 -0
- flashforge_python_api-1.0.1/docs/protocols.md +63 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/files.py +19 -3
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/client.py +1 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/models/machine_info.py +2 -2
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/pyproject.toml +1 -1
- flashforge_python_api-1.0.1/tests/fixtures/printer_responses.py +145 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/printer_config.py +22 -1
- flashforge_python_api-1.0.1/tests/test_5m_pro_live_integration.py +80 -0
- flashforge_python_api-1.0.1/tests/test_additional_parsers.py +87 -0
- flashforge_python_api-1.0.1/tests/test_client.py +182 -0
- flashforge_python_api-1.0.1/tests/test_control.py +128 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_discovery.py +116 -11
- flashforge_python_api-1.0.1/tests/test_files.py +217 -0
- flashforge_python_api-1.0.1/tests/test_info.py +102 -0
- flashforge_python_api-1.0.1/tests/test_job_control.py +199 -0
- flashforge_python_api-1.0.1/tests/test_tcp_client.py +156 -0
- flashforge_python_api-1.0.1/tests/test_temp_control.py +111 -0
- flashforge_python_api-1.0.0/PKG-INFO +0 -123
- flashforge_python_api-1.0.0/README.md +0 -74
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/.claude/commands/push.md +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/.claude/settings.local.json +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/LICENSE +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/ai_specs/TEST_COVERAGE_SPEC.md +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/examples/complete_feature_demo.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/examples/discovery_example.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/examples/tcp_client_example.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/examples/unified_client_example.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/constants/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/constants/commands.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/constants/endpoints.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/control.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/info.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/job_control.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/controls/temp_control.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/filament/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/filament/filament.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/misc/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/misc/scientific_notation.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/misc/temperature.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/network/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/network/fnet_code.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/api/network/utils.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/discovery/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/discovery/discovery.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/models/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/models/responses.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/ff_client.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/gcode/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/gcode/gcode_controller.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/gcode/gcodes.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/endstop_status.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/location_info.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/print_status.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/printer_info.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/temp_info.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/parsers/thumbnail_info.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/flashforge/tcp/tcp_client.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/__init__.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/conftest.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/run_tests.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_ad5x_job_control.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_ad5x_live_integration.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_ad5x_models.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_parsers.py +0 -0
- {flashforge_python_api-1.0.0 → flashforge_python_api-1.0.1}/tests/test_utility_classes.py +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
name: Publish to GitHub Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: 'Version to release (e.g., 1.0.1). Leave empty to auto-generate timestamped version (e.g. 1.0.0-2023...) if tag exists.'
|
|
8
|
+
required: false
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout repository
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.12'
|
|
28
|
+
|
|
29
|
+
- name: Install Hatch
|
|
30
|
+
run: |
|
|
31
|
+
pip install hatch
|
|
32
|
+
|
|
33
|
+
- name: Determine and Update Version
|
|
34
|
+
id: version
|
|
35
|
+
shell: python
|
|
36
|
+
env:
|
|
37
|
+
INPUT_VERSION: ${{ inputs.version }}
|
|
38
|
+
REF_NAME: ${{ github.ref_name }}
|
|
39
|
+
run: |
|
|
40
|
+
import os
|
|
41
|
+
import re
|
|
42
|
+
import subprocess
|
|
43
|
+
import sys
|
|
44
|
+
from datetime import datetime
|
|
45
|
+
|
|
46
|
+
def get_current_version():
|
|
47
|
+
with open("pyproject.toml", "r") as f:
|
|
48
|
+
content = f.read()
|
|
49
|
+
match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
|
|
50
|
+
if match:
|
|
51
|
+
return match.group(1)
|
|
52
|
+
sys.exit("Could not find version in pyproject.toml")
|
|
53
|
+
|
|
54
|
+
def update_pyproject(new_version):
|
|
55
|
+
with open("pyproject.toml", "r") as f:
|
|
56
|
+
content = f.read()
|
|
57
|
+
|
|
58
|
+
new_content = re.sub(r'version\s*=\s*["\'][^"\']+["\']', f'version = "{new_version}"', content, count=1)
|
|
59
|
+
|
|
60
|
+
with open("pyproject.toml", "w") as f:
|
|
61
|
+
f.write(new_content)
|
|
62
|
+
print(f"Updated pyproject.toml to version {new_version}")
|
|
63
|
+
|
|
64
|
+
def tag_exists(tag):
|
|
65
|
+
try:
|
|
66
|
+
subprocess.check_output(["git", "rev-parse", tag], stderr=subprocess.STDOUT)
|
|
67
|
+
return True
|
|
68
|
+
except subprocess.CalledProcessError:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
input_version = os.environ.get("INPUT_VERSION")
|
|
72
|
+
ref_name = os.environ.get("REF_NAME")
|
|
73
|
+
current_version = get_current_version()
|
|
74
|
+
print(f"Current version in pyproject.toml: {current_version}")
|
|
75
|
+
|
|
76
|
+
new_version = current_version
|
|
77
|
+
should_commit = False
|
|
78
|
+
should_push_commit = False
|
|
79
|
+
|
|
80
|
+
if input_version:
|
|
81
|
+
# Manual version input
|
|
82
|
+
if input_version != current_version:
|
|
83
|
+
new_version = input_version
|
|
84
|
+
should_commit = True
|
|
85
|
+
should_push_commit = True
|
|
86
|
+
print(f"Using input version: {new_version} (Will update, commit, and push)")
|
|
87
|
+
else:
|
|
88
|
+
print("Input version matches current version. (No file change needed)")
|
|
89
|
+
else:
|
|
90
|
+
# No input - Check for existing tag
|
|
91
|
+
tag = f"v{current_version}"
|
|
92
|
+
if tag_exists(tag):
|
|
93
|
+
# Tag exists, generate timestamp version
|
|
94
|
+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
95
|
+
new_version = f"{current_version}-{timestamp}"
|
|
96
|
+
should_commit = True
|
|
97
|
+
should_push_commit = False # Do not push ephemeral version to main
|
|
98
|
+
print(f"Tag {tag} exists. Generated timestamp version: {new_version} (Will commit locally but NOT push to branch)")
|
|
99
|
+
else:
|
|
100
|
+
print(f"Tag {tag} does not exist. Using current version: {current_version} (First release of this version)")
|
|
101
|
+
|
|
102
|
+
# Configure git
|
|
103
|
+
subprocess.run(["git", "config", "user.name", "github-actions[bot]"], check=True)
|
|
104
|
+
subprocess.run(["git", "config", "user.email", "github-actions[bot]@users.noreply.github.com"], check=True)
|
|
105
|
+
|
|
106
|
+
if should_commit:
|
|
107
|
+
update_pyproject(new_version)
|
|
108
|
+
subprocess.run(["git", "add", "pyproject.toml"], check=True)
|
|
109
|
+
subprocess.run(["git", "commit", "-m", f"Release {new_version}"], check=True)
|
|
110
|
+
|
|
111
|
+
if should_push_commit:
|
|
112
|
+
# Pull changes to avoid conflicts before pushing
|
|
113
|
+
subprocess.run(["git", "fetch", "origin", ref_name], check=True)
|
|
114
|
+
subprocess.run(["git", "pull", "--rebase", "origin", ref_name], check=True)
|
|
115
|
+
# Push to specific branch
|
|
116
|
+
subprocess.run(["git", "push", "origin", f"HEAD:{ref_name}"], check=True)
|
|
117
|
+
else:
|
|
118
|
+
print("Skipping push of commit (Ephemeral/Timestamp release).")
|
|
119
|
+
|
|
120
|
+
# Create tag if it doesn't exist
|
|
121
|
+
tag_name = f"v{new_version}"
|
|
122
|
+
if not tag_exists(tag_name):
|
|
123
|
+
print(f"Creating tag {tag_name}")
|
|
124
|
+
subprocess.run(["git", "tag", tag_name], check=True)
|
|
125
|
+
subprocess.run(["git", "push", "origin", tag_name], check=True)
|
|
126
|
+
else:
|
|
127
|
+
print(f"Tag {tag_name} already exists.")
|
|
128
|
+
|
|
129
|
+
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh:
|
|
130
|
+
fh.write(f"version={new_version}\n")
|
|
131
|
+
|
|
132
|
+
- name: Build
|
|
133
|
+
run: hatch build
|
|
134
|
+
|
|
135
|
+
- name: Create Release
|
|
136
|
+
uses: softprops/action-gh-release@v2
|
|
137
|
+
with:
|
|
138
|
+
tag_name: v${{ steps.version.outputs.version }}
|
|
139
|
+
name: Release ${{ steps.version.outputs.version }}
|
|
140
|
+
files: dist/*
|
|
141
|
+
generate_release_notes: true
|
|
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.1] - 2025-12-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Fixed Pydantic validation error for `estimated_time` field in `FFPrinterDetail` and `FFMachineInfo` models. Changed type from `int` to `float` to handle printer API responses that return fractional time values.
|
|
12
|
+
|
|
8
13
|
## [1.0.0] - 2025-01-02
|
|
9
14
|
|
|
10
15
|
### Added
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Claude Development Notes
|
|
2
|
+
|
|
3
|
+
## PyPI Publishing
|
|
4
|
+
|
|
5
|
+
### Configuration Files
|
|
6
|
+
- `.pypirc` - Contains PyPI authentication token for publishing packages
|
|
7
|
+
- Location: Project root directory
|
|
8
|
+
- Status: Git-ignored (not tracked in version control)
|
|
9
|
+
- Contains username and API token for automated PyPI uploads
|
|
10
|
+
- Note: This file exists locally but won't be visible in git status
|
|
11
|
+
|
|
12
|
+
### Publishing to PyPI
|
|
13
|
+
To publish a new version:
|
|
14
|
+
|
|
15
|
+
1. Update version in `pyproject.toml`
|
|
16
|
+
2. Clean previous builds: `rm -rf dist/ build/ *.egg-info`
|
|
17
|
+
3. Build: `python -m build`
|
|
18
|
+
4. Upload: `python -m twine upload dist/*`
|
|
19
|
+
- Credentials will be read automatically from `.pypirc`
|
|
20
|
+
|
|
21
|
+
### Package Info
|
|
22
|
+
- Package name: `flashforge-python-api`
|
|
23
|
+
- PyPI URL: https://pypi.org/project/flashforge-python-api/
|
|
24
|
+
- Build system: Hatchling
|
|
25
|
+
- Current version: 1.0.0
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flashforge-python-api
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: A comprehensive Python library for controlling FlashForge 3D printers
|
|
5
|
+
Project-URL: Homepage, https://github.com/GhostTypes/ff-5mp-api-py
|
|
6
|
+
Project-URL: Documentation, https://github.com/GhostTypes/ff-5mp-api-py#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/GhostTypes/ff-5mp-api-py.git
|
|
8
|
+
Project-URL: Issues, https://github.com/GhostTypes/ff-5mp-api-py/issues
|
|
9
|
+
Author-email: GhostTypes <notghosttypes@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: 3d-printer,api,async,control,flashforge,python
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
26
|
+
Requires-Dist: netifaces>=0.11.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: requests>=2.31.0
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: black>=23.0.0; extra == 'all'
|
|
31
|
+
Requires-Dist: mypy>=1.0.0; extra == 'all'
|
|
32
|
+
Requires-Dist: pillow>=10.0.0; extra == 'all'
|
|
33
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'all'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
37
|
+
Requires-Dist: ruff>=0.1.0; extra == 'all'
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
46
|
+
Provides-Extra: imaging
|
|
47
|
+
Requires-Dist: pillow>=10.0.0; extra == 'imaging'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
<div align="center">
|
|
51
|
+
|
|
52
|
+
# FlashForge Python API
|
|
53
|
+
|
|
54
|
+
A comprehensive Python library for controlling FlashForge 3D printers.
|
|
55
|
+
|
|
56
|
+
[](https://pypi.org/project/flashforge-python-api/)
|
|
57
|
+
[](https://www.python.org/)
|
|
58
|
+
[](LICENSE)
|
|
59
|
+
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div align="center">
|
|
63
|
+
|
|
64
|
+
## Features & Capabilities
|
|
65
|
+
|
|
66
|
+
| Feature | Description |
|
|
67
|
+
| :--- | :--- |
|
|
68
|
+
| **Printer Discovery** | Automatic UDP broadcast discovery of printers on the network |
|
|
69
|
+
| **Full Control** | Movement (G1), Homing (G28), Temperature (M104/M140), Fans, LED |
|
|
70
|
+
| **Real-time Monitoring** | Live status (M119), Temperatures (M105), Print Progress (M27) |
|
|
71
|
+
| **Job Management** | Start, Pause, Resume, Cancel, File Upload & Listing |
|
|
72
|
+
| **Advanced Parsing** | Thumbnail extraction (M662), Endstop monitoring, Machine state |
|
|
73
|
+
| **Dual Protocol** | Modern HTTP API + Legacy TCP G-code support |
|
|
74
|
+
| **Async Support** | Native async/await implementation for all operations |
|
|
75
|
+
| **Type Safety** | Full type hints and Pydantic models for robust development |
|
|
76
|
+
|
|
77
|
+
<br>
|
|
78
|
+
|
|
79
|
+
## Supported Hardware
|
|
80
|
+
|
|
81
|
+
| Model | Support Level | Connection Type |
|
|
82
|
+
| :--- | :--- | :--- |
|
|
83
|
+
| **FlashForge Adventurer 5M / 5M Pro** | Full Support | HTTP + TCP |
|
|
84
|
+
| **FlashForge Adventurer 5X** | Full Support | HTTP + TCP |
|
|
85
|
+
| **FlashForge Adventurer 3 / 4** | Partial Support | TCP (Legacy) |
|
|
86
|
+
| **Other Networked FlashForge Printers** | Experimental | TCP (Generic) |
|
|
87
|
+
|
|
88
|
+
<br>
|
|
89
|
+
|
|
90
|
+
## Compatible Slicers
|
|
91
|
+
|
|
92
|
+
| Slicer | Compatibility | Notes |
|
|
93
|
+
| :--- | :--- | :--- |
|
|
94
|
+
| **OrcaSlicer** | High | Recommended for Adventurer 5M series |
|
|
95
|
+
| **FlashPrint** | Full | Official FlashForge slicer |
|
|
96
|
+
| **Orca-FlashForge** | High | Optimized for FlashForge printers |
|
|
97
|
+
| **Cura / PrusaSlicer** | Basic | Requires correct G-code flavor |
|
|
98
|
+
|
|
99
|
+
<br>
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
| Command |
|
|
104
|
+
| :--- |
|
|
105
|
+
| `pip install flashforge-python-api` |
|
|
106
|
+
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div align="center">
|
|
110
|
+
<h2>Usage Examples</h2>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div align="center">
|
|
114
|
+
<h3>Printer Discovery</h3>
|
|
115
|
+
Discover printers on your local network automatically.
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from flashforge import FlashForgePrinterDiscovery
|
|
120
|
+
import asyncio
|
|
121
|
+
|
|
122
|
+
async def discover():
|
|
123
|
+
discovery = FlashForgePrinterDiscovery()
|
|
124
|
+
printers = await discovery.discover_printers_async()
|
|
125
|
+
for printer in printers:
|
|
126
|
+
print(f"Found: {printer.name} at {printer.ip_address}")
|
|
127
|
+
|
|
128
|
+
asyncio.run(discover())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
<div align="center">
|
|
132
|
+
<h3>Basic Control</h3>
|
|
133
|
+
Connect to a printer and perform basic operations like setting temperature and homing.
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from flashforge import FlashForgeClient
|
|
138
|
+
import asyncio
|
|
139
|
+
|
|
140
|
+
async def control_printer():
|
|
141
|
+
# Initialize client with printer details
|
|
142
|
+
client = FlashForgeClient("192.168.1.100", "SERIAL_NUMBER", "CHECK_CODE")
|
|
143
|
+
|
|
144
|
+
if await client.initialize():
|
|
145
|
+
print(f"Connected to {client.printer_name}")
|
|
146
|
+
|
|
147
|
+
# Set bed temperature to 60°C
|
|
148
|
+
await client.temp_control.set_bed_temp(60)
|
|
149
|
+
|
|
150
|
+
# Home all axes
|
|
151
|
+
await client.control.home_xyz()
|
|
152
|
+
|
|
153
|
+
await client.dispose()
|
|
154
|
+
|
|
155
|
+
asyncio.run(control_printer())
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
<div align="center">
|
|
159
|
+
<h3>Real-time Status Monitoring</h3>
|
|
160
|
+
Monitor printer status, temperatures, and print progress.
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from flashforge import FlashForgeClient
|
|
165
|
+
import asyncio
|
|
166
|
+
|
|
167
|
+
async def monitor_printer():
|
|
168
|
+
async with FlashForgeClient("192.168.1.100", "SERIAL", "CODE") as client:
|
|
169
|
+
# Get comprehensive status
|
|
170
|
+
status = await client.get_printer_status()
|
|
171
|
+
print(f"Machine State: {status.machine_state}")
|
|
172
|
+
|
|
173
|
+
# Get temperatures via TCP
|
|
174
|
+
temps = await client.tcp_client.get_temp_info()
|
|
175
|
+
if temps:
|
|
176
|
+
bed = temps.get_bed_temp()
|
|
177
|
+
extruder = temps.get_extruder_temp()
|
|
178
|
+
print(f"Bed: {bed.get_current()}°C / {bed.get_target()}°C")
|
|
179
|
+
print(f"Extruder: {extruder.get_current()}°C / {extruder.get_target()}°C")
|
|
180
|
+
|
|
181
|
+
# Check print progress
|
|
182
|
+
layer_p, sd_p, current_layer = await client.tcp_client.get_print_progress()
|
|
183
|
+
print(f"Progress: {layer_p}% (Layer {current_layer})")
|
|
184
|
+
|
|
185
|
+
asyncio.run(monitor_printer())
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
<div align="center">
|
|
189
|
+
<h3>File Operations & Thumbnails</h3>
|
|
190
|
+
List files on the printer and extract thumbnails.
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from flashforge import FlashForgeClient
|
|
195
|
+
import asyncio
|
|
196
|
+
|
|
197
|
+
async def file_ops():
|
|
198
|
+
async with FlashForgeClient("192.168.1.100", "SERIAL", "CODE") as client:
|
|
199
|
+
# List files
|
|
200
|
+
files = await client.files.get_file_list()
|
|
201
|
+
for filename in files:
|
|
202
|
+
print(f"File: {filename}")
|
|
203
|
+
|
|
204
|
+
# Get thumbnail
|
|
205
|
+
thumb = await client.tcp_client.get_thumbnail(filename)
|
|
206
|
+
if thumb and thumb.has_image_data():
|
|
207
|
+
print(f"Thumbnail found: {len(thumb.get_image_bytes())} bytes")
|
|
208
|
+
# thumb.save_to_file_sync(f"{filename}.png")
|
|
209
|
+
|
|
210
|
+
asyncio.run(file_ops())
|
|
211
|
+
```
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# FlashForge Python API
|
|
4
|
+
|
|
5
|
+
A comprehensive Python library for controlling FlashForge 3D printers.
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/flashforge-python-api/)
|
|
8
|
+
[](https://www.python.org/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
15
|
+
## Features & Capabilities
|
|
16
|
+
|
|
17
|
+
| Feature | Description |
|
|
18
|
+
| :--- | :--- |
|
|
19
|
+
| **Printer Discovery** | Automatic UDP broadcast discovery of printers on the network |
|
|
20
|
+
| **Full Control** | Movement (G1), Homing (G28), Temperature (M104/M140), Fans, LED |
|
|
21
|
+
| **Real-time Monitoring** | Live status (M119), Temperatures (M105), Print Progress (M27) |
|
|
22
|
+
| **Job Management** | Start, Pause, Resume, Cancel, File Upload & Listing |
|
|
23
|
+
| **Advanced Parsing** | Thumbnail extraction (M662), Endstop monitoring, Machine state |
|
|
24
|
+
| **Dual Protocol** | Modern HTTP API + Legacy TCP G-code support |
|
|
25
|
+
| **Async Support** | Native async/await implementation for all operations |
|
|
26
|
+
| **Type Safety** | Full type hints and Pydantic models for robust development |
|
|
27
|
+
|
|
28
|
+
<br>
|
|
29
|
+
|
|
30
|
+
## Supported Hardware
|
|
31
|
+
|
|
32
|
+
| Model | Support Level | Connection Type |
|
|
33
|
+
| :--- | :--- | :--- |
|
|
34
|
+
| **FlashForge Adventurer 5M / 5M Pro** | Full Support | HTTP + TCP |
|
|
35
|
+
| **FlashForge Adventurer 5X** | Full Support | HTTP + TCP |
|
|
36
|
+
| **FlashForge Adventurer 3 / 4** | Partial Support | TCP (Legacy) |
|
|
37
|
+
| **Other Networked FlashForge Printers** | Experimental | TCP (Generic) |
|
|
38
|
+
|
|
39
|
+
<br>
|
|
40
|
+
|
|
41
|
+
## Compatible Slicers
|
|
42
|
+
|
|
43
|
+
| Slicer | Compatibility | Notes |
|
|
44
|
+
| :--- | :--- | :--- |
|
|
45
|
+
| **OrcaSlicer** | High | Recommended for Adventurer 5M series |
|
|
46
|
+
| **FlashPrint** | Full | Official FlashForge slicer |
|
|
47
|
+
| **Orca-FlashForge** | High | Optimized for FlashForge printers |
|
|
48
|
+
| **Cura / PrusaSlicer** | Basic | Requires correct G-code flavor |
|
|
49
|
+
|
|
50
|
+
<br>
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
| Command |
|
|
55
|
+
| :--- |
|
|
56
|
+
| `pip install flashforge-python-api` |
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div align="center">
|
|
61
|
+
<h2>Usage Examples</h2>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div align="center">
|
|
65
|
+
<h3>Printer Discovery</h3>
|
|
66
|
+
Discover printers on your local network automatically.
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from flashforge import FlashForgePrinterDiscovery
|
|
71
|
+
import asyncio
|
|
72
|
+
|
|
73
|
+
async def discover():
|
|
74
|
+
discovery = FlashForgePrinterDiscovery()
|
|
75
|
+
printers = await discovery.discover_printers_async()
|
|
76
|
+
for printer in printers:
|
|
77
|
+
print(f"Found: {printer.name} at {printer.ip_address}")
|
|
78
|
+
|
|
79
|
+
asyncio.run(discover())
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
<div align="center">
|
|
83
|
+
<h3>Basic Control</h3>
|
|
84
|
+
Connect to a printer and perform basic operations like setting temperature and homing.
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from flashforge import FlashForgeClient
|
|
89
|
+
import asyncio
|
|
90
|
+
|
|
91
|
+
async def control_printer():
|
|
92
|
+
# Initialize client with printer details
|
|
93
|
+
client = FlashForgeClient("192.168.1.100", "SERIAL_NUMBER", "CHECK_CODE")
|
|
94
|
+
|
|
95
|
+
if await client.initialize():
|
|
96
|
+
print(f"Connected to {client.printer_name}")
|
|
97
|
+
|
|
98
|
+
# Set bed temperature to 60°C
|
|
99
|
+
await client.temp_control.set_bed_temp(60)
|
|
100
|
+
|
|
101
|
+
# Home all axes
|
|
102
|
+
await client.control.home_xyz()
|
|
103
|
+
|
|
104
|
+
await client.dispose()
|
|
105
|
+
|
|
106
|
+
asyncio.run(control_printer())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
<div align="center">
|
|
110
|
+
<h3>Real-time Status Monitoring</h3>
|
|
111
|
+
Monitor printer status, temperatures, and print progress.
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from flashforge import FlashForgeClient
|
|
116
|
+
import asyncio
|
|
117
|
+
|
|
118
|
+
async def monitor_printer():
|
|
119
|
+
async with FlashForgeClient("192.168.1.100", "SERIAL", "CODE") as client:
|
|
120
|
+
# Get comprehensive status
|
|
121
|
+
status = await client.get_printer_status()
|
|
122
|
+
print(f"Machine State: {status.machine_state}")
|
|
123
|
+
|
|
124
|
+
# Get temperatures via TCP
|
|
125
|
+
temps = await client.tcp_client.get_temp_info()
|
|
126
|
+
if temps:
|
|
127
|
+
bed = temps.get_bed_temp()
|
|
128
|
+
extruder = temps.get_extruder_temp()
|
|
129
|
+
print(f"Bed: {bed.get_current()}°C / {bed.get_target()}°C")
|
|
130
|
+
print(f"Extruder: {extruder.get_current()}°C / {extruder.get_target()}°C")
|
|
131
|
+
|
|
132
|
+
# Check print progress
|
|
133
|
+
layer_p, sd_p, current_layer = await client.tcp_client.get_print_progress()
|
|
134
|
+
print(f"Progress: {layer_p}% (Layer {current_layer})")
|
|
135
|
+
|
|
136
|
+
asyncio.run(monitor_printer())
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
<div align="center">
|
|
140
|
+
<h3>File Operations & Thumbnails</h3>
|
|
141
|
+
List files on the printer and extract thumbnails.
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from flashforge import FlashForgeClient
|
|
146
|
+
import asyncio
|
|
147
|
+
|
|
148
|
+
async def file_ops():
|
|
149
|
+
async with FlashForgeClient("192.168.1.100", "SERIAL", "CODE") as client:
|
|
150
|
+
# List files
|
|
151
|
+
files = await client.files.get_file_list()
|
|
152
|
+
for filename in files:
|
|
153
|
+
print(f"File: {filename}")
|
|
154
|
+
|
|
155
|
+
# Get thumbnail
|
|
156
|
+
thumb = await client.tcp_client.get_thumbnail(filename)
|
|
157
|
+
if thumb and thumb.has_image_data():
|
|
158
|
+
print(f"Thumbnail found: {len(thumb.get_image_bytes())} bytes")
|
|
159
|
+
# thumb.save_to_file_sync(f"{filename}.png")
|
|
160
|
+
|
|
161
|
+
asyncio.run(file_ops())
|
|
162
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# FlashForge API Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the documentation for the FlashForge Python API. This library provides a comprehensive interface for controlling and monitoring FlashForge 3D printers, including the Adventurer 5M and 5M Pro series.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The FlashForge API allows you to interact with your printer using both TCP and HTTP protocols. It provides a unified client that handles connection management, command execution, and status monitoring.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Unified Client**: A single entry point (`FlashForgeClient`) for all printer operations.
|
|
12
|
+
- **Automatic Discovery**: Find printers on your local network automatically.
|
|
13
|
+
- **Full Control**: Manage print jobs, movement, temperature, fans, LEDs, and more.
|
|
14
|
+
- **Real-time Monitoring**: Get detailed status updates, including temperatures, progress, and camera feed URL.
|
|
15
|
+
- **File Management**: List, upload, and delete files on the printer.
|
|
16
|
+
- **Type Safety**: Fully typed with Pydantic models for robust data handling.
|
|
17
|
+
- **Async Support**: Built on `asyncio` for efficient non-blocking operations.
|
|
18
|
+
|
|
19
|
+
## Navigation
|
|
20
|
+
|
|
21
|
+
- [Getting Started](#getting-started)
|
|
22
|
+
- [Client API](client.md): Documentation for the main `FlashForgeClient` and `FlashForgePrinterDiscovery`.
|
|
23
|
+
- [Data Models](models.md): Detailed reference for all data objects (MachineInfo, Temperature, etc.).
|
|
24
|
+
- [Protocols](protocols.md): Understanding the underlying TCP and HTTP protocols.
|
|
25
|
+
- [Advanced Topics](advanced.md): Async patterns, error handling, and more.
|
|
26
|
+
- [API Reference](api_reference.md): Complete list of classes and methods.
|
|
27
|
+
|
|
28
|
+
## Getting Started
|
|
29
|
+
|
|
30
|
+
### Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install flashforge-api
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Basic Usage
|
|
37
|
+
|
|
38
|
+
Here's a simple example of how to discover a printer and connect to it:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import asyncio
|
|
42
|
+
from flashforge import FlashForgePrinterDiscovery, FlashForgeClient
|
|
43
|
+
|
|
44
|
+
async def main():
|
|
45
|
+
# 1. Discover printers
|
|
46
|
+
discovery = FlashForgePrinterDiscovery()
|
|
47
|
+
printers = await discovery.discover_printers_async()
|
|
48
|
+
|
|
49
|
+
if not printers:
|
|
50
|
+
print("No printers found.")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
printer_info = printers[0]
|
|
54
|
+
print(f"Found printer: {printer_info.name} at {printer_info.ip_address}")
|
|
55
|
+
|
|
56
|
+
# 2. Connect to the printer
|
|
57
|
+
# Note: 'check_code' is currently unused by some printers but required by the constructor
|
|
58
|
+
client = FlashForgeClient(printer_info.ip_address, printer_info.serial_number, check_code="")
|
|
59
|
+
|
|
60
|
+
await client.initialize()
|
|
61
|
+
|
|
62
|
+
# 3. Get printer status
|
|
63
|
+
status = await client.get_printer_status()
|
|
64
|
+
print(f"Status: {status.machine_state}")
|
|
65
|
+
print(f"Bed Temp: {status.print_bed.current}°C")
|
|
66
|
+
print(f"Nozzle Temp: {status.extruder.current}°C")
|
|
67
|
+
|
|
68
|
+
# 4. Clean up
|
|
69
|
+
await client.dispose()
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
asyncio.run(main())
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Context Manager Support
|
|
76
|
+
|
|
77
|
+
The client supports Python's async context manager protocol for automatic resource management:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
async with FlashForgeClient(ip, serial, check_code="") as client:
|
|
81
|
+
status = await client.get_printer_status()
|
|
82
|
+
print(f"Ready: {status.machine_state == 'ready'}")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Support
|
|
86
|
+
|
|
87
|
+
If you encounter any issues or have questions, please check the [Issues](https://github.com/yourusername/flashforge-api/issues) page on GitHub.
|