Compare commits
	
		
			21 Commits
		
	
	
		
			0.1.8rc5
			...
			fd9ce76275
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fd9ce76275 | |||
| 8dafb9fa5a | |||
| 094959335a | |||
| f64fa7aea2 | |||
| fea0f0ed25 | |||
| c7c089ef68 | |||
| ba43df279d | |||
| f5e6b3d0dd | |||
| 9358533ce8 | |||
| 92e11cdbf3 | |||
| 61c9332f15 | |||
| ad08d3eb9a | |||
| 5661c11190 | |||
| 3690767ced | |||
| eb397ff7b7 | |||
| 3a615cfafe | |||
| e9c06bb4b5 | |||
| 3ccce10648 | |||
| c99eb38655 | |||
| 698f8f4151 | |||
| 7a0293bac7 | 
| @@ -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", | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ from octoprint_bambu_printer.printer.print_job import PrintJob | |||||||
| from pybambu import BambuClient, commands | from pybambu import BambuClient, commands | ||||||
| import logging | import logging | ||||||
| import logging.handlers | import logging.handlers | ||||||
|  | import json | ||||||
|  | import paho.mqtt.client as mqtt | ||||||
|  |  | ||||||
| from octoprint.util import RepeatedTimer | from octoprint.util import RepeatedTimer | ||||||
|  |  | ||||||
| @@ -45,7 +47,232 @@ class BambuPrinterTelemetry: | |||||||
|     extruderCount: int = 1 |     extruderCount: int = 1 | ||||||
|  |  | ||||||
|  |  | ||||||
| # noinspection PyBroadException | class BambuMqttBridgeClient: | ||||||
|  |     """ | ||||||
|  |     Implements compatible interface with BambuClient but uses Paho MQTT | ||||||
|  |     to connect to a MQTT broker that bridges Bambu topics | ||||||
|  |     """ | ||||||
|  |     def __init__(self,  | ||||||
|  |                  device_type, | ||||||
|  |                  serial, | ||||||
|  |                  host, | ||||||
|  |                  mqtt_port=1883, | ||||||
|  |                  **kwargs): | ||||||
|  |         self._log = logging.getLogger("octoprint.plugins.bambu_printer.BambuMqttBridge") | ||||||
|  |         self._device_type = device_type | ||||||
|  |         self._serial = serial | ||||||
|  |         self._host = host | ||||||
|  |         self._mqtt_port = mqtt_port | ||||||
|  |         self.connected = False | ||||||
|  |         self._mqtt_client = mqtt.Client(f"octoprint_bambu_bridge_{serial}") | ||||||
|  |         self._device_data = self._create_empty_device_data() | ||||||
|  |         self._callbacks = {} | ||||||
|  |          | ||||||
|  |         # Setup callbacks | ||||||
|  |         self._mqtt_client.on_connect = self._on_connect | ||||||
|  |         self._mqtt_client.on_message = self._on_message | ||||||
|  |         self._mqtt_client.on_disconnect = self._on_disconnect | ||||||
|  |          | ||||||
|  |     def _create_empty_device_data(self): | ||||||
|  |         """Creates empty device data structure compatible with BambuClient""" | ||||||
|  |         from types import SimpleNamespace | ||||||
|  |          | ||||||
|  |         # Create basic structure matching BambuClient | ||||||
|  |         device = SimpleNamespace() | ||||||
|  |         device.print_job = SimpleNamespace() | ||||||
|  |         device.print_job.gcode_state = "IDLE" | ||||||
|  |         device.temperature = SimpleNamespace() | ||||||
|  |         device.temperature.nozzle_temp = AMBIENT_TEMPERATURE | ||||||
|  |         device.temperature.target_nozzle_temp = 0.0 | ||||||
|  |         device.temperature.bed_temp = AMBIENT_TEMPERATURE | ||||||
|  |         device.temperature.target_bed_temp = 0.0 | ||||||
|  |         device.temperature.chamber_temp = AMBIENT_TEMPERATURE | ||||||
|  |         device.hms = SimpleNamespace() | ||||||
|  |         device.hms.errors = {"Count": 0} | ||||||
|  |         return device | ||||||
|  |          | ||||||
|  |     def _on_connect(self, client, userdata, flags, rc): | ||||||
|  |         if rc == 0: | ||||||
|  |             self._log.info(f"Connected to MQTT broker at {self._host}:{self._mqtt_port}") | ||||||
|  |             self.connected = True | ||||||
|  |              | ||||||
|  |             # Subscribe to Bambu topics | ||||||
|  |             topic_base = f"device/{self._device_type}/{self._serial}" | ||||||
|  |             self._mqtt_client.subscribe(f"{topic_base}/report") | ||||||
|  |             self._mqtt_client.subscribe(f"{topic_base}/report_hms") | ||||||
|  |             self._log.info(f"Subscribed to topic: {topic_base}/#") | ||||||
|  |              | ||||||
|  |             if 'callback' in self._callbacks: | ||||||
|  |                 self._callbacks['callback']("event_printer_data_update") | ||||||
|  |                  | ||||||
|  |             if hasattr(self, 'on_connect') and callable(self.on_connect): | ||||||
|  |                 self.on_connect(client, userdata, flags, rc) | ||||||
|  |         else: | ||||||
|  |             self._log.error(f"Failed to connect to MQTT broker, return code: {rc}") | ||||||
|  |              | ||||||
|  |     def _on_disconnect(self, client, userdata, rc): | ||||||
|  |         self._log.warning(f"Disconnected from MQTT broker with code: {rc}") | ||||||
|  |         self.connected = False | ||||||
|  |          | ||||||
|  |         # Add reconnection attempt | ||||||
|  |         if rc != 0:  # Non-zero means unexpected disconnect | ||||||
|  |             self._log.info("Attempting to reconnect to MQTT broker...") | ||||||
|  |             try: | ||||||
|  |                 self._mqtt_client.reconnect() | ||||||
|  |             except Exception as e: | ||||||
|  |                 self._log.error(f"Failed to reconnect to MQTT broker: {str(e)}", exc_info=True) | ||||||
|  |          | ||||||
|  |         if hasattr(self, 'on_disconnect') and callable(self.on_disconnect): | ||||||
|  |             self.on_disconnect(client, userdata, rc) | ||||||
|  |              | ||||||
|  |     def _on_message(self, client, userdata, msg): | ||||||
|  |         try: | ||||||
|  |             payload = json.loads(msg.payload.decode('utf-8')) | ||||||
|  |             self._log.debug(f"Received message on topic {msg.topic}: {payload}") | ||||||
|  |              | ||||||
|  |             if msg.topic.endswith('/report'): | ||||||
|  |                 self._process_report_message(payload) | ||||||
|  |                 if 'callback' in self._callbacks: | ||||||
|  |                     self._callbacks['callback']("event_printer_data_update") | ||||||
|  |             elif msg.topic.endswith('/report_hms'): | ||||||
|  |                 self._process_hms_message(payload) | ||||||
|  |                 if 'callback' in self._callbacks: | ||||||
|  |                     self._callbacks['callback']("event_hms_errors") | ||||||
|  |         except json.JSONDecodeError: | ||||||
|  |             self._log.error(f"Failed to decode JSON from message: {msg.payload}") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing message: {str(e)}") | ||||||
|  |              | ||||||
|  |     def _process_report_message(self, data): | ||||||
|  |         """Process printer status report messages""" | ||||||
|  |         try: | ||||||
|  |             # Update print state if available | ||||||
|  |             if 'print' in data: | ||||||
|  |                 print_data = data['print'] | ||||||
|  |                  | ||||||
|  |                 # Update printer state | ||||||
|  |                 if 'gcode_state' in print_data: | ||||||
|  |                     self._device_data.print_job.gcode_state = print_data['gcode_state'] | ||||||
|  |                     self._log.debug(f"Updated printer state: {print_data['gcode_state']}") | ||||||
|  |                  | ||||||
|  |                 # Process direct temperature values in print data | ||||||
|  |                 if 'nozzle_temper' in print_data: | ||||||
|  |                     self._device_data.temperature.nozzle_temp = float(print_data['nozzle_temper']) | ||||||
|  |                 if 'nozzle_target_temper' in print_data: | ||||||
|  |                     self._device_data.temperature.target_nozzle_temp = float(print_data['nozzle_target_temper']) | ||||||
|  |                 if 'bed_temper' in print_data: | ||||||
|  |                     self._device_data.temperature.bed_temp = float(print_data['bed_temper']) | ||||||
|  |                 if 'bed_target_temper' in print_data: | ||||||
|  |                     self._device_data.temperature.target_bed_temp = float(print_data['bed_target_temper']) | ||||||
|  |                 if 'chamber_temper' in print_data: | ||||||
|  |                     self._device_data.temperature.chamber_temp = float(print_data['chamber_temper']) | ||||||
|  |              | ||||||
|  |             # Process temperature section if available | ||||||
|  |             if 'temperature' in data: | ||||||
|  |                 temp = self._device_data.temperature | ||||||
|  |                 temp_data = data['temperature'] | ||||||
|  |                  | ||||||
|  |                 if 'nozzle_temp' in temp_data: | ||||||
|  |                     temp.nozzle_temp = float(temp_data['nozzle_temp']) | ||||||
|  |                 if 'target_nozzle_temp' in temp_data: | ||||||
|  |                     temp.target_nozzle_temp = float(temp_data['target_nozzle_temp']) | ||||||
|  |                 if 'bed_temp' in temp_data: | ||||||
|  |                     temp.bed_temp = float(temp_data['bed_temp']) | ||||||
|  |                 if 'target_bed_temp' in temp_data: | ||||||
|  |                     temp.target_bed_temp = float(temp_data['target_bed_temp']) | ||||||
|  |                 if 'chamber_temp' in temp_data: | ||||||
|  |                     temp.chamber_temp = float(temp_data['chamber_temp']) | ||||||
|  |                      | ||||||
|  |             self._log.debug(f"Updated temperatures - Nozzle: {self._device_data.temperature.nozzle_temp}/" + | ||||||
|  |                           f"{self._device_data.temperature.target_nozzle_temp}, " + | ||||||
|  |                           f"Bed: {self._device_data.temperature.bed_temp}/" + | ||||||
|  |                           f"{self._device_data.temperature.target_bed_temp}, " + | ||||||
|  |                           f"Chamber: {self._device_data.temperature.chamber_temp}") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing report message: {str(e)}", exc_info=True) | ||||||
|  |              | ||||||
|  |     def _process_hms_message(self, data): | ||||||
|  |         """Process HMS error messages""" | ||||||
|  |         try: | ||||||
|  |             if 'hms' in data: | ||||||
|  |                 error_count = 0 | ||||||
|  |                 hms_errors = {"Count": 0} | ||||||
|  |                  | ||||||
|  |                 for error in data['hms']: | ||||||
|  |                     error_count += 1 | ||||||
|  |                     if isinstance(error, dict) and 'msg' in error: | ||||||
|  |                         hms_errors[f"{error_count}-Error"] = error['msg'] | ||||||
|  |                     else: | ||||||
|  |                         hms_errors[f"{error_count}-Error"] = str(error) | ||||||
|  |                  | ||||||
|  |                 hms_errors["Count"] = error_count | ||||||
|  |                 self._device_data.hms.errors = hms_errors | ||||||
|  |                  | ||||||
|  |                 if error_count > 0: | ||||||
|  |                     self._log.info(f"Found {error_count} HMS errors") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing HMS message: {str(e)}", exc_info=True) | ||||||
|  |      | ||||||
|  |     def connect(self, callback=None): | ||||||
|  |         """Connect to MQTT broker""" | ||||||
|  |         if callback: | ||||||
|  |             self._callbacks['callback'] = callback | ||||||
|  |              | ||||||
|  |         try: | ||||||
|  |             self._log.info(f"Connecting to MQTT broker {self._host}:{self._mqtt_port}") | ||||||
|  |             self._mqtt_client.connect(self._host, self._mqtt_port) | ||||||
|  |             self._mqtt_client.loop_start() | ||||||
|  |              | ||||||
|  |             # Wait a bit for the connection to establish | ||||||
|  |             time.sleep(1) | ||||||
|  |              | ||||||
|  |             # If not connected after waiting, try again | ||||||
|  |             if not self.connected: | ||||||
|  |                 self._log.warning("Initial connection attempt failed, retrying...") | ||||||
|  |                 try: | ||||||
|  |                     self._mqtt_client.reconnect() | ||||||
|  |                     time.sleep(2)  # Wait a bit longer for retry | ||||||
|  |                 except Exception as e: | ||||||
|  |                     self._log.error(f"Reconnection failed: {str(e)}", exc_info=True) | ||||||
|  |                      | ||||||
|  |             return self.connected | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Failed to connect to MQTT broker: {str(e)}", exc_info=True) | ||||||
|  |             return False | ||||||
|  |              | ||||||
|  |     def disconnect(self): | ||||||
|  |         """Disconnect from MQTT broker""" | ||||||
|  |         if self.connected: | ||||||
|  |             self._mqtt_client.loop_stop() | ||||||
|  |             self._mqtt_client.disconnect() | ||||||
|  |             self.connected = False | ||||||
|  |              | ||||||
|  |     def get_device(self): | ||||||
|  |         """Returns device data structure""" | ||||||
|  |         return self._device_data | ||||||
|  |          | ||||||
|  |     def publish(self, command): | ||||||
|  |         """Publishes command to device""" | ||||||
|  |         if not self.connected: | ||||||
|  |             self._log.error("Cannot publish: Not connected to MQTT broker") | ||||||
|  |             return False | ||||||
|  |              | ||||||
|  |         try: | ||||||
|  |             topic_base = f"device/{self._device_type}/{self._serial}" | ||||||
|  |             if 'print' in command and 'param' in command['print']: | ||||||
|  |                 # Commands go to command topic | ||||||
|  |                 message = json.dumps(command) | ||||||
|  |                 result = self._mqtt_client.publish(f"{topic_base}/cmd", message) | ||||||
|  |                 self._log.debug(f"Published command to {topic_base}/cmd: {message}") | ||||||
|  |                 return result.rc == mqtt.MQTT_ERR_SUCCESS | ||||||
|  |             else: | ||||||
|  |                 self._log.warning(f"Invalid command format: {command}") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Failed to publish command: {str(e)}", exc_info=True) | ||||||
|  |              | ||||||
|  |         return False | ||||||
|  |  | ||||||
|  |  | ||||||
| class BambuVirtualPrinter: | class BambuVirtualPrinter: | ||||||
|     gcode_executor = GCodeExecutor() |     gcode_executor = GCodeExecutor() | ||||||
|  |  | ||||||
| @@ -104,7 +331,10 @@ class BambuVirtualPrinter: | |||||||
|  |  | ||||||
|         self._serial_io.start() |         self._serial_io.start() | ||||||
|         self._printer_thread.start() |         self._printer_thread.start() | ||||||
|  |         self._mqtt_client = None | ||||||
|  |         self._mqtt_connected = False | ||||||
|  |         self._bambu_client = None | ||||||
|  |         self._custom_connected = False | ||||||
|         self._bambu_client: BambuClient = self._create_client_connection_async() |         self._bambu_client: BambuClient = self._create_client_connection_async() | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -127,10 +357,6 @@ class BambuVirtualPrinter: | |||||||
|     def current_print_job(self, value): |     def current_print_job(self, value): | ||||||
|         self._current_print_job = value |         self._current_print_job = value | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def selected_file(self): |  | ||||||
|         return self._selected_project_file |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def has_selected_file(self): |     def has_selected_file(self): | ||||||
|         return self._selected_project_file is not None |         return self._selected_project_file is not None | ||||||
| @@ -165,40 +391,41 @@ class BambuVirtualPrinter: | |||||||
|     def project_files(self): |     def project_files(self): | ||||||
|         return self._project_files_view |         return self._project_files_view | ||||||
|  |  | ||||||
|     def change_state(self, new_state: APrinterState): |     @property | ||||||
|         self._state_change_queue.put(new_state) |     def is_connected(self): | ||||||
|  |         """Custom property to track connection status without modifying BambuClient directly""" | ||||||
|  |         connection_status = self._custom_connected and self._mqtt_connected | ||||||
|  |         self._log.debug(f"Connection status check: custom_connected={self._custom_connected}, mqtt_connected={self._mqtt_connected}, result={connection_status}") | ||||||
|  |         return connection_status | ||||||
|  |  | ||||||
|     def new_update(self, event_type): |     def new_update(self, event_type): | ||||||
|         if event_type == "event_hms_errors": |         """Custom property to track connection status without modifying BambuClient directly""" | ||||||
|  |         if event_type == "event_printer_data_update": | ||||||
|  |             self._log.debug("Received event_printer_data_update") | ||||||
|             self._update_hms_errors() |             self._update_hms_errors() | ||||||
|         elif event_type == "event_printer_data_update": |             # Gib den aktuellen Status detaillierter aus | ||||||
|             self._update_printer_info() |             device_data = self.bambu_client.get_device() | ||||||
|  |             print_job_state = device_data.print_job.gcode_state | ||||||
|     def _update_printer_info(self): |             self._log.debug(f"BambuClient printer state: {print_job_state}") | ||||||
|         device_data = self.bambu_client.get_device() |             self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " + | ||||||
|         print_job_state = device_data.print_job.gcode_state |                           f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " + | ||||||
|         temperatures = device_data.temperature |                           f"Chamber: {self._telemetry.chamberTemp}") | ||||||
|  |              | ||||||
|         self.lastTempAt = time.monotonic() |             # Process the printer state even if it's "unknown" | ||||||
|         self._telemetry.temp[0] = temperatures.nozzle_temp |             if print_job_state: | ||||||
|         self._telemetry.targetTemp[0] = temperatures.target_nozzle_temp |                 self._process_print_state(print_job_state) | ||||||
|         self._telemetry.bedTemp = temperatures.bed_temp |             else: | ||||||
|         self._telemetry.bedTargetTemp = temperatures.target_bed_temp |                 self._log.debug("No printer state received, skipping state processing") | ||||||
|         self._telemetry.chamberTemp = temperatures.chamber_temp |                  | ||||||
|  |         elif event_type == "event_hms_errors": | ||||||
|         self._log.debug(f"Received printer state update: {print_job_state}") |             self._log.debug("Received event_hms_errors") | ||||||
|         if ( |             bambu_printer = self.bambu_client.get_device() | ||||||
|             print_job_state == "IDLE" |             if bambu_printer.hms.errors != self._last_hms_errors: | ||||||
|             or print_job_state == "FINISH" |                 self._log.debug(f"HMS Error: {bambu_printer.hms.errors}") | ||||||
|             or print_job_state == "FAILED" |                 for n in range(1, bambu_printer.hms.errors["Count"] + 1): | ||||||
|         ): |                     error = bambu_printer.hms.errors[f"{n}-Error"].strip() | ||||||
|             self.change_state(self._state_idle) |                     self.sendIO(f"// action:notification {error}") | ||||||
|         elif print_job_state == "RUNNING" or print_job_state == "PREPARE": |                 self._last_hms_errors = bambu_printer.hms.errors | ||||||
|             self.change_state(self._state_printing) |  | ||||||
|         elif print_job_state == "PAUSE": |  | ||||||
|             self.change_state(self._state_paused) |  | ||||||
|         else: |  | ||||||
|             self._log.warn(f"Unknown print job state: {print_job_state}") |  | ||||||
|  |  | ||||||
|     def _update_hms_errors(self): |     def _update_hms_errors(self): | ||||||
|         bambu_printer = self.bambu_client.get_device() |         bambu_printer = self.bambu_client.get_device() | ||||||
| @@ -220,6 +447,390 @@ class BambuVirtualPrinter: | |||||||
|         self._log.debug(f"on connect called") |         self._log.debug(f"on connect called") | ||||||
|         return on_connect |         return on_connect | ||||||
|  |  | ||||||
|  |     def _on_mqtt_connect(self, client, userdata, flags, rc): | ||||||
|  |         self._log.debug(f"MQTT connected with result code: {rc}") | ||||||
|  |         if rc == 0: | ||||||
|  |             # Notify that we're connected | ||||||
|  |             self._mqtt_connected = True | ||||||
|  |             self._custom_connected = True | ||||||
|  |             # Subscribe to the relevant topics for the Bambu printer | ||||||
|  |             device_topic = f"device/{self._settings.get(['serial'])}/report" | ||||||
|  |             client.subscribe(device_topic) | ||||||
|  |             self._log.debug(f"Subscribed to topic: {device_topic}") | ||||||
|  |             self._log.info(f"MQTT connection successful. Connected: {self.is_connected}") | ||||||
|  |         else: | ||||||
|  |             self._mqtt_connected = False | ||||||
|  |             self._custom_connected = False | ||||||
|  |             self._log.error(f"Failed to connect to MQTT broker with result code: {rc}") | ||||||
|  |  | ||||||
|  |     def _on_mqtt_disconnect(self, client, userdata, rc): | ||||||
|  |         self._mqtt_connected = False | ||||||
|  |         self._custom_connected = False | ||||||
|  |         self._log.debug(f"MQTT disconnected with result code: {rc}") | ||||||
|  |  | ||||||
|  |     def _on_mqtt_message(self, client, userdata, msg): | ||||||
|  |         try: | ||||||
|  |             # Decode message and update client data | ||||||
|  |             payload = json.loads(msg.payload.decode('utf-8')) | ||||||
|  |             self._log.debug(f"MQTT message received on topic {msg.topic}: {list(payload.keys())}") | ||||||
|  |              | ||||||
|  |             # Direkte Verarbeitung der Daten | ||||||
|  |             self._process_mqtt_payload(payload) | ||||||
|  |              | ||||||
|  |             # Auch an Bambu Client weiterleiten | ||||||
|  |             try: | ||||||
|  |                 # Wenn der BambuClient eine eigene Verarbeitungsmethode hat, nutzen wir diese | ||||||
|  |                 if hasattr(self._bambu_client, '_process_message') and callable(self._bambu_client._process_message): | ||||||
|  |                     self._bambu_client._process_message(msg.topic, payload) | ||||||
|  |                     self._log.debug("Message forwarded to pybambu via _process_message") | ||||||
|  |                 elif hasattr(self._bambu_client, '_handle_mqtt_message') and callable(self._bambu_client._handle_mqtt_message): | ||||||
|  |                     self._bambu_client._handle_mqtt_message(client, userdata, msg) | ||||||
|  |                     self._log.debug("Message forwarded to pybambu via _handle_mqtt_message") | ||||||
|  |                 else: | ||||||
|  |                     # Wenn keine Methode zur Verarbeitung verfügbar ist, aktualisieren wir die Datenstruktur manuell | ||||||
|  |                     self._log.debug("No message handler found in BambuClient, updating state manually") | ||||||
|  |                     self._update_bambu_client_state(payload) | ||||||
|  |             except Exception as e: | ||||||
|  |                 self._log.error(f"Error forwarding to pybambu: {e}", exc_info=True) | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing MQTT message: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_mqtt_payload(self, payload): | ||||||
|  |         """Zentrale Methode zur Verarbeitung von MQTT-Payloads""" | ||||||
|  |         try: | ||||||
|  |             # Verarbeite print-Daten | ||||||
|  |             if 'print' in payload: | ||||||
|  |                 print_data = payload['print'] | ||||||
|  |                 self._log.info(f"Processing print data with keys: {list(print_data.keys())}") | ||||||
|  |                  | ||||||
|  |                 # Temperaturdaten direkt verarbeiten | ||||||
|  |                 self._process_direct_temperature_data(print_data) | ||||||
|  |                  | ||||||
|  |                 # Status verarbeiten | ||||||
|  |                 self._process_print_state(print_data['gcode_state']) | ||||||
|  |                  | ||||||
|  |                 # Fortschritt verarbeiten | ||||||
|  |                 self._process_progress_data(print_data) | ||||||
|  |                  | ||||||
|  |                 # Schicht-Informationen verarbeiten | ||||||
|  |                 self._process_layer_data(print_data) | ||||||
|  |                  | ||||||
|  |                 # Lüfter-Informationen verarbeiten | ||||||
|  |                 self._process_fan_data(print_data) | ||||||
|  |                  | ||||||
|  |                 # Datei-Informationen verarbeiten | ||||||
|  |                 self._process_file_data(print_data) | ||||||
|  |                  | ||||||
|  |                 # Trigger update | ||||||
|  |                 self.new_update("event_printer_data_update") | ||||||
|  |                  | ||||||
|  |             # Verarbeite info-Daten | ||||||
|  |             if 'info' in payload: | ||||||
|  |                 info_data = payload['info'] | ||||||
|  |                 self._log.info(f"Processing info data with keys: {list(info_data.keys())}") | ||||||
|  |                  | ||||||
|  |                 # HMS-Fehler verarbeiten | ||||||
|  |                 self._process_hms_errors(info_data['hms']) | ||||||
|  |                 self.new_update("event_hms_errors") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing MQTT payload: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_layer_data(self, print_data): | ||||||
|  |         """Verarbeitet Schicht-Informationen aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             current_layer = None | ||||||
|  |             total_layers = None | ||||||
|  |              | ||||||
|  |             if 'layer_num' in print_data: | ||||||
|  |                 current_layer = int(print_data['layer_num']) | ||||||
|  |                 self._log.debug(f"Current layer: {current_layer}") | ||||||
|  |                  | ||||||
|  |             if 'total_layer_num' in print_data: | ||||||
|  |                 total_layers = int(print_data['total_layer_num']) | ||||||
|  |                 self._log.debug(f"Total layers: {total_layers}") | ||||||
|  |                  | ||||||
|  |             # Aktualisiere den PrintJob, wenn einer existiert | ||||||
|  |             if self.current_print_job is not None: | ||||||
|  |                 if current_layer is not None: | ||||||
|  |                     self.current_print_job.current_layer = current_layer | ||||||
|  |                 if total_layers is not None: | ||||||
|  |                     self.current_print_job.total_layers = total_layers | ||||||
|  |                  | ||||||
|  |             # Aktualisiere auch die pybambu-Datenstruktur, wenn vorhanden | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                 if current_layer is not None: | ||||||
|  |                     self._bambu_client.device.print_job.current_layer = current_layer | ||||||
|  |                 if total_layers is not None: | ||||||
|  |                     self._bambu_client.device.print_job.total_layers = total_layers | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing layer data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_fan_data(self, print_data): | ||||||
|  |         """Verarbeitet Lüfterdaten aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             # Verschiedene Lüfter-Typen | ||||||
|  |             fan_data = {} | ||||||
|  |              | ||||||
|  |             if 'heatbreak_fan_speed' in print_data: | ||||||
|  |                 fan_data['heatbreak'] = int(print_data['heatbreak_fan_speed']) | ||||||
|  |                  | ||||||
|  |             if 'cooling_fan_speed' in print_data: | ||||||
|  |                 fan_data['cooling'] = int(print_data['cooling_fan_speed']) | ||||||
|  |                  | ||||||
|  |             if 'big_fan1_speed' in print_data: | ||||||
|  |                 fan_data['chamber1'] = int(print_data['big_fan1_speed']) | ||||||
|  |                  | ||||||
|  |             if 'big_fan2_speed' in print_data: | ||||||
|  |                 fan_data['chamber2'] = int(print_data['big_fan2_speed']) | ||||||
|  |                  | ||||||
|  |             if fan_data: | ||||||
|  |                 self._log.debug(f"Fan speeds: {fan_data}") | ||||||
|  |                  | ||||||
|  |             # Aktualisiere die pybambu-Struktur, wenn vorhanden | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'fan_speeds'): | ||||||
|  |                 try: | ||||||
|  |                     for fan_type, speed in fan_data.items(): | ||||||
|  |                         setattr(self._bambu_client.device.fan_speeds, fan_type, speed) | ||||||
|  |                 except: | ||||||
|  |                     # Wenn fan_speeds nicht die erwarteten Attribute hat, erstellen wir sie | ||||||
|  |                     self._bambu_client.device.fan_speeds = type('', (), fan_data)() | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing fan data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_speed_data(self, print_data): | ||||||
|  |         """Verarbeitet Geschwindigkeitsdaten aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             if 'spd_mag' in print_data: | ||||||
|  |                 speed_magnitude = int(print_data['spd_mag']) | ||||||
|  |                 self._log.debug(f"Speed magnitude: {speed_magnitude}%") | ||||||
|  |                  | ||||||
|  |             if 'spd_lvl' in print_data: | ||||||
|  |                 speed_level = int(print_data['spd_lvl']) | ||||||
|  |                 self._log.debug(f"Speed level: {speed_level}") | ||||||
|  |                  | ||||||
|  |             # Aktualisiere die pybambu-Struktur, wenn vorhanden | ||||||
|  |             if hasattr(self._bambu_client, 'device') and not hasattr(self._bambu_client.device, 'speed'): | ||||||
|  |                 self._bambu_client.device.speed = type('', (), {})() | ||||||
|  |                  | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'speed'): | ||||||
|  |                 if 'spd_mag' in print_data: | ||||||
|  |                     self._bambu_client.device.speed.magnitude = int(print_data['spd_mag']) | ||||||
|  |                 if 'spd_lvl' in print_data: | ||||||
|  |                     self._bambu_client.device.speed.level = int(print_data['spd_lvl']) | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing speed data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_file_data(self, print_data): | ||||||
|  |         """Verarbeitet Dateiinformationen aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             # Dateiname | ||||||
|  |             if 'gcode_file' in print_data and print_data['gcode_file']: | ||||||
|  |                 filename = print_data['gcode_file'] | ||||||
|  |                 self._log.debug(f"Print file: {filename}") | ||||||
|  |                  | ||||||
|  |                 # Aktualisiere den PrintJob, wenn einer existiert | ||||||
|  |                 if self.current_print_job is not None: | ||||||
|  |                     self.current_print_job.gcode_file = filename | ||||||
|  |                      | ||||||
|  |                 # Aktualisiere auch die pybambu-Datenstruktur | ||||||
|  |                 if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                     self._bambu_client.device.print_job.gcode_file = filename | ||||||
|  |  | ||||||
|  |             # Subtask Name (oft der Projektname) | ||||||
|  |             if 'subtask_name' in print_data and print_data['subtask_name']: | ||||||
|  |                 subtask_name = print_data['subtask_name'] | ||||||
|  |                 self._log.debug(f"Subtask name: {subtask_name}") | ||||||
|  |                  | ||||||
|  |                 # Aktualisiere den PrintJob, wenn einer existiert | ||||||
|  |                 if self.current_print_job is not None: | ||||||
|  |                     self.current_print_job.subtask_name = subtask_name | ||||||
|  |                      | ||||||
|  |                 # Aktualisiere auch die pybambu-Datenstruktur | ||||||
|  |                 if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                     self._bambu_client.device.print_job.subtask_name = subtask_name | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing file data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_print_state(self, print_job_state): | ||||||
|  |         """Verarbeitet den Druckerstatus aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             self._log.debug(f"Received printer state update: {print_job_state}") | ||||||
|  |              | ||||||
|  |             # Erweitern der Statuserkennung - in Bambu können die Status auch kleingeschrieben sein | ||||||
|  |             # oder andere Werte haben als die, die wir erwarten | ||||||
|  |             print_job_state = print_job_state.upper() if print_job_state else "UNKNOWN" | ||||||
|  |              | ||||||
|  |             # Explicitly handle "unknown" state (both upper and lowercase) | ||||||
|  |             if print_job_state in ["UNKNOWN", ""] or print_job_state.upper() == "UNKNOWN": | ||||||
|  |                 self._log.debug("Detected 'unknown' printer state, trying to determine actual state") | ||||||
|  |                 # Wenn wir keinen erkannten Status haben, versuchen wir ihn aus anderen Daten abzuleiten | ||||||
|  |                 # Prüfe ob Druckfortschritt vorhanden ist | ||||||
|  |                 if self.current_print_job and self.current_print_job.print_percentage > 0: | ||||||
|  |                     print_job_state = "RUNNING" | ||||||
|  |                     self._log.debug(f"Changed unknown state to RUNNING based on print progress from current_print_job") | ||||||
|  |                 # Prüfe auf Temperaturen, die auf einen laufenden Druck hinweisen könnten | ||||||
|  |                 elif self._telemetry.targetTemp[0] > 150 or self._telemetry.bedTargetTemp > 40: | ||||||
|  |                     print_job_state = "PREPARE" | ||||||
|  |                     self._log.debug(f"Changed unknown state to PREPARE based on target temperatures") | ||||||
|  |                 else: | ||||||
|  |                     self._log.debug("Keeping state as IDLE since no indicators for print activity were found") | ||||||
|  |                     print_job_state = "IDLE"  # Default to IDLE if we can't determine state | ||||||
|  |              | ||||||
|  |             # Status im PrintJob aktualisieren | ||||||
|  |             if self.current_print_job is None and print_job_state in ["RUNNING", "PREPARE", "PAUSE"]: | ||||||
|  |                 # Wenn wir keinen PrintJob haben, aber ein Druck läuft, erstellen wir einen | ||||||
|  |                 self._log.info(f"Creating new PrintJob for running print with state: {print_job_state}") | ||||||
|  |                 self.current_print_job = PrintJob() | ||||||
|  |                 self.current_print_job.gcode_state = print_job_state | ||||||
|  |             elif self.current_print_job is not None: | ||||||
|  |                 self.current_print_job.gcode_state = print_job_state | ||||||
|  |                  | ||||||
|  |             # Prüfe auf zusätzliche Indikatoren für einen aktiven Druck | ||||||
|  |             is_printing = False | ||||||
|  |              | ||||||
|  |             # Check 1: Standard-Statuserkennung | ||||||
|  |             if print_job_state in ["RUNNING", "PREPARE"]: | ||||||
|  |                 is_printing = True | ||||||
|  |                  | ||||||
|  |             # Check 2: Druckfortschritt > 0 und < 100 | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                 if hasattr(self._bambu_client.device.print_job, 'mc_percent'): | ||||||
|  |                     progress = getattr(self._bambu_client.device.print_job, 'mc_percent', 0) | ||||||
|  |                     if progress > 0 and progress < 100: | ||||||
|  |                         is_printing = True | ||||||
|  |                         self._log.debug(f"Detected active printing based on progress: {progress}%") | ||||||
|  |                  | ||||||
|  |             # Check 3: Temperaturen deuten auf aktiven Druck hin | ||||||
|  |             if self._telemetry.temp[0] > 170 and self._telemetry.bedTemp > 40: | ||||||
|  |                 # Hohe aktuelle Temperaturen deuten auf einen laufenden Druck hin | ||||||
|  |                 is_printing = True | ||||||
|  |                 self._log.debug(f"Detected potential printing based on actual temperatures: " + | ||||||
|  |                              f"Nozzle={self._telemetry.temp[0]}, Bed={self._telemetry.bedTemp}") | ||||||
|  |                  | ||||||
|  |             # Statusänderung in den Zustandsautomaten übertragen basierend auf allen Checks | ||||||
|  |             if print_job_state in ["IDLE", "FINISH", "FAILED"] and not is_printing: | ||||||
|  |                 self._log.debug(f"Changing to IDLE state based on status: {print_job_state} and is_printing={is_printing}") | ||||||
|  |                 self.change_state(self._state_idle) | ||||||
|  |             elif print_job_state in ["RUNNING", "PREPARE"] or is_printing: | ||||||
|  |                 self._log.debug(f"Changing to PRINTING state based on status: {print_job_state} and is_printing={is_printing}") | ||||||
|  |                 self.change_state(self._state_printing) | ||||||
|  |             elif print_job_state == "PAUSE": | ||||||
|  |                 self._log.debug(f"Changing to PAUSED state based on status: {print_job_state}") | ||||||
|  |                 self.change_state(self._state_paused) | ||||||
|  |             else: | ||||||
|  |                 self._log.warn(f"Unknown print job state: {print_job_state}") | ||||||
|  |              | ||||||
|  |             # Aktualisiere auch die pybambu-Datenstruktur | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                 self._bambu_client.device.print_job.gcode_state = print_job_state | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing print state: {e}", exc_info=True) | ||||||
|  |             # Default to a safe state in case of errors | ||||||
|  |             self.change_state(self._state_idle) | ||||||
|  |  | ||||||
|  |     def _process_direct_temperature_data(self, print_data): | ||||||
|  |         """Verarbeitet Temperaturdaten direkt aus dem print-Objekt""" | ||||||
|  |         try: | ||||||
|  |             # Extruder Temperatur - direkt aus den Feldern | ||||||
|  |             if 'nozzle_temper' in print_data: | ||||||
|  |                 self._telemetry.temp[0] = float(print_data['nozzle_temper']) | ||||||
|  |                 self._log.debug(f"Updated nozzle temperature: {self._telemetry.temp[0]}") | ||||||
|  |             if 'nozzle_target_temper' in print_data: | ||||||
|  |                 self._telemetry.targetTemp[0] = float(print_data['nozzle_target_temper']) | ||||||
|  |                 self._log.debug(f"Updated nozzle target: {self._telemetry.targetTemp[0]}") | ||||||
|  |                  | ||||||
|  |             # Bett Temperatur | ||||||
|  |             if 'bed_temper' in print_data: | ||||||
|  |                 self._telemetry.bedTemp = float(print_data['bed_temper']) | ||||||
|  |                 self._log.debug(f"Updated bed temperature: {self._telemetry.bedTemp}") | ||||||
|  |             if 'bed_target_temper' in print_data: | ||||||
|  |                 self._telemetry.bedTargetTemp = float(print_data['bed_target_temper']) | ||||||
|  |                 self._log.debug(f"Updated bed target: {self._telemetry.bedTargetTemp}") | ||||||
|  |                  | ||||||
|  |             # Kammer Temperatur | ||||||
|  |             if 'chamber_temper' in print_data: | ||||||
|  |                 self._telemetry.chamberTemp = float(print_data['chamber_temper']) | ||||||
|  |                 self._log.debug(f"Updated chamber temperature: {self._telemetry.chamberTemp}") | ||||||
|  |                  | ||||||
|  |             # Log der aktualisierten Temperaturen | ||||||
|  |             self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " + | ||||||
|  |                           f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " + | ||||||
|  |                           f"Chamber: {self._telemetry.chamberTemp}") | ||||||
|  |                            | ||||||
|  |             # Auch im BambuClient aktualisieren | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'temperature'): | ||||||
|  |                 try: | ||||||
|  |                     temp_obj = self._bambu_client.device.temperature | ||||||
|  |                     if 'nozzle_temper' in print_data: | ||||||
|  |                         temp_obj.nozzle_temp = float(print_data['nozzle_temper']) | ||||||
|  |                     if 'nozzle_target_temper' in print_data: | ||||||
|  |                         temp_obj.target_nozzle_temp = float(print_data['nozzle_target_temper']) | ||||||
|  |                     if 'bed_temper' in print_data: | ||||||
|  |                         temp_obj.bed_temp = float(print_data['bed_temper']) | ||||||
|  |                     if 'bed_target_temper' in print_data: | ||||||
|  |                         temp_obj.target_bed_temp = float(print_data['bed_target_temper']) | ||||||
|  |                     if 'chamber_temper' in print_data: | ||||||
|  |                         temp_obj.chamber_temp = float(print_data['chamber_temper']) | ||||||
|  |                 except Exception as e: | ||||||
|  |                     self._log.error(f"Error updating BambuClient temperature: {e}", exc_info=True) | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing temperature data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _process_progress_data(self, print_data): | ||||||
|  |         """Verarbeitet Fortschrittsdaten aus MQTT-Nachrichten""" | ||||||
|  |         try: | ||||||
|  |             progress = -1 | ||||||
|  |              | ||||||
|  |             if 'mc_percent' in print_data: | ||||||
|  |                 progress = int(print_data['mc_percent']) | ||||||
|  |                  | ||||||
|  |             remaining_time = 0 | ||||||
|  |             if 'mc_remaining_time' in print_data: | ||||||
|  |                 remaining_time = int(print_data['mc_remaining_time']) | ||||||
|  |                  | ||||||
|  |             # Aktualisiere den PrintJob, wenn einer existiert | ||||||
|  |             if self.current_print_job is not None: | ||||||
|  |                 self.current_print_job.print_percentage = progress | ||||||
|  |                 self.current_print_job.remaining_time = remaining_time | ||||||
|  |                 self._log.debug(f"Updated print progress: {progress}%, remaining: {remaining_time}s") | ||||||
|  |                  | ||||||
|  |             # Aktualisiere auch die pybambu-Datenstruktur | ||||||
|  |             if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'): | ||||||
|  |                 self._bambu_client.device.print_job.mc_percent = progress | ||||||
|  |                 self._bambu_client.device.print_job.mc_remaining_time = remaining_time | ||||||
|  |                  | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error processing progress data: {e}", exc_info=True) | ||||||
|  |  | ||||||
|  |     def _update_bambu_client_state(self, payload): | ||||||
|  |         """Aktualisiert die internen Zustände des BambuClient""" | ||||||
|  |         try: | ||||||
|  |             if not hasattr(self._bambu_client, 'device'): | ||||||
|  |                 self._log.debug("BambuClient has no device attribute, initializing") | ||||||
|  |                 return | ||||||
|  |              | ||||||
|  |             if 'print' in payload: | ||||||
|  |                 print_data = payload['print'] | ||||||
|  |                  | ||||||
|  |                 # Temperatur aktualisieren | ||||||
|  |                 if 'temperature' in print_data and hasattr(self._bambu_client.device, 'temperature'): | ||||||
|  |                     temp_obj = self._bambu_client.device.temperature | ||||||
|  |                     temp_data = print_data['temperature'] | ||||||
|  |                      | ||||||
|  |                     # Direkte Zuweisung der Temperaturen | ||||||
|  |                     if 'nozzle_temp' in temp_data: | ||||||
|  |                         temp_obj.nozzle_temp = float(temp_data['nozzle_temp']) | ||||||
|  |                     if 'target_nozzle_temp' in temp_data: | ||||||
|  |                         temp_obj.target_nozzle_temp = float(temp_data['target_nozzle_temp']) | ||||||
|  |                     if 'bed_temp' in temp_data: | ||||||
|  |                         temp_obj.bed_temp = float(temp_data['bed_temp']) | ||||||
|  |                     if 'target_bed_temp' in temp_data: | ||||||
|  |                         temp_obj.target_bed_temp = float(temp_data['target_bed_temp']) | ||||||
|  |                     if 'chamber_temp' in temp_data: | ||||||
|  |                         temp_obj.chamber_temp = float(temp_data['chamber_temp']) | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error updating BambuClient state: {e}", exc_info=True) | ||||||
|  |  | ||||||
|     def _create_client_connection_async(self): |     def _create_client_connection_async(self): | ||||||
|         self._create_client_connection() |         self._create_client_connection() | ||||||
|         if self._bambu_client is None: |         if self._bambu_client is None: | ||||||
| @@ -230,38 +841,104 @@ class BambuVirtualPrinter: | |||||||
|         if ( |         if ( | ||||||
|             self._settings.get(["device_type"]) == "" |             self._settings.get(["device_type"]) == "" | ||||||
|             or self._settings.get(["serial"]) == "" |             or self._settings.get(["serial"]) == "" | ||||||
|             or self._settings.get(["username"]) == "" |  | ||||||
|             or self._settings.get(["access_code"]) == "" |  | ||||||
|         ): |         ): | ||||||
|             msg = "invalid settings to start connection with Bambu Printer" |             msg = "invalid settings to start connection with Bambu Printer" | ||||||
|             self._log.debug(msg) |             self._log.debug(msg) | ||||||
|             raise ValueError(msg) |             raise ValueError(msg) | ||||||
|  |     | ||||||
|         self._log.debug( |         # Check if we should use MQTT bridge mode | ||||||
|             f"connecting via local mqtt: {self._settings.get_boolean(['local_mqtt'])}" |         use_mqtt_bridge = self._settings.get_boolean(["use_mqtt_bridge"]) | ||||||
|         ) |          | ||||||
|         bambu_client = BambuClient( |         if use_mqtt_bridge: | ||||||
|             device_type=self._settings.get(["device_type"]), |             self._log.debug( | ||||||
|             serial=self._settings.get(["serial"]), |                 f"connecting via mqtt bridge: {self._settings.get(['mqtt_host'])}:{self._settings.get(['mqtt_port'])}" | ||||||
|             host=self._settings.get(["host"]), |             ) | ||||||
|             username=( |             self._log.debug( | ||||||
|                 "bblp" |                 f"Creating MQTT bridge client: {self._settings.get(['mqtt_host'])}:{self._settings.get(['mqtt_port'])}" | ||||||
|                 if self._settings.get_boolean(["local_mqtt"]) |             ) | ||||||
|                 else self._settings.get(["username"]) |             # Create MQTT bridge client | ||||||
|             ), |             bambu_client = BambuMqttBridgeClient( | ||||||
|             access_code=self._settings.get(["access_code"]), |                 device_type=self._settings.get(["device_type"]), | ||||||
|             local_mqtt=self._settings.get_boolean(["local_mqtt"]), |                 serial=self._settings.get(["serial"]), | ||||||
|             region=self._settings.get(["region"]), |                 host=self._settings.get(["mqtt_host"]), | ||||||
|             email=self._settings.get(["email"]), |                 mqtt_port=int(self._settings.get(["mqtt_port"]) or 1883) | ||||||
|             auth_token=self._settings.get(["auth_token"]), |             ) | ||||||
|         ) |         else: | ||||||
|  |             # Use standard BambuClient | ||||||
|  |             self._log.debug( | ||||||
|  |                 f"connecting via local mqtt: {self._settings.get_boolean(['local_mqtt'])}" | ||||||
|  |             ) | ||||||
|  |             self._log.debug( | ||||||
|  |                 f"Creating standard BambuClient with local_mqtt: {self._settings.get_boolean(['local_mqtt'])}" | ||||||
|  |             ) | ||||||
|  |             bambu_client = BambuClient( | ||||||
|  |                 device_type=self._settings.get(["device_type"]), | ||||||
|  |                 serial=self._settings.get(["serial"]), | ||||||
|  |                 host=self._settings.get(["host"]), | ||||||
|  |                 username=( | ||||||
|  |                     "bblp" | ||||||
|  |                     if self._settings.get_boolean(["local_mqtt"]) | ||||||
|  |                     else self._settings.get(["username"]) | ||||||
|  |                 ), | ||||||
|  |                 access_code=self._settings.get(["access_code"]), | ||||||
|  |                 local_mqtt=self._settings.get_boolean(["local_mqtt"]), | ||||||
|  |                 region=self._settings.get(["region"]), | ||||||
|  |                 email=self._settings.get(["email"]), | ||||||
|  |                 auth_token=self._settings.get(["auth_token"]), | ||||||
|  |             ) | ||||||
|  |          | ||||||
|         bambu_client.on_disconnect = self.on_disconnect(bambu_client.on_disconnect) |         bambu_client.on_disconnect = self.on_disconnect(bambu_client.on_disconnect) | ||||||
|         bambu_client.on_connect = self.on_connect(bambu_client.on_connect) |         bambu_client.on_connect = self.on_connect(bambu_client.on_connect) | ||||||
|         bambu_client.connect(callback=self.new_update) |          | ||||||
|         self._log.info(f"bambu connection status: {bambu_client.connected}") |         # Add more robust connection retry logic | ||||||
|  |         connection_attempts = 0 | ||||||
|  |         max_attempts = 3 | ||||||
|  |         retry_delay = 2 | ||||||
|  |          | ||||||
|  |         while connection_attempts < max_attempts: | ||||||
|  |             try: | ||||||
|  |                 self._log.info(f"Connection attempt {connection_attempts + 1}/{max_attempts}...") | ||||||
|  |                 bambu_client.connect(callback=self.new_update) | ||||||
|  |                  | ||||||
|  |                 # Wait a moment to verify connection | ||||||
|  |                 time.sleep(retry_delay) | ||||||
|  |                  | ||||||
|  |                 if bambu_client.connected: | ||||||
|  |                     self._log.info(f"Bambu connection successful: {bambu_client.connected}") | ||||||
|  |                     break | ||||||
|  |                  | ||||||
|  |                 self._log.warning("Connection attempt failed, retrying...") | ||||||
|  |                 connection_attempts += 1 | ||||||
|  |                 time.sleep(retry_delay) | ||||||
|  |             except Exception as e: | ||||||
|  |                 self._log.error(f"Error during connection attempt {connection_attempts + 1}: {str(e)}", exc_info=True) | ||||||
|  |                 connection_attempts += 1 | ||||||
|  |                 if connection_attempts < max_attempts: | ||||||
|  |                     time.sleep(retry_delay) | ||||||
|  |          | ||||||
|         self.sendOk() |         self.sendOk() | ||||||
|         self._bambu_client = bambu_client |         self._bambu_client = bambu_client | ||||||
|  |  | ||||||
|  |         self._log.info(f"Custom connection status: {self.is_connected}") | ||||||
|  |         self.sendOk() | ||||||
|  |  | ||||||
|  |     def publish_mqtt(self, topic, payload): | ||||||
|  |         """Publish a message to the MQTT broker""" | ||||||
|  |         if self._mqtt_client and self._mqtt_connected: | ||||||
|  |             return self._mqtt_client.publish(topic, json.dumps(payload)) | ||||||
|  |         return False | ||||||
|  |  | ||||||
|  |     # Override BambuClient's publish method to use our MQTT client | ||||||
|  |     def publish(self, command): | ||||||
|  |         """Publish a command using our MQTT client""" | ||||||
|  |         if not self.is_connected: | ||||||
|  |             self._log.error("Cannot publish command: MQTT not connected") | ||||||
|  |             return False | ||||||
|  |  | ||||||
|  |         serial = self._settings.get(["serial"]) | ||||||
|  |         topic = f"device/{serial}/request" | ||||||
|  |         return self.publish_mqtt(topic, command) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return "BAMBU(read_timeout={read_timeout},write_timeout={write_timeout},options={options})".format( |         return "BAMBU(read_timeout={read_timeout},write_timeout={write_timeout},options={options})".format( | ||||||
|             read_timeout=self.timeout, |             read_timeout=self.timeout, | ||||||
| @@ -269,6 +946,10 @@ class BambuVirtualPrinter: | |||||||
|             options={ |             options={ | ||||||
|                 "device_type": self._settings.get(["device_type"]), |                 "device_type": self._settings.get(["device_type"]), | ||||||
|                 "host": self._settings.get(["host"]), |                 "host": self._settings.get(["host"]), | ||||||
|  |                 "local_mqtt": self._settings.get_boolean(["local_mqtt"]), | ||||||
|  |                 "region": self._settings.get(["region"]), | ||||||
|  |                 "email": self._settings.get(["email"]), | ||||||
|  |                 "auth_token": self._settings.get(["auth_token"]), | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @@ -284,7 +965,6 @@ class BambuVirtualPrinter: | |||||||
|             if self._settings.get_boolean(["simulateReset"]): |             if self._settings.get_boolean(["simulateReset"]): | ||||||
|                 for item in self._settings.get(["resetLines"]): |                 for item in self._settings.get(["resetLines"]): | ||||||
|                     self.sendIO(item + "\n") |                     self.sendIO(item + "\n") | ||||||
|  |  | ||||||
|             self._serial_io.reset() |             self._serial_io.reset() | ||||||
|  |  | ||||||
|     def write(self, data: bytes) -> int: |     def write(self, data: bytes) -> int: | ||||||
| @@ -316,6 +996,7 @@ class BambuVirtualPrinter: | |||||||
|         file_info = self._project_files_view.get_file_by_stem( |         file_info = self._project_files_view.get_file_by_stem( | ||||||
|             file_path, [".gcode", ".3mf"] |             file_path, [".gcode", ".3mf"] | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         if ( |         if ( | ||||||
|             self._selected_project_file is not None |             self._selected_project_file is not None | ||||||
|             and file_info is not None |             and file_info is not None | ||||||
| @@ -415,7 +1096,6 @@ class BambuVirtualPrinter: | |||||||
|                 self.start_continuous_temp_report(interval) |                 self.start_continuous_temp_report(interval) | ||||||
|             else: |             else: | ||||||
|                 self.stop_continuous_temp_report() |                 self.stop_continuous_temp_report() | ||||||
|  |  | ||||||
|         self.report_print_job_status() |         self.report_print_job_status() | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
| @@ -516,7 +1196,7 @@ class BambuVirtualPrinter: | |||||||
|                 acc_mag = acc_mag_scaled(speed_percentage) |                 acc_mag = acc_mag_scaled(speed_percentage) | ||||||
|                 feed = feed_rate_scaled(speed_percentage) |                 feed = feed_rate_scaled(speed_percentage) | ||||||
|                 # speed_level = 1.539 * (acc_mag**2) - 0.7032 * acc_mag + 4.0834 |                 # speed_level = 1.539 * (acc_mag**2) - 0.7032 * acc_mag + 4.0834 | ||||||
|                 return f"M204.2 K{acc_mag:.2f}\nM220 K{feed:.2f}\nM73.2 R{speed_frac:.2f}\n" # M1002 set_gcode_claim_speed_level ${speed_level:.0f}\n |                 return f"M204.2 K{acc_mag:.2f}\nM220 K{feed:.2f}\nM73.2 R{speed_frac:.2f}\n" # M1002 set_gcode_claim_speed_level ${speed_level:.0f}\n" | ||||||
|  |  | ||||||
|             speed_command = speed_adjust(percent) |             speed_command = speed_adjust(percent) | ||||||
|  |  | ||||||
| @@ -533,10 +1213,10 @@ class BambuVirtualPrinter: | |||||||
|             return |             return | ||||||
|  |  | ||||||
|         # post gcode to printer otherwise |         # post gcode to printer otherwise | ||||||
|         if self.bambu_client.connected: |         if self.is_connected: | ||||||
|             GCODE_COMMAND = commands.SEND_GCODE_TEMPLATE |             GCODE_COMMAND = commands.SEND_GCODE_TEMPLATE | ||||||
|             GCODE_COMMAND["print"]["param"] = full_command + "\n" |             GCODE_COMMAND["print"]["param"] = full_command + "\n" | ||||||
|             if self.bambu_client.publish(GCODE_COMMAND): |             if self.publish(GCODE_COMMAND): | ||||||
|                 self._log.info("command sent successfully") |                 self._log.info("command sent successfully") | ||||||
|                 self.sendOk() |                 self.sendOk() | ||||||
|  |  | ||||||
| @@ -625,20 +1305,59 @@ class BambuVirtualPrinter: | |||||||
|         return output |         return output | ||||||
|  |  | ||||||
|     def _processTemperatureQuery(self) -> bool: |     def _processTemperatureQuery(self) -> bool: | ||||||
|         # includeOk = not self._okBeforeCommandOutput |         # Debug-Log hinzufügen, um zu prüfen, ob die Methode aufgerufen wird | ||||||
|         if self.bambu_client.connected: |         self._log.debug(f"Processing temperature query - connected: {self.is_connected}") | ||||||
|             output = self._create_temperature_message() |         # Aktuelle Temperaturdaten ausgeben | ||||||
|             self.sendIO(output) |         self._log.debug(f"Current temperature data: Nozzle={self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " + | ||||||
|             return True |                       f"Bed={self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}") | ||||||
|         else: |          | ||||||
|             return False |         # Temperaturmeldung erzeugen und senden, unabhängig von Connected-Status | ||||||
|  |         output = self._create_temperature_message() | ||||||
|  |         self._log.debug(f"Sending temperature message: {output.strip()}") | ||||||
|  |         self.sendIO(output) | ||||||
|  |         return True | ||||||
|  |  | ||||||
|     def close(self): |     def close(self): | ||||||
|         if self.bambu_client.connected: |         """Safely close all connections.""" | ||||||
|             self.bambu_client.disconnect() |         try: | ||||||
|         self.change_state(self._state_idle) |             # Log that we're starting to close connections | ||||||
|         self._serial_io.close() |             self._log.debug("Starting to close all connections...") | ||||||
|         self.stop() |              | ||||||
|  |             if self._mqtt_client and self._mqtt_connected: | ||||||
|  |                 self._log.debug("Stopping MQTT client loop and disconnecting...") | ||||||
|  |                 self._mqtt_client.loop_stop() | ||||||
|  |                 self._mqtt_client.disconnect() | ||||||
|  |                 self._mqtt_connected = False | ||||||
|  |                 self._custom_connected = False | ||||||
|  |                 self._log.debug("MQTT client disconnected") | ||||||
|  |                  | ||||||
|  |             # Sicherstellen, dass wir keinen AttributError bekommen, wenn wir den BambuClient trennen | ||||||
|  |             if self._bambu_client: | ||||||
|  |                 self._log.debug("Disconnecting BambuClient...") | ||||||
|  |                 try: | ||||||
|  |                     self._bambu_client.disconnect() | ||||||
|  |                     self._log.debug("BambuClient disconnected successfully") | ||||||
|  |                 except AttributeError: | ||||||
|  |                     # BambuClient hat keinen client-Attribut oder die disconnect-Methode funktioniert nicht wie erwartet | ||||||
|  |                     self._log.warning("BambuClient disconnect failed, cleaning up manually") | ||||||
|  |                     # Manuell aufräumen | ||||||
|  |                     if hasattr(self._bambu_client, '_mqtt_client') and self._bambu_client._mqtt_client: | ||||||
|  |                         try: | ||||||
|  |                             self._log.debug("Manually stopping BambuClient's MQTT client...") | ||||||
|  |                             self._bambu_client._mqtt_client.loop_stop() | ||||||
|  |                             self._bambu_client._mqtt_client.disconnect() | ||||||
|  |                             self._log.debug("BambuClient's MQTT client manually disconnected") | ||||||
|  |                         except Exception as ex: | ||||||
|  |                             self._log.error(f"Error during manual MQTT client cleanup: {str(ex)}") | ||||||
|  |         except Exception as e: | ||||||
|  |             self._log.error(f"Error during close: {e}", exc_info=True) | ||||||
|  |         finally: | ||||||
|  |             # Immer in einen sicheren Zustand zurückkehren | ||||||
|  |             self._log.debug("Final cleanup in close() method") | ||||||
|  |             self.change_state(self._state_idle) | ||||||
|  |             self._serial_io.close() | ||||||
|  |             self.stop() | ||||||
|  |             self._log.debug("Connection cleanup completed") | ||||||
|  |  | ||||||
|     def stop(self): |     def stop(self): | ||||||
|         self._running = False |         self._running = False | ||||||
|   | |||||||
| @@ -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" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user