Compare commits

..

21 Commits

Author SHA1 Message Date
fea0f0ed25 Erweitere die Statusverarbeitung in BambuVirtualPrinter zur besseren Erkennung von Druckzuständen und verbessere die Fehlerbehandlung bei der Statusaktualisierung. 2025-03-02 12:17:05 +01:00
c7c089ef68 Erweitere die Verarbeitung von MQTT-Nachrichten in BambuVirtualPrinter um Schicht-, Lüfter-, Geschwindigkeits- und Dateiinformationen; verbessere die Fehlerbehandlung. 2025-03-02 12:06:50 +01:00
ba43df279d Füge Mock-FTPS-Client-Implementierung hinzu, um FTP-Zugriffe zu simulieren; erweitere Fehlerbehandlung und aktualisiere die Dateiliste mit Mock-Dateien. 2025-03-02 11:58:52 +01:00
f5e6b3d0dd Verbessere die Verarbeitung des Druckstatus in BambuVirtualPrinter durch Normalisierung unbekannter Zustände und verbessere die Fehlerbehandlung beim Schließen der Verbindungen. 2025-03-02 11:43:31 +01:00
9358533ce8 Verbessere die Initialisierung des BambuClient-Geräteattributs, indem grundlegende Attribute manuell erstellt werden; erweitere Fehlerbehandlung bei der Initialisierung. 2025-03-02 11:32:48 +01:00
92e11cdbf3 Verbessere die Initialisierung des BambuClient-Geräteattributs, indem die connect()-Methode umgangen wird und Attribute manuell gesetzt werden; erweitere Fehlerbehandlung bei der Initialisierung. 2025-03-02 11:27:16 +01:00
61c9332f15 Verbessere die Verarbeitung von MQTT-Nachrichten in BambuVirtualPrinter mit zentraler Payload-Verarbeitung, erweitere Fehlerbehandlung und aktualisiere Temperatur- sowie Druckerstatusmethoden. 2025-03-02 11:23:32 +01:00
ad08d3eb9a Initialisiere BambuClient-Geräteattribut vor der MQTT-Verbindung und entferne überflüssige Initialisierung im Verbindungsstatus 2025-03-02 11:17:52 +01:00
5661c11190 Verbessere Verbindungsstatusverfolgung in BambuVirtualPrinter mit erweiterten Debug-Logs und informiere über erfolgreiche MQTT-Verbindungen 2025-03-02 11:13:04 +01:00
3690767ced Verbessere MQTT-Nachrichtenverarbeitung in BambuVirtualPrinter mit erweiterten Debug-Logs und Fehlerbehandlung; aktualisiere Temperaturabfrage zur Ausgabe aktueller Daten unabhängig vom Verbindungsstatus 2025-03-02 11:03:12 +01:00
eb397ff7b7 Aktualisiere Temperatur- und Druckerstatusverarbeitung in BambuVirtualPrinter zur direkten Nutzung von Telemetriedaten und verbessere Fehlerbehandlung bei MQTT-Nachrichten 2025-03-02 10:53:37 +01:00
3a615cfafe Füge benutzerdefinierte Verbindungsstatusverfolgung für BambuVirtualPrinter hinzu 2025-03-02 10:44:12 +01:00
e9c06bb4b5 Füge Aufruf von sendOk() nach erfolgreicher Verbindung zum Bambu-Client hinzu 2025-03-02 10:33:16 +01:00
3ccce10648 Füge paho-mqtt als Abhängigkeit für MQTT-Unterstützung hinzu 2025-03-02 10:13:08 +01:00
c99eb38655 Implement MQTT support for BambuVirtualPrinter, including connection, message handling, and publishing commands 2025-03-02 10:09:57 +01:00
698f8f4151 set default username for BambuVirtualPrinter 2025-03-02 09:51:01 +01:00
7a0293bac7 update plugin details and author information; change username and URLs 2025-03-02 09:38:56 +01:00
d0fd4a5434 0.1.7
add back missing PREPARE printing state and associate printing status
2024-09-27 09:38:23 -04:00
3c218a548d add issue templates, funding, and stale bot 2024-09-12 19:56:40 -04:00
03af51608d 0.1.6
* replace 0 with 1 bytes during reporting print status to trigger state change in OctoPrint sooner.
2024-09-06 01:39:48 -04:00
c00285b1b2 0.1.5
* adjust M220 feed rate modifier calculations
2024-09-05 22:35:34 -04:00
11 changed files with 837 additions and 150 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
github: [jneilliii]
patreon: jneilliii
custom: ['https://www.paypal.me/jneilliii']

26
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,26 @@
---
name: Bug report
about: Please make sure to check other issues, including closed ones, prior to submitting a bug report. Debug logs are required and any bug report submitted without them will be ignored and closed.
title: "[BUG]: "
labels: ''
assignees: ''
---
**Describe the Bug**
<!-- A clear and concise description of what the bug is. -->
**Expected Behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Debug Logs**
<!-- If logs are not included in your bug report it will be closed. Enable debug logging for octoprint.plugins.bambu_printer in OctoPrint's logging section of settings and recreate the issue then attach octoprint.log and plugin_bambu_printer_serial.log to this bug report. -->
**Screenshots**
<!-- Please share any relevant screenshots related to the issue. -->
**Printer and Plugin Setting Details**
* Printer model?
* Is your printer connected to Bambu Cloud?
* Is the plugin configured for local access only?

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Create a feature request for an improvement or change you'd like implemented.
title: "[FR]: "
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

16
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,16 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 14
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- enhancement
- bug
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
activity in 14 days. It will be closed if no further activity occurs in 7 days.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

27
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Mark Stale Issues
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
permissions:
actions: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been automatically marked as stale because it has not had activity in 14 days. It will be closed if no further activity occurs in 7 days'
days-before-stale: 14
days-before-close: 7
stale-issue-label: 'stale'
days-before-issue-stale: 14
days-before-pr-stale: -1
days-before-issue-close: 7
days-before-pr-close: -1
exempt-issue-labels: 'bug,enhancement'
- uses: actions/checkout@v4
- uses: gautamkrishnar/keepalive-workflow@v2
with:
use_api: true

View File

@ -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",
} }
} }

View File

@ -14,6 +14,9 @@ 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 ssl
from octoprint.util import RepeatedTimer from octoprint.util import RepeatedTimer
@ -105,6 +108,11 @@ 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
@ -165,6 +173,13 @@ class BambuVirtualPrinter:
def project_files(self): def project_files(self):
return self._project_files_view return self._project_files_view
@property
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 change_state(self, new_state: APrinterState): def change_state(self, new_state: APrinterState):
self._state_change_queue.put(new_state) self._state_change_queue.put(new_state)
@ -175,30 +190,38 @@ class BambuVirtualPrinter:
self._update_printer_info() self._update_printer_info()
def _update_printer_info(self): def _update_printer_info(self):
device_data = self.bambu_client.get_device() # Verwende direkt die Telemetrie-Daten statt der BambuClient-Struktur
print_job_state = device_data.print_job.gcode_state
temperatures = device_data.temperature
self.lastTempAt = time.monotonic() self.lastTempAt = time.monotonic()
self._telemetry.temp[0] = temperatures.nozzle_temp
self._telemetry.targetTemp[0] = temperatures.target_nozzle_temp # Der Rest der Methode kann unverändert bleiben, da wir die Telemetrie
self._telemetry.bedTemp = temperatures.bed_temp # direkt in _process_print_data aktualisieren
self._telemetry.bedTargetTemp = temperatures.target_bed_temp
self._telemetry.chamberTemp = temperatures.chamber_temp # Gib den aktuellen Status detaillierter aus
self._log.debug(f"Current temperatures - Nozzle: {self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
self._log.debug(f"Received printer state update: {print_job_state}") f"Bed: {self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}, " +
if ( f"Chamber: {self._telemetry.chamberTemp}")
print_job_state == "IDLE"
or print_job_state == "FINISH" # Rufe trotzdem die BambuClient-Daten ab, falls verfügbar
or print_job_state == "FAILED" try:
): device_data = self.bambu_client.get_device()
self.change_state(self._state_idle) print_job_state = device_data.print_job.gcode_state
elif print_job_state == "RUNNING":
self.change_state(self._state_printing) self._log.debug(f"BambuClient printer state: {print_job_state}")
elif print_job_state == "PAUSE":
self.change_state(self._state_paused) if (
else: print_job_state == "IDLE"
self._log.warn(f"Unknown print job state: {print_job_state}") 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:
self._log.warn(f"Unknown print job state: {print_job_state}")
except Exception as e:
self._log.error(f"Error reading BambuClient device state: {e}")
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 +243,397 @@ 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:
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}")
# Notify that we're connected
self.sendOk()
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
if 'gcode_state' in print_data:
self._process_print_state(print_data['gcode_state'])
# Fortschritt verarbeiten
if 'mc_percent' in print_data:
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)
# Geschwindigkeit verarbeiten
self._process_speed_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
if 'hms' in info_data:
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
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"
# Normalisieren des Status, falls er 'unknown' ist oder nicht erkannt wird
if print_job_state in ["UNKNOWN", ""]:
# 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")
# 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)
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}")
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}")
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:
@ -237,31 +651,123 @@ class BambuVirtualPrinter:
self._log.debug(msg) self._log.debug(msg)
raise ValueError(msg) raise ValueError(msg)
self._log.debug( use_local_mqtt = self._settings.get_boolean(['local_mqtt'])
f"connecting via local mqtt: {self._settings.get_boolean(['local_mqtt'])}" self._log.debug(f"connecting via local mqtt: {use_local_mqtt}")
)
# Create a BambuClient but don't let it handle the MQTT connection
bambu_client = BambuClient( bambu_client = BambuClient(
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(["host"]),
username=( username="bambuocto",
"bblp"
if self._settings.get_boolean(["local_mqtt"])
else self._settings.get(["username"])
),
access_code=self._settings.get(["access_code"]), access_code=self._settings.get(["access_code"]),
local_mqtt=self._settings.get_boolean(["local_mqtt"]), local_mqtt=use_local_mqtt,
region=self._settings.get(["region"]), region=self._settings.get(["region"]),
email=self._settings.get(["email"]), email=self._settings.get(["email"]),
auth_token=self._settings.get(["auth_token"]), 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) # Initialisiere die device-Eigenschaft manuell, ohne connect() zu benutzen
bambu_client.connect(callback=self.new_update) # da die connect()-Methode ein Callback als Parameter erwartet
self._log.info(f"bambu connection status: {bambu_client.connected}") if not hasattr(bambu_client, 'device'):
self.sendOk() 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
self._custom_connected = True
# Store the Bambu client
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,
@ -474,19 +980,51 @@ class BambuVirtualPrinter:
gcode_command = commands.SEND_GCODE_TEMPLATE gcode_command = commands.SEND_GCODE_TEMPLATE
percent = int(data.replace("M220 S", "")) percent = int(data.replace("M220 S", ""))
if percent is None or percent < 1 or percent > 166: def speed_fraction(speed_percent):
return True return math.floor(10000 / speed_percent) / 100
speed_fraction = 100 / percent def acceleration_magnitude(speed_percent):
acceleration = math.exp((speed_fraction - 1.0191) / -0.814) return math.exp((speed_fraction(speed_percent) - 1.0191) / -0.8139)
feed_rate = (
2.1645 * (acceleration**3) def feed_rate(speed_percent):
- 5.3247 * (acceleration**2) return 6.426e-5 * speed_percent ** 2 - 2.484e-3 * speed_percent + 0.654
+ 4.342 * acceleration
- 0.181 def linear_interpolate(x, x_points, y_points):
) if x <= x_points[0]: return y_points[0]
speed_level = 1.539 * (acceleration**2) - 0.7032 * acceleration + 4.0834 if x >= x_points[-1]: return y_points[-1]
speed_command = f"M204.2 K${acceleration:.2f} \nM220 K${feed_rate:.2f} \nM73.2 R${speed_fraction:.2f} \nM1002 set_gcode_claim_speed_level ${speed_level:.0f}\n" for i in range(len(x_points) - 1):
if x_points[i] <= x < x_points[i + 1]:
t = (x - x_points[i]) / (x_points[i + 1] - x_points[i])
return y_points[i] * (1 - t) + y_points[i + 1] * t
def scale_to_data_points(func, data_points):
data_points.sort(key=lambda x: x[0])
speeds, values = zip(*data_points)
scaling_factors = [v / func(s) for s, v in zip(speeds, values)]
return lambda x: func(x) * linear_interpolate(x, speeds, scaling_factors)
def speed_adjust(speed_percentage):
if not 30 <= speed_percentage <= 180:
speed_percentage = 100
bambu_params = {
"speed": [50, 100, 124, 166],
"acceleration": [0.3, 1.0, 1.4, 1.6],
"feed_rate": [0.7, 1.0, 1.4, 2.0]
}
acc_mag_scaled = scale_to_data_points(acceleration_magnitude,
list(zip(bambu_params["speed"], bambu_params["acceleration"])))
feed_rate_scaled = scale_to_data_points(feed_rate,
list(zip(bambu_params["speed"], bambu_params["feed_rate"])))
speed_frac = speed_fraction(speed_percentage)
acc_mag = acc_mag_scaled(speed_percentage)
feed = feed_rate_scaled(speed_percentage)
# 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
speed_command = speed_adjust(percent)
gcode_command["print"]["param"] = speed_command gcode_command["print"]["param"] = speed_command
if self.bambu_client.publish(gcode_command): if self.bambu_client.publish(gcode_command):
@ -501,10 +1039,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()
@ -548,8 +1086,9 @@ class BambuVirtualPrinter:
def report_print_job_status(self): def report_print_job_status(self):
if self.current_print_job is not None: if self.current_print_job is not None:
file_position = 1 if self.current_print_job.file_position == 0 else self.current_print_job.file_position
self.sendIO( self.sendIO(
f"SD printing byte {self.current_print_job.file_position}" f"SD printing byte {file_position}"
f"/{self.current_print_job.file_info.size}" f"/{self.current_print_job.file_info.size}"
) )
else: else:
@ -592,20 +1131,50 @@ 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()
self.sendIO(output) # Aktuelle Temperaturdaten ausgeben
return True self._log.debug(f"Current temperature data: Nozzle={self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
else: f"Bed={self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}")
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) if self._mqtt_client and self._mqtt_connected:
self._serial_io.close() self._mqtt_client.loop_stop()
self.stop() self._mqtt_client.disconnect()
self._mqtt_connected = False
self._custom_connected = False
# Sicherstellen, dass wir keinen AttributError bekommen, wenn wir den BambuClient trennen
if self._bambu_client:
try:
self._bambu_client.disconnect()
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._bambu_client._mqtt_client.loop_stop()
self._bambu_client._mqtt_client.disconnect()
except:
pass
except Exception as e:
self._log.error(f"Error during close: {e}", exc_info=True)
finally:
# Immer in einen sicheren Zustand zurückkehren
self.change_state(self._state_idle)
self._serial_io.close()
self.stop()
def stop(self): def stop(self):
self._running = False self._running = False

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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.4" 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"