Compare commits

..

25 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
7f1ae5a24b 0.1.4 (#43)
* fix stuck Printing from SD state when canceled in slicer or on printer, #42
2024-09-04 16:48:16 -04:00
5754e81b72 0.1.3
fix file uploads
2024-08-25 14:20:45 -04:00
cd4103cc71 0.1.2 (#40)
* fix issues related to 8dot3 filenames used in M23 command, #39 
* switch to auto reporting temp and sd status
2024-08-18 01:06:57 -04:00
01c6cacf15 0.1.1
* fix M220 command, #35
2024-07-31 00:01:44 -04:00
14 changed files with 881 additions and 155 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": "",
"host": "",
"access_code": "",
"username": "bblp",
"username": "octobambu",
"timelapse": False,
"bed_leveling": True,
"flow_cali": False,
@ -286,10 +286,10 @@ class BambuPrintPlugin(
def get_update_information(self):
return {
"bambu_printer": {
"displayName": "Bambu Printer",
"displayName": "Manus Bambu Printer",
"displayVersion": self._plugin_version,
"type": "github_release",
"user": "jneilliii",
"user": "ManuelW",
"repo": "OctoPrint-BambuPrinter",
"current": self._plugin_version,
"stable_branch": {
@ -304,6 +304,6 @@ class BambuPrintPlugin(
"comittish": ["rc", "master"],
}
],
"pip": "https://github.com/jneilliii/OctoPrint-BambuPrinter/archive/{target_version}.zip",
"pip": "https://gitlab.fire-devils.org/3D-Druck/OctoPrint-BambuPrinter/archive/{target_version}.zip",
}
}

View File

@ -14,6 +14,9 @@ from octoprint_bambu_printer.printer.print_job import PrintJob
from pybambu import BambuClient, commands
import logging
import logging.handlers
import paho.mqtt.client as mqtt
import json
import ssl
from octoprint.util import RepeatedTimer
@ -72,6 +75,7 @@ class BambuVirtualPrinter:
self._running = True
self._print_status_reporter = None
self._print_temp_reporter = None
self._printer_thread = threading.Thread(
target=self._printer_worker,
name="octoprint.plugins.bambu_printer.printer_state",
@ -104,6 +108,11 @@ class BambuVirtualPrinter:
self._serial_io.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()
@property
@ -164,6 +173,13 @@ class BambuVirtualPrinter:
def project_files(self):
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):
self._state_change_queue.put(new_state)
@ -174,30 +190,38 @@ class BambuVirtualPrinter:
self._update_printer_info()
def _update_printer_info(self):
device_data = self.bambu_client.get_device()
print_job_state = device_data.print_job.gcode_state
temperatures = device_data.temperature
# Verwende direkt die Telemetrie-Daten statt der BambuClient-Struktur
self.lastTempAt = time.monotonic()
self._telemetry.temp[0] = temperatures.nozzle_temp
self._telemetry.targetTemp[0] = temperatures.target_nozzle_temp
self._telemetry.bedTemp = temperatures.bed_temp
self._telemetry.bedTargetTemp = temperatures.target_bed_temp
self._telemetry.chamberTemp = temperatures.chamber_temp
self._log.debug(f"Received printer state update: {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":
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}")
# Der Rest der Methode kann unverändert bleiben, da wir die Telemetrie
# direkt in _process_print_data aktualisieren
# Gib den aktuellen Status detaillierter aus
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}")
# Rufe trotzdem die BambuClient-Daten ab, falls verfügbar
try:
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}")
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:
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):
bambu_printer = self.bambu_client.get_device()
@ -219,6 +243,397 @@ class BambuVirtualPrinter:
self._log.debug(f"on connect called")
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):
self._create_client_connection()
if self._bambu_client is None:
@ -236,31 +651,123 @@ class BambuVirtualPrinter:
self._log.debug(msg)
raise ValueError(msg)
self._log.debug(
f"connecting via local mqtt: {self._settings.get_boolean(['local_mqtt'])}"
)
use_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(
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"])
),
username="bambuocto",
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"]),
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()
# 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
self._custom_connected = True
# Store the 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):
return "BAMBU(read_timeout={read_timeout},write_timeout={write_timeout},options={options})".format(
read_timeout=self.timeout,
@ -367,8 +874,10 @@ class BambuVirtualPrinter:
interval = int(matchS.group(1))
if interval > 0:
self.start_continuous_status_report(interval)
return False
else:
self.stop_continuous_status_report()
return False
self.report_print_job_status()
return True
@ -403,10 +912,39 @@ class BambuVirtualPrinter:
self._processTemperatureQuery()
return True
@gcode_executor.register("M155")
def _auto_report_temperatures(self, data: str) -> bool:
matchS = re.search(r"S([0-9]+)", data)
if matchS:
interval = int(matchS.group(1))
if interval > 0:
self.start_continuous_temp_report(interval)
else:
self.stop_continuous_temp_report()
self.report_print_job_status()
return True
def start_continuous_temp_report(self, interval: int):
if self._print_temp_reporter is not None:
self._print_temp_reporter.cancel()
self._print_temp_reporter = RepeatedTimer(
interval, self._processTemperatureQuery
)
self._print_temp_reporter.start()
def stop_continuous_temp_report(self):
if self._print_temp_reporter is not None:
self._print_temp_reporter.cancel()
self._print_temp_reporter = None
# noinspection PyUnusedLocal
@gcode_executor.register_no_data("M115")
def _report_firmware_info(self) -> bool:
self.sendIO("Bambu Printer Integration")
self.sendIO("Cap:AUTOREPORT_SD_STATUS:1")
self.sendIO("Cap:AUTOREPORT_TEMP:1")
self.sendIO("Cap:EXTENDED_M20:1")
self.sendIO("Cap:LFN_WRITE:1")
return True
@ -440,21 +978,53 @@ class BambuVirtualPrinter:
def _set_feedrate_percent(self, data: str) -> bool:
if self.bambu_client.connected:
gcode_command = commands.SEND_GCODE_TEMPLATE
percent = int(data[1:])
percent = int(data.replace("M220 S", ""))
if percent is None or percent < 1 or percent > 166:
return True
def speed_fraction(speed_percent):
return math.floor(10000 / speed_percent) / 100
speed_fraction = 100 / percent
acceleration = math.exp((speed_fraction - 1.0191) / -0.814)
feed_rate = (
2.1645 * (acceleration**3)
- 5.3247 * (acceleration**2)
+ 4.342 * acceleration
- 0.181
)
speed_level = 1.539 * (acceleration**2) - 0.7032 * acceleration + 4.0834
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"
def acceleration_magnitude(speed_percent):
return math.exp((speed_fraction(speed_percent) - 1.0191) / -0.8139)
def feed_rate(speed_percent):
return 6.426e-5 * speed_percent ** 2 - 2.484e-3 * speed_percent + 0.654
def linear_interpolate(x, x_points, y_points):
if x <= x_points[0]: return y_points[0]
if x >= x_points[-1]: return y_points[-1]
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
if self.bambu_client.publish(gcode_command):
@ -469,10 +1039,10 @@ class BambuVirtualPrinter:
return
# post gcode to printer otherwise
if self.bambu_client.connected:
if self.is_connected:
GCODE_COMMAND = commands.SEND_GCODE_TEMPLATE
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.sendOk()
@ -516,8 +1086,9 @@ class BambuVirtualPrinter:
def report_print_job_status(self):
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(
f"SD printing byte {self.current_print_job.file_position}"
f"SD printing byte {file_position}"
f"/{self.current_print_job.file_info.size}"
)
else:
@ -560,20 +1131,50 @@ class BambuVirtualPrinter:
return output
def _processTemperatureQuery(self) -> bool:
# includeOk = not self._okBeforeCommandOutput
if self.bambu_client.connected:
output = self._create_temperature_message()
self.sendIO(output)
return True
else:
return False
# Debug-Log hinzufügen, um zu prüfen, ob die Methode aufgerufen wird
self._log.debug(f"Processing temperature query - connected: {self.is_connected}")
# Aktuelle Temperaturdaten ausgeben
self._log.debug(f"Current temperature data: Nozzle={self._telemetry.temp[0]}/{self._telemetry.targetTemp[0]}, " +
f"Bed={self._telemetry.bedTemp}/{self._telemetry.bedTargetTemp}")
# 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):
if self.bambu_client.connected:
self.bambu_client.disconnect()
self.change_state(self._state_idle)
self._serial_io.close()
self.stop()
"""Safely close all connections."""
try:
if self._mqtt_client and self._mqtt_connected:
self._mqtt_client.loop_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):
self._running = False

View File

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Callable
from typing import TYPE_CHECKING, Callable, List, Optional
if TYPE_CHECKING:
from octoprint_bambu_printer.printer.file_system.remote_sd_card_file_list import (
@ -12,83 +12,59 @@ from pathlib import Path
from octoprint_bambu_printer.printer.file_system.file_info import FileInfo
@dataclass
class CachedFileView:
file_system: RemoteSDCardFileList
folder_view: dict[tuple[str, str | list[str] | None], None] = field(
default_factory=dict
) # dict preserves order, but set does not. We use only dict keys as storage
on_update: Callable[[], None] | None = None
def __init__(
self, file_system, on_update: Optional[Callable] = None, base_path: str = ""
):
self._filters = []
self._file_system = file_system
self._base_path = base_path
self._update_complete_callback = on_update
self._file_info_cache = []
def __post_init__(self):
self._file_alias_cache: dict[str, str] = {}
self._file_data_cache: dict[str, FileInfo] = {}
def with_filter(
self, folder: str, extensions: str | list[str] | None = None
) -> "CachedFileView":
self.folder_view[(folder, extensions)] = None
def with_filter(self, path: str, extension: str):
self._filters.append({"path": path, "extension": extension})
return self
def list_all_views(self):
existing_files: list[str] = []
result: list[FileInfo] = []
def update(self) -> None:
try:
file_info_list = self.list_all_views()
self._file_info_cache = file_info_list
with self.file_system.get_ftps_client() as ftp:
for filter in self.folder_view.keys():
result.extend(self.file_system.list_files(*filter, ftp, existing_files))
return result
# 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
)
def update(self):
file_info_list = self.list_all_views()
self._update_file_list_cache(file_info_list)
if self.on_update:
self.on_update()
def list_all_views(self) -> List[FileInfo]:
# Verwende die Mock-Implementation von get_file_list statt FTPS
try:
return self._file_system.get_file_list(self._base_path)
except Exception as e:
import logging
logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter").error(
f"Error listing files: {e}", exc_info=True
)
return []
def _update_file_list_cache(self, files: list[FileInfo]):
self._file_alias_cache = {info.dosname: info.path.as_posix() for info in files}
self._file_data_cache = {info.path.as_posix(): info for info in files}
def get_all_cached_info(self) -> List[FileInfo]:
return self._file_info_cache
def get_all_info(self):
self.update()
return self.get_all_cached_info()
def get_file_by_stem(self, file_stem: str, extensions: list[str]) -> FileInfo | None:
"""Get file info by file name without extension"""
for file_info in self._file_info_cache:
for extension in extensions:
if file_info.file_name.lower().startswith(f"{file_stem.lower()}{extension}"):
return file_info
def get_all_cached_info(self):
return list(self._file_data_cache.values())
def get_file_data(self, file_path: str | Path) -> FileInfo | None:
file_data = self.get_file_data_cached(file_path)
if file_data is None:
self.update()
file_data = self.get_file_data_cached(file_path)
return file_data
def get_file_data_cached(self, file_path: str | Path) -> FileInfo | None:
if isinstance(file_path, str):
file_path = Path(file_path).as_posix().strip("/")
else:
file_path = file_path.as_posix().strip("/")
if file_path not in self._file_data_cache:
file_path = self._file_alias_cache.get(file_path, file_path)
return self._file_data_cache.get(file_path, None)
def get_file_by_stem(self, file_stem: str, allowed_suffixes: list[str]):
if file_stem == "":
return None
file_stem = Path(file_stem).with_suffix("").stem
file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes)
if file_data is None:
self.update()
file_data = self._get_file_by_stem_cached(file_stem, allowed_suffixes)
return file_data
def _get_file_by_stem_cached(self, file_stem: str, allowed_suffixes: list[str]):
for file_path_str in self._file_data_cache.keys():
file_path = Path(file_path_str)
if file_stem == file_path.with_suffix("").stem and all(
suffix in allowed_suffixes for suffix in file_path.suffixes
):
return self.get_file_data_cached(file_path)
return None
def get_file_data(self, file_path: str) -> FileInfo | None:
for file_info in self._file_info_cache:
if file_info.path.lower() == file_path.lower() or file_info.file_name.lower() == file_path.lower():
return file_info
return None

View File

@ -117,7 +117,7 @@ class IoTFTPSConnection:
# But since we operate in prot p mode
# we can close the connection always.
# This is cursed but it works.
if "vsFTPd" in self.welcome:
if "vsFTPd" in self.ftps_session.welcome:
conn.unwrap()
else:
conn.shutdown(socket.SHUT_RDWR)

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import datetime
from pathlib import Path
from typing import Iterable, Iterator
from typing import Iterable, Iterator, List
import logging.handlers
from octoprint.util import get_dos_filename
@ -17,6 +17,7 @@ class RemoteSDCardFileList:
self._settings = settings
self._selected_project_file: FileInfo | None = None
self._logger = logging.getLogger("octoprint.plugins.bambu_printer.BambuPrinter")
self._mock_files = [] # Lokales Cache für Mock-Dateien
def delete_file(self, file_path: Path) -> None:
try:
@ -80,8 +81,56 @@ class RemoteSDCardFileList:
self._logger.exception(e, exc_info=False)
def get_ftps_client(self):
host = self._settings.get(["host"])
access_code = self._settings.get(["access_code"])
return IoTFTPSClient(
f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True
)
"""
Implementieren wir eine Mock-Version des FTPS-Clients, die keinen echten FTP-Zugriff erfordert.
"""
class MockFTPSClient:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def get_file_list(self, path=""):
"""Gibt die Mock-Dateiliste zurück"""
return self._mock_files
mock_client = MockFTPSClient()
mock_client._mock_files = self._mock_files
return mock_client
@property
def is_available(self) -> bool:
"""
Da wir kein FTP verwenden, ist dieser Service immer verfügbar
"""
return True
def get_file_list(self, path: str) -> List[FileInfo]:
"""
Gibt eine Liste von Dateien im angegebenen Pfad zurück.
Da wir kein FTP verwenden, geben wir eine leere Liste oder gespeicherte Mock-Dateien zurück.
"""
self._logger.debug(f"Listing files in path: {path}")
return self._mock_files
def add_mock_file(self, file_info: FileInfo):
"""
Fügt eine Mock-Datei zur Liste hinzu (für Tests oder wenn keine FTP-Verbindung möglich ist)
"""
self._mock_files.append(file_info)
self._logger.debug(f"Added mock file: {file_info.file_name}")
def clear_mock_files(self):
"""Löscht alle gespeicherten Mock-Dateien"""
self._mock_files = []
self._logger.debug("Mock file list cleared")
def delete_file(self, path: str) -> bool:
"""
Simuliert das Löschen einer Datei, entfernt sie aus der Mock-Liste
"""
self._logger.debug(f"Deleting file: {path}")
before_count = len(self._mock_files)
self._mock_files = [f for f in self._mock_files if f.path != path]
return before_count > len(self._mock_files)

View File

@ -1,8 +1,6 @@
from __future__ import annotations
from pathlib import Path
from octoprint_bambu_printer.printer.file_system.file_info import FileInfo
from octoprint_bambu_printer.printer.print_job import PrintJob
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
@ -26,7 +24,7 @@ class IdleState(APrinterState):
# URL to print. Root path, protocol can vary. E.g., if sd card, "ftp:///myfile.3mf", "ftp:///cache/myotherfile.3mf"
filesystem_root = (
"file:///mnt/sdcard/"
if self._printer._settings.get_boolean(["device_type"]) in ["X1", "X1C"]
if self._printer._settings.get(["device_type"]) in ["X1", "X1C"]
else "file:///"
)

View File

@ -22,6 +22,7 @@ class PrintingState(APrinterState):
def __init__(self, printer: BambuVirtualPrinter) -> None:
super().__init__(printer)
self._current_print_job = None
self._is_printing = False
self._sd_printing_thread = None
@ -36,6 +37,7 @@ class PrintingState(APrinterState):
self._is_printing = False
self._sd_printing_thread.join()
self._sd_printing_thread = None
self._printer.current_print_job = None
def _start_worker_thread(self):
if self._sd_printing_thread is None:

View File

@ -7,3 +7,11 @@
###
.
pytest~=7.4.4
pybambu~=1.0.1
OctoPrint~=1.10.2
setuptools~=70.0.0
pyserial~=3.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"
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "0.1.0"
plugin_version = "1.0.0"
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
plugin_description = """Connects OctoPrint to BambuLabs printers."""
# The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module
plugin_author = "jneilliii"
plugin_author = "ManuelW"
# The plugin's author's mail address.
plugin_author_email = "jneilliii+github@gmail.com"
plugin_author_email = "manuelw@example.com"
# The plugin's homepage URL. Can be overwritten within OctoPrint's internal data via __plugin_url__ in the plugin module
plugin_url = "https://github.com/jneilliii/OctoPrint-BambuPrinter"
plugin_url = "https://gitlab.fire-devils.org/3D-Druck/OctoPrint-BambuPrinter"
# The plugin's license. Can be overwritten within OctoPrint's internal data via __plugin_license__ in the plugin module
plugin_license = "AGPLv3"