Developing an extra - issue with http request

Hey, I’m new to the development of Klipper add-ons, I’m trying to create an extra whose end goal is to read commands from a custom device’s serial port and translate them to “Klipper language”. Some of these commands require knowledge of the current state of the printer, namely the temperature and target of the heaters. Part of the test code I’ve written so far are these two functions, which quickly highlight the issue I’m facing

    def do_request(self):
        try:
            self.gcode.respond_info("Request attempt")
            now=time.time()
            data = requests.get("http://localhost/printer/objects/query?extruder&heater_bed", stream=True).json()

            status = data["result"]["status"]
            temps = {k: {"temperature": v["temperature"], "target": v["target"]}
                for k, v in status.items()
                if "temperature" in v and "target" in v}
            self.gcode.respond_info(str(temps))
            self.gcode.respond_info(str(time.time()-now))

        except Exception as e:
            self.gcode.respond_info(f"Request failed: {e}")
    
    def cmd_REQUEST(self, eventtime=None):
        #threading.Thread(target=self.do_request).start()
        self.do_request()
            
        return self.reactor.NEVER

If I run the request as is (call the REQUEST command from Klipper so it runs the do_request function) the HTTP request times out, probably because I am attempting to stall Klipper for too long. If I set up a separate thread which runs the do_request command, the request goes through correctly and I get the response I am looking for. Spawning a thread for each request is most likely not great, since there will be moments where I’ll need to handle a handful of requests a second.

All this said, is there a way to salvage this code, or am I on the wrong track entirely? How do I handle a request so it doesn’t halt Klipper?

First note that Klipper does not have an add-on system, so you should consider whether you can more cleanly accomplish your goal in another way (e.g. a separate daemon that reads commands and interacts with Klipper’s API.)

Secondly: why are you querying the API to get information from Klipper? The whole point of developing an unsupported Klipper add-on is to have access to Klipper internals. Just access the data you need.

1 Like

I am reading the Moonraker API because, after having a conversation with someone who knows Klipper far better than I do, that was their recommendation. Also because Klipper’s API is not very documented, or I havent been able to find a way to get even simple info like temperatures from it. A separate daemon might be a solution, simpler one as well, but I wanted to learn a little about the development of Klipper extras.

If you would be so kind to show me a snippet that “just accesses the data I need”, because I have barely been able to find any documentation, and the only guide I found barely scratches the surface (doesn’t show how to access printer data at all).

If you can’t read the existing code in klipper/klippy/extras at master · Klipper3d/klipper · GitHub to figure out how to access printer data, you should not be thinking about trying to mess with Klipper internals.

It’s not like a little documentation would hurt, eh? There is not a single mention of what objects I can access, how to access them (apart from “use lookup_objects”) and then what to do with them. Not a single example on the whole world wide web.

I am just asking for some guidance, it’s the first time I work on this kinda stuff. I’m not asking whether you think I’m fit to “mess with Klipper internals”, as though I was going to compromise anything in large scale. Worst case scenario I thrash my own printer. That said, I asked a question, which you promptly didn’t answer and then just said I shouldn’t bother even trying.

Say I wanted to read the existing code, which of the components do you suggest I look into first?

@GoTVm ,

  1. You might want to look at heater_generic.py

  2. Example to get access to the heater bed, then you can get temperature from it.

    printer.objects['heater_bed'].heater.smoothed_temp # can use .target_temp to get target
    
  3. I wrote a Klippy extra tutorial that you might find helpful.


Keep in mind that Klippy extras are unsupported officially, hence the lack of official docs around them.

1 Like

Thanks for the insight.

I followed your guide to set up the simple extra I have right now, but I don’t think you accessed any of the printer’s internal values in any of the three examples.

I thought extras were supported since they come stock with the Klipper installation, guess I was wrong; as I said, I’ve only ever used Klipper, never developed for it.

Is there a list of objects I can access with their respective parameters, or do you just get them from the different .py files? I see you used .smoothed_temp, which I hadn’t seen mentioned anywhere before now. Another question I have regarding printer object access: say I try to access the extruder, I get a PrinterExtruder object (IIRC, I’m not at the computer right now). How do I know which arguments and methods I can call from that class?

You can usually look in the extras folder for the objects you want to access. The get_status() method is a good place to find what state variables you can easily access.

Example:

Like normal python, you can log the output of dir(some_object) or find the relevant source file.

While they do work well and are quite powerful, they will make your Klipper installation appear “dirty” which will make it harder to get support.