Merge branch 'neu'
This commit is contained in:
commit
094959335a
@ -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,6 +47,167 @@ class BambuPrinterTelemetry:
|
|||||||
extruderCount: int = 1
|
extruderCount: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
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")
|
||||||
|
|
||||||
|
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
|
||||||
|
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"""
|
||||||
|
if 'print' in data and 'gcode_state' in data['print']:
|
||||||
|
self._device_data.print_job.gcode_state = data['print']['gcode_state']
|
||||||
|
|
||||||
|
if 'temperature' in data:
|
||||||
|
temp = self._device_data.temperature
|
||||||
|
temp_data = data['temperature']
|
||||||
|
|
||||||
|
if 'nozzle_temp' in temp_data:
|
||||||
|
temp.nozzle_temp = temp_data['nozzle_temp']
|
||||||
|
if 'target_nozzle_temp' in temp_data:
|
||||||
|
temp.target_nozzle_temp = temp_data['target_nozzle_temp']
|
||||||
|
if 'bed_temp' in temp_data:
|
||||||
|
temp.bed_temp = temp_data['bed_temp']
|
||||||
|
if 'target_bed_temp' in temp_data:
|
||||||
|
temp.target_bed_temp = temp_data['target_bed_temp']
|
||||||
|
if 'chamber_temp' in temp_data:
|
||||||
|
temp.chamber_temp = temp_data['chamber_temp']
|
||||||
|
|
||||||
|
def _process_hms_message(self, data):
|
||||||
|
"""Process HMS error messages"""
|
||||||
|
if 'hms' in data:
|
||||||
|
error_count = 0
|
||||||
|
hms_errors = {"Count": 0}
|
||||||
|
|
||||||
|
for error in data['hms']:
|
||||||
|
error_count += 1
|
||||||
|
hms_errors[f"{error_count}-Error"] = error['msg']
|
||||||
|
|
||||||
|
hms_errors["Count"] = error_count
|
||||||
|
self._device_data.hms.errors = hms_errors
|
||||||
|
|
||||||
|
def connect(self, callback=None):
|
||||||
|
"""Connect to MQTT broker"""
|
||||||
|
if callback:
|
||||||
|
self._callbacks['callback'] = callback
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._mqtt_client.connect(self._host, self._mqtt_port)
|
||||||
|
self._mqtt_client.loop_start()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._log.error(f"Failed to connect to MQTT broker: {str(e)}")
|
||||||
|
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:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
topic_base = f"device/{self._device_type}/{self._serial}"
|
||||||
|
if 'print' in command and 'param' in command['print']:
|
||||||
|
# Assuming commands go to command topic
|
||||||
|
message = json.dumps(command)
|
||||||
|
self._mqtt_client.publish(f"{topic_base}/cmd", message)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self._log.error(f"Failed to publish command: {str(e)}")
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
class BambuVirtualPrinter:
|
class BambuVirtualPrinter:
|
||||||
gcode_executor = GCodeExecutor()
|
gcode_executor = GCodeExecutor()
|
||||||
@ -644,107 +804,51 @@ 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
|
|
||||||
bambu_client = BambuClient(
|
|
||||||
device_type=self._settings.get(["device_type"]),
|
|
||||||
serial=self._settings.get(["serial"]),
|
|
||||||
host=self._settings.get(["host"]),
|
|
||||||
username="bambuocto",
|
|
||||||
access_code=self._settings.get(["access_code"]),
|
|
||||||
local_mqtt=use_local_mqtt,
|
|
||||||
region=self._settings.get(["region"]),
|
|
||||||
email=self._settings.get(["email"]),
|
|
||||||
auth_token=self._settings.get(["auth_token"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialisiere die device-Eigenschaft manuell, ohne connect() zu benutzen
|
|
||||||
# da die connect()-Methode ein Callback als Parameter erwartet
|
|
||||||
if not hasattr(bambu_client, 'device'):
|
|
||||||
self._log.debug("BambuClient has no device attribute, initializing manually")
|
|
||||||
# Statt eine BambuDevice-Klasse direkt zu importieren oder connect() zu verwenden,
|
|
||||||
# initialisieren wir die grundlegenden Attribute anders
|
|
||||||
try:
|
|
||||||
# Manuell die notwendigen Attribute erstellen
|
|
||||||
bambu_client.device = type('', (), {})() # Ein leeres Objekt erstellen
|
|
||||||
|
|
||||||
# Grundlegende Attribute hinzufügen
|
|
||||||
bambu_client.device.temperature = type('', (), {
|
|
||||||
'nozzle_temp': 21.0,
|
|
||||||
'target_nozzle_temp': 0.0,
|
|
||||||
'bed_temp': 21.0,
|
|
||||||
'target_bed_temp': 0.0,
|
|
||||||
'chamber_temp': 21.0,
|
|
||||||
})()
|
|
||||||
|
|
||||||
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:
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Inject our MQTT client into the BambuClient without modifying 'connected'
|
|
||||||
bambu_client._mqtt_client = self._mqtt_client
|
|
||||||
|
|
||||||
# Instead of modifying bambu_client.connected, we'll use our custom property
|
if use_mqtt_bridge:
|
||||||
self._custom_connected = True
|
self._log.debug(
|
||||||
|
f"connecting via mqtt bridge: {self._settings.get(['mqtt_host'])}:{self._settings.get(['mqtt_port'])}"
|
||||||
# Store the Bambu client
|
)
|
||||||
|
# Create MQTT bridge client
|
||||||
|
bambu_client = BambuMqttBridgeClient(
|
||||||
|
device_type=self._settings.get(["device_type"]),
|
||||||
|
serial=self._settings.get(["serial"]),
|
||||||
|
host=self._settings.get(["mqtt_host"]),
|
||||||
|
mqtt_port=int(self._settings.get(["mqtt_port"]) or 1883)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Use standard BambuClient
|
||||||
|
self._log.debug(
|
||||||
|
f"connecting via 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_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}")
|
||||||
|
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}")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user