From 8449b1c68ef3a3e047aece8fc44d28389bf34369 Mon Sep 17 00:00:00 2001 From: mfm Date: Tue, 23 Jan 2018 15:01:58 +0100 Subject: [PATCH] Initial commit. --- .editorconfig | 17 ++ .gitignore | 9 ++ MANIFEST.in | 4 + README.md | 19 +++ babel.cfg | 6 + octoprint_klipper/__init__.py | 74 +++++++++ octoprint_klipper/static/css/klipper.css | 10 ++ octoprint_klipper/static/js/klipper.js | 147 ++++++++++++++++++ octoprint_klipper/static/less/klipper.less | 5 + .../templates/klipper_navbar.jinja2 | 1 + .../templates/klipper_settings.jinja2 | 90 +++++++++++ .../templates/klipper_sidebar.jinja2 | 21 +++ .../templates/klipper_tab.jinja2 | 26 ++++ requirements.txt | 9 ++ setup.py | 96 ++++++++++++ 15 files changed, 534 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 babel.cfg create mode 100644 octoprint_klipper/__init__.py create mode 100644 octoprint_klipper/static/css/klipper.css create mode 100644 octoprint_klipper/static/js/klipper.js create mode 100644 octoprint_klipper/static/less/klipper.less create mode 100644 octoprint_klipper/templates/klipper_navbar.jinja2 create mode 100644 octoprint_klipper/templates/klipper_settings.jinja2 create mode 100644 octoprint_klipper/templates/klipper_sidebar.jinja2 create mode 100644 octoprint_klipper/templates/klipper_tab.jinja2 create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..82c8e05 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[**.py] +indent_style = tab + +[**.js] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecfcd6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.pyc +*.swp +.idea +*.iml +build +dist +*.egg* +.DS_Store +*.zip diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..eb58b06 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include README.md +recursive-include octoprint_klipper/templates * +recursive-include octoprint_klipper/translations * +recursive-include octoprint_klipper/static * diff --git a/README.md b/README.md new file mode 100644 index 0000000..237fd0e --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Klipper + +This plugin assists in managing and monitoring the [Klipper](https://github.com/KevinOConnor/klipper) 3D printer firmware. +Besides that it provides some usefull tools. + + +**STILL UNDER DEVELOPMENT** + +## Setup + +Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wiki/Plugin:-Plugin-Manager) +or manually using this URL: + + https://github.com/mmone/OctoPrintKlipper/archive/master.zip + + +## Configuration + +Click on the wrench icon in the titlebar and select "Klipper" to get to the available settings for this plugin. \ No newline at end of file diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 0000000..b6f5945 --- /dev/null +++ b/babel.cfg @@ -0,0 +1,6 @@ +[python: */**.py] +[jinja2: */**.jinja2] +extensions=jinja2.ext.autoescape, jinja2.ext.with_ + +[javascript: */**.js] +extract_messages = gettext, ngettext diff --git a/octoprint_klipper/__init__.py b/octoprint_klipper/__init__.py new file mode 100644 index 0000000..baba3f2 --- /dev/null +++ b/octoprint_klipper/__init__.py @@ -0,0 +1,74 @@ +# coding=utf-8 +from __future__ import absolute_import +import logging +import octoprint.plugin +import octoprint.plugin.core + +class KlipperPlugin( + octoprint.plugin.StartupPlugin, + octoprint.plugin.TemplatePlugin, + octoprint.plugin.SettingsPlugin, + octoprint.plugin.AssetPlugin): + + def on_after_startup(self): + #self._settings.set(["appearance"]["components"]["order"]["sidebar"]["test"]); + self._logger.info("startup hook ---------- {value} ----------".format(value=self._settings.get(["replace_connection_panel"])) ) + + def get_settings_defaults(self): + return dict( + serialport="/tmp/printer", + replace_connection_panel=True, + macros=[], + probePoints=[]) + + def get_template_configs(self): + return [ + dict(type="navbar", custom_bindings=False), + dict(type="settings", custom_bindings=True), + dict(type="sidebar", + custom_bindings=True, + replaces= "connection" if self._settings.get(["replace_connection_panel"]) else "") + ] + + def get_assets(self): + return dict( + js=["js/klipper.js"], + css=["css/klipper.css"], + less=["css/klipper.less"] + ) + + def on_parse_gcode(self, comm, line, *args, **kwargs): + if "ok" not in line: + return line + + self._plugin_manager.send_plugin_message(self._identifier, dict(message=line)) + #from octoprint.util.comm import parse_firmware_line + + # Create a dict with all the keys/values returned by the M115 request + #printer_data = parse_firmware_line(line) + self._logger.info("Machine type detected {line}.".format(line=line)) + #self._logger.info("Machine type detected: {machine}.".format(machine=printer_data["MACHINE_TYPE"])) + + return line + + def on_printer_action(self, comm, line, action, *args, **kwargs): + #if not action == "custom": + # return + + self._logger.info("action recieved:".action) + +__plugin_name__ = "Klipper" + +def __plugin_load__(): + global __plugin_implementation__ + global __plugin_hooks__ + + __plugin_implementation__ = KlipperPlugin() + __plugin_hooks__ = { + "octoprint.comm.protocol.gcode.received": __plugin_implementation__.on_parse_gcode, + "octoprint.comm.protocol.action": __plugin_implementation__.on_printer_action + } + +#__plugin_name__ = "Klipper" +#__plugin_implementation__ = KlipperPlugin() + diff --git a/octoprint_klipper/static/css/klipper.css b/octoprint_klipper/static/css/klipper.css new file mode 100644 index 0000000..2424ce1 --- /dev/null +++ b/octoprint_klipper/static/css/klipper.css @@ -0,0 +1,10 @@ +#tab_plugin_klipper iframe { + width: 100%; + height: 600px; + border: 1px solid #808080; +} + +.plugin_klipper_msg { + overflow-y: scroll; + height: 100%; +} \ No newline at end of file diff --git a/octoprint_klipper/static/js/klipper.js b/octoprint_klipper/static/js/klipper.js new file mode 100644 index 0000000..2ab31e3 --- /dev/null +++ b/octoprint_klipper/static/js/klipper.js @@ -0,0 +1,147 @@ +$(function() { + function KlipperViewModel(parameters) { + var self = this; + // injection settingsViewModel + self.settings = parameters[0]; + // injection loginStateViewModel + self.loginState = parameters[1]; + // injection connectionViewModel + self.connectionState = parameters[2]; + + self.shortStatus = ko.observable(); + + self.logMessages = ko.observableArray(); + /* + self.onConnectToHost = function() { + console.log("Connecting"); + self.shortStatus("Connecting to Host"); + OctoPrint.connection.connect({"port" : "VIRTUAL"}); + self.connectButtonText("Disconnect"); + console.log(self.loginState); + }*/ + + self.onGetStatus = function() { + self.shortStatus("Update Status") + } + + self.onRestartFirmware = function() { + //OctoPrint.control.sendGcode("FIRMWARE_RESTART") + self.shortStatus("Restarting Firmware"); + console.log("Restart firmware"); + }; + + self.onRestartHost = function() { + + self.shortStatus("Restarting Host"); + console.log("Restart Host"); + self.logMessage("Restarted Host"); + //OctoPrint.control.sendGcode("RESTART") + }; + + self.onBeforeBinding = function() { + self.connectionState.selectedPort("VIRTUAL"); + } + + self.onAfterBinding = function() { + self.connectionState.selectedPort("VIRTUAL"); + console.log(self.connectionState.selectedPort()); + self.shortStatus("Idle"); + } + + self.onDataUpdaterPluginMessage = function(plugin, message) { + //console.log(message); + self.logMessage("plugin: " +plugin+ " message recieved: " + message["message"]); + } + + self.logMessage = function(message) { + self.logMessages.push({time: Date.now(), msg: message}); + } + + self.onClearLog = function() { + self.logMessages.removeAll(); + }; + + self.isActive = function() { + return self.connectionState.isOperational() && self.loginState.isUser(); + } + + // ------------ Settings ---------------- // + + self.moveItemDown = function(list, item) { + var i = list().indexOf(item); + if (i < list().length - 1) { + var rawList = list(); + list.splice(i, 2, rawList[i + 1], rawList[i]); + } + } + + self.moveItemUp = function(list, item) { + var i = list().indexOf(item); + if (i > 0) { + var rawList = list(); + list.splice(i-1, 2, rawList[i], rawList[i-1]); + } + } + + self.executeMacro = function(macro) { + console.log(macro.name()); + OctoPrint.control.sendGcode(macro.macro()); + } + + self.addMacro = function() { + self.settings.settings.plugins.klipper.macros.push({name: 'Macro', macro: ''}); + } + + self.removeMacro = function(macro) { + self.settings.settings.plugins.klipper.macros.remove(macro); + } + + self.moveMacroUp = function(macro) { + self.moveItemUp(self.settings.settings.plugins.klipper.macros, macro) + } + + self.moveMacroDown = function(macro) { + self.moveItemDown(self.settings.settings.plugins.klipper.macros, macro) + } + + + + self.addProbePoint = function() { + self.settings.settings.plugins.klipper.probePoints.push({x: 0, y:0, z:0}); + } + + self.removeProbePoint = function(point) { + self.settings.settings.plugins.klipper.probePoints.remove(point); + } + + self.moveProbePointUp = function(macro) { + self.moveItemUp(self.settings.settings.plugins.klipper.probePoints, macro) + } + + self.moveProbePointDown = function(macro) { + self.moveItemDown(self.settings.settings.plugins.klipper.probePoints, macro) + } + } + /* + var KlipperMacroCollection = function(name, gcode) { + this.name = name; + this.macros = ko.observableArray(macros); + + this.addMacro = function() { + this.push({name: 'macro 2', macro: 'G1 X1 Y2 Z3'}); + }.bind(this); + }*/ + + // This is how our plugin registers itself with the application, by adding some configuration + // information to the global variable OCTOPRINT_VIEWMODELS + OCTOPRINT_VIEWMODELS.push({ + // This is the constructor to call for instantiating the plugin + construct: KlipperViewModel, + + // dependencies to inject into the plugin. + dependencies: ["settingsViewModel", "loginStateViewModel", "connectionViewModel"], + + // elements this view model will be bound to. + elements: ["#tab_plugin_klipper", "#sidebar_plugin_klipper", "#settings_plugin_klipper"] + }); +}); \ No newline at end of file diff --git a/octoprint_klipper/static/less/klipper.less b/octoprint_klipper/static/less/klipper.less new file mode 100644 index 0000000..602468b --- /dev/null +++ b/octoprint_klipper/static/less/klipper.less @@ -0,0 +1,5 @@ +#tab_plugin_klipper iframe { + width: 100%; + height: 600px; + border: 1px solid #808080; +} \ No newline at end of file diff --git a/octoprint_klipper/templates/klipper_navbar.jinja2 b/octoprint_klipper/templates/klipper_navbar.jinja2 new file mode 100644 index 0000000..83e79b2 --- /dev/null +++ b/octoprint_klipper/templates/klipper_navbar.jinja2 @@ -0,0 +1 @@ +Status: Connected diff --git a/octoprint_klipper/templates/klipper_settings.jinja2 b/octoprint_klipper/templates/klipper_settings.jinja2 new file mode 100644 index 0000000..ca53123 --- /dev/null +++ b/octoprint_klipper/templates/klipper_settings.jinja2 @@ -0,0 +1,90 @@ +
+
+

{{ _('Settings') }}

+

+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+

{{ _('Macros') }}

+

+
+
+ +
+
+
+ +
+
+ + + +
+
+ +
+ +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+ + +
+

{{ _('Probe Points') }}

+
+
+
+
+
X
+
Y
+
Z
+
+
+
+
+
+ +
+
+
+
+
+
+ + + +
+
+
+
+
+
+ +
+
+
diff --git a/octoprint_klipper/templates/klipper_sidebar.jinja2 b/octoprint_klipper/templates/klipper_sidebar.jinja2 new file mode 100644 index 0000000..32bced1 --- /dev/null +++ b/octoprint_klipper/templates/klipper_sidebar.jinja2 @@ -0,0 +1,21 @@ + + + + + + +
+
+ +
+ +
+
\ No newline at end of file diff --git a/octoprint_klipper/templates/klipper_tab.jinja2 b/octoprint_klipper/templates/klipper_tab.jinja2 new file mode 100644 index 0000000..031ca99 --- /dev/null +++ b/octoprint_klipper/templates/klipper_tab.jinja2 @@ -0,0 +1,26 @@ +
+
+ +
+
+
+
+
+
+
+ + + + + + +
+
+
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a1dc463 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +### +# This file is only here to make sure that something like +# +# pip install -e . +# +# works as expected. Requirements can be found in setup.py. +### + +. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..001e093 --- /dev/null +++ b/setup.py @@ -0,0 +1,96 @@ +# coding=utf-8 + +######################################################################################################################## +### Do not forget to adjust the following variables to your own plugin. + +# The plugin's identifier, has to be unique +plugin_identifier = "klipper" + +# The plugin's python package, should be "octoprint_", has to be unique +plugin_package = "octoprint_klipper" + +# The plugin's human readable name. Can be overwritten within OctoPrint's internal data via __plugin_name__ in the +# plugin module +plugin_name = "Klipper" + +# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module +plugin_version = "0.1.0" + +# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin +# module +plugin_description = """A plugin for octoprint to control and monitor the Klipper 3D printer software.""" + +# The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module +plugin_author = "Martin Mühlhäuser" + +# The plugin's author's mail address. +plugin_author_email = "github@mmone.de" + +# 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/mmone/OctoPrintKlipper" + +# The plugin's license. Can be overwritten within OctoPrint's internal data via __plugin_license__ in the plugin module +plugin_license = "AGPLv3" + +# Any additional requirements besides OctoPrint should be listed here +plugin_requires = [] + +### -------------------------------------------------------------------------------------------------------------------- +### More advanced options that you usually shouldn't have to touch follow after this point +### -------------------------------------------------------------------------------------------------------------------- + +# Additional package data to install for this plugin. The subfolders "templates", "static" and "translations" will +# already be installed automatically if they exist. Note that if you add something here you'll also need to update +# MANIFEST.in to match to ensure that python setup.py sdist produces a source distribution that contains all your +# files. This is sadly due to how python's setup.py works, see also http://stackoverflow.com/a/14159430/2028598 +plugin_additional_data = [] + +# Any additional python packages you need to install with your plugin that are not contained in .* +plugin_additional_packages = [] + +# Any python packages within .* you do NOT want to install with your plugin +plugin_ignored_packages = [] + +# Additional parameters for the call to setuptools.setup. If your plugin wants to register additional entry points, +# define dependency links or other things like that, this is the place to go. Will be merged recursively with the +# default setup parameters as provided by octoprint_setuptools.create_plugin_setup_parameters using +# octoprint.util.dict_merge. +# +# Example: +# plugin_requires = ["someDependency==dev"] +# additional_setup_parameters = {"dependency_links": ["https://github.com/someUser/someRepo/archive/master.zip#egg=someDependency-dev"]} +additional_setup_parameters = {} + +######################################################################################################################## + +from setuptools import setup + +try: + import octoprint_setuptools +except: + print("Could not import OctoPrint's setuptools, are you sure you are running that under " + "the same python installation that OctoPrint is installed under?") + import sys + sys.exit(-1) + +setup_parameters = octoprint_setuptools.create_plugin_setup_parameters( + identifier=plugin_identifier, + package=plugin_package, + name=plugin_name, + version=plugin_version, + description=plugin_description, + author=plugin_author, + mail=plugin_author_email, + url=plugin_url, + license=plugin_license, + requires=plugin_requires, + additional_packages=plugin_additional_packages, + ignored_packages=plugin_ignored_packages, + additional_data=plugin_additional_data +) + +if len(additional_setup_parameters): + from octoprint.util import dict_merge + setup_parameters = dict_merge(setup_parameters, additional_setup_parameters) + +setup(**setup_parameters)