2020-08-30 01:23:22 +03:00
# <Octoprint Klipper Plugin>
2018-08-19 14:49:24 +03:00
2020-08-30 01:23:22 +03:00
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# 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/>.
2020-08-29 21:13:27 +03:00
2021-03-27 01:39:08 +03:00
from __future__ import absolute_import , division , print_function , unicode_literals
2018-01-23 21:30:55 +03:00
import datetime
2018-01-23 17:01:58 +03:00
import logging
import octoprint . plugin
import octoprint . plugin . core
2018-08-13 19:22:58 +03:00
import glob
import os
2020-09-28 00:46:47 +03:00
import sys
2018-02-08 18:38:48 +03:00
from octoprint . util . comm import parse_firmware_line
2021-05-30 16:01:41 +03:00
from octoprint . access . permissions import Permissions , ADMIN_GROUP
2018-08-13 19:22:58 +03:00
from . modules import KlipperLogAnalyzer
2018-08-09 03:16:07 +03:00
import flask
2021-03-11 21:42:05 +03:00
from flask_babel import gettext
2018-01-23 17:01:58 +03:00
2021-03-27 01:39:08 +03:00
try :
import configparser
except ImportError :
import ConfigParser as configparser
2021-04-03 01:49:40 +03:00
if sys . version_info [ 0 ] < 3 :
import StringIO
2018-01-23 17:01:58 +03:00
class KlipperPlugin (
2021-03-27 01:39:08 +03:00
octoprint . plugin . StartupPlugin ,
octoprint . plugin . TemplatePlugin ,
octoprint . plugin . SettingsPlugin ,
octoprint . plugin . AssetPlugin ,
octoprint . plugin . SimpleApiPlugin ,
octoprint . plugin . EventHandlerPlugin ) :
_parsing_response = False
2021-04-07 01:00:42 +03:00
_parsing_check_response = True
2021-03-27 01:39:08 +03:00
_message = " "
2021-03-29 01:57:23 +03:00
def __init__ ( self ) :
self . _logger = logging . getLogger ( " octoprint.plugins.klipper " )
self . _octoklipper_logger = logging . getLogger ( " octoprint.plugins.klipper.debug " )
2021-03-27 01:39:08 +03:00
# -- Startup Plugin
2021-03-29 01:57:23 +03:00
def on_startup ( self , host , port ) :
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 )
self . _octoklipper_logger . addHandler ( octoklipper_logging_handler )
self . _octoklipper_logger . setLevel (
logging . DEBUG if self . _settings . get_boolean ( [ " debug_logging " ] ) else logging . INFO )
self . _octoklipper_logger . propagate = False
2021-03-27 01:39:08 +03:00
def on_after_startup ( self ) :
klipper_port = self . _settings . get ( [ " connection " , " port " ] )
additional_ports = self . _settings . global_get (
[ " serial " , " additionalPorts " ] )
if klipper_port not in additional_ports :
additional_ports . append ( klipper_port )
self . _settings . global_set (
[ " serial " , " additionalPorts " ] , additional_ports )
self . _settings . save ( )
2021-03-29 01:57:23 +03:00
self . log_info (
2021-03-27 01:39:08 +03:00
" Added klipper serial port {} to list of additional ports. " . format ( klipper_port ) )
# -- Settings Plugin
def get_additional_permissions ( self , * args , * * kwargs ) :
return [
{
" key " : " CONFIG " ,
" name " : " Config Klipper " ,
" description " : gettext ( " Allows to config klipper " ) ,
" default_groups " : [ ADMIN_GROUP ] ,
" 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 " ]
} ,
]
2020-08-29 21:13:27 +03:00
2021-03-27 01:39:08 +03:00
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 (
2021-03-29 01:57:23 +03:00
debug_logging = False ,
2021-03-27 01:39:08 +03:00
configpath = " ~/printer.cfg " ,
2021-03-29 01:57:23 +03:00
old_config = " " ,
2021-03-27 01:39:08 +03:00
logpath = " /tmp/klippy.log " ,
reload_command = " RESTART " ,
2021-05-04 02:01:57 +03:00
shortStatus_navbar = True ,
shortStatus_sidebar = True ,
2021-04-11 01:17:19 +03:00
parse_check = False ,
fontsize = 9
2021-03-27 01:39:08 +03:00
)
)
2018-02-08 18:38:48 +03:00
2021-03-27 01:39:08 +03:00
def on_settings_load ( self ) :
data = octoprint . plugin . SettingsPlugin . on_settings_load ( self )
2020-08-30 01:25:20 +03:00
2021-03-27 01:39:08 +03:00
configpath = os . path . expanduser (
self . _settings . get ( [ " configuration " , " configpath " ] )
)
2021-05-30 16:01:41 +03:00
data [ " config " ] = " "
2021-03-27 01:39:08 +03:00
try :
f = open ( configpath , " r " )
data [ " config " ] = f . read ( )
f . close ( )
except IOError :
2021-03-29 01:57:23 +03:00
self . log_error (
2021-03-27 01:39:08 +03:00
" 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
2018-02-02 16:05:21 +03:00
2021-03-27 01:39:08 +03:00
def on_settings_save ( self , data ) :
2018-08-09 03:16:07 +03:00
2021-03-29 01:57:23 +03:00
self . log_debug (
2021-03-27 01:39:08 +03:00
" Save klipper configs "
)
2021-03-16 21:06:16 +03:00
2021-03-27 01:39:08 +03:00
if " config " in data :
2021-05-30 16:01:40 +03:00
check_parse = self . _settings . get ( [ " configuration " , " parse_check " ] )
self . log_debug ( " check_parse: {} " . format ( check_parse ) )
2021-04-07 01:00:42 +03:00
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 " ] )
)
2021-05-30 16:01:41 +03:00
if self . file_exist ( configpath ) :
self . copy_cfg_to_backup ( configpath )
if self . _parsing_check_response or not check_parse :
2021-04-07 01:00:42 +03:00
try :
2021-03-27 01:39:08 +03:00
f = open ( configpath , " w " )
f . write ( data [ " config " ] )
f . close ( )
2021-04-07 01:00:42 +03:00
self . log_debug ( " Writing Klipper config to {} " . format ( configpath ) )
except IOError :
self . log_error ( " Error: Couldn ' t write Klipper config file: {} " . format ( configpath ) )
else :
2021-04-02 22:23:03 +03:00
#load the reload command from changed data if it is not existing load the saved setting
if self . key_exist ( data , " configuration " , " reload_command " ) :
reload_command = os . path . expanduser (
data [ " configuration " ] [ " reload_command " ]
)
else :
reload_command = self . _settings . get ( [ " configuration " , " reload_command " ] )
if reload_command != " manually " :
# Restart klippy to reload config
self . _printer . commands ( reload_command )
2021-04-07 01:00:42 +03:00
self . log_info ( " Restarting Klipper. " )
# we dont want to write the klipper conf to the octoprint settings
data . pop ( " config " , None )
2021-03-27 01:39:08 +03:00
# save the rest of changed settings into config.yaml of octoprint
2021-03-29 01:57:23 +03:00
old_debug_logging = self . _settings . get_boolean ( [ " configuration " , " debug_logging " ] )
2021-03-27 01:39:08 +03:00
octoprint . plugin . SettingsPlugin . on_settings_save ( self , data )
2021-03-29 01:57:23 +03:00
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 )
2021-03-27 01:39:08 +03:00
def get_settings_restricted_paths ( self ) :
return dict (
admin = [
[ " connection " , " port " ] ,
[ " configuration " , " configpath " ] ,
[ " configuration " , " replace_connection_panel " ]
] ,
user = [
[ " macros " ] ,
[ " probe " ]
]
)
2020-10-04 06:31:10 +03:00
2021-03-27 01:39:08 +03:00
def get_settings_version ( self ) :
2021-05-04 02:01:57 +03:00
return 3
2020-10-04 06:31:10 +03:00
2021-03-27 01:39:08 +03:00
def on_settings_migrate ( self , target , current ) :
if current is None :
settings = self . _settings
2019-02-16 05:46:52 +03:00
2021-03-27 01:39:08 +03:00
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 " ] ) :
2021-05-04 02:01:57 +03:00
self . log_info ( " migrate setting for: configPath " )
2021-03-27 01:39:08 +03:00
settings . set ( [ " config_path " ] , settings . get ( [ " configPath " ] ) )
settings . remove ( [ " configPath " ] )
2021-05-30 16:01:40 +03:00
if current is not None and current < 3 :
2021-05-04 02:01:57 +03:00
settings = self . _settings
if settings . has ( [ " configuration " , " navbar " ] ) :
self . log_info ( " migrate setting for: configuration/navbar " )
settings . set ( [ " configuration " , " shortStatus_navbar " ] , settings . get ( [ " configuration " , " navbar " ] ) )
settings . remove ( [ " configuration " , " navbar " ] )
2021-03-27 01:39:08 +03:00
# -- 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
2018-08-09 03:16:07 +03:00
)
2021-03-27 01:39:08 +03:00
]
# -- Asset Plugin
def get_assets ( self ) :
return dict (
js = [ " js/klipper.js " ,
" js/klipper_settings.js " ,
" js/klipper_leveling.js " ,
" js/klipper_pid_tuning.js " ,
" js/klipper_offset.js " ,
" js/klipper_param_macro.js " ,
" js/klipper_graph.js "
] ,
2021-05-11 23:22:37 +03:00
css = [ " css/klipper.css " ]
2021-03-27 01:39:08 +03:00
)
# -- Event Handler Plugin
def on_event ( self , event , payload ) :
if " UserLoggedIn " == event :
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 " ] ) )
2021-04-11 01:17:19 +03:00
elif " // probe " in line or " // Failed to verify BLTouch " in line :
2021-04-07 01:00:42 +03:00
msg = line . strip ( ' / ' )
self . log_info ( msg )
2021-05-25 14:20:27 +03:00
self . write_parsing_response_buffer ( )
2021-03-27 01:39:08 +03:00
elif " // " in line :
2021-04-07 01:00:42 +03:00
# add lines with // to a buffer
2021-03-27 01:39:08 +03:00
self . _message = self . _message + line . strip ( ' / ' )
if not self . _parsing_response :
self . update_status ( " info " , self . _message )
self . _parsing_response = True
2021-04-07 01:00:42 +03:00
elif " !! " in line :
msg = line . strip ( ' ! ' )
self . update_status ( " error " , msg )
self . log_error ( msg )
self . write_parsing_response_buffer ( )
2021-03-27 01:39:08 +03:00
else :
2021-04-07 01:00:42 +03:00
self . write_parsing_response_buffer ( )
2021-03-27 01:39:08 +03:00
return line
2021-04-07 01:00:42 +03:00
def write_parsing_response_buffer ( self ) :
# write buffer with // lines after a gcode response without //
if self . _parsing_response :
self . _parsing_response = False
self . log_info ( self . _message )
self . _message = " "
2021-03-27 01:39:08 +03:00
def get_api_commands ( self ) :
return dict (
listLogFiles = [ ] ,
getStats = [ " logFile " ] ,
2021-03-29 01:57:23 +03:00
reloadConfig = [ ] ,
2021-05-30 16:01:41 +03:00
reloadCfgBackup = [ ] ,
2021-03-29 01:57:23 +03:00
checkConfig = [ " config " ]
2021-03-27 01:39:08 +03:00
)
def on_api_command ( self , command , data ) :
if command == " listLogFiles " :
files = [ ]
logpath = os . path . expanduser (
self . _settings . get ( [ " configuration " , " logpath " ] )
2018-08-15 02:17:12 +03:00
)
2021-03-27 01:39:08 +03:00
if self . file_exist ( logpath ) :
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 " :
2021-05-30 16:01:40 +03:00
self . log_debug ( " reloadConfig " )
return self . reload_cfg ( )
elif command == " reloadCfgBackup " :
self . log_debug ( " reloadCfgBackup " )
2021-05-30 16:01:41 +03:00
configpath = os . path . expanduser (
self . _settings . get ( [ " configuration " , " configpath " ] )
)
return self . copy_cfg_from_backup ( configpath )
2021-03-29 01:57:23 +03:00
elif command == " checkConfig " :
if " config " in data :
2021-05-30 16:01:41 +03:00
#self.write_cfg_backup(data["config"])
2021-05-30 16:01:40 +03:00
if self . key_exist ( data , " configuration " , " parse_check " ) :
check_parse = data [ " configuration " ] [ " parse_check " ]
else :
check_parse = self . _settings . get ( [ " configuration " , " parse_check " ] )
if check_parse and not self . validate_configfile ( data [ " config " ] ) :
2021-04-07 01:00:42 +03:00
self . log_debug ( " validateConfig not ok " )
2021-03-29 01:57:23 +03:00
self . _settings . set ( [ " configuration " , " old_config " ] , data [ " config " ] )
return flask . jsonify ( checkConfig = " not OK " )
else :
2021-04-07 01:00:42 +03:00
self . log_debug ( " validateConfig ok " )
2021-03-29 01:57:23 +03:00
self . _settings . set ( [ " configuration " , " old_config " ] , " " )
return flask . jsonify ( checkConfig = " OK " )
2021-03-27 01:39:08 +03:00
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 send_message ( self , type , subtype , title , payload ) :
2021-05-30 16:01:40 +03:00
"""
Send Message over API to FrontEnd
"""
2021-03-27 01:39:08 +03:00
self . _plugin_manager . send_plugin_message (
self . _identifier ,
dict (
time = datetime . datetime . now ( ) . strftime ( " % H: % M: % S " ) ,
type = type ,
subtype = subtype ,
title = title ,
payload = payload
)
)
def poll_status ( self ) :
self . _printer . commands ( " STATUS " )
def update_status ( self , type , status ) :
self . send_message ( " status " , type , status , status )
def log_info ( self , message ) :
2021-03-29 01:57:23 +03:00
self . _octoklipper_logger . info ( message )
2021-03-27 01:39:08 +03:00
self . send_message ( " log " , " info " , message , message )
2021-03-29 01:57:23 +03:00
def log_debug ( self , message ) :
self . _octoklipper_logger . debug ( message )
2021-05-04 02:01:57 +03:00
self . _logger . info ( message )
2021-03-27 01:39:08 +03:00
# sends a message to frontend(in klipper.js -> self.onDataUpdaterPluginMessage) and write it to the console.
# _mtype, subtype=debug/info, title of message, message)
2021-03-29 01:57:23 +03:00
self . send_message ( " console " , " debug " , message , message )
2021-03-27 01:39:08 +03:00
def log_error ( self , error ) :
2021-03-29 01:57:23 +03:00
self . _octoklipper_logger . error ( error )
2021-05-04 02:01:57 +03:00
self . _logger . info ( error )
2021-03-27 01:39:08 +03:00
self . send_message ( " log " , " error " , error , error )
def file_exist ( self , filepath ) :
if not os . path . isfile ( filepath ) :
2021-03-29 01:57:23 +03:00
self . send_message ( " PopUp " , " warning " , " OctoKlipper Settings " ,
2021-03-27 01:39:08 +03:00
" Klipper " + filepath + " does not exist! " )
return False
else :
return True
def key_exist ( self , dict , key1 , key2 ) :
try :
dict [ key1 ] [ key2 ]
except KeyError :
return False
else :
return True
def validate_configfile ( self , dataToBeValidated ) :
"""
2021-05-04 02:01:57 +03:00
- - - > SyntaxCheck for a given data < - - - -
2021-03-27 01:39:08 +03:00
"""
2021-05-30 16:01:40 +03:00
check_parse = self . _settings . get ( [ " configuration " , " parse_check " ] )
if check_parse :
try :
dataToValidated = configparser . RawConfigParser ( strict = False )
#
if sys . version_info [ 0 ] < 3 :
buf = StringIO . StringIO ( dataToBeValidated )
dataToValidated . readfp ( buf )
else :
dataToValidated . read_string ( dataToBeValidated )
2021-03-27 01:39:08 +03:00
2021-05-30 16:01:40 +03:00
sections_search_list = [ " bltouch " ,
" probe " ]
value_search_list = [ " x_offset " ,
" y_offset " ,
" z_offset " ]
try :
# cycle through sections and then values
for y in sections_search_list :
for x in value_search_list :
if dataToValidated . has_option ( y , x ) :
a_float = dataToValidated . getfloat ( y , x )
except ValueError as error :
self . log_error (
" Error: Invalid Value for <b> " + x + " </b> in Section: <b> " + y + " </b> \n " +
" {} " . format ( str ( error ) )
)
self . send_message ( " PopUp " , " warning " , " OctoKlipper: Invalid Config \n " ,
" Config got not saved! \n " +
" You can reload your last changes \n " +
" on the ' Klipper Configuration ' tab. \n \n " +
" Invalid Value for <b> " + x + " </b> in Section: <b> " + y + " </b> \n " + " {} " . format ( str ( error ) ) )
self . _parsing_check_response = False
return False
except configparser . Error as error :
if sys . version_info [ 0 ] < 3 :
error . message = error . message . replace ( " \\ n " , " " )
error . message = error . message . replace ( " file: u " , " Klipper Configuration " , 1 )
error . message = error . message . replace ( " ' " , " " , 2 )
error . message = error . message . replace ( " u ' " , " ' " , 1 )
2021-04-26 03:25:12 +03:00
2021-05-30 16:01:40 +03:00
else :
error . message = error . message . replace ( " \\ n " , " " )
error . message = error . message . replace ( " file: " , " Klipper Configuration " , 1 )
error . message = error . message . replace ( " ' " , " " , 2 )
2021-04-26 03:25:12 +03:00
self . log_error (
2021-05-30 16:01:40 +03:00
" Error: Invalid Klipper config file: \n " +
2021-04-26 03:25:12 +03:00
" {} " . format ( str ( error ) )
)
2021-05-30 16:01:40 +03:00
self . send_message ( " PopUp " , " warning " , " OctoKlipper: Invalid Config data \n " ,
" Config got not saved! \n " +
" You can reload your last changes \n " +
" on the ' Klipper Configuration ' tab. \n \n " + str ( error ) )
2021-04-26 03:25:12 +03:00
self . _parsing_check_response = False
return False
2021-05-30 16:01:40 +03:00
else :
self . _parsing_check_response = True
return True
def reload_cfg ( 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 . _settings . set ( [ " config " ] , data [ " config " ] )
# self.send_message("reload", "config", "", data["config"])
# send the configdata to frontend to update ace editor
2021-04-03 01:49:40 +03:00
if sys . version_info [ 0 ] < 3 :
2021-05-30 16:01:40 +03:00
data [ " config " ] = data [ " config " ] . decode ( ' utf-8 ' )
return flask . jsonify ( data = data [ " config " ] )
2021-05-30 16:01:41 +03:00
def copy_cfg_from_backup ( self , dst ) :
2021-05-30 16:01:40 +03:00
"""
2021-05-30 16:01:41 +03:00
Copy the backuped config files in the data folder of OctoKlipper to the given destination
2021-05-30 16:01:40 +03:00
"""
2021-05-30 16:01:41 +03:00
from shutil import copy
2021-05-30 16:01:40 +03:00
2021-05-30 16:01:41 +03:00
bak_config_path = os . path . join ( self . get_plugin_data_folder ( ) , " configs/ " )
src_files = os . listdir ( bak_config_path )
nio_files = [ ]
self . log_debug ( " reloadCfgBackupPath: " + src_files )
for file_name in src_files :
full_file_name = os . path . join ( bak_config_path , file_name )
if os . path . isfile ( full_file_name ) :
try :
copy ( full_file_name , dst )
except IOError :
self . log_error (
" Error: Klipper config file not found at: {} " . format (
full_file_name )
)
nio_files . append ( full_file_name )
else :
self . log_debug ( " File done: " + full_file_name )
return nio_files
2021-05-30 16:01:40 +03:00
2021-05-30 16:01:41 +03:00
def copy_cfg_to_backup ( self , src ) :
2021-05-30 16:01:40 +03:00
"""
2021-05-30 16:01:41 +03:00
Copy the config file into the data folder of OctoKlipper
2021-05-30 16:01:40 +03:00
"""
2021-05-30 16:01:41 +03:00
from shutil import copyfile
filename = os . path . basename ( src )
dst = os . path . join ( self . get_plugin_data_folder ( ) , " configs " , " " , filename )
self . log_debug ( " CopyCfgBackupPath: " + dst )
2021-05-30 16:01:40 +03:00
try :
2021-05-30 16:01:41 +03:00
copyfile ( src , dst )
2021-05-30 16:01:40 +03:00
except IOError :
2021-03-29 01:57:23 +03:00
self . log_error (
2021-05-30 16:01:41 +03:00
" Error: Couldn ' t copy Klipper config file to {} " . format (
dst )
2021-03-27 01:39:08 +03:00
)
else :
2021-05-30 16:01:40 +03:00
self . log_debug ( " CfgBackup writen " )
2021-03-27 01:39:08 +03:00
2020-08-29 21:13:27 +03:00
__plugin_name__ = " OctoKlipper "
__plugin_pythoncompat__ = " >=2.7,<4 "
2021-09-05 16:28:17 +03:00
__plugin_settings_overlay__ = {
' system ' : {
' actions ' : [ {
' action ' : ' octoklipper_restart ' ,
' command ' : ' sudo service klipper restart ' ,
' name ' : ' Restart Klipper ' ,
' confirm ' : ' <h3><center><b> ' + gettext ( " You are about to restart Klipper! " ) + ' <br> ' + gettext ( " This will stop ongoing prints! " ) + ' </b></center></h3><br>Command = " sudo service klipper restart " '
} ]
}
}
2020-08-29 21:13:27 +03:00
2021-03-27 01:39:08 +03:00
2018-01-23 17:01:58 +03:00
def __plugin_load__ ( ) :
2021-03-27 01:39:08 +03:00
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
}