I have been working recently on support for an official “Klipper extension system” (aka an official “plugin” system). This has been discussed a few times before (for example, Possible Klipper "plugins" instead of macros? ).
This is still in a “proposal” and “development” state. However, I do have an initial software implementation. Klipper patches at:
And an example extension at:
This code is in a very early state. The main goal at this point is to test the high-level concepts.
There are two major goals of the extension system:
Make it easy for users to find, install, version, and update common Klipper enhancements provided by the community.
Define a clear programming interface between Klipper and these extensions to reduce the chance of errors. In particular, to reduce the chance of things breaking as new software updates become available.
There are two initial targets for the extension system: a) make it possible to batch up “macro config includes” into an easily deployed and versioned extension; and b) to make it possible for an extension to define new G-Code commands that are implemented in a general purpose programming language (ie, not limited to Jinja2). As time goes on, the goal will be to continue to add new API calls so that additional functionality can be implemented in an extension.
On the technical side, there are envisioned to be four major components to this proposal:
Changes to Klipper to identify extensions and start them if requested.
Example “extension code” so that developers can rapidly implement a new community extension.
A new “global listing repository” that extension developers can submit their community extensions to.
A tool to help users identify extensions available in the “global listing repository” and make it easy for them to install and/or update them. Ideally this tool will be directly available from the Klipper GUIs.
The “klipper patches” link above has initial support for the first component. A new mechanism has been added for Klipper to find and launch locally found extensions, and new API calls have been defined (see the docs/API_Server.md file). Documentation is limited at this point, as this is still a “work in progress”.
The “example extension” repo listed above is the very beginnings of the second component. More work is needed here. The current example code interacts with Klipper to append the config snippets in test_extension/config_snippet.txt to Klipper’s main “printer.cfg” file.
The third and fourth components are still in a discussion phase. I hope to gather feedback on these components over the next several weeks.
Because the section name is used to find the extension, there is both the chance for conflicts, and the lack of a way to specify multiple sections for one extension. Some common host modules today leverage this pretty heavily, such as tmc_autotune.
The extmgr assumes that the python interpreter is at extdir/extname/bin/python, if it were created there using virtualenv defaults, the kernel would not reused mapped pages, and there would be N copies of the interpreter resident. Symlink venvs can help reduce this, though in the simple case, where an extention has no requirements other than those needed by klippy, reusing klippy’s own interp (and venv, if applicable) may be an alternative.
The extmgr has no way to get information about a module besides loading it. With how far back klippy will work (py 3.3 as of this writing) some additional metadata (such as required python versions, minimum klipper versions and/or capability flags (if those are implemented later)). This metadata could also contain specification for building the venv, allowing a future tool to better manage them. (and possibly integrate with moonraker’s update_manager system)
I’m going to use the term “plugin” to refer to an addition to Klipper, whether an extra or an extension.
Since extensions are outside of the klipper directory, the klippy.log wouldn’t report a “dirty” version. Would this be a concern, since a broken extension could still break Klipper?
Extensions look great for simpler, non-intrusive plugins, like my KlipperMaintenance.
For more complex, intrusive plugins like Happy Hare, or my DynamicMacros, the old “extras” system seems better. Extras allow for deeper control of Klipper, which is crucial for some plugins.
Overall, I think this is a good first step to a better plugin system for Klipper, starting with simpler plugins.
The proposal here would be for the external code to interact with the main Klipper code through a defined API. That is, the code would run in its own process, and be much less likely to interfere with timing, memory usage, and similar. The external code would only have access to defined API calls, so no real risk of internal changes to Klipper breaking external code. If something does go wrong, there should ideally be sufficient logging to rapidly determine who should be contacted. So, there would not be any particular reason to mark the code as “dirty” in the logs.
Modifying code in the Klipper repo itself is notably different (this includes “adding” code). Code modified/added in this way can easily destabilize the Klipper code (eg, invalid use of threads, stressing the memory garbage collection system, pausing the main processing, consuming excessive bandwidth, etc.). If things do go wrong, there is little ability to quickly identify what code is at fault - it quickly leads to “finger pointing” as people search for “someone else to solve the problem”. There is also no way to realistically manage internal changes in such a way that external code modifications do not break (as we have no way to even know what the external changes may depend on).