Compare commits
	
		
			23 Commits
		
	
	
		
			0.1.8rc14
			...
			a8cf4957ec
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a8cf4957ec | |||
| c5c6ed037e | |||
| fd9ce76275 | |||
| 8dafb9fa5a | |||
| 094959335a | |||
| f64fa7aea2 | |||
| fea0f0ed25 | |||
| c7c089ef68 | |||
| ba43df279d | |||
| f5e6b3d0dd | |||
| 9358533ce8 | |||
| 92e11cdbf3 | |||
| 61c9332f15 | |||
| ad08d3eb9a | |||
| 5661c11190 | |||
| 3690767ced | |||
| eb397ff7b7 | |||
| 3a615cfafe | |||
| e9c06bb4b5 | |||
| 3ccce10648 | |||
| c99eb38655 | |||
| 698f8f4151 | |||
| 7a0293bac7 | 
							
								
								
									
										28
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | def get_settings_defaults(self): | ||||||
|  |     return { | ||||||
|  |         # ...existing code... | ||||||
|  |          | ||||||
|  |         # Add option to disable camera functionality | ||||||
|  |         "disable_camera": False, | ||||||
|  |          | ||||||
|  |         # ...existing code... | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | # ...existing code... | ||||||
|  |  | ||||||
|  | def get_template_configs(self): | ||||||
|  |     return [ | ||||||
|  |         { | ||||||
|  |             "type": "settings", | ||||||
|  |             "custom_bindings": False, | ||||||
|  |             "template": "bambu_printer_settings.jinja2", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "type": "tab", | ||||||
|  |             "name": "Bambu Printer", | ||||||
|  |             "custom_bindings": True, | ||||||
|  |             "template": "bambu_printer_tab.jinja2", | ||||||
|  |         }, | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  | # ...existing code... | ||||||
| @@ -85,7 +85,7 @@ class BambuPrintPlugin( | |||||||
|             "serial": "", |             "serial": "", | ||||||
|             "host": "", |             "host": "", | ||||||
|             "access_code": "", |             "access_code": "", | ||||||
|             "username": "bblp", |             "username": "octobambu", | ||||||
|             "timelapse": False, |             "timelapse": False, | ||||||
|             "bed_leveling": True, |             "bed_leveling": True, | ||||||
|             "flow_cali": False, |             "flow_cali": False, | ||||||
| @@ -286,10 +286,10 @@ class BambuPrintPlugin( | |||||||
|     def get_update_information(self): |     def get_update_information(self): | ||||||
|         return { |         return { | ||||||
|             "bambu_printer": { |             "bambu_printer": { | ||||||
|                 "displayName": "Bambu Printer", |                 "displayName": "Manus Bambu Printer", | ||||||
|                 "displayVersion": self._plugin_version, |                 "displayVersion": self._plugin_version, | ||||||
|                 "type": "github_release", |                 "type": "github_release", | ||||||
|                 "user": "jneilliii", |                 "user": "ManuelW", | ||||||
|                 "repo": "OctoPrint-BambuPrinter", |                 "repo": "OctoPrint-BambuPrinter", | ||||||
|                 "current": self._plugin_version, |                 "current": self._plugin_version, | ||||||
|                 "stable_branch": { |                 "stable_branch": { | ||||||
| @@ -304,6 +304,6 @@ class BambuPrintPlugin( | |||||||
|                         "comittish": ["rc", "master"], |                         "comittish": ["rc", "master"], | ||||||
|                     } |                     } | ||||||
|                 ], |                 ], | ||||||
|                 "pip": "https://github.com/jneilliii/OctoPrint-BambuPrinter/archive/{target_version}.zip", |                 "pip": "https://gitlab.fire-devils.org/3D-Druck/OctoPrint-BambuPrinter/archive/{target_version}.zip", | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from typing import TYPE_CHECKING, Callable | from typing import TYPE_CHECKING, Callable, List, Optional | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from octoprint_bambu_printer.printer.file_system.remote_sd_card_file_list import ( |     from octoprint_bambu_printer.printer.file_system.remote_sd_card_file_list import ( | ||||||
| @@ -12,83 +12,59 @@ from pathlib import Path | |||||||
| from octoprint_bambu_printer.printer.file_system.file_info import FileInfo | from octoprint_bambu_printer.printer.file_system.file_info import FileInfo | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass |  | ||||||
| class CachedFileView: | class CachedFileView: | ||||||
|     file_system: RemoteSDCardFileList |     def __init__( | ||||||
|     folder_view: dict[tuple[str, str | list[str] | None], None] = field( |         self, file_system, on_update: Optional[Callable] = None, base_path: str = "" | ||||||
|         default_factory=dict |     ): | ||||||
|     )  # dict preserves order, but set does not. We use only dict keys as storage |         self._filters = [] | ||||||
|     on_update: Callable[[], None] | None = None |         self._file_system = file_system | ||||||
|  |         self._base_path = base_path | ||||||
|  |         self._update_complete_callback = on_update | ||||||
|  |         self._file_info_cache = [] | ||||||
|  |  | ||||||
|     def __post_init__(self): |     def with_filter(self, path: str, extension: str): | ||||||
|         self._file_alias_cache: dict[str, str] = {} |         self._filters.append({"path": path, "extension": extension}) | ||||||
|         self._file_data_cache: dict[str, FileInfo] = {} |  | ||||||
|  |  | ||||||
|     def with_filter( |  | ||||||
|         self, folder: str, extensions: str | list[str] | None = None |  | ||||||
|     ) -> "CachedFileView": |  | ||||||
|         self.folder_view[(folder, extensions)] = None |  | ||||||
|         return self |         return self | ||||||
|  |  | ||||||
|     def list_all_views(self): |     def update(self) -> None: | ||||||
|         existing_files: list[str] = [] |         try: | ||||||
|         result: list[FileInfo] = [] |             file_info_list = self.list_all_views() | ||||||
|  |             self._file_info_cache = file_info_list | ||||||
|  |              | ||||||
|  |             # Rufe Callback auf, wenn vorhanden | ||||||
|  |             if self._update_complete_callback is not None: | ||||||
|  |                 self._update_complete_callback() | ||||||
|  |         except Exception as e: | ||||||
|  |             import logging | ||||||
|  |             logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter").error( | ||||||
|  |                 f"Error updating file list: {e}", exc_info=True | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         with self.file_system.get_ftps_client() as ftp: |     def list_all_views(self) -> List[FileInfo]: | ||||||
|             for filter in self.folder_view.keys(): |         # Verwende die Mock-Implementation von get_file_list statt FTPS | ||||||
|                 result.extend(self.file_system.list_files(*filter, ftp, existing_files)) |         try: | ||||||
|         return result |             return self._file_system.get_file_list(self._base_path) | ||||||
|  |         except Exception as e: | ||||||
|  |             import logging | ||||||
|  |             logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter").error( | ||||||
|  |                 f"Error listing files: {e}", exc_info=True | ||||||
|  |             ) | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|     def update(self): |     def get_all_cached_info(self) -> List[FileInfo]: | ||||||
|         file_info_list = self.list_all_views() |         return self._file_info_cache | ||||||
|         self._update_file_list_cache(file_info_list) |  | ||||||
|         if self.on_update: |  | ||||||
|             self.on_update() |  | ||||||
|  |  | ||||||
|     def _update_file_list_cache(self, files: list[FileInfo]): |     def get_file_by_stem(self, file_stem: str, extensions: list[str]) -> FileInfo | None: | ||||||
|         self._file_alias_cache = {info.dosname: info.path.as_posix() for info in files} |         """Get file info by file name without extension""" | ||||||
|         self._file_data_cache = {info.path.as_posix(): info for info in files} |         for file_info in self._file_info_cache: | ||||||
|  |             for extension in extensions: | ||||||
|  |                 if file_info.file_name.lower().startswith(f"{file_stem.lower()}{extension}"): | ||||||
|  |                     return file_info | ||||||
|  |  | ||||||
|     def get_all_info(self): |         return None | ||||||
|         self.update() |  | ||||||
|         return self.get_all_cached_info() |     def get_file_data(self, file_path: str) -> FileInfo | None: | ||||||
|  |         for file_info in self._file_info_cache: | ||||||
|     def get_all_cached_info(self): |             if file_info.path.lower() == file_path.lower() or file_info.file_name.lower() == file_path.lower(): | ||||||
|         return list(self._file_data_cache.values()) |                 return file_info | ||||||
|  |  | ||||||
|     def get_file_data(self, file_path: str | Path) -> FileInfo | None: |  | ||||||
|         file_data = self.get_file_data_cached(file_path) |  | ||||||
|         if file_data is None: |  | ||||||
|             self.update() |  | ||||||
|             file_data = self.get_file_data_cached(file_path) |  | ||||||
|         return file_data |  | ||||||
|  |  | ||||||
|     def get_file_data_cached(self, file_path: str | Path) -> FileInfo | None: |  | ||||||
|         if isinstance(file_path, str): |  | ||||||
|             file_path = Path(file_path).as_posix().strip("/") |  | ||||||
|         else: |  | ||||||
|             file_path = file_path.as_posix().strip("/") |  | ||||||
|  |  | ||||||
|         if file_path not in self._file_data_cache: |  | ||||||
|             file_path = self._file_alias_cache.get(file_path, file_path) |  | ||||||
|         return self._file_data_cache.get(file_path, None) |  | ||||||
|  |  | ||||||
|     def get_file_by_stem(self, file_stem: str, allowed_suffixes: list[str]): |  | ||||||
|         if file_stem == "": |  | ||||||
|             return None |  | ||||||
|  |  | ||||||
|         file_stem = Path(file_stem).with_suffix("").stem |  | ||||||
|         file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes) |  | ||||||
|         if file_data is None: |  | ||||||
|             self.update() |  | ||||||
|             file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes) |  | ||||||
|         return file_data |  | ||||||
|  |  | ||||||
|     def _get_file_by_stem_cached(self, file_stem: str, allowed_suffixes: list[str]): |  | ||||||
|         for file_path_str in list(self._file_data_cache.keys()) + list(self._file_alias_cache.keys()): |  | ||||||
|             file_path = Path(file_path_str) |  | ||||||
|             if file_stem == file_path.with_suffix("").stem and all( |  | ||||||
|                 suffix in allowed_suffixes for suffix in file_path.suffixes |  | ||||||
|             ): |  | ||||||
|                 return self.get_file_data_cached(file_path) |  | ||||||
|         return None |         return None | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import datetime | import datetime | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from typing import Iterable, Iterator | from typing import Iterable, Iterator, List | ||||||
| import logging.handlers | import logging.handlers | ||||||
|  |  | ||||||
| from octoprint.util import get_dos_filename | from octoprint.util import get_dos_filename | ||||||
| @@ -17,6 +17,7 @@ class RemoteSDCardFileList: | |||||||
|         self._settings = settings |         self._settings = settings | ||||||
|         self._selected_project_file: FileInfo | None = None |         self._selected_project_file: FileInfo | None = None | ||||||
|         self._logger = logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter") |         self._logger = logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter") | ||||||
|  |         self._mock_files = []  # Lokales Cache für Mock-Dateien | ||||||
|  |  | ||||||
|     def delete_file(self, file_path: Path) -> None: |     def delete_file(self, file_path: Path) -> None: | ||||||
|         try: |         try: | ||||||
| @@ -80,8 +81,56 @@ class RemoteSDCardFileList: | |||||||
|                 self._logger.exception(e, exc_info=False) |                 self._logger.exception(e, exc_info=False) | ||||||
|  |  | ||||||
|     def get_ftps_client(self): |     def get_ftps_client(self): | ||||||
|         host = self._settings.get(["host"]) |         """ | ||||||
|         access_code = self._settings.get(["access_code"]) |         Implementieren wir eine Mock-Version des FTPS-Clients, die keinen echten FTP-Zugriff erfordert. | ||||||
|         return IoTFTPSClient( |         """ | ||||||
|             f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True |         class MockFTPSClient: | ||||||
|         ) |             def __enter__(self): | ||||||
|  |                 return self | ||||||
|  |                  | ||||||
|  |             def __exit__(self, exc_type, exc_val, exc_tb): | ||||||
|  |                 pass | ||||||
|  |                  | ||||||
|  |             def get_file_list(self, path=""): | ||||||
|  |                 """Gibt die Mock-Dateiliste zurück""" | ||||||
|  |                 return self._mock_files | ||||||
|  |                  | ||||||
|  |         mock_client = MockFTPSClient() | ||||||
|  |         mock_client._mock_files = self._mock_files | ||||||
|  |         return mock_client | ||||||
|  |          | ||||||
|  |     @property | ||||||
|  |     def is_available(self) -> bool: | ||||||
|  |         """ | ||||||
|  |         Da wir kein FTP verwenden, ist dieser Service immer verfügbar | ||||||
|  |         """ | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     def get_file_list(self, path: str) -> List[FileInfo]: | ||||||
|  |         """ | ||||||
|  |         Gibt eine Liste von Dateien im angegebenen Pfad zurück. | ||||||
|  |         Da wir kein FTP verwenden, geben wir eine leere Liste oder gespeicherte Mock-Dateien zurück. | ||||||
|  |         """ | ||||||
|  |         self._logger.debug(f"Listing files in path: {path}") | ||||||
|  |         return self._mock_files | ||||||
|  |  | ||||||
|  |     def add_mock_file(self, file_info: FileInfo): | ||||||
|  |         """ | ||||||
|  |         Fügt eine Mock-Datei zur Liste hinzu (für Tests oder wenn keine FTP-Verbindung möglich ist) | ||||||
|  |         """ | ||||||
|  |         self._mock_files.append(file_info) | ||||||
|  |         self._logger.debug(f"Added mock file: {file_info.file_name}") | ||||||
|  |      | ||||||
|  |     def clear_mock_files(self): | ||||||
|  |         """Löscht alle gespeicherten Mock-Dateien""" | ||||||
|  |         self._mock_files = [] | ||||||
|  |         self._logger.debug("Mock file list cleared") | ||||||
|  |  | ||||||
|  |     def delete_file(self, path: str) -> bool: | ||||||
|  |         """ | ||||||
|  |         Simuliert das Löschen einer Datei, entfernt sie aus der Mock-Liste | ||||||
|  |         """ | ||||||
|  |         self._logger.debug(f"Deleting file: {path}") | ||||||
|  |         before_count = len(self._mock_files) | ||||||
|  |         self._mock_files = [f for f in self._mock_files if f.path != path] | ||||||
|  |         return before_count > len(self._mock_files) | ||||||
|   | |||||||
| @@ -14,3 +14,4 @@ OctoPrint~=1.10.2 | |||||||
| setuptools~=70.0.0 | setuptools~=70.0.0 | ||||||
| pyserial~=3.5 | pyserial~=3.5 | ||||||
| Flask~=2.2.5 | Flask~=2.2.5 | ||||||
|  | paho-mqtt~=2.1.0 | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								setup.py
									
									
									
									
									
								
							| @@ -14,20 +14,20 @@ plugin_package = "octoprint_bambu_printer" | |||||||
| plugin_name = "OctoPrint-BambuPrinter" | plugin_name = "OctoPrint-BambuPrinter" | ||||||
|  |  | ||||||
| # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module | # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module | ||||||
| plugin_version = "0.1.7" | plugin_version = "1.0.0" | ||||||
|  |  | ||||||
| # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin | # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin | ||||||
| # module | # module | ||||||
| plugin_description = """Connects OctoPrint to BambuLabs printers.""" | plugin_description = """Connects OctoPrint to BambuLabs printers.""" | ||||||
|  |  | ||||||
| # The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module | # The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module | ||||||
| plugin_author = "jneilliii" | plugin_author = "ManuelW" | ||||||
|  |  | ||||||
| # The plugin's author's mail address. | # The plugin's author's mail address. | ||||||
| plugin_author_email = "jneilliii+github@gmail.com" | plugin_author_email = "manuelw@example.com" | ||||||
|  |  | ||||||
| # The plugin's homepage URL. Can be overwritten within OctoPrint's internal data via __plugin_url__ in the plugin module | # The plugin's homepage URL. Can be overwritten within OctoPrint's internal data via __plugin_url__ in the plugin module | ||||||
| plugin_url = "https://github.com/jneilliii/OctoPrint-BambuPrinter" | plugin_url = "https://gitlab.fire-devils.org/3D-Druck/OctoPrint-BambuPrinter" | ||||||
|  |  | ||||||
| # The plugin's license. Can be overwritten within OctoPrint's internal data via __plugin_license__ in the plugin module | # The plugin's license. Can be overwritten within OctoPrint's internal data via __plugin_license__ in the plugin module | ||||||
| plugin_license = "AGPLv3" | plugin_license = "AGPLv3" | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								templates/bambu_printer_settings.jinja2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								templates/bambu_printer_settings.jinja2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <div class="control-group"> | ||||||
|  |     <label class="control-label">{{ _('Connection Options') }}</label> | ||||||
|  |     <div class="controls"> | ||||||
|  |         <label class="checkbox"> | ||||||
|  |             <input type="checkbox" data-bind="checked: settings.plugins.bambu_printer.use_mqtt_bridge"> {{ _('Use MQTT Bridge') }} | ||||||
|  |             <span class="help-block"> | ||||||
|  |                 {{ _('Connect via a MQTT broker that bridges communications from the printer. Useful for connecting to a printer on a different network.') }} | ||||||
|  |             </span> | ||||||
|  |         </label> | ||||||
|  |         <label class="checkbox"> | ||||||
|  |             <input type="checkbox" data-bind="checked: settings.plugins.bambu_printer.disable_camera"> {{ _('Disable Camera Functionality') }} | ||||||
|  |             <span class="help-block"> | ||||||
|  |                 {{ _('Disable camera streaming and image capture to avoid connection errors. Enable this if you see frequent connection refused errors in the logs.') }} | ||||||
|  |             </span> | ||||||
|  |         </label> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
		Reference in New Issue
	
	Block a user