Compare commits
6 Commits
fea0f0ed25
...
master
Author | SHA1 | Date | |
---|---|---|---|
a8cf4957ec | |||
c5c6ed037e | |||
fd9ce76275 | |||
8dafb9fa5a | |||
094959335a | |||
f64fa7aea2 |
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...
|
@ -14,9 +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 paho.mqtt.client as mqtt
|
|
||||||
import json
|
import json
|
||||||
import ssl
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
from octoprint.util import RepeatedTimer
|
from octoprint.util import RepeatedTimer
|
||||||
|
|
||||||
@ -48,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()
|
||||||
|
|
||||||
@ -107,12 +331,20 @@ class BambuVirtualPrinter:
|
|||||||
|
|
||||||
self._serial_io.start()
|
self._serial_io.start()
|
||||||
self._printer_thread.start()
|
self._printer_thread.start()
|
||||||
|
|
||||||
self._mqtt_client = None
|
self._mqtt_client = None
|
||||||
self._mqtt_connected = False
|
self._mqtt_connected = False
|
||||||
self._bambu_client = None
|
self._bambu_client = None
|
||||||
self._custom_connected = False
|
self._custom_connected = False
|
||||||
|
|
||||||
|
# Store initial connection errors to avoid logging the same errors repeatedly
|
||||||
|
self._connection_error_logged = False
|
||||||
|
self._camera_error_logged = False
|
||||||
|
self._last_connection_attempt = 0
|
||||||
|
self._connection_retry_backoff = 10 # Start with 10 seconds between retries
|
||||||
|
|
||||||
|
# Track if we should disable camera functionality due to persistent errors
|
||||||
|
self._disable_camera = self._settings.get_boolean(["disable_camera"]) or False
|
||||||
|
|
||||||
self._bambu_client: BambuClient = self._create_client_connection_async()
|
self._bambu_client: BambuClient = self._create_client_connection_async()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -135,10 +367,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
|
||||||
@ -180,48 +408,34 @@ class BambuVirtualPrinter:
|
|||||||
self._log.debug(f"Connection status check: custom_connected={self._custom_connected}, mqtt_connected={self._mqtt_connected}, result={connection_status}")
|
self._log.debug(f"Connection status check: custom_connected={self._custom_connected}, mqtt_connected={self._mqtt_connected}, result={connection_status}")
|
||||||
return connection_status
|
return connection_status
|
||||||
|
|
||||||
def change_state(self, new_state: APrinterState):
|
|
||||||
self._state_change_queue.put(new_state)
|
|
||||||
|
|
||||||
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":
|
|
||||||
self._update_printer_info()
|
|
||||||
|
|
||||||
def _update_printer_info(self):
|
|
||||||
# Verwende direkt die Telemetrie-Daten statt der BambuClient-Struktur
|
|
||||||
self.lastTempAt = time.monotonic()
|
|
||||||
|
|
||||||
# Der Rest der Methode kann unverändert bleiben, da wir die Telemetrie
|
|
||||||
# direkt in _process_print_data aktualisieren
|
|
||||||
|
|
||||||
# Gib den aktuellen Status detaillierter aus
|
# Gib den aktuellen Status detaillierter aus
|
||||||
|
device_data = self.bambu_client.get_device()
|
||||||
|
print_job_state = device_data.print_job.gcode_state
|
||||||
|
self._log.debug(f"BambuClient printer state: {print_job_state}")
|
||||||
self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
||||||
f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " +
|
f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " +
|
||||||
f"Chamber: {self._telemetry.chamberTemp}")
|
f"Chamber: {self._telemetry.chamberTemp}")
|
||||||
|
|
||||||
# Rufe trotzdem die BambuClient-Daten ab, falls verfügbar
|
# Process the printer state even if it's "unknown"
|
||||||
try:
|
if print_job_state:
|
||||||
device_data = self.bambu_client.get_device()
|
self._process_print_state(print_job_state)
|
||||||
print_job_state = device_data.print_job.gcode_state
|
|
||||||
|
|
||||||
self._log.debug(f"BambuClient printer state: {print_job_state}")
|
|
||||||
|
|
||||||
if (
|
|
||||||
print_job_state == "IDLE"
|
|
||||||
or print_job_state == "FINISH"
|
|
||||||
or print_job_state == "FAILED"
|
|
||||||
):
|
|
||||||
self.change_state(self._state_idle)
|
|
||||||
elif print_job_state == "RUNNING" or print_job_state == "PREPARE":
|
|
||||||
self.change_state(self._state_printing)
|
|
||||||
elif print_job_state == "PAUSE":
|
|
||||||
self.change_state(self._state_paused)
|
|
||||||
else:
|
else:
|
||||||
self._log.warn(f"Unknown print job state: {print_job_state}")
|
self._log.debug("No printer state received, skipping state processing")
|
||||||
except Exception as e:
|
|
||||||
self._log.error(f"Error reading BambuClient device state: {e}")
|
elif event_type == "event_hms_errors":
|
||||||
|
self._log.debug("Received event_hms_errors")
|
||||||
|
bambu_printer = self.bambu_client.get_device()
|
||||||
|
if bambu_printer.hms.errors != self._last_hms_errors:
|
||||||
|
self._log.debug(f"HMS Error: {bambu_printer.hms.errors}")
|
||||||
|
for n in range(1, bambu_printer.hms.errors["Count"] + 1):
|
||||||
|
error = bambu_printer.hms.errors[f"{n}-Error"].strip()
|
||||||
|
self.sendIO(f"// action:notification {error}")
|
||||||
|
self._last_hms_errors = bambu_printer.hms.errors
|
||||||
|
|
||||||
def _update_hms_errors(self):
|
def _update_hms_errors(self):
|
||||||
bambu_printer = self.bambu_client.get_device()
|
bambu_printer = self.bambu_client.get_device()
|
||||||
@ -246,18 +460,20 @@ class BambuVirtualPrinter:
|
|||||||
def _on_mqtt_connect(self, client, userdata, flags, rc):
|
def _on_mqtt_connect(self, client, userdata, flags, rc):
|
||||||
self._log.debug(f"MQTT connected with result code: {rc}")
|
self._log.debug(f"MQTT connected with result code: {rc}")
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
|
# Notify that we're connected
|
||||||
self._mqtt_connected = True
|
self._mqtt_connected = True
|
||||||
self._custom_connected = True
|
self._custom_connected = True
|
||||||
|
|
||||||
# Subscribe to the relevant topics for the Bambu printer
|
# Subscribe to the relevant topics for the Bambu printer
|
||||||
device_topic = f"device/{self._settings.get(['serial'])}/report"
|
device_topic = f"device/{self._settings.get(['serial'])}/report"
|
||||||
client.subscribe(device_topic)
|
client.subscribe(device_topic)
|
||||||
|
|
||||||
self._log.debug(f"Subscribed to topic: {device_topic}")
|
self._log.debug(f"Subscribed to topic: {device_topic}")
|
||||||
self._log.info(f"MQTT connection successful. Connected: {self.is_connected}")
|
self._log.info(f"MQTT connection successful. Connected: {self.is_connected}")
|
||||||
|
|
||||||
# Notify that we're connected
|
# Try to patch client for better error handling after successful connection
|
||||||
self.sendOk()
|
try:
|
||||||
|
self._patch_bambu_client_for_connection_errors()
|
||||||
|
except Exception as e:
|
||||||
|
self._log.warning(f"Failed to patch BambuClient for better error handling: {str(e)}")
|
||||||
else:
|
else:
|
||||||
self._mqtt_connected = False
|
self._mqtt_connected = False
|
||||||
self._custom_connected = False
|
self._custom_connected = False
|
||||||
@ -292,7 +508,6 @@ class BambuVirtualPrinter:
|
|||||||
self._update_bambu_client_state(payload)
|
self._update_bambu_client_state(payload)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error forwarding to pybambu: {e}", exc_info=True)
|
self._log.error(f"Error forwarding to pybambu: {e}", exc_info=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error processing MQTT message: {e}", exc_info=True)
|
self._log.error(f"Error processing MQTT message: {e}", exc_info=True)
|
||||||
|
|
||||||
@ -308,11 +523,9 @@ class BambuVirtualPrinter:
|
|||||||
self._process_direct_temperature_data(print_data)
|
self._process_direct_temperature_data(print_data)
|
||||||
|
|
||||||
# Status verarbeiten
|
# Status verarbeiten
|
||||||
if 'gcode_state' in print_data:
|
|
||||||
self._process_print_state(print_data['gcode_state'])
|
self._process_print_state(print_data['gcode_state'])
|
||||||
|
|
||||||
# Fortschritt verarbeiten
|
# Fortschritt verarbeiten
|
||||||
if 'mc_percent' in print_data:
|
|
||||||
self._process_progress_data(print_data)
|
self._process_progress_data(print_data)
|
||||||
|
|
||||||
# Schicht-Informationen verarbeiten
|
# Schicht-Informationen verarbeiten
|
||||||
@ -321,9 +534,6 @@ class BambuVirtualPrinter:
|
|||||||
# Lüfter-Informationen verarbeiten
|
# Lüfter-Informationen verarbeiten
|
||||||
self._process_fan_data(print_data)
|
self._process_fan_data(print_data)
|
||||||
|
|
||||||
# Geschwindigkeit verarbeiten
|
|
||||||
self._process_speed_data(print_data)
|
|
||||||
|
|
||||||
# Datei-Informationen verarbeiten
|
# Datei-Informationen verarbeiten
|
||||||
self._process_file_data(print_data)
|
self._process_file_data(print_data)
|
||||||
|
|
||||||
@ -336,7 +546,6 @@ class BambuVirtualPrinter:
|
|||||||
self._log.info(f"Processing info data with keys: {list(info_data.keys())}")
|
self._log.info(f"Processing info data with keys: {list(info_data.keys())}")
|
||||||
|
|
||||||
# HMS-Fehler verarbeiten
|
# HMS-Fehler verarbeiten
|
||||||
if 'hms' in info_data:
|
|
||||||
self._process_hms_errors(info_data['hms'])
|
self._process_hms_errors(info_data['hms'])
|
||||||
self.new_update("event_hms_errors")
|
self.new_update("event_hms_errors")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -363,13 +572,12 @@ class BambuVirtualPrinter:
|
|||||||
if total_layers is not None:
|
if total_layers is not None:
|
||||||
self.current_print_job.total_layers = total_layers
|
self.current_print_job.total_layers = total_layers
|
||||||
|
|
||||||
# Aktualisiere auch die pybambu-Datenstruktur
|
# Aktualisiere auch die pybambu-Datenstruktur, wenn vorhanden
|
||||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
||||||
if current_layer is not None:
|
if current_layer is not None:
|
||||||
self._bambu_client.device.print_job.current_layer = current_layer
|
self._bambu_client.device.print_job.current_layer = current_layer
|
||||||
if total_layers is not None:
|
if total_layers is not None:
|
||||||
self._bambu_client.device.print_job.total_layers = total_layers
|
self._bambu_client.device.print_job.total_layers = total_layers
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error processing layer data: {e}", exc_info=True)
|
self._log.error(f"Error processing layer data: {e}", exc_info=True)
|
||||||
|
|
||||||
@ -456,7 +664,6 @@ class BambuVirtualPrinter:
|
|||||||
# Aktualisiere auch die pybambu-Datenstruktur
|
# Aktualisiere auch die pybambu-Datenstruktur
|
||||||
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
if hasattr(self._bambu_client, 'device') and hasattr(self._bambu_client.device, 'print_job'):
|
||||||
self._bambu_client.device.print_job.subtask_name = subtask_name
|
self._bambu_client.device.print_job.subtask_name = subtask_name
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error processing file data: {e}", exc_info=True)
|
self._log.error(f"Error processing file data: {e}", exc_info=True)
|
||||||
|
|
||||||
@ -469,8 +676,9 @@ class BambuVirtualPrinter:
|
|||||||
# oder andere Werte haben als die, die wir erwarten
|
# oder andere Werte haben als die, die wir erwarten
|
||||||
print_job_state = print_job_state.upper() if print_job_state else "UNKNOWN"
|
print_job_state = print_job_state.upper() if print_job_state else "UNKNOWN"
|
||||||
|
|
||||||
# Normalisieren des Status, falls er 'unknown' ist oder nicht erkannt wird
|
# Explicitly handle "unknown" state (both upper and lowercase)
|
||||||
if print_job_state in ["UNKNOWN", ""]:
|
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
|
# Wenn wir keinen erkannten Status haben, versuchen wir ihn aus anderen Daten abzuleiten
|
||||||
# Prüfe ob Druckfortschritt vorhanden ist
|
# Prüfe ob Druckfortschritt vorhanden ist
|
||||||
if self.current_print_job and self.current_print_job.print_percentage > 0:
|
if self.current_print_job and self.current_print_job.print_percentage > 0:
|
||||||
@ -480,6 +688,9 @@ class BambuVirtualPrinter:
|
|||||||
elif self._telemetry.targetTemp[0] > 150 or self._telemetry.bedTargetTemp > 40:
|
elif self._telemetry.targetTemp[0] > 150 or self._telemetry.bedTargetTemp > 40:
|
||||||
print_job_state = "PREPARE"
|
print_job_state = "PREPARE"
|
||||||
self._log.debug(f"Changed unknown state to PREPARE based on target temperatures")
|
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
|
# Status im PrintJob aktualisieren
|
||||||
if self.current_print_job is None and print_job_state in ["RUNNING", "PREPARE", "PAUSE"]:
|
if self.current_print_job is None and print_job_state in ["RUNNING", "PREPARE", "PAUSE"]:
|
||||||
@ -509,7 +720,7 @@ class BambuVirtualPrinter:
|
|||||||
if self._telemetry.temp[0] > 170 and self._telemetry.bedTemp > 40:
|
if self._telemetry.temp[0] > 170 and self._telemetry.bedTemp > 40:
|
||||||
# Hohe aktuelle Temperaturen deuten auf einen laufenden Druck hin
|
# Hohe aktuelle Temperaturen deuten auf einen laufenden Druck hin
|
||||||
is_printing = True
|
is_printing = True
|
||||||
self._log.debug(f"Detected potential printing based on actual temperatures: "
|
self._log.debug(f"Detected potential printing based on actual temperatures: " +
|
||||||
f"Nozzle={self._telemetry.temp[0]}, Bed={self._telemetry.bedTemp}")
|
f"Nozzle={self._telemetry.temp[0]}, Bed={self._telemetry.bedTemp}")
|
||||||
|
|
||||||
# Statusänderung in den Zustandsautomaten übertragen basierend auf allen Checks
|
# Statusänderung in den Zustandsautomaten übertragen basierend auf allen Checks
|
||||||
@ -530,6 +741,8 @@ class BambuVirtualPrinter:
|
|||||||
self._bambu_client.device.print_job.gcode_state = print_job_state
|
self._bambu_client.device.print_job.gcode_state = print_job_state
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error processing print state: {e}", exc_info=True)
|
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):
|
def _process_direct_temperature_data(self, print_data):
|
||||||
"""Verarbeitet Temperaturdaten direkt aus dem print-Objekt"""
|
"""Verarbeitet Temperaturdaten direkt aus dem print-Objekt"""
|
||||||
@ -575,8 +788,7 @@ class BambuVirtualPrinter:
|
|||||||
if 'chamber_temper' in print_data:
|
if 'chamber_temper' in print_data:
|
||||||
temp_obj.chamber_temp = float(print_data['chamber_temper'])
|
temp_obj.chamber_temp = float(print_data['chamber_temper'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error updating BambuClient temperature: {e}")
|
self._log.error(f"Error updating BambuClient temperature: {e}", exc_info=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error processing temperature data: {e}", exc_info=True)
|
self._log.error(f"Error processing temperature data: {e}", exc_info=True)
|
||||||
|
|
||||||
@ -584,6 +796,7 @@ class BambuVirtualPrinter:
|
|||||||
"""Verarbeitet Fortschrittsdaten aus MQTT-Nachrichten"""
|
"""Verarbeitet Fortschrittsdaten aus MQTT-Nachrichten"""
|
||||||
try:
|
try:
|
||||||
progress = -1
|
progress = -1
|
||||||
|
|
||||||
if 'mc_percent' in print_data:
|
if 'mc_percent' in print_data:
|
||||||
progress = int(print_data['mc_percent'])
|
progress = int(print_data['mc_percent'])
|
||||||
|
|
||||||
@ -632,7 +845,7 @@ class BambuVirtualPrinter:
|
|||||||
if 'chamber_temp' in temp_data:
|
if 'chamber_temp' in temp_data:
|
||||||
temp_obj.chamber_temp = float(temp_data['chamber_temp'])
|
temp_obj.chamber_temp = float(temp_data['chamber_temp'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error updating BambuClient state: {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()
|
||||||
@ -644,112 +857,165 @@ 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)
|
||||||
|
|
||||||
use_local_mqtt = self._settings.get_boolean(['local_mqtt'])
|
# Check if we should use MQTT bridge mode
|
||||||
self._log.debug(f"connecting via local mqtt: {use_local_mqtt}")
|
use_mqtt_bridge = self._settings.get_boolean(["use_mqtt_bridge"])
|
||||||
|
|
||||||
# Create a BambuClient but don't let it handle the MQTT connection
|
if use_mqtt_bridge:
|
||||||
bambu_client = BambuClient(
|
self._log.debug(
|
||||||
|
f"connecting via mqtt bridge: {self._settings.get(['mqtt_host'])}:{self._settings.get(['mqtt_port'])}"
|
||||||
|
)
|
||||||
|
self._log.debug(
|
||||||
|
f"Creating MQTT bridge client: {self._settings.get(['mqtt_host'])}:{self._settings.get(['mqtt_port'])}"
|
||||||
|
)
|
||||||
|
# Create MQTT bridge client
|
||||||
|
bambu_client = BambuMqttBridgeClient(
|
||||||
device_type=self._settings.get(["device_type"]),
|
device_type=self._settings.get(["device_type"]),
|
||||||
serial=self._settings.get(["serial"]),
|
serial=self._settings.get(["serial"]),
|
||||||
host=self._settings.get(["host"]),
|
host=self._settings.get(["mqtt_host"]),
|
||||||
username="bambuocto",
|
mqtt_port=int(self._settings.get(["mqtt_port"]) or 1883)
|
||||||
access_code=self._settings.get(["access_code"]),
|
)
|
||||||
local_mqtt=use_local_mqtt,
|
else:
|
||||||
region=self._settings.get(["region"]),
|
# Use standard BambuClient
|
||||||
email=self._settings.get(["email"]),
|
self._log.debug(
|
||||||
auth_token=self._settings.get(["auth_token"]),
|
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'])}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Initialisiere die device-Eigenschaft manuell, ohne connect() zu benutzen
|
# Set up client parameters
|
||||||
# da die connect()-Methode ein Callback als Parameter erwartet
|
client_params = {
|
||||||
if not hasattr(bambu_client, 'device'):
|
"device_type": self._settings.get(["device_type"]),
|
||||||
self._log.debug("BambuClient has no device attribute, initializing manually")
|
"serial": self._settings.get(["serial"]),
|
||||||
# Statt eine BambuDevice-Klasse direkt zu importieren oder connect() zu verwenden,
|
"host": self._settings.get(["host"]),
|
||||||
# initialisieren wir die grundlegenden Attribute anders
|
"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"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add disable_camera parameter if it's enabled in settings
|
||||||
|
if self._disable_camera:
|
||||||
|
self._log.info("Camera functionality is disabled in settings")
|
||||||
|
client_params["disable_camera"] = True
|
||||||
|
client_params["enable_camera_stream"] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Manuell die notwendigen Attribute erstellen
|
bambu_client = BambuClient(**client_params)
|
||||||
bambu_client.device = type('', (), {})() # Ein leeres Objekt erstellen
|
except TypeError as e:
|
||||||
|
# Handle the case where pybambu doesn't support these parameters yet
|
||||||
# Grundlegende Attribute hinzufügen
|
if "disable_camera" in str(e) or "enable_camera_stream" in str(e):
|
||||||
bambu_client.device.temperature = type('', (), {
|
self._log.warning("This version of pybambu doesn't support camera disabling parameters, trying without them")
|
||||||
'nozzle_temp': 21.0,
|
if "disable_camera" in client_params:
|
||||||
'target_nozzle_temp': 0.0,
|
del client_params["disable_camera"]
|
||||||
'bed_temp': 21.0,
|
if "enable_camera_stream" in client_params:
|
||||||
'target_bed_temp': 0.0,
|
del client_params["enable_camera_stream"]
|
||||||
'chamber_temp': 21.0,
|
bambu_client = BambuClient(**client_params)
|
||||||
})()
|
|
||||||
|
|
||||||
bambu_client.device.print_job = type('', (), {
|
|
||||||
'gcode_state': 'IDLE',
|
|
||||||
'gcode_file': '',
|
|
||||||
'mc_percent': 0,
|
|
||||||
'mc_remaining_time': 0,
|
|
||||||
})()
|
|
||||||
|
|
||||||
bambu_client.device.hms = type('', (), {
|
|
||||||
'errors': {'Count': 0},
|
|
||||||
'update_from_payload': lambda x: None
|
|
||||||
})()
|
|
||||||
|
|
||||||
self._log.debug("Created device attributes manually")
|
|
||||||
except Exception as e:
|
|
||||||
self._log.error(f"Error initializing BambuClient: {e}", exc_info=True)
|
|
||||||
|
|
||||||
# Set up our own MQTT client
|
|
||||||
self._mqtt_client = mqtt.Client()
|
|
||||||
self._mqtt_client.on_connect = self._on_mqtt_connect
|
|
||||||
self._mqtt_client.on_disconnect = self._on_mqtt_disconnect
|
|
||||||
self._mqtt_client.on_message = self._on_mqtt_message
|
|
||||||
|
|
||||||
# Configure connection based on local or cloud
|
|
||||||
if use_local_mqtt:
|
|
||||||
host = self._settings.get(["host"])
|
|
||||||
port = 1883
|
|
||||||
username = "octobambu"
|
|
||||||
|
|
||||||
self._mqtt_client.username_pw_set(username)
|
|
||||||
else:
|
else:
|
||||||
# Cloud connection settings
|
|
||||||
region = self._settings.get(["region"])
|
|
||||||
host = f"mqtt-{region}.bambulab.com"
|
|
||||||
port = 8883
|
|
||||||
username = self._settings.get(["email"])
|
|
||||||
password = self._settings.get(["auth_token"])
|
|
||||||
|
|
||||||
self._mqtt_client.username_pw_set(username, password)
|
|
||||||
self._mqtt_client.tls_set()
|
|
||||||
|
|
||||||
# Connect MQTT
|
|
||||||
try:
|
|
||||||
self._mqtt_client.connect(host, port, 60)
|
|
||||||
self._mqtt_client.loop_start()
|
|
||||||
self._log.info(f"MQTT client started with {host}:{port}")
|
|
||||||
|
|
||||||
# Explicitly set the connection status
|
|
||||||
self._custom_connected = True
|
|
||||||
except Exception as e:
|
|
||||||
self._log.error(f"Failed to connect to MQTT broker: {e}")
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Inject our MQTT client into the BambuClient without modifying 'connected'
|
bambu_client.on_disconnect = self.on_disconnect(bambu_client.on_disconnect)
|
||||||
bambu_client._mqtt_client = self._mqtt_client
|
bambu_client.on_connect = self.on_connect(bambu_client.on_connect)
|
||||||
|
|
||||||
# Instead of modifying bambu_client.connected, we'll use our custom property
|
# Add more robust connection retry logic
|
||||||
self._custom_connected = True
|
connection_attempts = 0
|
||||||
|
max_attempts = 3
|
||||||
|
retry_delay = 2
|
||||||
|
|
||||||
# Store the Bambu client
|
while connection_attempts < max_attempts:
|
||||||
|
try:
|
||||||
|
self._log.info(f"Connection attempt {connection_attempts + 1}/{max_attempts}...")
|
||||||
|
|
||||||
|
# Check if we need to wait based on backoff
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time - self._last_connection_attempt < self._connection_retry_backoff:
|
||||||
|
wait_time = self._connection_retry_backoff - (current_time - self._last_connection_attempt)
|
||||||
|
self._log.debug(f"Waiting {wait_time:.1f}s before retry due to backoff")
|
||||||
|
time.sleep(wait_time)
|
||||||
|
|
||||||
|
self._last_connection_attempt = time.time()
|
||||||
|
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}")
|
||||||
|
# Reset connection error flags on successful connection
|
||||||
|
self._connection_error_logged = False
|
||||||
|
self._connection_retry_backoff = 10 # Reset backoff on success
|
||||||
|
break
|
||||||
|
|
||||||
|
self._log.warning("Connection attempt failed, retrying...")
|
||||||
|
connection_attempts += 1
|
||||||
|
# Increase backoff time for future connection attempts
|
||||||
|
self._connection_retry_backoff = min(300, self._connection_retry_backoff * 2) # Cap at 5 minutes
|
||||||
|
time.sleep(retry_delay)
|
||||||
|
except Exception as e:
|
||||||
|
if not self._connection_error_logged:
|
||||||
|
self._log.error(f"Error during connection attempt {connection_attempts + 1}: {str(e)}", exc_info=True)
|
||||||
|
self._connection_error_logged = True
|
||||||
|
else:
|
||||||
|
self._log.debug(f"Repeated connection error during attempt {connection_attempts + 1}: {str(e)}")
|
||||||
|
|
||||||
|
connection_attempts += 1
|
||||||
|
# Increase backoff time for future connection attempts
|
||||||
|
self._connection_retry_backoff = min(300, self._connection_retry_backoff * 2)
|
||||||
|
|
||||||
|
if connection_attempts < max_attempts:
|
||||||
|
time.sleep(retry_delay)
|
||||||
|
|
||||||
|
self.sendOk()
|
||||||
self._bambu_client = bambu_client
|
self._bambu_client = bambu_client
|
||||||
|
|
||||||
self._log.info(f"Custom connection status: {self.is_connected}")
|
self._log.info(f"Custom connection status: {self.is_connected}")
|
||||||
self.sendOk()
|
self.sendOk()
|
||||||
|
|
||||||
|
def _patch_bambu_client_for_connection_errors(self):
|
||||||
|
"""Patch the BambuClient instance to handle connection errors gracefully"""
|
||||||
|
if not self._bambu_client:
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we need to modify how the library handles connection errors, particularly
|
||||||
|
# for the Chamber Image functionality, we can patch the relevant methods here
|
||||||
|
|
||||||
|
# Check if the client has chamber image functionality and it's causing errors
|
||||||
|
if hasattr(self._bambu_client, '_chamber_image_thread') and self._bambu_client._chamber_image_thread:
|
||||||
|
original_run = None
|
||||||
|
|
||||||
|
# Find the run function in the thread class
|
||||||
|
if hasattr(self._bambu_client._chamber_image_thread, 'run'):
|
||||||
|
original_run = self._bambu_client._chamber_image_thread.run
|
||||||
|
|
||||||
|
# Create a wrapper that catches connection errors
|
||||||
|
def patched_run(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
return original_run(*args, **kwargs)
|
||||||
|
except ConnectionRefusedError as e:
|
||||||
|
# Only log first occurrence to avoid log spam
|
||||||
|
if not self._camera_error_logged:
|
||||||
|
self._log.warning(f"Chamber image connection refused: {str(e)}. Further errors will be suppressed.")
|
||||||
|
self._camera_error_logged = True
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
self._log.error(f"Chamber image error: {str(e)}", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Apply the patched method
|
||||||
|
self._bambu_client._chamber_image_thread.run = patched_run
|
||||||
|
self._log.debug("Patched chamber image thread to handle connection errors")
|
||||||
|
|
||||||
def publish_mqtt(self, topic, payload):
|
def publish_mqtt(self, topic, payload):
|
||||||
"""Publish a message to the MQTT broker"""
|
"""Publish a message to the MQTT broker"""
|
||||||
if self._mqtt_client and self._mqtt_connected:
|
if self._mqtt_client and self._mqtt_connected:
|
||||||
@ -765,7 +1031,6 @@ class BambuVirtualPrinter:
|
|||||||
|
|
||||||
serial = self._settings.get(["serial"])
|
serial = self._settings.get(["serial"])
|
||||||
topic = f"device/{serial}/request"
|
topic = f"device/{serial}/request"
|
||||||
|
|
||||||
return self.publish_mqtt(topic, command)
|
return self.publish_mqtt(topic, command)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -775,6 +1040,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"]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -790,7 +1059,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:
|
||||||
@ -822,6 +1090,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
|
||||||
@ -921,7 +1190,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
|
||||||
|
|
||||||
@ -1022,7 +1290,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)
|
||||||
|
|
||||||
@ -1133,7 +1401,6 @@ class BambuVirtualPrinter:
|
|||||||
def _processTemperatureQuery(self) -> bool:
|
def _processTemperatureQuery(self) -> bool:
|
||||||
# Debug-Log hinzufügen, um zu prüfen, ob die Methode aufgerufen wird
|
# Debug-Log hinzufügen, um zu prüfen, ob die Methode aufgerufen wird
|
||||||
self._log.debug(f"Processing temperature query - connected: {self.is_connected}")
|
self._log.debug(f"Processing temperature query - connected: {self.is_connected}")
|
||||||
|
|
||||||
# Aktuelle Temperaturdaten ausgeben
|
# Aktuelle Temperaturdaten ausgeben
|
||||||
self._log.debug(f"Current temperature data: Nozzle={self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
self._log.debug(f"Current temperature data: Nozzle={self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
|
||||||
f"Bed={self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}")
|
f"Bed={self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}")
|
||||||
@ -1142,39 +1409,49 @@ class BambuVirtualPrinter:
|
|||||||
output = self._create_temperature_message()
|
output = self._create_temperature_message()
|
||||||
self._log.debug(f"Sending temperature message: {output.strip()}")
|
self._log.debug(f"Sending temperature message: {output.strip()}")
|
||||||
self.sendIO(output)
|
self.sendIO(output)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Safely close all connections."""
|
"""Safely close all connections."""
|
||||||
try:
|
try:
|
||||||
|
# Log that we're starting to close connections
|
||||||
|
self._log.debug("Starting to close all connections...")
|
||||||
|
|
||||||
if self._mqtt_client and self._mqtt_connected:
|
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.loop_stop()
|
||||||
self._mqtt_client.disconnect()
|
self._mqtt_client.disconnect()
|
||||||
self._mqtt_connected = False
|
self._mqtt_connected = False
|
||||||
self._custom_connected = False
|
self._custom_connected = False
|
||||||
|
self._log.debug("MQTT client disconnected")
|
||||||
|
|
||||||
# Sicherstellen, dass wir keinen AttributError bekommen, wenn wir den BambuClient trennen
|
# Sicherstellen, dass wir keinen AttributError bekommen, wenn wir den BambuClient trennen
|
||||||
if self._bambu_client:
|
if self._bambu_client:
|
||||||
|
self._log.debug("Disconnecting BambuClient...")
|
||||||
try:
|
try:
|
||||||
self._bambu_client.disconnect()
|
self._bambu_client.disconnect()
|
||||||
|
self._log.debug("BambuClient disconnected successfully")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# BambuClient hat keinen client-Attribut oder die disconnect-Methode funktioniert nicht wie erwartet
|
# BambuClient hat keinen client-Attribut oder die disconnect-Methode funktioniert nicht wie erwartet
|
||||||
self._log.warning("BambuClient disconnect failed, cleaning up manually")
|
self._log.warning("BambuClient disconnect failed, cleaning up manually")
|
||||||
# Manuell aufräumen
|
# Manuell aufräumen
|
||||||
if hasattr(self._bambu_client, '_mqtt_client') and self._bambu_client._mqtt_client:
|
if hasattr(self._bambu_client, '_mqtt_client') and self._bambu_client._mqtt_client:
|
||||||
try:
|
try:
|
||||||
|
self._log.debug("Manually stopping BambuClient's MQTT client...")
|
||||||
self._bambu_client._mqtt_client.loop_stop()
|
self._bambu_client._mqtt_client.loop_stop()
|
||||||
self._bambu_client._mqtt_client.disconnect()
|
self._bambu_client._mqtt_client.disconnect()
|
||||||
except:
|
self._log.debug("BambuClient's MQTT client manually disconnected")
|
||||||
pass
|
except Exception as ex:
|
||||||
|
self._log.error(f"Error during manual MQTT client cleanup: {str(ex)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.error(f"Error during close: {e}", exc_info=True)
|
self._log.error(f"Error during close: {e}", exc_info=True)
|
||||||
finally:
|
finally:
|
||||||
# Immer in einen sicheren Zustand zurückkehren
|
# Immer in einen sicheren Zustand zurückkehren
|
||||||
|
self._log.debug("Final cleanup in close() method")
|
||||||
self.change_state(self._state_idle)
|
self.change_state(self._state_idle)
|
||||||
self._serial_io.close()
|
self._serial_io.close()
|
||||||
self.stop()
|
self.stop()
|
||||||
|
self._log.debug("Connection cleanup completed")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._running = False
|
self._running = False
|
||||||
|
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