Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API routes invalid for some things in the new API #514

Open
NovodoOfficial opened this issue Nov 30, 2024 · 0 comments
Open

API routes invalid for some things in the new API #514

NovodoOfficial opened this issue Nov 30, 2024 · 0 comments

Comments

@NovodoOfficial
Copy link

NovodoOfficial commented Nov 30, 2024

Some API routes are missing from the new API, I'm working on a project and have looked at the openapi files on how to control my Prusa Mini. The legacy API provided ways to move the printhead in the openapi legacy file but not in the current openapi file.

I am attempting to send API requests through python to move the printhead but, sending a request to the legacy API endpoint results with a 401 error, I fixed this with correct and tested Digest authentication. This new authentication results in a 404 error. I know it is possible to do this as I can via the Prusa connect control screen:

image

This is a half-working implementation that I made in python:

from requests.auth import HTTPDigestAuth
import requests
import time
import os

class DebugError(Exception):
    pass

class Printer:
    def __init__(self, printer_ip, password, username="maker", debug=False):
        self.printer_ip = printer_ip
        self.username = username
        self.password = password
        self.debug = debug

        self.auth = HTTPDigestAuth(self.username, self.password)

    def send_gcode(self, gcode):
        gcode_path = "temp_prusa_api_file_gcode_command.gcode"

        with open(gcode_path, 'w') as file:
            file.write(gcode)

        self.upload_gcode(gcode_path)

    def upload_gcode(self, gcode_path, auto_start=True, overwrite=True, output=False):
        file_name = os.path.basename(gcode_path)

        upload_url = f"{self.printer_ip}/api/v1/files/usb//{file_name}"

        if self.debug:
            with open(gcode_path, 'r') as file:
                print(f"GCODE:\n{file.read()}")
            return

        headers = {
            "Origin": self.printer_ip,
            "Content-Type": "text/x.gcode",
            "Accept": "application/json",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "en-US,en;q=0.5",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0"
        }

        if overwrite:
            headers["Overwrite"] = "?1"
        else:
            headers["Overwrite"] = "?0"

        if auto_start:
            headers["Print-After-Upload"] = "?1"
        else:
            headers["Print-After-Upload"] = "?0"

        status_printed = False

        while True:
            status = self.get_status()["printer"]["state"]
            if status.upper().strip() in ["FINISHED", "IDLE", "STOPPED"]:
                break
            elif not status_printed:
                status_printed = True
                print("Waiting for printer to be free")
            time.sleep(3)

        with open(gcode_path, 'rb') as file:
            response = requests.put(upload_url, data=file, headers=headers, auth=self.auth)

        if response.status_code == 201:
            if output:
                print(f"File {file_name} uploaded successfully to: {self.printer_ip}")
        else:
            raise DebugError(f"Failed to upload the file. Status code:\n{response.status_code}\n\nResponse:\n{response.text}")
        
    def get_status(self):
        if self.debug:
            return {'storage': {'path': '/usb/', 'name': 'usb', 'read_only': False}, 'printer': {'state': 'IDLE', 'temp_bed': 21.8, 'target_bed': 0.0, 'temp_nozzle': 22.2, 'target_nozzle': 0.0, 'axis_z': 105.6, 'axis_x': 170.0, 'axis_y': 170.0, 'flow': 100, 'speed': 100, 'fan_hotend': 0, 'fan_print': 0}}

        status_url = f"{self.printer_ip}/api/v1/status"
        status_response = requests.get(status_url, auth=self.auth)

        if status_response.status_code != 200:
            raise DebugError(f"Failed to fetch printer status. Status code:\n{status_response.status_code}\n\nResponse:\n{status_response.text}")
        
        return status_response.json()

class Move:
    def __init__(self):
        self.move = [None, None, None, None]

    def clear(self):
        self.move = [None, None, None, None]

    def x(self, location):
        self.move[0] = location

    def y(self, location):
        self.move[1] = location

    def z(self, location):
        self.move[2] = location
    
    def feed(self, feedrate):
        self.move[3] = feedrate

    def xy(self, locationX, locationY):
        self.move[0] = locationX
        self.move[1] = locationY

class GcodeList:
    def __init__(self):
        self.gcode_list = []
        self.lastX, self.lastY, self.lastZ = 0, 0, 0

    def clear(self):
        self.gcode_list = []

    def parse(self):
        gcode = "\n".join(self.gcode_list)

        return gcode

    def AddMove(self, move):
        if isinstance(move, list):
            if len(move) != 4:
                raise DebugError(f"Move is not a 4-length tuple:\n{move}")
            
            if move[0] == None and move[1] == None and move[2] == None and move[3] == None:
                return
            
            x = move[0]
            y = move[1]
            z = move[2]

            feed = move[3]

            x_gcode = f" X{x}"
            y_gcode = f" Y{y}"
            z_gcode = f" Z{z}"

            feed_gcode = f" F{feed}"

            if x == None:
                x_gcode = ""
            else:
                self.lastX = x
            if y == None:
                y_gcode = ""
            else:
                self.lastY = y
            if z == None:
                z_gcode = ""
            else:
                self.lastZ = z
                
            if feed == None:
                feed_gcode = ""

            gcode = f"G1{x_gcode}{y_gcode}{z_gcode}{feed_gcode}"

            self.gcode_list.append(gcode)

        if isinstance(move, str):
            if move == "home":
                self.gcode_list.append("G28")
            elif move == "absmode":
                self.gcode_list.append("G90")
            else:
                self.gcode_list.append(move)

    def AddPause(self, time_message=""):
        if isinstance(time_message, int):
            self.gcode_list.append(f"G4 {time_message}")
        elif isinstance(time_message, str):
            if time_message:
                self.gcode_list.append(f"M0 {time_message}")
            else:
                self.gcode_list.append(f"M0")

    def Message(self, message=""):
        if message:
            self.gcode_list.append(f"M117 {message}")
        else:
            self.gcode_list.append(f"M117")

This approach works but has many issues, the biggest one being once the gcode file has finished "printing", it will park the print head which can get in the way. Any help with this issue or support about an API endpoint would be greatly appreciated!

EDIT

I have recently switched to the PrusaLinkPy module, this has helped improve efficiency, but still has issues such as the print head parking and the lack of the ability to move the printhead directly

@NovodoOfficial NovodoOfficial changed the title No way to move the printhead API routes invalid for some things in the new API Dec 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant