Compare commits

...

8 Commits

Author SHA1 Message Date
3d0cc26147 0.0.12
fix issue with last PR that broke the ability to recognize currently printing file.
2024-02-12 21:35:09 -05:00
ff58636e41 0.0.11
support cache folder listing for P1 devices
add gcode command support
2024-02-12 19:14:10 -05:00
f54ab5c29f Merge pull request #5 from Pavulon87/use-cache-folder
Allow use of cache folder and custom gcode commands
2024-02-12 18:33:27 -05:00
7a4439c53e allow use of cache folder and custom g-gcode commands 2024-02-12 16:17:17 +01:00
9eb8b0da65 0.0.10
fix cancel command, #4
2024-02-12 00:09:04 -05:00
ef969d3d3b 0.0.9
fix upload_file and delete_file to return boolean as it did before switching the ftps client module for A1/P1 devices
2024-02-11 15:40:28 -05:00
3d92d73879 0.0.8
fix delete command
2024-02-10 17:56:50 -05:00
41dad23c49 pin paho-mqtt to versions less than 2 2024-02-10 13:33:09 -05:00
3 changed files with 99 additions and 48 deletions

View File

@ -125,7 +125,7 @@ class IoTFTPSClient:
with open(dest, "wb") as file:
self.ftps_session.retrbinary(f"RETR {source}", file.write)
def upload_file(self, source: str, dest: str, callback=None):
def upload_file(self, source: str, dest: str, callback=None) -> bool:
"""upload a file to a path inside the FTPS server"""
file_size = os.path.getsize(source)
@ -133,43 +133,50 @@ class IoTFTPSClient:
block_size = max(file_size // 100, 8192)
rest = None
# Taken from ftplib.storbinary but with custom ssl handling
# due to the shitty bambu p1p ftps server TODO fix properly.
with open(source, "rb") as fp:
self.ftps_session.voidcmd('TYPE I')
try:
# Taken from ftplib.storbinary but with custom ssl handling
# due to the shitty bambu p1p ftps server TODO fix properly.
with open(source, "rb") as fp:
self.ftps_session.voidcmd('TYPE I')
with self.ftps_session.transfercmd(f"STOR {dest}", rest) as conn:
while 1:
buf = fp.read(block_size)
with self.ftps_session.transfercmd(f"STOR {dest}", rest) as conn:
while 1:
buf = fp.read(block_size)
if not buf:
break
if not buf:
break
conn.sendall(buf)
conn.sendall(buf)
if callback:
callback(buf)
if callback:
callback(buf)
# shutdown ssl layer
if ftplib._SSLSocket is not None and isinstance(conn, ftplib._SSLSocket):
# Yeah this is suposed to be conn.unwrap
# 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:
conn.unwrap()
else:
conn.shutdown(socket.SHUT_RDWR)
# shutdown ssl layer
if ftplib._SSLSocket is not None and isinstance(conn, ftplib._SSLSocket):
# Yeah this is suposed to be conn.unwrap
# 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:
conn.unwrap()
else:
conn.shutdown(socket.SHUT_RDWR)
return self.ftps_session.voidresp()
return True
except Exception as ex:
print(f"unexpected exception occurred: {ex}")
pass
return False
# Old api call.
# self.ftps_session.storbinary(
# f"STOR {dest}", file, blocksize=block_size, callback=callback)
def delete_file(self, path: str):
def delete_file(self, path: str) -> bool:
"""delete a file from under a path inside the FTPS server"""
self.ftps_session.delete(path)
try:
self.ftps_session.delete(path)
return True
except Exception as ex:
print(f"unexpected exception occurred: {ex}")
pass
return False
def move_file(self, source: str, dest: str):
"""move a file inside the FTPS server to another path inside the FTPS server"""

View File

@ -68,6 +68,7 @@ class BambuPrinter:
self._sdCardReady = True
self._sdPrinter = None
self._sdPrinting = False
self._sdPrintStarting = False
self._sdPrintingSemaphore = threading.Event()
self._sdPrintingPausedSemaphore = threading.Event()
self._selectedSdFile = None
@ -163,14 +164,14 @@ class BambuPrinter:
self.bedTargetTemp = temperatures.get("target_bed_temp", 0.0)
self.chamberTemp = temperatures.get("chamber_temp", 0.0)
if print_job.get("gcode_state") == "RUNNING":
if print_job.get("gcode_state") == "RUNNING" or print_job.get("gcode_state") == "PREPARE":
if not self._sdPrintingSemaphore.is_set():
self._sdPrintingSemaphore.set()
if self._sdPrintingPausedSemaphore.is_set():
self._sdPrintingPausedSemaphore.clear()
self._sdPrintStarting = False
if not self._sdPrinting:
filename = print_job.get("subtask_name")
# TODO: swap this out to use 8 dot 3 name based on long name/path
self._selectSdFile(filename)
self._startSdPrint(from_printer=True)
@ -185,9 +186,12 @@ class BambuPrinter:
self._send("// action:paused")
self._sendPaused()
if print_job.get("gcode_state") == "FINISH" and self._sdPrintingSemaphore.is_set():
self._selectedSdFilePos = self._selectedSdFileSize
self._finishSdPrint()
if ( print_job.get("gcode_state") == "FINISH" or print_job.get("gcode_state") == "FAILED" ):
if self._sdPrintStarting is False:
self._sdPrinting = False
if self._sdPrintingSemaphore.is_set():
self._selectedSdFilePos = self._selectedSdFileSize
self._finishSdPrint()
def _create_connection(self):
if (self._settings.get(["device_type"]) != "" and
self._settings.get(["serial"]) != "" and
@ -241,6 +245,7 @@ class BambuPrinter:
self._sdCardReady = True
self._sdPrinting = False
self._sdPrintStarting = False
if self._sdPrinter:
self._sdPrinting = False
self._sdPrintingSemaphore.clear()
@ -429,6 +434,14 @@ class BambuPrinter:
else:
self._sendOk()
if self.bambu.connected:
GCODE_COMMAND = commands.SEND_GCODE_TEMPLATE
GCODE_COMMAND['print']['param'] = data + "\n"
if self.bambu.publish(GCODE_COMMAND):
self._logger.info("command sent successfully")
self._sendOk()
continue
finally:
self._logger.debug(f"{data}")
@ -474,13 +487,18 @@ class BambuPrinter:
def _gcode_M524(self, data: str) -> bool:
if self._sdCardReady:
self._cancelSdPrint()
return self._cancelSdPrint()
return False
def _gcode_M26(self, data: str) -> bool:
self._logger.debug("ignoring M26 command.")
self._send("M26 disabled for Bambu")
return True
if data == "M26 S0":
if self._sdCardReady:
return self._cancelSdPrint()
return False
else:
self._logger.debug("ignoring M26 command.")
self._send("M26 disabled for Bambu")
return True
def _gcode_M27(self, data: str) -> bool:
def report():
@ -509,8 +527,8 @@ class BambuPrinter:
# noinspection PyUnusedLocal
def _gcode_M29(self, data: str) -> bool:
self._logger.debug("ignoring M28 command.")
self._send("M28 disabled for Bambu")
self._logger.debug("ignoring M29 command.")
self._send("M29 disabled for Bambu")
return True
def _gcode_M30(self, data: str) -> bool:
@ -625,7 +643,7 @@ class BambuPrinter:
access_code = self._settings.get(["access_code"])
ftp = IoTFTPSClient(f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True)
filelist = ftp.list_files("", ".3mf")
filelist = ftp.list_files("", ".3mf") or []
for entry in filelist:
if entry.startswith("/"):
@ -643,8 +661,29 @@ class BambuPrinter:
"size": filesize,
"timestamp": unix_timestamp_to_m20_timestamp(int(filedate))
}
result[filename.lower()] = data
result[dosname.lower()] = filename.lower()
result[filename.lower()] = data
filelistcache = ftp.list_files("cache/", ".3mf") or []
for entry in filelistcache:
if entry.startswith("/"):
filename = entry[1:]
else:
filename = entry
filesize = ftp.ftps_session.size("cache/"+entry)
date_str = ftp.ftps_session.sendcmd(f"MDTM cache/{entry}").replace("213 ", "")
filedate = datetime.datetime.strptime(date_str, "%Y%m%d%H%M%S").replace(tzinfo=datetime.timezone.utc).timestamp()
dosname = get_dos_filename(filename, existing_filenames=list(result.keys())).lower()
data = {
"dosname": dosname,
"name": filename,
"path": "cache/"+filename,
"size": filesize,
"timestamp": unix_timestamp_to_m20_timestamp(int(filedate))
}
result[dosname.lower()] = filename.lower()
result[filename.lower()] = data
return result
@ -680,6 +719,7 @@ class BambuPrinter:
if self._selectedSdFile is not None:
if self._sdPrinter is None:
self._sdPrinting = True
self._sdPrintStarting = True
self._sdPrinter = threading.Thread(target=self._sdPrintingWorker, kwargs={"from_printer": from_printer})
self._sdPrinter.start()
# self._sdPrintingSemaphore.set()
@ -699,18 +739,21 @@ class BambuPrinter:
else:
self._logger.info("print pause failed")
def _cancelSdPrint(self):
def _cancelSdPrint(self) -> bool:
if self.bambu.connected:
if self.bambu.publish(commands.STOP):
self._logger.info("print cancelled")
self._finishSdPrint()
return True
else:
self._logger.info("print cancel failed")
return False
def _setSdPos(self, pos):
self._newSdFilePos = pos
def _reportSdStatus(self):
if self._sdPrinter is not None and (self._sdPrintingSemaphore.is_set() or self._sdPrintingPausedSemaphore.is_set()):
if ( self._sdPrinter is not None or self._sdPrintStarting is True ) and self._selectedSdFileSize > 0:
self._send(f"SD printing byte {self._selectedSdFilePos}/{self._selectedSdFileSize}")
else:
self._send("Not SD printing")
@ -799,6 +842,7 @@ class BambuPrinter:
self._selectedSdFilePos = 0
self._selectedSdFileSize = 0
self._sdPrinting = False
self._sdPrintStarting = False
self._sdPrinter = None
def _deleteSdFile(self, filename: str) -> None:
@ -811,7 +855,7 @@ class BambuPrinter:
if file is not None:
ftp = IoTFTPSClient(f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True)
try:
if ftp.delete_file(filename):
if ftp.delete_file(file["path"]):
self._logger.debug(f"{filename} deleted")
else:
raise Exception("delete failed")

View File

@ -14,7 +14,7 @@ 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.0.6"
plugin_version = "0.0.12"
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
@ -33,7 +33,7 @@ plugin_url = "https://github.com/jneilliii/OctoPrint-BambuPrinter"
plugin_license = "AGPLv3"
# Any additional requirements besides OctoPrint should be listed here
plugin_requires = ["paho-mqtt", "python-dateutil", "pybambu>=1.0.1"]
plugin_requires = ["paho-mqtt<2", "python-dateutil", "pybambu>=1.0.1"]
### --------------------------------------------------------------------------------------------------------------------
### More advanced options that you usually shouldn't have to touch follow after this point