Compare commits
	
		
			23 Commits
		
	
	
		
			0.1.8rc8
			...
			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": "", | ||||
|             "host": "", | ||||
|             "access_code": "", | ||||
|             "username": "bblp", | ||||
|             "username": "octobambu", | ||||
|             "timelapse": False, | ||||
|             "bed_leveling": True, | ||||
|             "flow_cali": False, | ||||
| @@ -286,10 +286,10 @@ class BambuPrintPlugin( | ||||
|     def get_update_information(self): | ||||
|         return { | ||||
|             "bambu_printer": { | ||||
|                 "displayName": "Bambu Printer", | ||||
|                 "displayName": "Manus Bambu Printer", | ||||
|                 "displayVersion": self._plugin_version, | ||||
|                 "type": "github_release", | ||||
|                 "user": "jneilliii", | ||||
|                 "user": "ManuelW", | ||||
|                 "repo": "OctoPrint-BambuPrinter", | ||||
|                 "current": self._plugin_version, | ||||
|                 "stable_branch": { | ||||
| @@ -304,6 +304,6 @@ class BambuPrintPlugin( | ||||
|                         "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 typing import TYPE_CHECKING, Callable | ||||
| from typing import TYPE_CHECKING, Callable, List, Optional | ||||
|  | ||||
| if TYPE_CHECKING: | ||||
|     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 | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class CachedFileView: | ||||
|     file_system: RemoteSDCardFileList | ||||
|     folder_view: dict[tuple[str, str | list[str] | None], None] = field( | ||||
|         default_factory=dict | ||||
|     )  # dict preserves order, but set does not. We use only dict keys as storage | ||||
|     on_update: Callable[[], None] | None = None | ||||
|     def __init__( | ||||
|         self, file_system, on_update: Optional[Callable] = None, base_path: str = "" | ||||
|     ): | ||||
|         self._filters = [] | ||||
|         self._file_system = file_system | ||||
|         self._base_path = base_path | ||||
|         self._update_complete_callback = on_update | ||||
|         self._file_info_cache = [] | ||||
|  | ||||
|     def __post_init__(self): | ||||
|         self._file_alias_cache: dict[str, str] = {} | ||||
|         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 | ||||
|     def with_filter(self, path: str, extension: str): | ||||
|         self._filters.append({"path": path, "extension": extension}) | ||||
|         return self | ||||
|  | ||||
|     def list_all_views(self): | ||||
|         existing_files: list[str] = [] | ||||
|         result: list[FileInfo] = [] | ||||
|     def update(self) -> None: | ||||
|         try: | ||||
|             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: | ||||
|             for filter in self.folder_view.keys(): | ||||
|                 result.extend(self.file_system.list_files(*filter, ftp, existing_files)) | ||||
|         return result | ||||
|     def list_all_views(self) -> List[FileInfo]: | ||||
|         # Verwende die Mock-Implementation von get_file_list statt FTPS | ||||
|         try: | ||||
|             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): | ||||
|         file_info_list = self.list_all_views() | ||||
|         self._update_file_list_cache(file_info_list) | ||||
|         if self.on_update: | ||||
|             self.on_update() | ||||
|     def get_all_cached_info(self) -> List[FileInfo]: | ||||
|         return self._file_info_cache | ||||
|  | ||||
|     def _update_file_list_cache(self, files: list[FileInfo]): | ||||
|         self._file_alias_cache = {info.dosname: info.path.as_posix() for info in files} | ||||
|         self._file_data_cache = {info.path.as_posix(): info for info in files} | ||||
|     def get_file_by_stem(self, file_stem: str, extensions: list[str]) -> FileInfo | None: | ||||
|         """Get file info by file name without extension""" | ||||
|         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): | ||||
|         self.update() | ||||
|         return self.get_all_cached_info() | ||||
|  | ||||
|     def get_all_cached_info(self): | ||||
|         return list(self._file_data_cache.values()) | ||||
|  | ||||
|     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 | ||||
|  | ||||
|     def get_file_data(self, file_path: str) -> FileInfo | None: | ||||
|         for file_info in self._file_info_cache: | ||||
|             if file_info.path.lower() == file_path.lower() or file_info.file_name.lower() == file_path.lower(): | ||||
|                 return file_info | ||||
|         return None | ||||
|   | ||||
| @@ -2,7 +2,7 @@ from __future__ import annotations | ||||
|  | ||||
| import datetime | ||||
| from pathlib import Path | ||||
| from typing import Iterable, Iterator | ||||
| from typing import Iterable, Iterator, List | ||||
| import logging.handlers | ||||
|  | ||||
| from octoprint.util import get_dos_filename | ||||
| @@ -17,6 +17,7 @@ class RemoteSDCardFileList: | ||||
|         self._settings = settings | ||||
|         self._selected_project_file: FileInfo | None = None | ||||
|         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: | ||||
|         try: | ||||
| @@ -80,8 +81,56 @@ class RemoteSDCardFileList: | ||||
|                 self._logger.exception(e, exc_info=False) | ||||
|  | ||||
|     def get_ftps_client(self): | ||||
|         host = self._settings.get(["host"]) | ||||
|         access_code = self._settings.get(["access_code"]) | ||||
|         return IoTFTPSClient( | ||||
|             f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True | ||||
|         ) | ||||
|         """ | ||||
|         Implementieren wir eine Mock-Version des FTPS-Clients, die keinen echten FTP-Zugriff erfordert. | ||||
|         """ | ||||
|         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 | ||||
| pyserial~=3.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" | ||||
|  | ||||
| # 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 | ||||
| # module | ||||
| 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 | ||||
| plugin_author = "jneilliii" | ||||
| plugin_author = "ManuelW" | ||||
|  | ||||
| # 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 | ||||
| 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 | ||||
| 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