Merge branch 'rc'
This commit is contained in:
commit
d5abe67c56
|
@ -8,10 +8,17 @@ end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
indent_style = space
|
||||||
|
max_line_length = 90
|
||||||
|
|
||||||
[**.py]
|
[**.py]
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[**.js]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[**.yaml]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[/static/less/**.less]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[**.jinja2]
|
||||||
|
indent_size = 3
|
||||||
|
|
|
@ -8,4 +8,6 @@ dist
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.zip
|
*.zip
|
||||||
|
|
||||||
*.bak
|
*.bak
|
||||||
|
.vscode
|
||||||
|
.vscode/**
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
## Fork information:
|
## Fork information:
|
||||||
- This is forked from [the original](https://github.com/mmone/OctoprintKlipperPlugin) version 0.2.5
|
- This is forked from [the original](https://github.com/mmone/OctoprintKlipperPlugin) version 0.2.5
|
||||||
- The fork now supports Python3 (hopefully without any new bugs)
|
- The fork now supports Python3 (hopefully without any new bugs)
|
||||||
- The current version is 0.3.3 and includes the pull requests left on the old plugin page that fixes several bugs and Themify support.
|
- The current version is 0.3.4rc1 and includes the pull requests left on the old plugin page that fixes several bugs and Themify support.
|
||||||
|
|
||||||
## Fork Installation Information:
|
## Fork Installation Information:
|
||||||
- Uninstall any other versions of the plugin using Plugin Manager or other means, as necessary.
|
- Uninstall any other versions of the plugin using Plugin Manager or other means, as necessary.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import octoprint.plugin
|
import octoprint.plugin
|
||||||
|
@ -26,365 +27,556 @@ from .modules import KlipperLogAnalyzer
|
||||||
import flask
|
import flask
|
||||||
from flask_babel import gettext
|
from flask_babel import gettext
|
||||||
|
|
||||||
|
try:
|
||||||
|
import configparser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser as configparser
|
||||||
|
|
||||||
class KlipperPlugin(
|
class KlipperPlugin(
|
||||||
octoprint.plugin.StartupPlugin,
|
octoprint.plugin.StartupPlugin,
|
||||||
octoprint.plugin.TemplatePlugin,
|
octoprint.plugin.TemplatePlugin,
|
||||||
octoprint.plugin.SettingsPlugin,
|
octoprint.plugin.SettingsPlugin,
|
||||||
octoprint.plugin.AssetPlugin,
|
octoprint.plugin.AssetPlugin,
|
||||||
octoprint.plugin.SimpleApiPlugin,
|
octoprint.plugin.SimpleApiPlugin,
|
||||||
octoprint.plugin.EventHandlerPlugin):
|
octoprint.plugin.EventHandlerPlugin):
|
||||||
|
|
||||||
_parsing_response = False
|
_parsing_response = False
|
||||||
_message = ""
|
_parsing_check_response = False
|
||||||
|
_message = ""
|
||||||
|
|
||||||
#-- Startup Plugin
|
def __init__(self):
|
||||||
|
self._logger = logging.getLogger("octoprint.plugins.klipper")
|
||||||
|
self._octoklipper_logger = logging.getLogger("octoprint.plugins.klipper.debug")
|
||||||
|
|
||||||
def on_after_startup(self):
|
# -- Startup Plugin
|
||||||
klipper_port = self._settings.get(["connection", "port"])
|
def on_startup(self, host, port):
|
||||||
additional_ports = self._settings.global_get(["serial", "additionalPorts"])
|
from octoprint.logging.handlers import CleaningTimedRotatingFileHandler
|
||||||
|
octoklipper_logging_handler = CleaningTimedRotatingFileHandler(
|
||||||
|
self._settings.get_plugin_logfile_path(postfix="debug"), when="D", backupCount=3)
|
||||||
|
octoklipper_logging_handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s"))
|
||||||
|
octoklipper_logging_handler.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
if klipper_port not in additional_ports:
|
self._octoklipper_logger.addHandler(octoklipper_logging_handler)
|
||||||
additional_ports.append(klipper_port)
|
self._octoklipper_logger.setLevel(
|
||||||
self._settings.global_set(["serial", "additionalPorts"], additional_ports)
|
logging.DEBUG if self._settings.get_boolean(["debug_logging"]) else logging.INFO)
|
||||||
self._settings.save()
|
self._octoklipper_logger.propagate = False
|
||||||
self._logger.info("Added klipper serial port {} to list of additional ports.".format(klipper_port))
|
|
||||||
|
|
||||||
#-- Settings Plugin
|
def on_after_startup(self):
|
||||||
|
klipper_port = self._settings.get(["connection", "port"])
|
||||||
|
additional_ports = self._settings.global_get(
|
||||||
|
["serial", "additionalPorts"])
|
||||||
|
|
||||||
def get_additional_permissions(self, *args, **kwargs):
|
if klipper_port not in additional_ports:
|
||||||
|
additional_ports.append(klipper_port)
|
||||||
|
self._settings.global_set(
|
||||||
|
["serial", "additionalPorts"], additional_ports)
|
||||||
|
self._settings.save()
|
||||||
|
self.log_info(
|
||||||
|
"Added klipper serial port {} to list of additional ports.".format(klipper_port))
|
||||||
|
|
||||||
|
# -- Settings Plugin
|
||||||
|
|
||||||
|
def get_additional_permissions(self, *args, **kwargs):
|
||||||
return [
|
return [
|
||||||
dict(key="CONFIG",
|
{
|
||||||
name="Config Klipper",
|
"key": "CONFIG",
|
||||||
description=gettext("Allows to config klipper"),
|
"name": "Config Klipper",
|
||||||
default_groups=[ADMIN_GROUP],
|
"description": gettext("Allows to config klipper"),
|
||||||
dangerous=True,
|
"default_groups": [ADMIN_GROUP],
|
||||||
roles=["admin"]
|
"dangerous": True,
|
||||||
|
"roles": ["admin"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MACRO",
|
||||||
|
"name": "Use Klipper Macros",
|
||||||
|
"description": gettext("Allows to use klipper macros"),
|
||||||
|
"default_groups": [ADMIN_GROUP],
|
||||||
|
"dangerous": True,
|
||||||
|
"roles": ["admin"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_settings_defaults(self):
|
||||||
|
return dict(
|
||||||
|
connection=dict(
|
||||||
|
port="/tmp/printer",
|
||||||
|
replace_connection_panel=True
|
||||||
|
),
|
||||||
|
macros=[dict(
|
||||||
|
name="E-Stop",
|
||||||
|
macro="M112",
|
||||||
|
sidebar=True,
|
||||||
|
tab=True
|
||||||
|
)],
|
||||||
|
probe=dict(
|
||||||
|
height=0,
|
||||||
|
lift=5,
|
||||||
|
speed_xy=1500,
|
||||||
|
speed_z=500,
|
||||||
|
points=[dict(
|
||||||
|
name="point-1",
|
||||||
|
x=0,
|
||||||
|
y=0
|
||||||
|
)]
|
||||||
|
),
|
||||||
|
configuration=dict(
|
||||||
|
debug_logging=False,
|
||||||
|
configpath="~/printer.cfg",
|
||||||
|
old_config="",
|
||||||
|
logpath="/tmp/klippy.log",
|
||||||
|
reload_command="RESTART",
|
||||||
|
navbar=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_settings_load(self):
|
||||||
|
data = octoprint.plugin.SettingsPlugin.on_settings_load(self)
|
||||||
|
|
||||||
|
configpath = os.path.expanduser(
|
||||||
|
self._settings.get(["configuration", "configpath"])
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
f = open(configpath, "r")
|
||||||
|
data["config"] = f.read()
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
self.log_error(
|
||||||
|
"Error: Klipper config file not found at: {}".format(
|
||||||
|
configpath)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.send_message("reload", "config", "", data["config"])
|
||||||
|
# send the configdata to frontend to update ace editor
|
||||||
|
return data
|
||||||
|
|
||||||
|
def on_settings_save(self, data):
|
||||||
|
|
||||||
|
self.log_debug(
|
||||||
|
"Save klipper configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
if "config" in data:
|
||||||
|
try:
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
data["config"] = data["config"].encode('utf-8')
|
||||||
|
|
||||||
|
# check for configpath if it was changed during changing of the configfile
|
||||||
|
if self.key_exist(data, "configuration", "configpath"):
|
||||||
|
configpath = os.path.expanduser(
|
||||||
|
data["configuration"]["configpath"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# if the configpath was not changed during changing the printer.cfg. Then the configpath would not be in data[]
|
||||||
|
configpath = os.path.expanduser(
|
||||||
|
self._settings.get(["configuration", "configpath"])
|
||||||
|
)
|
||||||
|
if self.file_exist(configpath) and self._parsing_check_response:
|
||||||
|
f = open(configpath, "w")
|
||||||
|
f.write(data["config"])
|
||||||
|
f.close()
|
||||||
|
# Restart klippy to reload config
|
||||||
|
self._printer.commands(self._settings.get(
|
||||||
|
["configuration", "reload_command"]))
|
||||||
|
self.log_info("Reloading Klipper Configuration.")
|
||||||
|
self.log_debug("Writing Klipper config to {}".format(configpath))
|
||||||
|
except IOError:
|
||||||
|
self.log_error("Error: Couldn't write Klipper config file: {}".format(configpath))
|
||||||
|
else:
|
||||||
|
# we dont want to write the klipper conf to the octoprint settings
|
||||||
|
data.pop("config", None)
|
||||||
|
|
||||||
|
# save the rest of changed settings into config.yaml of octoprint
|
||||||
|
old_debug_logging = self._settings.get_boolean(["configuration", "debug_logging"])
|
||||||
|
|
||||||
|
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
|
||||||
|
|
||||||
|
new_debug_logging = self._settings.get_boolean(["configuration", "debug_logging"])
|
||||||
|
if old_debug_logging != new_debug_logging:
|
||||||
|
if new_debug_logging:
|
||||||
|
self._octoklipper_logger.setLevel(logging.DEBUG)
|
||||||
|
else:
|
||||||
|
self._octoklipper_logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
def get_settings_restricted_paths(self):
|
||||||
|
return dict(
|
||||||
|
admin=[
|
||||||
|
["connection", "port"],
|
||||||
|
["configuration", "configpath"],
|
||||||
|
["configuration", "replace_connection_panel"]
|
||||||
|
],
|
||||||
|
user=[
|
||||||
|
["macros"],
|
||||||
|
["probe"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_settings_version(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
def on_settings_migrate(self, target, current):
|
||||||
|
if current is None:
|
||||||
|
settings = self._settings
|
||||||
|
|
||||||
|
if settings.has(["serialport"]):
|
||||||
|
settings.set(["connection", "port"],
|
||||||
|
settings.get(["serialport"]))
|
||||||
|
settings.remove(["serialport"])
|
||||||
|
|
||||||
|
if settings.has(["replace_connection_panel"]):
|
||||||
|
settings.set(
|
||||||
|
["connection", "replace_connection_panel"],
|
||||||
|
settings.get(["replace_connection_panel"])
|
||||||
|
)
|
||||||
|
settings.remove(["replace_connection_panel"])
|
||||||
|
|
||||||
|
if settings.has(["probeHeight"]):
|
||||||
|
settings.set(["probe", "height"],
|
||||||
|
settings.get(["probeHeight"]))
|
||||||
|
settings.remove(["probeHeight"])
|
||||||
|
|
||||||
|
if settings.has(["probeLift"]):
|
||||||
|
settings.set(["probe", "lift"], settings.get(["probeLift"]))
|
||||||
|
settings.remove(["probeLift"])
|
||||||
|
|
||||||
|
if settings.has(["probeSpeedXy"]):
|
||||||
|
settings.set(["probe", "speed_xy"],
|
||||||
|
settings.get(["probeSpeedXy"]))
|
||||||
|
settings.remove(["probeSpeedXy"])
|
||||||
|
|
||||||
|
if settings.has(["probeSpeedZ"]):
|
||||||
|
settings.set(["probe", "speed_z"],
|
||||||
|
settings.get(["probeSpeedZ"]))
|
||||||
|
settings.remove(["probeSpeedZ"])
|
||||||
|
|
||||||
|
if settings.has(["probePoints"]):
|
||||||
|
points = settings.get(["probePoints"])
|
||||||
|
points_new = []
|
||||||
|
for p in points:
|
||||||
|
points_new.append(
|
||||||
|
dict(name="", x=int(p["x"]), y=int(p["y"]), z=0))
|
||||||
|
settings.set(["probe", "points"], points_new)
|
||||||
|
settings.remove(["probePoints"])
|
||||||
|
|
||||||
|
if settings.has(["configPath"]):
|
||||||
|
settings.set(["config_path"], settings.get(["configPath"]))
|
||||||
|
settings.remove(["configPath"])
|
||||||
|
|
||||||
|
# -- Template Plugin
|
||||||
|
|
||||||
|
def get_template_configs(self):
|
||||||
|
return [
|
||||||
|
dict(type="navbar", custom_bindings=True),
|
||||||
|
dict(type="settings", custom_bindings=True),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="Assisted Bed Leveling",
|
||||||
|
template="klipper_leveling_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="PID Tuning",
|
||||||
|
template="klipper_pid_tuning_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="Coordinate Offset",
|
||||||
|
template="klipper_offset_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="tab",
|
||||||
|
name="Klipper",
|
||||||
|
template="klipper_tab_main.jinja2",
|
||||||
|
suffix="_main",
|
||||||
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(type="sidebar",
|
||||||
|
custom_bindings=True,
|
||||||
|
icon="rocket",
|
||||||
|
replaces="connection" if self._settings.get_boolean(
|
||||||
|
["connection", "replace_connection_panel"]) else ""
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="Performance Graph",
|
||||||
|
template="klipper_graph_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
type="generic",
|
||||||
|
name="Macro Dialog",
|
||||||
|
template="klipper_param_macro_dialog.jinja2",
|
||||||
|
custom_bindings=True
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_settings_defaults(self):
|
|
||||||
return dict(
|
|
||||||
connection = dict(
|
|
||||||
port="/tmp/printer",
|
|
||||||
replace_connection_panel=True
|
|
||||||
),
|
|
||||||
macros = [dict(
|
|
||||||
name="E-Stop",
|
|
||||||
macro="M112",
|
|
||||||
sidebar=True,
|
|
||||||
tab=True
|
|
||||||
)],
|
|
||||||
probe = dict(
|
|
||||||
height=0,
|
|
||||||
lift=5,
|
|
||||||
speed_xy=1500,
|
|
||||||
speed_z=500,
|
|
||||||
points=[dict(
|
|
||||||
name="point-1",
|
|
||||||
x=0,
|
|
||||||
y=0
|
|
||||||
)]
|
|
||||||
),
|
|
||||||
configuration = dict(
|
|
||||||
configpath="~/printer.cfg",
|
|
||||||
logpath="/tmp/klippy.log",
|
|
||||||
reload_command="RESTART",
|
|
||||||
navbar=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_settings_load(self):
|
# -- Asset Plugin
|
||||||
data = octoprint.plugin.SettingsPlugin.on_settings_load(self)
|
|
||||||
|
|
||||||
filepath = os.path.expanduser(
|
def get_assets(self):
|
||||||
self._settings.get(["configuration", "configpath"])
|
return dict(
|
||||||
)
|
js=["js/klipper.js",
|
||||||
try:
|
"js/klipper_settings.js",
|
||||||
f = open(filepath, "r")
|
"js/klipper_leveling.js",
|
||||||
data["config"] = f.read()
|
"js/klipper_pid_tuning.js",
|
||||||
f.close()
|
"js/klipper_offset.js",
|
||||||
except IOError:
|
"js/klipper_param_macro.js",
|
||||||
self._logger.error(
|
"js/klipper_graph.js"
|
||||||
"Error: Klipper config file not found at: {}".format(filepath)
|
],
|
||||||
)
|
css=["css/klipper.css"],
|
||||||
return data
|
less=["css/klipper.less"]
|
||||||
|
)
|
||||||
|
|
||||||
def on_settings_save(self, data):
|
# -- Event Handler Plugin
|
||||||
if "config" in data:
|
|
||||||
try:
|
def on_event(self, event, payload):
|
||||||
filepath = os.path.expanduser(
|
if "UserLoggedIn" == event:
|
||||||
self._settings.get(["configuration", "configpath"])
|
self.update_status("info", "Klipper: Standby")
|
||||||
|
if "Connecting" == event:
|
||||||
|
self.update_status("info", "Klipper: Connecting ...")
|
||||||
|
elif "Connected" == event:
|
||||||
|
self.update_status("info", "Klipper: Connected to host")
|
||||||
|
self.log_info(
|
||||||
|
"Connected to host via {} @{}bps".format(payload["port"], payload["baudrate"]))
|
||||||
|
elif "Disconnected" == event:
|
||||||
|
self.update_status("info", "Klipper: Disconnected from host")
|
||||||
|
elif "Error" == event:
|
||||||
|
self.update_status("error", "Klipper: Error")
|
||||||
|
self.log_error(payload["error"])
|
||||||
|
|
||||||
|
# -- GCODE Hook
|
||||||
|
|
||||||
|
def on_parse_gcode(self, comm, line, *args, **kwargs):
|
||||||
|
|
||||||
|
if "FIRMWARE_VERSION" in line:
|
||||||
|
printerInfo = parse_firmware_line(line)
|
||||||
|
if "FIRMWARE_VERSION" in printerInfo:
|
||||||
|
self.log_info("Firmware version: {}".format(
|
||||||
|
printerInfo["FIRMWARE_VERSION"]))
|
||||||
|
elif "//" in line:
|
||||||
|
self._message = self._message + line.strip('/')
|
||||||
|
if not self._parsing_response:
|
||||||
|
self.update_status("info", self._message)
|
||||||
|
self._parsing_response = True
|
||||||
|
else:
|
||||||
|
if self._parsing_response:
|
||||||
|
self._parsing_response = False
|
||||||
|
self.log_info(self._message)
|
||||||
|
self._message = ""
|
||||||
|
if "!!" in line:
|
||||||
|
msg = line.strip('!')
|
||||||
|
self.update_status("error", msg)
|
||||||
|
self.log_error(msg)
|
||||||
|
return line
|
||||||
|
|
||||||
|
def get_api_commands(self):
|
||||||
|
return dict(
|
||||||
|
listLogFiles=[],
|
||||||
|
getStats=["logFile"],
|
||||||
|
reloadConfig=[],
|
||||||
|
checkConfig=["config"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_api_command(self, command, data):
|
||||||
|
if command == "listLogFiles":
|
||||||
|
files = []
|
||||||
|
logpath = os.path.expanduser(
|
||||||
|
self._settings.get(["configuration", "logpath"])
|
||||||
)
|
)
|
||||||
if sys.version_info[0] < 3:
|
if self.file_exist(logpath):
|
||||||
data["config"] = data["config"].encode('utf-8')
|
for f in glob.glob(self._settings.get(["configuration", "logpath"]) + "*"):
|
||||||
|
filesize = os.path.getsize(f)
|
||||||
|
files.append(dict(
|
||||||
|
name=os.path.basename(
|
||||||
|
f) + " ({:.1f} KB)".format(filesize / 1000.0),
|
||||||
|
file=f,
|
||||||
|
size=filesize
|
||||||
|
))
|
||||||
|
return flask.jsonify(data=files)
|
||||||
|
else:
|
||||||
|
return flask.jsonify(data=files)
|
||||||
|
elif command == "getStats":
|
||||||
|
if "logFile" in data:
|
||||||
|
log_analyzer = KlipperLogAnalyzer.KlipperLogAnalyzer(
|
||||||
|
data["logFile"])
|
||||||
|
return flask.jsonify(log_analyzer.analyze())
|
||||||
|
elif command == "reloadConfig":
|
||||||
|
data = octoprint.plugin.SettingsPlugin.on_settings_load(self)
|
||||||
|
|
||||||
f = open(filepath, "w")
|
configpath = os.path.expanduser(
|
||||||
f.write(data["config"])
|
self._settings.get(["configuration", "configpath"])
|
||||||
f.close()
|
|
||||||
self._logger.info(
|
|
||||||
"Writing Klipper config to {}".format(filepath)
|
|
||||||
)
|
)
|
||||||
# Restart klipply to reload config
|
|
||||||
self._printer.commands(self._settings.get(["configuration", "reload_command"]))
|
try:
|
||||||
self.logInfo("Reloading Klipper Configuration.")
|
f = open(configpath, "r")
|
||||||
except IOError:
|
data["config"] = f.read()
|
||||||
self._logger.error(
|
f.close()
|
||||||
"Error: Couldn't write Klipper config file: {}".format(filepath)
|
except IOError:
|
||||||
|
self.log_error(
|
||||||
|
"Error: Klipper config file not found at: {}".format(
|
||||||
|
configpath)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._settings.set(["config"], data["config"])
|
||||||
|
# self.send_message("reload", "config", "", data["config"])
|
||||||
|
# send the configdata to frontend to update ace editor
|
||||||
|
return flask.jsonify(data=data["config"])
|
||||||
|
elif command == "checkConfig":
|
||||||
|
if "config" in data:
|
||||||
|
if not self.validate_configfile(data["config"]):
|
||||||
|
self.log_debug("validateConfig ->" + data["config"])
|
||||||
|
self._settings.set(["configuration", "old_config"], data["config"])
|
||||||
|
return flask.jsonify(checkConfig="not OK")
|
||||||
|
else:
|
||||||
|
self._settings.set(["configuration", "old_config"], "")
|
||||||
|
return flask.jsonify(checkConfig="OK")
|
||||||
|
|
||||||
|
def get_update_information(self):
|
||||||
|
return dict(
|
||||||
|
klipper=dict(
|
||||||
|
displayName=self._plugin_name,
|
||||||
|
displayVersion=self._plugin_version,
|
||||||
|
type="github_release",
|
||||||
|
current=self._plugin_version,
|
||||||
|
user="thelastWallE",
|
||||||
|
repo="OctoprintKlipperPlugin",
|
||||||
|
pip="https://github.com/thelastWallE/OctoprintKlipperPlugin/archive/{target_version}.zip",
|
||||||
|
stable_branch=dict(
|
||||||
|
name="Stable",
|
||||||
|
branch="master",
|
||||||
|
comittish=["master"]
|
||||||
|
),
|
||||||
|
prerelease_branches=[
|
||||||
|
dict(
|
||||||
|
name="Release Candidate",
|
||||||
|
branch="rc",
|
||||||
|
comittish=["rc", "master"]
|
||||||
|
)]
|
||||||
)
|
)
|
||||||
data.pop("config", None) # we dont want to write the klipper conf to the octoprint settings
|
)
|
||||||
else:
|
|
||||||
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
|
|
||||||
|
|
||||||
def get_settings_restricted_paths(self):
|
#-- Helpers
|
||||||
return dict(
|
def send_message(self, type, subtype, title, payload):
|
||||||
admin=[
|
self._plugin_manager.send_plugin_message(
|
||||||
["connection", "port"],
|
self._identifier,
|
||||||
["configuration", "configpath"],
|
dict(
|
||||||
["configuration", "replace_connection_panel"]
|
time=datetime.datetime.now().strftime("%H:%M:%S"),
|
||||||
],
|
type=type,
|
||||||
user=[
|
subtype=subtype,
|
||||||
["macros"],
|
title=title,
|
||||||
["probe"]
|
payload=payload
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_settings_version(self):
|
|
||||||
return 2
|
|
||||||
|
|
||||||
def on_settings_migrate(self, target, current):
|
|
||||||
if current is None:
|
|
||||||
settings = self._settings
|
|
||||||
|
|
||||||
if settings.has(["serialport"]):
|
|
||||||
settings.set(["connection", "port"], settings.get(["serialport"]) )
|
|
||||||
settings.remove(["serialport"])
|
|
||||||
|
|
||||||
if settings.has(["replace_connection_panel"]):
|
|
||||||
settings.set(
|
|
||||||
["connection", "replace_connection_panel"],
|
|
||||||
settings.get(["replace_connection_panel"])
|
|
||||||
)
|
)
|
||||||
settings.remove(["replace_connection_panel"])
|
)
|
||||||
|
|
||||||
if settings.has(["probeHeight"]):
|
def poll_status(self):
|
||||||
settings.set(["probe", "height"], settings.get(["probeHeight"]))
|
self._printer.commands("STATUS")
|
||||||
settings.remove(["probeHeight"])
|
|
||||||
|
|
||||||
if settings.has(["probeLift"]):
|
def update_status(self, type, status):
|
||||||
settings.set(["probe", "lift"], settings.get(["probeLift"]))
|
self.send_message("status", type, status, status)
|
||||||
settings.remove(["probeLift"])
|
|
||||||
|
|
||||||
if settings.has(["probeSpeedXy"]):
|
def log_info(self, message):
|
||||||
settings.set(["probe", "speed_xy"], settings.get(["probeSpeedXy"]))
|
self._octoklipper_logger.info(message)
|
||||||
settings.remove(["probeSpeedXy"])
|
self.send_message("log", "info", message, message)
|
||||||
|
self.send_message("console", "info", message, message)
|
||||||
|
|
||||||
if settings.has(["probeSpeedZ"]):
|
def log_debug(self, message):
|
||||||
settings.set(["probe", "speed_z"], settings.get(["probeSpeedZ"]))
|
self._octoklipper_logger.debug(message)
|
||||||
settings.remove(["probeSpeedZ"])
|
# sends a message to frontend(in klipper.js -> self.onDataUpdaterPluginMessage) and write it to the console.
|
||||||
|
# _mtype, subtype=debug/info, title of message, message)
|
||||||
|
self.send_message("console", "debug", message, message)
|
||||||
|
|
||||||
if settings.has(["probePoints"]):
|
def log_error(self, error):
|
||||||
points = settings.get(["probePoints"])
|
self._octoklipper_logger.error(error)
|
||||||
points_new = []
|
self.send_message("log", "error", error, error)
|
||||||
for p in points:
|
self.send_message("console", "error", error, error)
|
||||||
points_new.append(dict(name="", x=int(p["x"]), y=int(p["y"]), z=0))
|
|
||||||
settings.set(["probe", "points"], points_new)
|
|
||||||
settings.remove(["probePoints"])
|
|
||||||
|
|
||||||
if settings.has(["configPath"]):
|
def file_exist(self, filepath):
|
||||||
settings.set(["config_path"], settings.get(["configPath"]))
|
if not os.path.isfile(filepath):
|
||||||
settings.remove(["configPath"])
|
self.send_message("PopUp", "warning", "OctoKlipper Settings",
|
||||||
|
"Klipper " + filepath + " does not exist!")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
#-- Template Plugin
|
def key_exist(self, dict, key1, key2):
|
||||||
|
try:
|
||||||
|
dict[key1][key2]
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def get_template_configs(self):
|
def validate_configfile(self, dataToBeValidated):
|
||||||
return [
|
"""
|
||||||
dict(type="navbar", custom_bindings=True),
|
--->For now this just checks if the given data can be parsed<----
|
||||||
dict(type="settings", custom_bindings=True),
|
|
||||||
dict(
|
|
||||||
type="generic",
|
|
||||||
name="Assisted Bed Leveling",
|
|
||||||
template="klipper_leveling_dialog.jinja2",
|
|
||||||
custom_bindings=True
|
|
||||||
),
|
|
||||||
dict(
|
|
||||||
type="generic",
|
|
||||||
name="PID Tuning",
|
|
||||||
template="klipper_pid_tuning_dialog.jinja2",
|
|
||||||
custom_bindings=True
|
|
||||||
),
|
|
||||||
dict(
|
|
||||||
type="generic",
|
|
||||||
name="Coordinate Offset",
|
|
||||||
template="klipper_offset_dialog.jinja2",
|
|
||||||
custom_bindings=True
|
|
||||||
),
|
|
||||||
dict(
|
|
||||||
type="tab",
|
|
||||||
name="Klipper",
|
|
||||||
template="klipper_tab_main.jinja2",
|
|
||||||
suffix="_main",
|
|
||||||
custom_bindings=True
|
|
||||||
),
|
|
||||||
dict(type="sidebar",
|
|
||||||
custom_bindings=True,
|
|
||||||
icon="rocket",
|
|
||||||
replaces= "connection" if self._settings.get_boolean(["connection", "replace_connection_panel"]) else ""
|
|
||||||
),
|
|
||||||
dict(
|
|
||||||
type="generic",
|
|
||||||
name="Performance Graph",
|
|
||||||
template="klipper_graph_dialog.jinja2",
|
|
||||||
custom_bindings=True
|
|
||||||
),
|
|
||||||
dict(
|
|
||||||
type="generic",
|
|
||||||
name="Macro Dialog",
|
|
||||||
template="klipper_param_macro_dialog.jinja2",
|
|
||||||
custom_bindings=True
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
#-- Asset Plugin
|
From https://www.opensourceforu.com/2015/03/practical-python-programming-writing-a-config-file-checker/
|
||||||
|
|
||||||
def get_assets(self):
|
Validates a given Config File in filetobevalidated against a correct config file pointed to by goldenfilepath
|
||||||
return dict(
|
returns a list of erroneous lines as a list[strings]
|
||||||
js=["js/klipper.js",
|
if config file is fine, it should return an empty list
|
||||||
"js/klipper_settings.js",
|
"""
|
||||||
"js/klipper_leveling.js",
|
#len(ValidateFile('c:\example.cfg', 'c:\example.cfg' ))== 0
|
||||||
"js/klipper_pid_tuning.js",
|
|
||||||
"js/klipper_offset.js",
|
|
||||||
"js/klipper_param_macro.js",
|
|
||||||
"js/klipper_graph.js"
|
|
||||||
],
|
|
||||||
css=["css/klipper.css"],
|
|
||||||
less=["css/klipper.less"]
|
|
||||||
)
|
|
||||||
|
|
||||||
#-- Event Handler Plugin
|
# learn golden file
|
||||||
|
#__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
|
||||||
|
#goldenfilepath = os.path.join(__location__, "goldenprinter.cfg")
|
||||||
|
#goldenconfig = ConfigParser.ConfigParser()
|
||||||
|
# dataToValidated.read(goldenfilepath)
|
||||||
|
|
||||||
def on_event(self, event, payload):
|
# learn file to be validated
|
||||||
if "UserLoggedIn" == event:
|
|
||||||
self.updateStatus("info","Klipper: Standby")
|
|
||||||
if "Connecting" == event:
|
|
||||||
self.updateStatus("info", "Klipper: Connecting ...")
|
|
||||||
elif "Connected" == event:
|
|
||||||
self.updateStatus("info", "Klipper: Connected to host")
|
|
||||||
self.logInfo("Connected to host via {} @{}bps".format(payload["port"], payload["baudrate"]))
|
|
||||||
elif "Disconnected" == event:
|
|
||||||
self.updateStatus("info", "Klipper: Disconnected from host")
|
|
||||||
elif "Error" == event:
|
|
||||||
self.updateStatus("error", "Klipper: Error")
|
|
||||||
self.logError(payload["error"])
|
|
||||||
|
|
||||||
#-- GCODE Hook
|
try:
|
||||||
|
dataToValidated = configparser.RawConfigParser()
|
||||||
|
dataToValidated.read_string(dataToBeValidated)
|
||||||
|
except configparser.Error as error:
|
||||||
|
error.message = error.message.replace("\\n","")
|
||||||
|
error.message = error.message.replace("file:","Klipper Configuration", 1)
|
||||||
|
error.message = error.message.replace("'","", 2)
|
||||||
|
error.source = "Klipper config"
|
||||||
|
self.log_error(
|
||||||
|
"Error: Invalid Klipper config file:\n" +
|
||||||
|
"{}".format(str(error))
|
||||||
|
)
|
||||||
|
self.send_message("PopUp", "warning", "Invalid Config",
|
||||||
|
"Config got not saved!\n" +
|
||||||
|
"You can reload your last changes\n" +
|
||||||
|
"on the 'Klipper Configuration' tab.\n\n" + str(error))
|
||||||
|
self._parsing_check_response = False
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self._parsing_check_response = True
|
||||||
|
return
|
||||||
|
|
||||||
def on_parse_gcode(self, comm, line, *args, **kwargs):
|
#incorrectlines = []
|
||||||
if "FIRMWARE_VERSION" in line:
|
# for section in dataToValidated.sections():
|
||||||
printerInfo = parse_firmware_line(line)
|
# #check each key is present in corresponding golden section
|
||||||
if "FIRMWARE_VERSION" in printerInfo:
|
# for key in dataToValidated.options(section):
|
||||||
self.logInfo("Firmware version: {}".format(printerInfo["FIRMWARE_VERSION"]))
|
# if not goldenconfig.has_option(section,key):
|
||||||
elif "//" in line:
|
# incorrectlines.append(key + "=" + dataToValidated.get(section,key))
|
||||||
self._message = self._message + line.strip('/')
|
# # print incorrect lines
|
||||||
if not self._parsing_response:
|
# if len(incorrectlines) > 0 :
|
||||||
self.updateStatus("info", self._message)
|
# self.send_message("errorPopUp","warning", "OctoKlipper Settings", "Invalid Klipper config file: " + str(incorrectlines))
|
||||||
self._parsing_response = True
|
# for k in incorrectlines:
|
||||||
else:
|
# print k
|
||||||
if self._parsing_response:
|
# self._logger.error(
|
||||||
self._parsing_response = False
|
# "Error: Invalid Klipper config line: {}".format(str(k))
|
||||||
self.logInfo(self._message)
|
# )
|
||||||
self._message = ""
|
# return incorrectlines
|
||||||
if "!!" in line:
|
|
||||||
msg = line.strip('!')
|
|
||||||
self.updateStatus("error", msg)
|
|
||||||
self.logError(msg)
|
|
||||||
return line
|
|
||||||
|
|
||||||
def get_api_commands(self):
|
|
||||||
return dict(
|
|
||||||
listLogFiles=[],
|
|
||||||
getStats=["logFile"],
|
|
||||||
loadConfig=["configFile"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_api_command(self, command, data):
|
|
||||||
if command == "listLogFiles":
|
|
||||||
files = []
|
|
||||||
for f in glob.glob(self._settings.get(["configuration", "logpath"]) + "*"):
|
|
||||||
filesize = os.path.getsize(f)
|
|
||||||
files.append(dict(
|
|
||||||
name=os.path.basename(f) + " ({:.1f} KB)".format(filesize / 1000.0),
|
|
||||||
file=f,
|
|
||||||
size=filesize
|
|
||||||
))
|
|
||||||
return flask.jsonify(data=files)
|
|
||||||
elif command == "getStats":
|
|
||||||
if "logFile" in data:
|
|
||||||
log_analyzer = KlipperLogAnalyzer.KlipperLogAnalyzer(data["logFile"])
|
|
||||||
return flask.jsonify(log_analyzer.analyze())
|
|
||||||
elif command == "loadConfig":
|
|
||||||
kc = Parser()
|
|
||||||
sections = kc.load(data["configFile"])
|
|
||||||
return flask.jsonify(sections)
|
|
||||||
|
|
||||||
def get_update_information(self):
|
|
||||||
return dict(
|
|
||||||
klipper=dict(
|
|
||||||
displayName=self._plugin_name,
|
|
||||||
displayVersion=self._plugin_version,
|
|
||||||
type="github_release",
|
|
||||||
current=self._plugin_version,
|
|
||||||
user="thelastWallE",
|
|
||||||
repo="OctoprintKlipperPlugin",
|
|
||||||
pip="https://github.com/thelastWallE/OctoprintKlipperPlugin/archive/{target_version}.zip",
|
|
||||||
stable_branch=dict(
|
|
||||||
name="Stable",
|
|
||||||
branch="master",
|
|
||||||
comittish=["master"]
|
|
||||||
),
|
|
||||||
prerelease_branches=[
|
|
||||||
dict(
|
|
||||||
name="Release Candidate",
|
|
||||||
branch="rc",
|
|
||||||
comittish=["rc", "master"],
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
#-- Helpers
|
|
||||||
def sendMessage(self, type, subtype, payload):
|
|
||||||
self._plugin_manager.send_plugin_message(
|
|
||||||
self._identifier,
|
|
||||||
dict(
|
|
||||||
time=datetime.datetime.now().strftime("%H:%M:%S"),
|
|
||||||
type=type,
|
|
||||||
subtype=subtype,
|
|
||||||
payload=payload
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def pollStatus(self):
|
|
||||||
self._printer.commands("STATUS")
|
|
||||||
|
|
||||||
def updateStatus(self, type, status):
|
|
||||||
self.sendMessage("status", type, status)
|
|
||||||
|
|
||||||
def logInfo(self, message):
|
|
||||||
self.sendMessage("log", "info", message)
|
|
||||||
|
|
||||||
def logError(self, error):
|
|
||||||
self.sendMessage("log", "error", error)
|
|
||||||
|
|
||||||
__plugin_name__ = "OctoKlipper"
|
__plugin_name__ = "OctoKlipper"
|
||||||
__plugin_pythoncompat__ = ">=2.7,<4"
|
__plugin_pythoncompat__ = ">=2.7,<4"
|
||||||
|
|
||||||
def __plugin_load__():
|
|
||||||
global __plugin_implementation__
|
|
||||||
global __plugin_hooks__
|
|
||||||
__plugin_implementation__ = KlipperPlugin()
|
|
||||||
__plugin_hooks__ = {
|
|
||||||
"octoprint.access.permissions": __plugin_implementation__.get_additional_permissions,
|
|
||||||
"octoprint.comm.protocol.gcode.received": __plugin_implementation__.on_parse_gcode,
|
|
||||||
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information
|
|
||||||
}
|
|
||||||
|
|
||||||
|
def __plugin_load__():
|
||||||
|
global __plugin_implementation__
|
||||||
|
global __plugin_hooks__
|
||||||
|
__plugin_implementation__ = KlipperPlugin()
|
||||||
|
__plugin_hooks__ = {
|
||||||
|
"octoprint.access.permissions": __plugin_implementation__.get_additional_permissions,
|
||||||
|
"octoprint.comm.protocol.gcode.received": __plugin_implementation__.on_parse_gcode,
|
||||||
|
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,519 @@
|
||||||
|
# Golden printer.cfg to check against. This should contain all settings under each section that are available.
|
||||||
|
|
||||||
|
[stepper_x]
|
||||||
|
step_pin:
|
||||||
|
# Step GPIO pin (triggered high). This parameter must be provided.
|
||||||
|
dir_pin:
|
||||||
|
# Direction GPIO pin (high indicates positive direction). This
|
||||||
|
# parameter must be provided.
|
||||||
|
enable_pin:
|
||||||
|
# Enable pin (default is enable high; use ! to indicate enable
|
||||||
|
# low). If this parameter is not provided then the stepper motor
|
||||||
|
# driver must always be enabled.
|
||||||
|
rotation_distance:
|
||||||
|
# Distance (in mm) that the axis travels with one full rotation of
|
||||||
|
# the stepper motor. This parameter must be provided.
|
||||||
|
microsteps:
|
||||||
|
# The number of microsteps the stepper motor driver uses. This
|
||||||
|
# parameter must be provided.
|
||||||
|
full_steps_per_rotation: 200
|
||||||
|
# The number of full steps for one rotation of the stepper motor.
|
||||||
|
# Set this to 200 for a 1.8 degree stepper motor or set to 400 for a
|
||||||
|
# 0.9 degree motor. The default is 200.
|
||||||
|
gear_ratio:
|
||||||
|
# The gear ratio if the stepper motor is connected to the axis via a
|
||||||
|
# gearbox. For example, one may specify "5:1" if a 5 to 1 gearbox is
|
||||||
|
# in use. If the axis has multiple gearboxes one may specify a comma
|
||||||
|
# separated list of gear ratios (for example, "57:11, 2:1"). If a
|
||||||
|
# gear_ratio is specified then rotation_distance specifies the
|
||||||
|
# distance the axis travels for one full rotation of the final gear.
|
||||||
|
# The default is to not use a gear ratio.
|
||||||
|
endstop_pin:
|
||||||
|
# Endstop switch detection pin. This parameter must be provided for
|
||||||
|
# the X, Y, and Z steppers on cartesian style printers.
|
||||||
|
position_min: 0
|
||||||
|
# Minimum valid distance (in mm) the user may command the stepper to
|
||||||
|
# move to. The default is 0mm.
|
||||||
|
position_endstop:
|
||||||
|
# Location of the endstop (in mm). This parameter must be provided
|
||||||
|
# for the X, Y, and Z steppers on cartesian style printers.
|
||||||
|
position_max:
|
||||||
|
# Maximum valid distance (in mm) the user may command the stepper to
|
||||||
|
# move to. This parameter must be provided for the X, Y, and Z
|
||||||
|
# steppers on cartesian style printers.
|
||||||
|
homing_speed: 5.0
|
||||||
|
# Maximum velocity (in mm/s) of the stepper when homing. The default
|
||||||
|
# is 5mm/s.
|
||||||
|
homing_retract_dist: 5.0
|
||||||
|
# Distance to backoff (in mm) before homing a second time during
|
||||||
|
# homing. Set this to zero to disable the second home. The default
|
||||||
|
# is 5mm.
|
||||||
|
homing_retract_speed:
|
||||||
|
# Speed to use on the retract move after homing in case this should
|
||||||
|
# be different from the homing speed, which is the default for this
|
||||||
|
# parameter
|
||||||
|
second_homing_speed:
|
||||||
|
# Velocity (in mm/s) of the stepper when performing the second home.
|
||||||
|
# The default is homing_speed/2.
|
||||||
|
homing_positive_dir:
|
||||||
|
# If true, homing will cause the stepper to move in a positive
|
||||||
|
# direction (away from zero); if false, home towards zero. It is
|
||||||
|
# better to use the default than to specify this parameter. The
|
||||||
|
# default is true if position_endstop is near position_max and false
|
||||||
|
# if near position_min.
|
||||||
|
|
||||||
|
[tmc2209 stepper_x]
|
||||||
|
uart_pin: PB15
|
||||||
|
run_current: 0.580
|
||||||
|
hold_current: 0.500
|
||||||
|
stealthchop_threshold: 250
|
||||||
|
|
||||||
|
[stepper_y]
|
||||||
|
step_pin:
|
||||||
|
dir_pin:
|
||||||
|
enable_pin:
|
||||||
|
rotation_distance:
|
||||||
|
microsteps:
|
||||||
|
full_steps_per_rotation: 200
|
||||||
|
gear_ratio:
|
||||||
|
endstop_pin:
|
||||||
|
position_min: 0
|
||||||
|
position_endstop:
|
||||||
|
position_max:
|
||||||
|
homing_speed: 5.0
|
||||||
|
homing_retract_dist: 5.0
|
||||||
|
homing_retract_speed:
|
||||||
|
second_homing_speed:
|
||||||
|
homing_positive_dir:
|
||||||
|
|
||||||
|
[tmc2209 stepper_y]
|
||||||
|
uart_pin: PC6
|
||||||
|
run_current: 0.580
|
||||||
|
hold_current: 0.500
|
||||||
|
stealthchop_threshold: 250
|
||||||
|
|
||||||
|
[stepper_z]
|
||||||
|
step_pin:
|
||||||
|
dir_pin:
|
||||||
|
enable_pin:
|
||||||
|
rotation_distance:
|
||||||
|
microsteps:
|
||||||
|
full_steps_per_rotation: 200
|
||||||
|
gear_ratio:
|
||||||
|
endstop_pin:
|
||||||
|
position_min: 0
|
||||||
|
position_endstop:
|
||||||
|
position_max:
|
||||||
|
homing_speed: 5.0
|
||||||
|
homing_retract_dist: 5.0
|
||||||
|
homing_retract_speed:
|
||||||
|
second_homing_speed:
|
||||||
|
homing_positive_dir:
|
||||||
|
|
||||||
|
[tmc2209 stepper_z]
|
||||||
|
uart_pin: PC10
|
||||||
|
run_current: 0.580
|
||||||
|
hold_current: 0.500
|
||||||
|
stealthchop_threshold: 10
|
||||||
|
|
||||||
|
[safe_z_home]
|
||||||
|
home_xy_position: 70,100
|
||||||
|
speed: 50
|
||||||
|
z_hop: 10
|
||||||
|
z_hop_speed: 4
|
||||||
|
|
||||||
|
# --------------------For Delta Kinematics: --------------------------
|
||||||
|
# The stepper_a section describes the stepper controlling the front
|
||||||
|
# left tower (at 210 degrees). This section also controls the homing
|
||||||
|
# parameters (homing_speed, homing_retract_dist) for all towers.
|
||||||
|
[stepper_a]
|
||||||
|
position_endstop:
|
||||||
|
# Distance (in mm) between the nozzle and the bed when the nozzle is
|
||||||
|
# in the center of the build area and the endstop triggers. This
|
||||||
|
# parameter must be provided for stepper_a; for stepper_b and
|
||||||
|
# stepper_c this parameter defaults to the value specified for
|
||||||
|
# stepper_a.
|
||||||
|
arm_length:
|
||||||
|
# Length (in mm) of the diagonal rod that connects this tower to the
|
||||||
|
# print head. This parameter must be provided for stepper_a; for
|
||||||
|
# stepper_b and stepper_c this parameter defaults to the value
|
||||||
|
# specified for stepper_a.
|
||||||
|
#angle:
|
||||||
|
# This option specifies the angle (in degrees) that the tower is
|
||||||
|
# at. The default is 210 for stepper_a, 330 for stepper_b, and 90
|
||||||
|
# for stepper_c.
|
||||||
|
|
||||||
|
# The stepper_b section describes the stepper controlling the front
|
||||||
|
# right tower (at 330 degrees).
|
||||||
|
[stepper_b]
|
||||||
|
position_endstop:
|
||||||
|
arm_length:
|
||||||
|
|
||||||
|
# The stepper_c section describes the stepper controlling the rear
|
||||||
|
# tower (at 90 degrees).
|
||||||
|
[stepper_c]
|
||||||
|
position_endstop:
|
||||||
|
arm_length:
|
||||||
|
|
||||||
|
# The delta_calibrate section enables a DELTA_CALIBRATE extended
|
||||||
|
# g-code command that can calibrate the tower endstop positions and
|
||||||
|
# angles.
|
||||||
|
[delta_calibrate]
|
||||||
|
radius:
|
||||||
|
# Radius (in mm) of the area that may be probed. This is the radius
|
||||||
|
# of nozzle coordinates to be probed; if using an automatic probe
|
||||||
|
# with an XY offset then choose a radius small enough so that the
|
||||||
|
# probe always fits over the bed. This parameter must be provided.
|
||||||
|
speed: 50
|
||||||
|
# The speed (in mm/s) of non-probing moves during the calibration.
|
||||||
|
# The default is 50.
|
||||||
|
horizontal_move_z: 5
|
||||||
|
# The height (in mm) that the head should be commanded to move to
|
||||||
|
# just prior to starting a probe operation. The default is 5.
|
||||||
|
|
||||||
|
# -------------- For Polar Kinematics ---------------------------
|
||||||
|
# The stepper_bed section is used to describe the stepper controlling
|
||||||
|
# the bed.
|
||||||
|
[stepper_bed]
|
||||||
|
gear_ratio:
|
||||||
|
# A gear_ratio must be specified and rotation_distance may not be
|
||||||
|
# specified. For example, if the bed has an 80 toothed pulley driven
|
||||||
|
# by a stepper with a 16 toothed pulley then one would specify a
|
||||||
|
# gear ratio of "80:16". This parameter must be provided.
|
||||||
|
max_z_velocity:
|
||||||
|
# This sets the maximum velocity (in mm/s) of movement along the z
|
||||||
|
# axis. This setting can be used to restrict the maximum speed of
|
||||||
|
# the z stepper motor. The default is to use max_velocity for
|
||||||
|
# max_z_velocity.
|
||||||
|
max_z_accel:
|
||||||
|
# This sets the maximum acceleration (in mm/s^2) of movement along
|
||||||
|
# the z axis. It limits the acceleration of the z stepper motor. The
|
||||||
|
# default is to use max_accel for max_z_accel.
|
||||||
|
|
||||||
|
# The stepper_arm section is used to describe the stepper controlling
|
||||||
|
# the carriage on the arm.
|
||||||
|
[stepper_arm]
|
||||||
|
|
||||||
|
# --------------------- For
|
||||||
|
|
||||||
|
|
||||||
|
[extruder]
|
||||||
|
step_pin: PB3
|
||||||
|
dir_pin: !PB4
|
||||||
|
enable_pin: !PD2
|
||||||
|
microsteps: 16
|
||||||
|
rotation_distance: 33.400
|
||||||
|
nozzle_diameter: 0.400
|
||||||
|
filament_diameter: 1.750
|
||||||
|
heater_pin: PC8
|
||||||
|
sensor_type: EPCOS 100K B57560G104F
|
||||||
|
sensor_pin: PA0
|
||||||
|
control: pid
|
||||||
|
pid_Kp=26.049
|
||||||
|
pid_Ki=1.258
|
||||||
|
pid_Kd=134.805
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 250
|
||||||
|
pressure_advance: 0.07
|
||||||
|
pressure_advance_smooth_time: 0.040
|
||||||
|
|
||||||
|
[tmc2209 extruder]
|
||||||
|
uart_pin: PC11
|
||||||
|
run_current: 0.670
|
||||||
|
hold_current: 0.500
|
||||||
|
stealthchop_threshold: 5
|
||||||
|
|
||||||
|
[heater_bed]
|
||||||
|
heater_pin: PC9
|
||||||
|
sensor_type: ATC Semitec 104GT-2
|
||||||
|
sensor_pin: PC3
|
||||||
|
control: pid
|
||||||
|
pid_Kp=72.740
|
||||||
|
pid_Ki=1.569
|
||||||
|
pid_Kd=842.877
|
||||||
|
min_temp: 0
|
||||||
|
max_temp: 130
|
||||||
|
|
||||||
|
[fan]
|
||||||
|
pin: PA8
|
||||||
|
|
||||||
|
[mcu]
|
||||||
|
serial:
|
||||||
|
# The serial port to connect to the MCU. If unsure (or if it
|
||||||
|
# changes) see the "Where's my serial port?" section of the FAQ.
|
||||||
|
# This parameter must be provided when using a serial port.
|
||||||
|
baud: 250000
|
||||||
|
# The baud rate to use. The default is 250000.
|
||||||
|
canbus_uuid:
|
||||||
|
# If using a device connected to a CAN bus then this sets the unique
|
||||||
|
# chip identifier to connect to. This value must be provided when using
|
||||||
|
# CAN bus for communication.
|
||||||
|
canbus_interface:
|
||||||
|
# If using a device connected to a CAN bus then this sets the CAN
|
||||||
|
# network interface to use. The default is 'can0'.
|
||||||
|
pin_map:
|
||||||
|
# This option may be used to enable Arduino pin name aliases. The
|
||||||
|
# default is to not enable the aliases.
|
||||||
|
restart_method:
|
||||||
|
# This controls the mechanism the host will use to reset the
|
||||||
|
# micro-controller. The choices are 'arduino', 'cheetah', 'rpi_usb',
|
||||||
|
# and 'command'. The 'arduino' method (toggle DTR) is common on
|
||||||
|
# Arduino boards and clones. The 'cheetah' method is a special
|
||||||
|
# method needed for some Fysetc Cheetah boards. The 'rpi_usb' method
|
||||||
|
# is useful on Raspberry Pi boards with micro-controllers powered
|
||||||
|
# over USB - it briefly disables power to all USB ports to
|
||||||
|
# accomplish a micro-controller reset. The 'command' method involves
|
||||||
|
# sending a Klipper command to the micro-controller so that it can
|
||||||
|
# reset itself. The default is 'arduino' if the micro-controller
|
||||||
|
# communicates over a serial port, 'command' otherwise.
|
||||||
|
|
||||||
|
[mcu my_extra_mcu]
|
||||||
|
serial:
|
||||||
|
baud: 250000
|
||||||
|
canbus_uuid:
|
||||||
|
canbus_interface:
|
||||||
|
pin_map:
|
||||||
|
restart_method:
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics:
|
||||||
|
# The type of printer in use. This option may be one of: cartesian,
|
||||||
|
# corexy, corexz, delta, rotary_delta, polar, winch, or none. This
|
||||||
|
# parameter must be specified.
|
||||||
|
max_velocity:
|
||||||
|
# Maximum velocity (in mm/s) of the toolhead (relative to the
|
||||||
|
# print). This parameter must be specified.
|
||||||
|
max_accel:
|
||||||
|
# Maximum acceleration (in mm/s^2) of the toolhead (relative to the
|
||||||
|
# print). This parameter must be specified.
|
||||||
|
max_accel_to_decel:
|
||||||
|
# A pseudo acceleration (in mm/s^2) controlling how fast the
|
||||||
|
# toolhead may go from acceleration to deceleration. It is used to
|
||||||
|
# reduce the top speed of short zig-zag moves (and thus reduce
|
||||||
|
# printer vibration from these moves). The default is half of
|
||||||
|
# max_accel.
|
||||||
|
square_corner_velocity: 5.0
|
||||||
|
# The maximum velocity (in mm/s) that the toolhead may travel a 90
|
||||||
|
# degree corner at. A non-zero value can reduce changes in extruder
|
||||||
|
# flow rates by enabling instantaneous velocity changes of the
|
||||||
|
# toolhead during cornering. This value configures the internal
|
||||||
|
# centripetal velocity cornering algorithm; corners with angles
|
||||||
|
# larger than 90 degrees will have a higher cornering velocity while
|
||||||
|
# corners with angles less than 90 degrees will have a lower
|
||||||
|
# cornering velocity. If this is set to zero then the toolhead will
|
||||||
|
# decelerate to zero at each corner. The default is 5mm/s.
|
||||||
|
#
|
||||||
|
# -----delta Kinematics:------
|
||||||
|
max_z_velocity:
|
||||||
|
# For delta printers this limits the maximum velocity (in mm/s) of
|
||||||
|
# moves with z axis movement. This setting can be used to reduce the
|
||||||
|
# maximum speed of up/down moves (which require a higher step rate
|
||||||
|
# than other moves on a delta printer). The default is to use
|
||||||
|
# max_velocity for max_z_velocity.
|
||||||
|
minimum_z_position: 0
|
||||||
|
# The minimum Z position that the user may command the head to move
|
||||||
|
# to. The default is 0.
|
||||||
|
delta_radius:
|
||||||
|
# Radius (in mm) of the horizontal circle formed by the three linear
|
||||||
|
# axis towers. This parameter may also be calculated as:
|
||||||
|
# delta_radius = smooth_rod_offset - effector_offset - carriage_offset
|
||||||
|
# This parameter must be provided.
|
||||||
|
print_radius:
|
||||||
|
# The radius (in mm) of valid toolhead XY coordinates. One may use
|
||||||
|
# this setting to customize the range checking of toolhead moves. If
|
||||||
|
# a large value is specified here then it may be possible to command
|
||||||
|
# the toolhead into a collision with a tower. The default is to use
|
||||||
|
# delta_radius for print_radius (which would normally prevent a
|
||||||
|
# tower collision).
|
||||||
|
|
||||||
|
max_z_velocity: 5
|
||||||
|
max_z_accel: 100
|
||||||
|
max_accel_to_decel: 1000
|
||||||
|
square_corner_velocity: 5.0
|
||||||
|
|
||||||
|
[bed_screws]
|
||||||
|
screw1: 30,30
|
||||||
|
screw2: 30,200
|
||||||
|
screw3: 200,30
|
||||||
|
screw4: 200,200
|
||||||
|
|
||||||
|
[bed_mesh]
|
||||||
|
speed: 50
|
||||||
|
horizontal_move_z: 5
|
||||||
|
mesh_min: 48,8
|
||||||
|
mesh_max: 200,230
|
||||||
|
probe_count: 7,7
|
||||||
|
fade_start: 1.0
|
||||||
|
fade_end: 0.0
|
||||||
|
umber of
|
||||||
|
algorithm: bicubic
|
||||||
|
|
||||||
|
[static_digital_output usb_pullup_enable]
|
||||||
|
pins: !PC13
|
||||||
|
|
||||||
|
[gcode_arcs]
|
||||||
|
resolution: 1.0
|
||||||
|
|
||||||
|
[bltouch]
|
||||||
|
sensor_pin: ^PC14
|
||||||
|
control_pin: PA1
|
||||||
|
x_offset: 48
|
||||||
|
y_offset: -2
|
||||||
|
z_offset: 1.23
|
||||||
|
pin_move_time: 0.75
|
||||||
|
speed: 1.0
|
||||||
|
lift_speed: 3.0
|
||||||
|
samples: 2
|
||||||
|
sample_retract_dist: 3
|
||||||
|
|
||||||
|
[board_pins]
|
||||||
|
aliases:
|
||||||
|
# EXP1 header
|
||||||
|
EXP1_1=PB5, EXP1_3=PA9, EXP1_5=PA10, EXP1_7=PB8, EXP1_9=<GND>,
|
||||||
|
EXP1_2=PB6, EXP1_4=<RST>, EXP1_6=PB9, EXP1_8=PB7, EXP1_10=<5V>
|
||||||
|
|
||||||
|
[display]
|
||||||
|
lcd_type: st7920
|
||||||
|
cs_pin: EXP1_7
|
||||||
|
sclk_pin: EXP1_6
|
||||||
|
sid_pin: EXP1_8
|
||||||
|
encoder_pins: ^EXP1_5, ^EXP1_3
|
||||||
|
click_pin: ^!EXP1_2
|
||||||
|
|
||||||
|
[output_pin beeper]
|
||||||
|
pin: EXP1_1
|
||||||
|
|
||||||
|
[firmware_retraction]
|
||||||
|
retract_length: 2.7
|
||||||
|
retract_speed: 25
|
||||||
|
unretract_extra_length: 0.0
|
||||||
|
unretract_speed: 25
|
||||||
|
|
||||||
|
[virtual_sdcard]
|
||||||
|
path: /home/pi/.octoprint/uploads
|
||||||
|
|
||||||
|
[gcode_macro DO_MESH]
|
||||||
|
gcode:
|
||||||
|
M140 S60
|
||||||
|
G28
|
||||||
|
M190 S60 ; Heat Bed to 60C
|
||||||
|
BED_MESH_CALIBRATE ;Mesh leveling
|
||||||
|
M140 S0 ;Turn-off bed
|
||||||
|
SAVE_CONFIG
|
||||||
|
|
||||||
|
[gcode_macro CHECK_DISTANCE]
|
||||||
|
gcode:
|
||||||
|
M140 S60
|
||||||
|
G28
|
||||||
|
M190 S60 ; Heat Bed to 60C
|
||||||
|
G1 Z15.8 F1000
|
||||||
|
|
||||||
|
[gcode_macro START_PRINT]
|
||||||
|
gcode:
|
||||||
|
M220 S100 ;Reset Feedrate
|
||||||
|
M221 S100 ;Reset Flowrate
|
||||||
|
G92 E0 ;Reset Extruder
|
||||||
|
G1 Z1.0 F3000 ;Move Z Axis up
|
||||||
|
G1 X0 Y20 Z0.3 F5000.0 ;Move to start position
|
||||||
|
G1 X0 Y200.0 Z0.3 F1500.0 E15 ;Draw the first line
|
||||||
|
G1 X0.4 Y200.0 Z0.3 F5000.0 ;Move to side a little
|
||||||
|
G1 X0.4 Y20 Z0.3 F1500.0 E27 ;Draw the second line
|
||||||
|
G1 Z1.6 F3000 ;Move Z Axis up
|
||||||
|
|
||||||
|
[gcode_macro END_PRINT]
|
||||||
|
gcode:
|
||||||
|
;MESH:ENDGCODE
|
||||||
|
G91 ;Relative positioning
|
||||||
|
G92 E0 ;Reset Extruder
|
||||||
|
G1 F2400 E-8
|
||||||
|
G1 Z0.2 F2400 ;Raise Z
|
||||||
|
G1 X5 Y5 F3000 ;Wipe out
|
||||||
|
G1 Z10 ;Raise Z more
|
||||||
|
G90 ;Absolute positionning
|
||||||
|
G1 X0 Y{machine_depth} ;Present print
|
||||||
|
M106 S0 ;Turn-off fan
|
||||||
|
M140 S0 ;Turn-off bed
|
||||||
|
M84 X Y E ;Disable all steppers but Z
|
||||||
|
|
||||||
|
[gcode_macro SET_RETRACTIONLENGTH]
|
||||||
|
gcode:
|
||||||
|
SET_RETRACTION RETRACT_LENGTH={params.LENGTH|float}
|
||||||
|
GET_RETRACTION
|
||||||
|
|
||||||
|
[pause_resume]
|
||||||
|
# M600: Filament Change. This macro will pause the printer, move the
|
||||||
|
# tool to the change position, and retract the filament 50mm. Adjust
|
||||||
|
# the retraction settings for your own extruder. After filament has
|
||||||
|
# been changed, the print can be resumed from its previous position
|
||||||
|
# with the "RESUME" gcode.
|
||||||
|
|
||||||
|
[gcode_macro PARK]
|
||||||
|
gcode:
|
||||||
|
G1 X125 Y200.0 Z200.0 F4000
|
||||||
|
|
||||||
|
[gcode_macro FILAMENT_LOAD]
|
||||||
|
gcode:
|
||||||
|
M83 ; set e to relative positioning
|
||||||
|
G92 E0.0
|
||||||
|
G1 E70 F500 ; Initially go fast
|
||||||
|
G92 E0.0
|
||||||
|
G1 E10 F200 ; then go slow
|
||||||
|
G92 E0.0
|
||||||
|
|
||||||
|
[gcode_macro FILAMENT_UNLOAD]
|
||||||
|
gcode:
|
||||||
|
M83 ; set e to relative positioning
|
||||||
|
# wiggle filament out of the nozzle
|
||||||
|
G1 E0.5 F1000
|
||||||
|
G1 E-0.5 F1000
|
||||||
|
G1 E1.0 F1000
|
||||||
|
G1 E-1.0 F1000
|
||||||
|
G1 E1.5 F1000
|
||||||
|
G1 E-1.5 F1000
|
||||||
|
G1 E2.0 F1000
|
||||||
|
|
||||||
|
G1 E-100.0 F3000 ;fully unload
|
||||||
|
G92 E0.0
|
||||||
|
|
||||||
|
[gcode_macro M600]
|
||||||
|
default_parameter_X: 0
|
||||||
|
default_parameter_Y: 0
|
||||||
|
default_parameter_Z: 50
|
||||||
|
gcode:
|
||||||
|
SAVE_GCODE_STATE NAME=M600_state
|
||||||
|
PAUSE
|
||||||
|
G91 ;relative
|
||||||
|
G1 E-.2 F2700
|
||||||
|
G1 Z{Z} ;raise nozzle
|
||||||
|
G90 ;absolute
|
||||||
|
G1 X{X} Y{Y} F3000
|
||||||
|
G91 ;relative
|
||||||
|
M300 P1000 ;beep
|
||||||
|
FILAMENT_UNLOAD ;
|
||||||
|
M300 P3000 ;beep
|
||||||
|
G90 ;absolute
|
||||||
|
RESTORE_GCODE_STATE NAME=M600_state
|
||||||
|
|
||||||
|
#*# <---------------------- SAVE_CONFIG ---------------------->
|
||||||
|
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
|
||||||
|
#*#
|
||||||
|
#*# [bed_mesh default]
|
||||||
|
#*# version = 1
|
||||||
|
#*# points =
|
||||||
|
#*# 0.087500, 0.080000, 0.068750, 0.047500, 0.075000, 0.078750, 0.085000
|
||||||
|
#*# 0.068750, 0.065000, 0.055000, 0.028750, 0.052500, 0.055000, 0.057500
|
||||||
|
#*# 0.092500, 0.091250, 0.082500, 0.048750, 0.070000, 0.063750, 0.066250
|
||||||
|
#*# 0.116250, 0.132500, 0.122500, 0.090000, 0.105000, 0.100000, 0.092500
|
||||||
|
#*# 0.128750, 0.135000, 0.130000, 0.086250, 0.096250, 0.076250, 0.076250
|
||||||
|
#*# 0.112500, 0.126250, 0.128750, 0.107500, 0.122500, 0.127500, 0.125000
|
||||||
|
#*# 0.120000, 0.133750, 0.141250, 0.097500, 0.125000, 0.106250, 0.100000
|
||||||
|
#*# tension = 0.2
|
||||||
|
#*# min_x = 48.0
|
||||||
|
#*# algo = bicubic
|
||||||
|
#*# y_count = 7
|
||||||
|
#*# mesh_y_pps = 2
|
||||||
|
#*# min_y = 8.0
|
||||||
|
#*# x_count = 7
|
||||||
|
#*# max_y = 230.0
|
||||||
|
#*# mesh_x_pps = 2
|
||||||
|
#*# max_x = 199.97
|
|
@ -4,12 +4,12 @@
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
# License, or (at your option) any later version.
|
# License, or (at your option) any later version.
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU Affero General Public License for more details.
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@ -27,12 +27,24 @@ class KlipperLogAnalyzer():
|
||||||
def __init__(self, log_file):
|
def __init__(self, log_file):
|
||||||
self.log_file = log_file
|
self.log_file = log_file
|
||||||
|
|
||||||
|
def read_log_file(self, logname):
|
||||||
|
try:
|
||||||
|
f = open(logname, 'r')
|
||||||
|
logdata = f.read()
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
print("Couldn't open log file")
|
||||||
|
return logdata
|
||||||
|
|
||||||
def analyze(self):
|
def analyze(self):
|
||||||
data = self.parse_log(self.log_file, None)
|
data = self.parse_log(self.log_file, None)
|
||||||
if not data:
|
if not data:
|
||||||
result = dict(error= "No relevant data available in \"{}\"".format(self.log_file))
|
result1 = dict(error= "No relevant data available in \"{}\"".format(self.log_file))
|
||||||
else:
|
else:
|
||||||
result = self.plot_mcu(data, self.MAXBANDWIDTH)
|
result1 = self.plot_mcu(data, self.MAXBANDWIDTH)
|
||||||
|
result = dict(plot = result1,
|
||||||
|
logfiledata = self.read_log_file(self.log_file)
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def parse_log(self, logname, mcu):
|
def parse_log(self, logname, mcu):
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plugin-klipper-log .log-item {
|
.plugin-klipper-log .log-item {
|
||||||
|
@ -34,14 +35,109 @@
|
||||||
|
|
||||||
.clear-btn {
|
.clear-btn {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#level .controls {
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#klipper-settings {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#klipper-settings a{
|
||||||
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#plugin-klipper-config {
|
#plugin-klipper-config {
|
||||||
|
height: 100%;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tab_plugin_klipper_main .row-fluid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab_plugin_klipper_main .span8 {
|
||||||
|
flex-grow: 8;
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab_plugin_klipper_main .span8 label {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab_plugin_klipper_main .span4 {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
/* max-width: 13%; */
|
||||||
|
float: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_plugin_klipper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_plugin_klipper form {
|
||||||
|
margin: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_plugin_klipper form .tab-content {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings_plugin_klipper form .tab-content .tab-pane {
|
||||||
|
height: calc(100% - 35px);
|
||||||
|
}
|
||||||
|
|
||||||
|
div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div#conf.tab-pane.active div.control-group {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div#conf.tab-pane.active div.control-group div.conf-editor {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#settings_plugin_klipper.tab-pane.active form.form-horizontal div.tab-content div#conf.tab-pane.active div.control-group button.btn.btn-small {
|
||||||
|
width: 30%;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0px 2px 2px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#macros #item.control-group {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
color: #333;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#klipper_graph_dialog {
|
||||||
|
width: 1050px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#klipper_graph_dialog .full-sized-box{
|
||||||
|
width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
#klipper_graph_dialog form {
|
#klipper_graph_dialog form {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#klipper_graph_dialog select {
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#klipper_graph_dialog .graph-footer {
|
#klipper_graph_dialog .graph-footer {
|
||||||
|
@ -61,8 +157,14 @@
|
||||||
#klipper_graph_dialog .fill-checkbox {
|
#klipper_graph_dialog .fill-checkbox {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
top: 0%;
|
||||||
margin-left: 10px;
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#klipper_graph_dialog .help-inline {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#klipper_graph_canvas {
|
#klipper_graph_canvas {
|
||||||
|
|
|
@ -4,18 +4,26 @@
|
||||||
// it under the terms of the GNU Affero General Public License as
|
// it under the terms of the GNU Affero General Public License as
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
// License, or (at your option) any later version.
|
// License, or (at your option) any later version.
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU Affero General Public License for more details.
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
$(function() {
|
$(function () {
|
||||||
function KlipperViewModel(parameters) {
|
function KlipperViewModel(parameters) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var console_debug = false;
|
||||||
|
|
||||||
|
self.header = OctoPrint.getRequestHeaders({
|
||||||
|
"content-type": "application/json",
|
||||||
|
"cache-control": "no-cache"
|
||||||
|
});
|
||||||
|
|
||||||
|
self.apiUrl = OctoPrint.getSimpleApiUrl("klipper");
|
||||||
|
|
||||||
self.settings = parameters[0];
|
self.settings = parameters[0];
|
||||||
self.loginState = parameters[1];
|
self.loginState = parameters[1];
|
||||||
|
@ -23,142 +31,223 @@ $(function() {
|
||||||
self.levelingViewModel = parameters[3];
|
self.levelingViewModel = parameters[3];
|
||||||
self.paramMacroViewModel = parameters[4];
|
self.paramMacroViewModel = parameters[4];
|
||||||
self.access = parameters[5];
|
self.access = parameters[5];
|
||||||
|
|
||||||
self.shortStatus = ko.observable();
|
self.shortStatus = ko.observable();
|
||||||
self.logMessages = ko.observableArray();
|
self.logMessages = ko.observableArray();
|
||||||
|
|
||||||
self.showLevelingDialog = function() {
|
self.showPopUp = function(popupType, popupTitle, message){
|
||||||
var dialog = $("#klipper_leveling_dialog");
|
var title = popupType.toUpperCase() + ": " + popupTitle;
|
||||||
dialog.modal({
|
new PNotify({
|
||||||
show: 'true',
|
title: title,
|
||||||
backdrop: 'static',
|
text: message,
|
||||||
keyboard: false
|
type: popupType,
|
||||||
});
|
hide: false
|
||||||
self.levelingViewModel.initView();
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
self.showPidTuningDialog = function() {
|
self.onSettingsShown = function () {
|
||||||
var dialog = $("#klipper_pid_tuning_dialog");
|
self.reloadConfig();
|
||||||
dialog.modal({
|
|
||||||
show: 'true',
|
|
||||||
backdrop: 'static',
|
|
||||||
keyboard: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.showOffsetDialog = function() {
|
|
||||||
var dialog = $("#klipper_offset_dialog");
|
|
||||||
dialog.modal({
|
|
||||||
show: 'true',
|
|
||||||
backdrop: 'static'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.showGraphDialog = function() {
|
|
||||||
var dialog = $("#klipper_graph_dialog");
|
|
||||||
dialog.modal({
|
|
||||||
show: 'true',
|
|
||||||
minHeight: "500px",
|
|
||||||
maxHeight: "600px"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.executeMacro = function(macro) {
|
|
||||||
var paramObjRegex = /{(.*?)}/g;
|
|
||||||
|
|
||||||
if (macro.macro().match(paramObjRegex) == null) {
|
|
||||||
OctoPrint.control.sendGcode(
|
|
||||||
// Use .split to create an array of strings which is sent to
|
|
||||||
// OctoPrint.control.sendGcode instead of a single string.
|
|
||||||
macro.macro().split(/\r\n|\r|\n/)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
self.paramMacroViewModel.process(macro);
|
|
||||||
|
|
||||||
var dialog = $("#klipper_macro_dialog");
|
|
||||||
dialog.modal({
|
|
||||||
show: 'true',
|
|
||||||
backdrop: 'static'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.navbarClicked = function() {
|
self.showLevelingDialog = function () {
|
||||||
|
var dialog = $("#klipper_leveling_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: "true",
|
||||||
|
backdrop: "static",
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
|
self.levelingViewModel.initView();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.showPidTuningDialog = function () {
|
||||||
|
var dialog = $("#klipper_pid_tuning_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: "true",
|
||||||
|
backdrop: "static",
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.showOffsetDialog = function () {
|
||||||
|
var dialog = $("#klipper_offset_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: "true",
|
||||||
|
backdrop: "static"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.showGraphDialog = function () {
|
||||||
|
var dialog = $("#klipper_graph_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: "true",
|
||||||
|
minHeight: "500px",
|
||||||
|
maxHeight: "600px"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.executeMacro = function (macro) {
|
||||||
|
var paramObjRegex = /{(.*?)}/g;
|
||||||
|
|
||||||
|
if (!self.hasRight("MACRO")) return;
|
||||||
|
|
||||||
|
if (macro.macro().match(paramObjRegex) == null) {
|
||||||
|
OctoPrint.control.sendGcode(
|
||||||
|
// Use .split to create an array of strings which is sent to
|
||||||
|
// OctoPrint.control.sendGcode instead of a single string.
|
||||||
|
macro.macro().split(/\r\n|\r|\n/)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.paramMacroViewModel.process(macro);
|
||||||
|
|
||||||
|
var dialog = $("#klipper_macro_dialog");
|
||||||
|
dialog.modal({
|
||||||
|
show: "true",
|
||||||
|
backdrop: "static"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.navbarClicked = function () {
|
||||||
$("#tab_plugin_klipper_main_link").find("a").click();
|
$("#tab_plugin_klipper_main_link").find("a").click();
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onGetStatus = function () {
|
||||||
|
OctoPrint.control.sendGcode("Status");
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onRestartFirmware = function () {
|
||||||
|
OctoPrint.control.sendGcode("FIRMWARE_RESTART");
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onRestartHost = function () {
|
||||||
|
OctoPrint.control.sendGcode("RESTART");
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onAfterBinding = function () {
|
||||||
|
self.connectionState.selectedPort(
|
||||||
|
self.settings.settings.plugins.klipper.connection.port()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onDataUpdaterPluginMessage = function(plugin, data) {
|
||||||
|
if(plugin == "klipper") {
|
||||||
|
switch(data.type) {
|
||||||
|
case "PopUp":
|
||||||
|
self.showPopUp(data.subtype, data.title, data.payload);
|
||||||
|
break;
|
||||||
|
case "reload":
|
||||||
|
break;
|
||||||
|
case "console":
|
||||||
|
self.consoleMessage(data.subtype, data.payload);
|
||||||
|
break;
|
||||||
|
case "status":
|
||||||
|
self.shortStatus(data.payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
self.logMessage(data.time, data.subtype, data.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if ("warningPopUp" == data.type){
|
||||||
|
// self.showPopUp(data.subtype, data.title, data.payload);
|
||||||
|
// return;
|
||||||
|
//} else if ("errorPopUp" == data.type){
|
||||||
|
// self.showPopUp(data.subtype, data.title, data.payload);
|
||||||
|
// return;
|
||||||
|
//} else if ("reload" == data.type){
|
||||||
|
// return;
|
||||||
|
//} else if ("console" == data.type) {
|
||||||
|
// self.consoleMessage(data.subtype, data.payload);
|
||||||
|
//} else if (data.type == "status") {
|
||||||
|
// self.shortStatus(data.payload);
|
||||||
|
//} else {
|
||||||
|
// self.logMessage(data.time, data.subtype, data.payload);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.logMessage = function (timestamp, type, message) {
|
||||||
|
self.logMessages.push({
|
||||||
|
time: timestamp,
|
||||||
|
type: type,
|
||||||
|
msg: message.replace(/\n/gi, "<br>")
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consoleMessage = function (type, message) {
|
||||||
|
if (type == "info"){
|
||||||
|
console.info("OctoKlipper : " + message);
|
||||||
|
} else if (type == "debug"){
|
||||||
|
if (console_debug){
|
||||||
|
console.debug("OctoKlipper : " + message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("OctoKlipper : " + message);
|
||||||
|
}
|
||||||
|
return
|
||||||
|
};
|
||||||
|
|
||||||
|
self.reloadConfig = function() {
|
||||||
|
var settings = {
|
||||||
|
"crossDomain": true,
|
||||||
|
"url": self.apiUrl,
|
||||||
|
"method": "POST",
|
||||||
|
"headers": self.header,
|
||||||
|
"processData": false,
|
||||||
|
"dataType": "json",
|
||||||
|
"data": JSON.stringify({command: "reloadConfig"})
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax(settings).done(function (response) {
|
||||||
|
self.consoleMessage(
|
||||||
|
"debug",
|
||||||
|
"Reloaded from Backend " + response);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onGetStatus = function() {
|
self.onClearLog = function () {
|
||||||
OctoPrint.control.sendGcode("Status")
|
self.logMessages.removeAll();
|
||||||
}
|
|
||||||
|
|
||||||
self.onRestartFirmware = function() {
|
|
||||||
OctoPrint.control.sendGcode("FIRMWARE_RESTART")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.onRestartHost = function() {
|
|
||||||
OctoPrint.control.sendGcode("RESTART")
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onAfterBinding = function() {
|
|
||||||
self.connectionState.selectedPort(self.settings.settings.plugins.klipper.connection.port());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.onDataUpdaterPluginMessage = function(plugin, message) {
|
|
||||||
if(plugin == "klipper") {
|
|
||||||
if(message["type"] == "status") {
|
|
||||||
self.shortStatus(message["payload"]);
|
|
||||||
} else {
|
|
||||||
self.logMessage(message["time"], message["subtype"], message["payload"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logMessage = function(timestamp, type, message) {
|
self.isActive = function () {
|
||||||
self.logMessages.push({
|
return self.connectionState.isOperational();
|
||||||
time: timestamp,
|
};
|
||||||
type: type,
|
|
||||||
msg: message.replace(/\n/gi, "<br>")}
|
self.hasRight = function (right_role, type) {
|
||||||
);
|
var arg = eval("self.access.permissions.PLUGIN_KLIPPER_" + right_role);
|
||||||
}
|
|
||||||
|
if (type == "Ko") {
|
||||||
self.onClearLog = function() {
|
return self.loginState.hasPermissionKo(arg);
|
||||||
self.logMessages.removeAll();
|
}
|
||||||
|
return self.loginState.hasPermission(arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.isActive = function() {
|
|
||||||
return self.connectionState.isOperational() && self.loginState.isUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OctoKlipper settings link
|
// OctoKlipper settings link
|
||||||
self.openOctoKlipperSettings = function(profile_type) {
|
self.openOctoKlipperSettings = function (profile_type) {
|
||||||
|
if (!self.hasRight("CONFIG")) return;
|
||||||
|
|
||||||
if (
|
$("a#navbar_show_settings").click();
|
||||||
!self.loginState.hasPermission(
|
$("li#settings_plugin_klipper_link a").click();
|
||||||
self.access.permissions.PLUGIN_KLIPPER_CONFIG
|
if (profile_type) {
|
||||||
)
|
var query = "#klipper-settings a[data-profile-type='" + profile_type + "']";
|
||||||
)
|
$(query).click();
|
||||||
return;
|
}
|
||||||
|
|
||||||
$('a#navbar_show_settings').click();
|
|
||||||
$('li#settings_plugin_klipper_link a').click();
|
|
||||||
if(profile_type)
|
|
||||||
{
|
|
||||||
var query= "#klipper-settings a[data-profile-type='"+profile_type+"']";
|
|
||||||
$(query).click();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
OCTOPRINT_VIEWMODELS.push({
|
OCTOPRINT_VIEWMODELS.push({
|
||||||
construct: KlipperViewModel,
|
construct: KlipperViewModel,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"settingsViewModel",
|
"settingsViewModel",
|
||||||
"loginStateViewModel",
|
"loginStateViewModel",
|
||||||
"connectionViewModel",
|
"connectionViewModel",
|
||||||
"klipperLevelingViewModel",
|
"klipperLevelingViewModel",
|
||||||
"klipperMacroDialogViewModel",
|
"klipperMacroDialogViewModel",
|
||||||
"accessViewModel"
|
"accessViewModel"
|
||||||
],
|
],
|
||||||
elements: ["#tab_plugin_klipper_main", "#sidebar_plugin_klipper", "#navbar_plugin_klipper"]
|
elements: [
|
||||||
|
"#tab_plugin_klipper_main",
|
||||||
|
"#sidebar_plugin_klipper",
|
||||||
|
"#navbar_plugin_klipper"
|
||||||
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
// it under the terms of the GNU Affero General Public License as
|
// it under the terms of the GNU Affero General Public License as
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
// License, or (at your option) any later version.
|
// License, or (at your option) any later version.
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU Affero General Public License for more details.
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@ -18,16 +18,17 @@ $(function() {
|
||||||
function KlipperGraphViewModel(parameters) {
|
function KlipperGraphViewModel(parameters) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.loginState = parameters[0];
|
self.loginState = parameters[0];
|
||||||
|
|
||||||
self.header = OctoPrint.getRequestHeaders({
|
self.header = OctoPrint.getRequestHeaders({
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"cache-control": "no-cache"
|
"cache-control": "no-cache"
|
||||||
});
|
});
|
||||||
|
|
||||||
self.apiUrl = OctoPrint.getSimpleApiUrl("klipper");
|
self.apiUrl = OctoPrint.getSimpleApiUrl("klipper");
|
||||||
|
|
||||||
self.availableLogFiles = ko.observableArray();
|
self.availableLogFiles = ko.observableArray();
|
||||||
self.logFile = ko.observable();
|
self.logFile = ko.observable();
|
||||||
|
self.klippylogFile = ko.observable();
|
||||||
self.status = ko.observable();
|
self.status = ko.observable();
|
||||||
self.datasets = ko.observableArray();
|
self.datasets = ko.observableArray();
|
||||||
self.datasetFill = ko.observable(false);
|
self.datasetFill = ko.observable(false);
|
||||||
|
@ -40,24 +41,24 @@ function KlipperGraphViewModel(parameters) {
|
||||||
self.canvas = $("#klipper_graph_canvas")[0]
|
self.canvas = $("#klipper_graph_canvas")[0]
|
||||||
self.canvasContext = self.canvas.getContext("2d");
|
self.canvasContext = self.canvas.getContext("2d");
|
||||||
self.spinnerDialog = $("#klipper_graph_spinner");
|
self.spinnerDialog = $("#klipper_graph_spinner");
|
||||||
|
|
||||||
Chart.defaults.global.elements.line.borderWidth=1;
|
Chart.defaults.global.elements.line.borderWidth=1;
|
||||||
Chart.defaults.global.elements.line.fill= false;
|
Chart.defaults.global.elements.line.fill= false;
|
||||||
Chart.defaults.global.elements.point.radius= 0;
|
Chart.defaults.global.elements.point.radius= 0;
|
||||||
|
|
||||||
var myChart = new Chart(self.canvas, {
|
var myChart = new Chart(self.canvas, {
|
||||||
type: "line"
|
type: "line"
|
||||||
});
|
});
|
||||||
|
|
||||||
if(self.loginState.loggedIn()) {
|
if(self.loginState.loggedIn()) {
|
||||||
self.listLogFiles();
|
self.listLogFiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.onUserLoggedIn = function(user) {
|
self.onUserLoggedIn = function(user) {
|
||||||
self.listLogFiles();
|
self.listLogFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.listLogFiles = function() {
|
self.listLogFiles = function() {
|
||||||
var settings = {
|
var settings = {
|
||||||
"crossDomain": true,
|
"crossDomain": true,
|
||||||
|
@ -68,45 +69,35 @@ function KlipperGraphViewModel(parameters) {
|
||||||
"dataType": "json",
|
"dataType": "json",
|
||||||
"data": JSON.stringify({command: "listLogFiles"})
|
"data": JSON.stringify({command: "listLogFiles"})
|
||||||
}
|
}
|
||||||
|
|
||||||
$.ajax(settings).done(function (response) {
|
$.ajax(settings).done(function (response) {
|
||||||
self.availableLogFiles.removeAll();
|
self.availableLogFiles.removeAll();
|
||||||
self.availableLogFiles(response["data"]);
|
self.availableLogFiles(response["data"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.saveGraphToPng = function() {
|
self.saveGraphToPng = function() {
|
||||||
button = $('#download-btn');
|
button = $('#download-btn');
|
||||||
var dataURL = self.canvas.toDataURL("image/png");//.replace("image/png", "image/octet-stream");
|
var dataURL = self.canvas.toDataURL("image/png");//.replace("image/png", "image/octet-stream");
|
||||||
button.attr("href", dataURL);
|
button.attr("href", dataURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.showSpinner = function(showDialog) {
|
self.showSpinner = function(showDialog) {
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
self.spinnerDialog.modal({
|
self.spinnerDialog.modal({
|
||||||
show: true,
|
show: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
backdrop: "static"
|
backdrop: "static"
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.spinnerDialog.modal("hide");
|
self.spinnerDialog.modal("hide");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.toggleDatasetFill = function() {
|
|
||||||
if(self.datasets) {
|
|
||||||
for (i=0; i < self.datasets().length; i++) {
|
|
||||||
self.datasets()[i].fill = self.datasetFill();
|
|
||||||
}
|
|
||||||
self.chart.update();
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.convertTime = function(val) {
|
self.convertTime = function(val) {
|
||||||
return moment(val, "X");
|
return moment(val, "X");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loadData = function() {
|
self.loadData = function() {
|
||||||
var settings = {
|
var settings = {
|
||||||
"crossDomain": true,
|
"crossDomain": true,
|
||||||
|
@ -122,17 +113,18 @@ function KlipperGraphViewModel(parameters) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.showSpinner(true);
|
self.showSpinner(true);
|
||||||
|
|
||||||
$.ajax(settings).done(function (response) {
|
$.ajax(settings).done(function (response) {
|
||||||
self.status("")
|
self.status("")
|
||||||
self.datasetFill(false);
|
self.datasetFill(false);
|
||||||
|
|
||||||
self.showSpinner(false);
|
self.showSpinner(false);
|
||||||
|
self.klippylogFile(response.logfiledata);
|
||||||
if("error" in response) {
|
|
||||||
self.status(response.error);
|
if("error" in response.plot) {
|
||||||
|
self.status(response.plot.error);
|
||||||
} else {
|
} else {
|
||||||
self.datasets.removeAll();
|
self.datasets.removeAll();
|
||||||
self.datasets.push(
|
self.datasets.push(
|
||||||
|
@ -141,40 +133,40 @@ function KlipperGraphViewModel(parameters) {
|
||||||
backgroundColor: "rgba(199, 44, 59, 0.5)",
|
backgroundColor: "rgba(199, 44, 59, 0.5)",
|
||||||
borderColor: "rgb(199, 44, 59)",
|
borderColor: "rgb(199, 44, 59)",
|
||||||
yAxisID: 'y-axis-1',
|
yAxisID: 'y-axis-1',
|
||||||
data: response.loads
|
data: response.plot.loads
|
||||||
});
|
});
|
||||||
|
|
||||||
self.datasets.push(
|
self.datasets.push(
|
||||||
{
|
{
|
||||||
label: "Bandwith",
|
label: "Bandwith",
|
||||||
backgroundColor: "rgba(255, 130, 1, 0.5)",
|
backgroundColor: "rgba(255, 130, 1, 0.5)",
|
||||||
borderColor: "rgb(255, 130, 1)",
|
borderColor: "rgb(255, 130, 1)",
|
||||||
yAxisID: 'y-axis-1',
|
yAxisID: 'y-axis-1',
|
||||||
data: response.bwdeltas
|
data: response.plot.bwdeltas
|
||||||
});
|
});
|
||||||
|
|
||||||
self.datasets.push(
|
self.datasets.push(
|
||||||
{
|
{
|
||||||
label: "Host Buffer",
|
label: "Host Buffer",
|
||||||
backgroundColor: "rgba(0, 145, 106, 0.5)",
|
backgroundColor: "rgba(0, 145, 106, 0.5)",
|
||||||
borderColor: "rgb(0, 145, 106)",
|
borderColor: "rgb(0, 145, 106)",
|
||||||
yAxisID: 'y-axis-1',
|
yAxisID: 'y-axis-1',
|
||||||
data: response.buffers
|
data: response.plot.buffers
|
||||||
});
|
});
|
||||||
|
|
||||||
self.datasets.push(
|
self.datasets.push(
|
||||||
{
|
{
|
||||||
label: "Awake Time",
|
label: "Awake Time",
|
||||||
backgroundColor: "rgba(33, 64, 95, 0.5)",
|
backgroundColor: "rgba(33, 64, 95, 0.5)",
|
||||||
borderColor: "rgb(33, 64, 95)",
|
borderColor: "rgb(33, 64, 95)",
|
||||||
yAxisID: 'y-axis-1',
|
yAxisID: 'y-axis-1',
|
||||||
data: response.awake
|
data: response.plot.awake
|
||||||
});
|
});
|
||||||
|
|
||||||
self.chart = new Chart(self.canvas, {
|
self.chart = new Chart(self.canvas, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {
|
data: {
|
||||||
labels: response.times,
|
labels: response.plot.times,
|
||||||
datasets: self.datasets()
|
datasets: self.datasets()
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
|
@ -211,13 +203,25 @@ function KlipperGraphViewModel(parameters) {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.toggleDatasetFill = function() {
|
||||||
|
if(self.datasets) {
|
||||||
|
for (i=0; i < self.datasets().length; i++) {
|
||||||
|
self.datasets()[i].fill = self.datasetFill();
|
||||||
|
}
|
||||||
|
if (self.chart) {
|
||||||
|
self.chart.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OCTOPRINT_VIEWMODELS.push({
|
OCTOPRINT_VIEWMODELS.push({
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
// it under the terms of the GNU Affero General Public License as
|
// it under the terms of the GNU Affero General Public License as
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
// License, or (at your option) any later version.
|
// License, or (at your option) any later version.
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU Affero General Public License for more details.
|
// GNU Affero General Public License for more details.
|
||||||
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@ -17,64 +17,178 @@ $(function() {
|
||||||
$('#klipper-settings a:first').tab('show');
|
$('#klipper-settings a:first').tab('show');
|
||||||
function KlipperSettingsViewModel(parameters) {
|
function KlipperSettingsViewModel(parameters) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var obKlipperConfig = null;
|
||||||
|
var editor = null;
|
||||||
|
|
||||||
self.settings = parameters[0];
|
self.settings = parameters[0];
|
||||||
|
|
||||||
|
self.header = OctoPrint.getRequestHeaders({
|
||||||
|
"content-type": "application/json",
|
||||||
|
"cache-control": "no-cache"
|
||||||
|
});
|
||||||
|
|
||||||
|
self.apiUrl = OctoPrint.getSimpleApiUrl("klipper");
|
||||||
|
|
||||||
|
self.onSettingsBeforeSave = function () {
|
||||||
|
if (editor.session) {
|
||||||
|
//console.debug("OctoKlipper : onSettingsBeforeSave:" + editor.session.getValue())
|
||||||
|
var settings = {
|
||||||
|
"crossDomain": true,
|
||||||
|
"url": self.apiUrl,
|
||||||
|
"method": "POST",
|
||||||
|
"headers": self.header,
|
||||||
|
"processData": false,
|
||||||
|
"dataType": "json",
|
||||||
|
"data": JSON.stringify({command: "checkConfig",
|
||||||
|
config: editor.session.getValue()})
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax(settings).done(function (response) {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.addMacro = function() {
|
self.addMacro = function() {
|
||||||
self.settings.settings.plugins.klipper.macros.push({
|
self.settings.settings.plugins.klipper.macros.push({
|
||||||
name: 'Macro',
|
name: 'Macro',
|
||||||
macro: '',
|
macro: '',
|
||||||
sidebar: true,
|
sidebar: true,
|
||||||
tab: true
|
tab: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.removeMacro = function(macro) {
|
self.removeMacro = function(macro) {
|
||||||
self.settings.settings.plugins.klipper.macros.remove(macro);
|
self.settings.settings.plugins.klipper.macros.remove(macro);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveMacroUp = function(macro) {
|
self.moveMacroUp = function(macro) {
|
||||||
self.moveItemUp(self.settings.settings.plugins.klipper.macros, macro)
|
self.moveItemUp(self.settings.settings.plugins.klipper.macros, macro)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveMacroDown = function(macro) {
|
self.moveMacroDown = function(macro) {
|
||||||
self.moveItemDown(self.settings.settings.plugins.klipper.macros, macro)
|
self.moveItemDown(self.settings.settings.plugins.klipper.macros, macro)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addProbePoint = function() {
|
self.addProbePoint = function() {
|
||||||
self.settings.settings.plugins.klipper.probe.points.push(
|
self.settings.settings.plugins.klipper.probe.points.push(
|
||||||
{
|
{
|
||||||
name: 'point-#',
|
name: 'point-#',
|
||||||
x:0, y:0, z:0
|
x:0, y:0, z:0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.removeProbePoint = function(point) {
|
self.removeProbePoint = function(point) {
|
||||||
self.settings.settings.plugins.klipper.probe.points.remove(point);
|
self.settings.settings.plugins.klipper.probe.points.remove(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveProbePointUp = function(macro) {
|
self.moveProbePointUp = function(macro) {
|
||||||
self.moveItemUp(self.settings.settings.plugins.klipper.probe.points, macro)
|
self.moveItemUp(self.settings.settings.plugins.klipper.probe.points, macro)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveProbePointDown = function(macro) {
|
self.moveProbePointDown = function(macro) {
|
||||||
self.moveItemDown(self.settings.settings.plugins.klipper.probe.points, macro)
|
self.moveItemDown(self.settings.settings.plugins.klipper.probe.points, macro)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveItemDown = function(list, item) {
|
self.moveItemDown = function(list, item) {
|
||||||
var i = list().indexOf(item);
|
var i = list().indexOf(item);
|
||||||
if (i < list().length - 1) {
|
if (i < list().length - 1) {
|
||||||
var rawList = list();
|
var rawList = list();
|
||||||
list.splice(i, 2, rawList[i + 1], rawList[i]);
|
list.splice(i, 2, rawList[i + 1], rawList[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.moveItemUp = function(list, item) {
|
self.moveItemUp = function(list, item) {
|
||||||
var i = list().indexOf(item);
|
var i = list().indexOf(item);
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
var rawList = list();
|
var rawList = list();
|
||||||
list.splice(i-1, 2, rawList[i], rawList[i-1]);
|
list.splice(i-1, 2, rawList[i], rawList[i-1]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.loadLastSession = function () {
|
||||||
|
if (self.settings.settings.plugins.klipper.configuration.old_config() != "") {
|
||||||
|
console.debug("OctoKlipper : lastSession:" + self.settings.settings.plugins.klipper.configuration.old_config())
|
||||||
|
if (editor.session) {
|
||||||
|
editor.session.setValue(self.settings.settings.plugins.klipper.configuration.old_config());
|
||||||
|
editor.clearSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.reloadFromFile = function () {
|
||||||
|
if (editor.session) {
|
||||||
|
var settings = {
|
||||||
|
"crossDomain": true,
|
||||||
|
"url": self.apiUrl,
|
||||||
|
"method": "POST",
|
||||||
|
"headers": self.header,
|
||||||
|
"processData": false,
|
||||||
|
"dataType": "json",
|
||||||
|
"data": JSON.stringify({command: "reloadConfig"})
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax(settings).done(function (response) {
|
||||||
|
editor.session.setValue(response["data"]);
|
||||||
|
editor.clearSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.configBound = function (config) {
|
||||||
|
config.withSilence = function() {
|
||||||
|
this.notifySubscribers = function() {
|
||||||
|
if (!this.isSilent) {
|
||||||
|
ko.subscribable.fn.notifySubscribers.apply(this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.silentUpdate = function(newValue) {
|
||||||
|
this.isSilent = true;
|
||||||
|
this(newValue);
|
||||||
|
this.isSilent = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
obKlipperConfig = config.withSilence();
|
||||||
|
if (editor) {
|
||||||
|
editor.setValue(obKlipperConfig());
|
||||||
|
editor.clearSelection();
|
||||||
|
}
|
||||||
|
return obKlipperConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
ace.config.set("basePath", "plugin/klipper/static/js/lib/ace/");
|
||||||
|
editor = ace.edit("plugin-klipper-config");
|
||||||
|
editor.setTheme("ace/theme/monokai");
|
||||||
|
editor.session.setMode("ace/mode/klipper_config");
|
||||||
|
editor.setOptions({
|
||||||
|
autoScrollEditorIntoView: true,
|
||||||
|
maxLines: "Infinity"
|
||||||
|
})
|
||||||
|
|
||||||
|
editor.session.on('change', function(delta) {
|
||||||
|
if (obKlipperConfig) {
|
||||||
|
obKlipperConfig.silentUpdate(editor.getValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Uncomment this if not using maxLines: "Infinity"...
|
||||||
|
// setInterval(function(){ editor.resize(); }, 500);
|
||||||
|
self.onDataUpdaterPluginMessage = function(plugin, data) {
|
||||||
|
if(plugin == "klipper") {
|
||||||
|
if ("reload" == data.type){
|
||||||
|
if ("config" == data.subtype){
|
||||||
|
if (editor.session) {
|
||||||
|
editor.session.setValue(data.payload);
|
||||||
|
editor.clearSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,422 @@
|
||||||
|
ace.define("ace/mode/klipper_config_highlight_rules",[], function(require, exports, module) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var oop = require("../lib/oop");
|
||||||
|
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
|
||||||
|
|
||||||
|
var KlipperConfigHighlightRules = function() {
|
||||||
|
|
||||||
|
this.$rules = {
|
||||||
|
start: [{
|
||||||
|
include: "#single_line_comment"
|
||||||
|
}, {
|
||||||
|
include: "#config_block"
|
||||||
|
}, {
|
||||||
|
include: "#config_line"
|
||||||
|
}, {
|
||||||
|
include: "#number"
|
||||||
|
}, {
|
||||||
|
include: "#config_line_start_gcode"
|
||||||
|
}],
|
||||||
|
"#single_line_comment": [{
|
||||||
|
token: "comment.line.number-sign",
|
||||||
|
regex: /#.*$/
|
||||||
|
}, {
|
||||||
|
token: "comment.line.gcode",
|
||||||
|
regex: /;.*$/
|
||||||
|
}],
|
||||||
|
"#number": [{
|
||||||
|
token: "constant.numeric",
|
||||||
|
regex: /\-?\d+(?:[\.,]\d+)?\b/
|
||||||
|
}, {
|
||||||
|
token: "constant.numeric",
|
||||||
|
regex: /\-?[\.,]\d+?\b/
|
||||||
|
}],
|
||||||
|
"#boolean": [{
|
||||||
|
token: "constant.language",
|
||||||
|
regex: /\b(?:true|false)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#string-single": [{
|
||||||
|
token: "text",
|
||||||
|
regex: /'/,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /'/,
|
||||||
|
next: "pop"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#string-double": [{
|
||||||
|
token: "text",
|
||||||
|
regex: /"/,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /"/,
|
||||||
|
next: "pop"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#config_block": [{
|
||||||
|
token: "text",
|
||||||
|
regex: /^\[/,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /\]\s*$/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
include: "#known_config_block_name"
|
||||||
|
}, {
|
||||||
|
include: "#known_driver_type"
|
||||||
|
}, {
|
||||||
|
defaultToken: "keyword.control"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#known_config_block_name": [{
|
||||||
|
token: "storage.type",
|
||||||
|
regex: /\b(?:ad5206|adc_temperature|bed_mesh|bed_screws|bed_tilt|bltouch|board_pins|controller_fan|delayed_gcode|delta_calibrate|display|display_data|display_template|dotstar|dual_carriage|endstop_phase|extruder_stepper|extruder[1-9]{0,1}|fan|filament_switch_sensor|firmware_retraction|force_move|gcode_arcs|gcode_button|gcode_macro|hall_filament_width_sensor|heater_bed|heater_fan|heater_generic|homing_heaters|homing_override|idle_timeout|include|manual_stepper|mcp4018|mcp4451|mcp4728|mcu|menu|multi_pin|neopixel|output_pin|pause_resume|printer|probe|quad_gantry_level|replicape|respond|safe_z_home|samd_sercom|screws_tilt_adjust|servo|skew_correction|static_digital_output|stepper_(?:bed|arm|[abcdxy]|z[1-9]{0,1})|sx1509|temperature_fan|temperature_sensor|thermistor|tsl1401cl_filament_width_sensor|verify_heater|virtual_sdcard|z_tilt)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#known_driver_type": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\btmc(?:2130|2208|2209|2660|5160)\b/,
|
||||||
|
caseInsensitive: true,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /(?=(\]))/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
defaultToken: "keyword.control"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#known_thermistor_type": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\b(?:EPCOS 100K B57560G104F|ATC Semitec 104GT-2|NTC 100K beta 3950|Honeywell 100K 135-104LAG-J01|NTC 100K MGB18-104F39050L32)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#known_extruder_sensor_type": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\b(?:MAX6675|MAX31855|MAX31856|MAX31865|PT100 INA826|AD595|AD8494|AD8495|AD8496|AD8497|PT1000|BME280)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#known_control_type": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\b(?:watermark|pid)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#known_display_type": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\b(?:hd44780|st7920|uc1701|ssd1306|sh1106)\b/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#serial": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /\/dev\/serial\/by-(?:id|path)\/[\d\w\/\-:\.]+/
|
||||||
|
}],
|
||||||
|
"#pin": [{
|
||||||
|
token: "support.type",
|
||||||
|
regex: /[\^~!]*(?:ar|analog)\d{1,2}/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}, {
|
||||||
|
token: "support.type",
|
||||||
|
regex: /(?:\b)[\^~!]*(?:z:)?[a-z]{1,2}\d{1,2}(?:\.\d{1,2})?/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#config_line_start_gcode": [{
|
||||||
|
token: ["variable.name", "text"],
|
||||||
|
regex: /^(gcode)(\s*[:=]\s*)/,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /(?=(\[))/,
|
||||||
|
next: "start"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_line"
|
||||||
|
}, {
|
||||||
|
include: "#single_line_comment"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#config_line": [{
|
||||||
|
token: ["variable.name", "text"],
|
||||||
|
regex: /^(?!(gcode))(\w+)(\s*[:=]\s*)/,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /$/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
include: "#known_thermistor_type"
|
||||||
|
}, {
|
||||||
|
include: "#known_extruder_sensor_type"
|
||||||
|
}, {
|
||||||
|
include: "#known_control_type"
|
||||||
|
}, {
|
||||||
|
include: "#known_display_type"
|
||||||
|
}, {
|
||||||
|
include: "#pin"
|
||||||
|
}, {
|
||||||
|
include: "#serial"
|
||||||
|
}, {
|
||||||
|
include: "#number"
|
||||||
|
}, {
|
||||||
|
include: "#boolean"
|
||||||
|
}, {
|
||||||
|
include: "#single_line_comment"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#gcode_line": [{
|
||||||
|
include: "#gcode_command"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_extended_command"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_parameter"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_extended_parameter"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_macro_block"
|
||||||
|
}],
|
||||||
|
"#gcode_command": [{
|
||||||
|
token: ["text", "keyword.operator"],
|
||||||
|
regex: /^(\s*)([A-z]+)(?![A-z])/,
|
||||||
|
caseInsensitive: true,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /(\s|$)/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
include: "#number"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_macro_block"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#gcode_parameter": [{
|
||||||
|
token: "variable.parameter",
|
||||||
|
regex: /\b[A-z]+(?![a-z])/,
|
||||||
|
caseInsensitive: true,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /(?=(\s|$))/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
include: "#number"
|
||||||
|
}, {
|
||||||
|
include: "#string-single"
|
||||||
|
}, {
|
||||||
|
include: "#string-double"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_macro_block"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#gcode_extended_command": [{
|
||||||
|
token: "keyword.operator",
|
||||||
|
regex: /^\s*(?:ABORT|ACCEPT|ACTIVATE_EXTRUDER|BED_MESH_CALIBRATE|BED_MESH_CLEAR|BED_MESH_MAP|BED_MESH_OUTPUT|BED_MESH_PROFILE|BED_SCREWS_ADJUST|BED_TILT_CALIBRATE|BLTOUCH_DEBUG|BLTOUCH_STORE|CALC_MEASURED_SKEW|CLEAR_PAUSE|DELTA_ANALYZE|DELTA_CALIBRATE|DUMP_TMC|ENDSTOP_PHASE_CALIBRATE|FIRMWARE_RESTART|FORCE_MOVE|GET_CURRENT_SKEW|GET_POSITION|GET_RETRACTION|HELP|MANUAL_PROBE|MANUAL_STEPPER|PAUSE|PID_CALIBRATE|PROBE|PROBE_ACCURACY|PROBE_CALIBRATE|QUAD_GANTRY_LEVEL|QUERY_ADC|QUERY_ENDSTOPS|QUERY_FILAMENT_SENSOR|QUERY_PROBE|RESPOND|RESTART|RESTORE_GCODE_STATE|RESUME|SAVE_CONFIG|SAVE_GCODE_STATE|SCREWS_TILT_CALCULATE|SET_DUAL_CARRIAGE|SET_EXTRUDER_STEP_DISTANCE|SET_FILAMENT_SENSOR|SET_GCODE_OFFSET|SET_GCODE_VARIABLE|SET_HEATER_TEMPERATURE|SET_IDLE_TIMEOUT|SET_KINEMATIC_POSITION|SET_LED|SET_PIN|SET_PRESSURE_ADVANCE|SET_RETRACTION|SET_SERVO|SET_SKEW|SET_STEPPER_ENABLE|SET_TMC_CURRENT|SET_TMC_FIELD|SET_VELOCITY_LIMIT|SKEW_PROFILE|STATUS|STEPPER_BUZZ|TESTZ|TUNING_TOWER|TURN_OFF_HEATERS|UPDATE_DELAYED_GCODE|Z_ENDSTOP_CALIBRATE|Z_TILT_ADJUST)\s/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}],
|
||||||
|
"#gcode_extended_parameter": [{
|
||||||
|
token: ["variable.parameter", "text"],
|
||||||
|
regex: /\b(AC|ACCEL|ACCEL_TO_DECEL|AD|ADVANCE|ANGLE|BAND|BD|BLUE|CARRIAGE|CLEAR|COMMAND|CURRENT|DISTANCE|DURATION|ENABLE|EXTRUDER|FACTOR|FIELD|GREEN|HEATER|HOLDCURRENT|ID|INDEX|LED|LIFT_SPEED|LOAD|MACRO|METHOD|MODE|MOVE_SPEED|MSG|NAME|PARAMETER|PGP|PIN|PREFIX|PROBE_SPEED|PULLUP|RED|REMOVE|RETRACT_LENGTH|RETRACT_SPEED|SAMPLE_RETRACT_DIST|SAMPLES|SAMPLES_RESULT|SAMPLES_TOLERANCE|SAMPLES_TOLERANCE_RETRIES|SAVE|SENSOR|SERVO|SET_POSITION|SMOOTH_TIME|SPEED|SQUARE_CORNER_VELOCITY|START|STEPPER|STOP_ON_ENDSTOP|SYNC|TARGET|TIMEOUT|TRANSMIT|TYPE|UNRETRACT_EXTRA_LENGTH|UNRETRACT_SPEED|VALUE|VARIABLE|VELOCITY|WIDTH|WRITE_FILE|X|X_ADJUST|XY|XZ|Y|Y_ADJUST|YZ|Z|Z_ADJUST)(=)/,
|
||||||
|
caseInsensitive: true,
|
||||||
|
push: [{
|
||||||
|
token: "text",
|
||||||
|
regex: /[^\d\w]/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
token: "constant.language",
|
||||||
|
regex: /5V|average|command|echo|error|manual|median|OD|output_mode_store|pin_down|pin_up|reset|self_test|set_5V_output_mode|set_5V_output_mode|set_OD_output_mode|touch_mode/,
|
||||||
|
caseInsensitive: true
|
||||||
|
}, {
|
||||||
|
include: "#number"
|
||||||
|
}, {
|
||||||
|
include: "#gcode_macro_block"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"#gcode_macro_block": [{
|
||||||
|
token: "string.unquoted",
|
||||||
|
regex: /{/,
|
||||||
|
push: [{
|
||||||
|
token: "string.unquoted",
|
||||||
|
regex: /}/,
|
||||||
|
next: "pop"
|
||||||
|
}, {
|
||||||
|
defaultToken: "string.unquoted"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.normalizeRules();
|
||||||
|
};
|
||||||
|
|
||||||
|
KlipperConfigHighlightRules.metaData = {
|
||||||
|
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||||
|
name: "Klipper Config",
|
||||||
|
scopeName: "source.klipper-config"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
oop.inherits(KlipperConfigHighlightRules, TextHighlightRules);
|
||||||
|
|
||||||
|
exports.KlipperConfigHighlightRules = KlipperConfigHighlightRules;
|
||||||
|
});
|
||||||
|
|
||||||
|
ace.define("ace/mode/folding/cstyle",[], function(require, exports, module) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var oop = require("../../lib/oop");
|
||||||
|
var Range = require("../../range").Range;
|
||||||
|
var BaseFoldMode = require("./fold_mode").FoldMode;
|
||||||
|
|
||||||
|
var FoldMode = exports.FoldMode = function(commentRegex) {
|
||||||
|
if (commentRegex) {
|
||||||
|
this.foldingStartMarker = new RegExp(
|
||||||
|
this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
|
||||||
|
);
|
||||||
|
this.foldingStopMarker = new RegExp(
|
||||||
|
this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
oop.inherits(FoldMode, BaseFoldMode);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/;
|
||||||
|
this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/;
|
||||||
|
this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/;
|
||||||
|
this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
|
||||||
|
this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
|
||||||
|
this._getFoldWidgetBase = this.getFoldWidget;
|
||||||
|
this.getFoldWidget = function(session, foldStyle, row) {
|
||||||
|
var line = session.getLine(row);
|
||||||
|
|
||||||
|
if (this.singleLineBlockCommentRe.test(line)) {
|
||||||
|
if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var fw = this._getFoldWidgetBase(session, foldStyle, row);
|
||||||
|
|
||||||
|
if (!fw && this.startRegionRe.test(line))
|
||||||
|
return "start"; // lineCommentRegionStart
|
||||||
|
|
||||||
|
return fw;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
|
||||||
|
var line = session.getLine(row);
|
||||||
|
|
||||||
|
if (this.startRegionRe.test(line))
|
||||||
|
return this.getCommentRegionBlock(session, line, row);
|
||||||
|
|
||||||
|
var match = line.match(this.foldingStartMarker);
|
||||||
|
if (match) {
|
||||||
|
var i = match.index;
|
||||||
|
|
||||||
|
if (match[1])
|
||||||
|
return this.openingBracketBlock(session, match[1], row, i);
|
||||||
|
|
||||||
|
var range = session.getCommentFoldRange(row, i + match[0].length, 1);
|
||||||
|
|
||||||
|
if (range && !range.isMultiLine()) {
|
||||||
|
if (forceMultiline) {
|
||||||
|
range = this.getSectionRange(session, row);
|
||||||
|
} else if (foldStyle != "all")
|
||||||
|
range = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foldStyle === "markbegin")
|
||||||
|
return;
|
||||||
|
|
||||||
|
var match = line.match(this.foldingStopMarker);
|
||||||
|
if (match) {
|
||||||
|
var i = match.index + match[0].length;
|
||||||
|
|
||||||
|
if (match[1])
|
||||||
|
return this.closingBracketBlock(session, match[1], row, i);
|
||||||
|
|
||||||
|
return session.getCommentFoldRange(row, i, -1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getSectionRange = function(session, row) {
|
||||||
|
var line = session.getLine(row);
|
||||||
|
var startIndent = line.search(/\S/);
|
||||||
|
var startRow = row;
|
||||||
|
var startColumn = line.length;
|
||||||
|
row = row + 1;
|
||||||
|
var endRow = row;
|
||||||
|
var maxRow = session.getLength();
|
||||||
|
while (++row < maxRow) {
|
||||||
|
line = session.getLine(row);
|
||||||
|
var indent = line.search(/\S/);
|
||||||
|
if (indent === -1)
|
||||||
|
continue;
|
||||||
|
if (startIndent > indent)
|
||||||
|
break;
|
||||||
|
var subRange = this.getFoldWidgetRange(session, "all", row);
|
||||||
|
|
||||||
|
if (subRange) {
|
||||||
|
if (subRange.start.row <= startRow) {
|
||||||
|
break;
|
||||||
|
} else if (subRange.isMultiLine()) {
|
||||||
|
row = subRange.end.row;
|
||||||
|
} else if (startIndent == indent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endRow = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
|
||||||
|
};
|
||||||
|
this.getCommentRegionBlock = function(session, line, row) {
|
||||||
|
var startColumn = line.search(/\s*$/);
|
||||||
|
var maxRow = session.getLength();
|
||||||
|
var startRow = row;
|
||||||
|
|
||||||
|
var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
|
||||||
|
var depth = 1;
|
||||||
|
while (++row < maxRow) {
|
||||||
|
line = session.getLine(row);
|
||||||
|
var m = re.exec(line);
|
||||||
|
if (!m) continue;
|
||||||
|
if (m[1]) depth--;
|
||||||
|
else depth++;
|
||||||
|
|
||||||
|
if (!depth) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var endRow = row;
|
||||||
|
if (endRow > startRow) {
|
||||||
|
return new Range(startRow, startColumn, endRow, line.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(FoldMode.prototype);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
ace.define("ace/mode/klipper_config",[], function(require, exports, module) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var oop = require("../lib/oop");
|
||||||
|
var TextMode = require("./text").Mode;
|
||||||
|
var KlipperConfigHighlightRules = require("./klipper_config_highlight_rules").KlipperConfigHighlightRules;
|
||||||
|
var FoldMode = require("./folding/cstyle").FoldMode;
|
||||||
|
|
||||||
|
var Mode = function() {
|
||||||
|
this.HighlightRules = KlipperConfigHighlightRules;
|
||||||
|
this.foldingRules = new FoldMode();
|
||||||
|
};
|
||||||
|
oop.inherits(Mode, TextMode);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
this.$id = "ace/mode/klipper_config"
|
||||||
|
}).call(Mode.prototype);
|
||||||
|
|
||||||
|
exports.Mode = Mode;
|
||||||
|
}); (function() {
|
||||||
|
ace.require(["ace/mode/klipper_config"], function(m) {
|
||||||
|
if (typeof module == "object" && typeof exports == "object" && module) {
|
||||||
|
module.exports = m;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],function(a,e,o){e.isDark=!0,e.cssClass="ace-monokai",e.cssText=".ace-monokai .ace_gutter {background: #2F3129;color: #8F908A}.ace-monokai .ace_print-margin {width: 1px;background: #555651}.ace-monokai {background-color: #272822;color: #F8F8F2}.ace-monokai .ace_cursor {color: #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection {background: #49483E}.ace-monokai.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #272822;}.ace-monokai .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active-line {background: #202020}.ace-monokai .ace_gutter-active-line {background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected-word {border: 1px solid #49483E}.ace-monokai .ace_invisible {color: #52524d}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta.ace_tag,.ace-monokai .ace_storage {color: #F92672}.ace-monokai .ace_punctuation,.ace-monokai .ace_punctuation.ace_tag {color: #fff}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other {color: #AE81FF}.ace-monokai .ace_invalid {color: #F8F8F0;background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated {color: #F8F8F0;background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function {color: #66D9EF}.ace-monokai .ace_fold {background-color: #A6E22E;border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type {font-style: italic;color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable {color: #A6E22E}.ace-monokai .ace_variable.ace_parameter {font-style: italic;color: #FD971F}.ace-monokai .ace_string {color: #E6DB74}.ace-monokai .ace_comment {color: #75715E}.ace-monokai .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ0FD0ZXBzd/wPAAjVAoxeSgNeAAAAAElFTkSuQmCC) right repeat-y}",a("../lib/dom").importCssString(e.cssText,e.cssClass)}),window.require(["ace/theme/monokai"],function(a){"object"==typeof module&&"object"==typeof exports&&module&&(module.exports=a)});
|
|
@ -1,5 +0,0 @@
|
||||||
#tab_plugin_klipper iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 600px;
|
|
||||||
border: 1px solid #808080;
|
|
||||||
}
|
|
|
@ -5,12 +5,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="full-sized-box">
|
<div class="full-sized-box">
|
||||||
|
<span class="help-inline" style="display:block; position: absolute">
|
||||||
|
<em>Click labels to hide/show dataset</em>
|
||||||
|
</span>
|
||||||
<script src="plugin/klipper/static/js/lib/Chart.bundle.min.js" type="text/javascript" defer></script>
|
<script src="plugin/klipper/static/js/lib/Chart.bundle.min.js" type="text/javascript" defer></script>
|
||||||
<canvas id="klipper_graph_canvas"></canvas>
|
<canvas id="klipper_graph_canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
<span class="help-inline" style="display:block; position: absolute">
|
|
||||||
<em>Click labels to hide/show dataset</em>
|
|
||||||
</span>
|
|
||||||
<label class="checkbox fill-checkbox">
|
<label class="checkbox fill-checkbox">
|
||||||
<input type="checkbox" data-bind="checked: datasetFill, click: toggleDatasetFill" />{{ _('Fill Datasets') }}
|
<input type="checkbox" data-bind="checked: datasetFill, click: toggleDatasetFill" />{{ _('Fill Datasets') }}
|
||||||
</label>
|
</label>
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
<button class="btn" data-bind="click: loadData"><i class="icon-signal"> </i>{{ _('Analyze Log') }}</button>
|
<button class="btn" data-bind="click: loadData"><i class="icon-signal"> </i>{{ _('Analyze Log') }}</button>
|
||||||
<button class="btn" data-dismiss="modal"><i class="icon-remove"> </i>{{ _('Close') }}</button>
|
<button class="btn" data-dismiss="modal"><i class="icon-remove"> </i>{{ _('Close') }}</button>
|
||||||
</form>
|
</form>
|
||||||
|
<textarea readonly id="plugin-klipper-klippylog" rows="31" class="block" data-bind="value: klippylogFile"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="klipper_graph_spinner" class="modal hide fade small" tabindex="-1" role="dialog" aria-hidden="true">
|
<div id="klipper_graph_spinner" class="modal hide fade small" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
|
|
@ -19,20 +19,27 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.connection.replace_connection_panel">
|
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.connection.replace_connection_panel">
|
||||||
</div>
|
</div>
|
||||||
</div><div class="control-group">
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
<label class="control-label">{{ _('Show NavBar Message') }}</label>
|
<label class="control-label">{{ _('Show NavBar Message') }}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.configuration.navbar">
|
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.configuration.navbar">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{ _('Klipper Config Path') }}</label>
|
<label class="control-label">{{ _('Enable debug logging') }}</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="checkbox" class="input-block-level" data-bind="checked: settings.settings.plugins.klipper.configuration.debug_logging">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">{{ _('Klipper Config File') }}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="input-block-level" data-bind="value: settings.settings.plugins.klipper.configuration.configpath">
|
<input type="text" class="input-block-level" data-bind="value: settings.settings.plugins.klipper.configuration.configpath">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">{{ _('Klipper Log Path') }}</label>
|
<label class="control-label">{{ _('Klipper Log File') }}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" class="input-block-level" data-bind="value: settings.settings.plugins.klipper.configuration.logpath">
|
<input type="text" class="input-block-level" data-bind="value: settings.settings.plugins.klipper.configuration.logpath">
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,8 +68,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||||
<div class="control-group">
|
<div class="control-group" id="item">
|
||||||
<label class="control-label">{{ _('Name') }}</label>
|
<label class="control-label">{{ _('Name') }}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
|
@ -105,13 +112,13 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<pre>
|
<pre>
|
||||||
PID_CALIBRATE
|
PID_CALIBRATE
|
||||||
HEATER={label:Heater, default:extruder, options:extruder|extruder1}
|
HEATER={label:Heater, default:extruder, options:extruder|extruder1}
|
||||||
TARGET={label:Target Temperature, unit:°C, default:190}
|
TARGET={label:Target Temperature, unit:°C, default:190}
|
||||||
WRITE_FILE={label:Write to File, default:0, options:0|1}
|
WRITE_FILE={label:Write to File, default:0, options:0|1}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Leveling -->
|
<!-- Leveling -->
|
||||||
<div class="tab-pane" id="level">
|
<div class="tab-pane" id="level">
|
||||||
|
@ -194,7 +201,19 @@ WRITE_FILE={label:Write to File, default:0, options:0|1}
|
||||||
<!-- Klipper Conf -->
|
<!-- Klipper Conf -->
|
||||||
<div class="tab-pane" id="conf">
|
<div class="tab-pane" id="conf">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<textarea id="plugin-klipper-config" rows="31" class="block" data-bind="value: settings.settings.plugins.klipper.config"></textarea>
|
<script src="plugin/klipper/static/js/lib/ace/ace.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="plugin/klipper/static/js/lib/ace/theme-monokai.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="plugin/klipper/static/js/lib/ace/mode-klipper_config.js" type="text/javascript"></script>
|
||||||
|
<button class="btn btn-small" data-bind='click: loadLastSession'
|
||||||
|
title="Reloads the last changes">
|
||||||
|
<i class="fas fa-redo"></i> {{ _('Reload last changes') }}
|
||||||
|
</button><button class="btn btn-small" data-bind='click: reloadFromFile'>
|
||||||
|
<i class="fas fa-upload"></i></a> {{ _('Reload from file') }}
|
||||||
|
</button>
|
||||||
|
<div class="conf-editor">
|
||||||
|
<input id="hdnLoadKlipperConfig" type="hidden" data-bind="value: configBound(settings.settings.plugins.klipper.config)" />
|
||||||
|
<div id="plugin-klipper-config"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,20 +3,16 @@
|
||||||
<label for="connection_printers" data-bind="css: {disabled: !connectionState.isErrorOrClosed()}, enable: connectionState.isErrorOrClosed() && loginState.isUser()">{{ _('Printer Profile') }}</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>
|
<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>
|
||||||
<button class="btn btn-block" data-bind="click: connectionState.connect, text: connectionState.buttonText(), enable: loginState.isUser()">{{ _('Connect') }}</button>
|
<button class="btn btn-block" data-bind="click: connectionState.connect, text: connectionState.buttonText(), enable: loginState.isUser()">{{ _('Connect') }}</button>
|
||||||
|
<button class="btn btn-block" data-bind="visible: hasRight('CONFIG', 'Ko'), click: function() {openOctoKlipperSettings('klipper-config');}">{{ _('Open Klipper config') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group" data-bind="visible: hasRight('MACRO', 'Ko')">
|
||||||
<div class="controls">
|
|
||||||
<button class="btn btn-block" data-bind="visible: loginState.hasPermissionKo(access.permissions.PLUGIN_KLIPPER_CONFIG), click: function() {openOctoKlipperSettings('klipper-config');}">{{ _('Open Klipper config') }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="control-group">
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="control-label small"><i class="icon-list-alt"></i> {{ _('Macros') }}</label>
|
<label class="control-label small"><i class="icon-list-alt"></i> {{ _('Macros') }}</label>
|
||||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||||
<!-- ko if: sidebar -->
|
<!-- ko if: sidebar -->
|
||||||
<button class="btn btn-block" data-bind="text: name, click: $parent.executeMacro, enable: $parent.isActive()"></button>
|
<button class="btn btn-block" data-bind="text: name, click: $parent.executeMacro, enable: $parent.isActive()"></button>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-mini pull-right clear-btn" data-bind="click: onClearLog, enable: isActive()"
|
<button class="btn btn-mini pull-right clear-btn" data-bind="click: onClearLog"
|
||||||
title="Clear Log">
|
title="Clear Log">
|
||||||
<i class="fa fa-trash"></i> {{ _('Clear') }}
|
<i class="fa fa-trash"></i> {{ _('Clear') }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<button class="btn btn-block btn-small" data-bind="click: onGetStatus, enable: isActive()" title="Query Klipper for its current status">
|
<button class="btn btn-block btn-small" data-bind="click: onGetStatus, enable: isActive()" title="Query Klipper for its current status">
|
||||||
<i class="fa icon-black fa-info-circle"></i> {{ _('Get Status') }}
|
<i class="fa icon-black fa-info-circle"></i> {{ _('Get Status') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-block btn-small" data-bind="visible: loginState.hasPermissionKo(access.permissions.PLUGIN_KLIPPER_CONFIG), click: function() {openOctoKlipperSettings('klipper-config');}" title="Open the Klipper configurationfile">
|
<button class="btn btn-block btn-small" data-bind="visible: hasRight('CONFIG', 'Ko'), click: function() {openOctoKlipperSettings('klipper-config');}" title="Open the Klipper configurationfile">
|
||||||
<i class="fa icon-black fa-file-code-o"></i> {{ _('Open Klipper config') }}
|
<i class="fa icon-black fa-file-code-o"></i> {{ _('Open Klipper config') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,13 +56,13 @@
|
||||||
title="Sets a offset for subsequent GCODE coordinates.">
|
title="Sets a offset for subsequent GCODE coordinates.">
|
||||||
{{ _('Coordinate Offset') }}
|
{{ _('Coordinate Offset') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-block btn-small" data-bind="click: showGraphDialog, enable: isActive()"
|
<button class="btn btn-block btn-small" data-bind="click: showGraphDialog"
|
||||||
title="Assists in debugging performance issues by analyzing the Klipper log files.">
|
title="Assists in debugging performance issues by analyzing the Klipper log files.">
|
||||||
{{ _('Performance Graph') }}
|
{{ _('Analyze Klipper Log') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls" data-bind="visible: hasRight('MACRO', 'Ko')">
|
||||||
<label class="control-label"><i class="icon-list-alt"></i> {{ _('Macros') }}</label>
|
<label class="control-label"><i class="icon-list-alt"></i> {{ _('Macros') }}</label>
|
||||||
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
<div data-bind="foreach: settings.settings.plugins.klipper.macros">
|
||||||
<!-- ko if: tab -->
|
<!-- ko if: tab -->
|
||||||
|
@ -73,4 +73,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -19,7 +19,7 @@ plugin_package = "octoprint_klipper"
|
||||||
|
|
||||||
plugin_name = "OctoKlipper"
|
plugin_name = "OctoKlipper"
|
||||||
|
|
||||||
plugin_version = "0.3.3"
|
plugin_version = "0.3.4rc5"
|
||||||
|
|
||||||
plugin_description = """A plugin for OctoPrint to configure,control and monitor the Klipper 3D printer software."""
|
plugin_description = """A plugin for OctoPrint to configure,control and monitor the Klipper 3D printer software."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue