Initial commit.
This commit is contained in:
commit
8449b1c68e
|
@ -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
|
|
@ -0,0 +1,9 @@
|
|||
*.pyc
|
||||
*.swp
|
||||
.idea
|
||||
*.iml
|
||||
build
|
||||
dist
|
||||
*.egg*
|
||||
.DS_Store
|
||||
*.zip
|
|
@ -0,0 +1,4 @@
|
|||
include README.md
|
||||
recursive-include octoprint_klipper/templates *
|
||||
recursive-include octoprint_klipper/translations *
|
||||
recursive-include octoprint_klipper/static *
|
|
@ -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.
|
|
@ -0,0 +1,6 @@
|
|||
[python: */**.py]
|
||||
[jinja2: */**.jinja2]
|
||||
extensions=jinja2.ext.autoescape, jinja2.ext.with_
|
||||
|
||||
[javascript: */**.js]
|
||||
extract_messages = gettext, ngettext
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#tab_plugin_klipper iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: 1px solid #808080;
|
||||
}
|
||||
|
||||
.plugin_klipper_msg {
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
}
|
|
@ -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"]
|
||||
});
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
#tab_plugin_klipper iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: 1px solid #808080;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<a href="#" data-bind="attr: {href: settings.settings.plugins.klipper.url}">Status: Connected</a>
|
|
@ -0,0 +1,90 @@
|
|||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<h4>{{ _('Settings') }}<h4>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Serial Port') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" class="input-block-level" data-bind="value: settings.settings.plugins.klipper.serialport">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Replace Connection Panel') }}</label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.replace_connection_panel">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="control-group">
|
||||
<h4>{{ _('Macros') }}<h4>
|
||||
</div>
|
||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Name') }}</label>
|
||||
<div class="controls">
|
||||
<div class="row-fluid">
|
||||
<div class="span9">
|
||||
<input type="text" class="input-block-level" data-bind="value: name">
|
||||
</div>
|
||||
<div class="span2">
|
||||
<a href='#' data-bind='click: $parent.moveMacroUp' class="fa fa-chevron-up"></a>
|
||||
<a href='#' data-bind='click: $parent.moveMacroDown' class="fa fa-chevron-down"></a>
|
||||
<a href='#' data-bind='click: $parent.removeMacro' class="fa fa-trash-o"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<label class="control-label">{{ _('GCODE') }}</label>
|
||||
<div class="controls">
|
||||
<div class="row-fluid">
|
||||
<div class="span9">
|
||||
<textarea rows="4" class="block" data-bind="value: macro">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="span2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<a href='#' data-bind='click: addMacro' class="fa fa-plus-circle"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="control-group">
|
||||
<h4>{{ _('Probe Points') }}</h4>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">X</div>
|
||||
<div class="span3">Y</div>
|
||||
<div class="span3">Z</div>
|
||||
<div class="span3"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-bind="foreach: settings.settings.plugins.klipper.probePoints" class="control-group">
|
||||
<label class="control-label" data-bind="text: $index"></label>
|
||||
<div class="controls">
|
||||
<div class="row-fluid">
|
||||
<div class="span3"><input type="text" class="input-block-level" data-bind="value: x"></div>
|
||||
<div class="span3"><input type="text" class="input-block-level" data-bind="value: y"></div>
|
||||
<div class="span3"><input type="text" class="input-block-level" data-bind="value: z"></div>
|
||||
<div class="span3">
|
||||
<a href='#' data-bind='click: $parent.moveProbePointUp' class="fa fa-sort-asc"></a>
|
||||
<a href='#' data-bind='click: $parent.moveProbePointDown' class="fa fa-sort-desc"></a>
|
||||
<a href='#' data-bind='click: $parent.removeMacro' class="fa fa-trash-o"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<a href='#' data-bind='click: addProbePoint' class="fa fa-plus-circle"></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,21 @@
|
|||
<label><span class="label label-success" data-bind="text: shortStatus">Connected</span></label>
|
||||
|
||||
<label for="connection_printers" data-bind="css: {disabled: !connectionState.isErrorOrClosed()}, enable: connectionState.isErrorOrClosed() && loginState.isUser()">{{ _('Printer Profile') }}</label>
|
||||
<select id="connection_printers" data-bind="options: connectionState.printerOptions, optionsText: 'name', optionsValue: 'id', value: connectionState.selectedPrinter, css: {disabled: !connectionState.isErrorOrClosed()}, enable: connectionState.isErrorOrClosed() && loginState.isUser()"></select>
|
||||
<!--
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_save" data-bind="checked: connectionState.saveSettings, css: {disabled: !connectionState.isErrorOrClosed()}, enable: connectionState.isErrorOrClosed() && loginState.isUser()"> {{ _('Save connection settings') }}
|
||||
</label>
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="connection_autoconnect" data-bind="checked: settings.serial_autoconnect, css: {disabled: !connectionState.isErrorOrClosed()}, enable: connectionState.isErrorOrClosed() && loginState.isUser()"> {{ _('Auto-connect on server startup') }}
|
||||
</label>
|
||||
-->
|
||||
<button class="btn btn-block" data-bind="click: connectionState.connect, text: connectionState.buttonText(), enable: loginState.isUser()">{{ _('Connect') }}</button>
|
||||
<hr>
|
||||
<div>
|
||||
<label>Macros</label>
|
||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||
<button class="btn btn-block" data-bind="text: name, click: $parent.executeMacro, enable: $parent.isActive()"></button>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
<div class="row-fluid">
|
||||
<div class="span8">
|
||||
<label>Log <span class="label label-success" data-bind="text: shortStatus">Connected</span> </label>
|
||||
<hr>
|
||||
<div class="plugin_klipper_msg" data-bind="foreach: logMessages">
|
||||
<div><i data-bind="text: formatDate(time)"></i> <span data-bind="text: msg"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div>
|
||||
<label>Process Management</label>
|
||||
<button class="btn btn-block" data-bind="click: onGetStatus, enable: isActive()">Status</button>
|
||||
<label>Restart</label>
|
||||
<button class="btn btn-block" data-bind="click: onRestartHost, enable: isActive()"><i class="fa fa-repeat"></i> Host</button>
|
||||
<button class="btn btn-block" data-bind="click: onRestartFirmware, enable: isActive()"><i class="fa fa-repeat"></i> MCU</button>
|
||||
<button class="btn btn-block" data-bind="click: onClearLog"><i class="fa fa-trash"></i> Clear Log</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<label>Macros</label>
|
||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||
<button class="btn btn-block" data-bind="text: name, click: $parent.executeMacro, enable: $parent.isActive()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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.
|
||||
###
|
||||
|
||||
.
|
|
@ -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_<plugin identifier>", 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_package>.*
|
||||
plugin_additional_packages = []
|
||||
|
||||
# Any python packages within <plugin_package>.* 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)
|
Loading…
Reference in New Issue