diff --git a/src/octoprint/plugins/lasercutterprofiles/__init__.py b/src/octoprint/plugins/lasercutterprofiles/__init__.py new file mode 100644 index 00000000..a9631410 --- /dev/null +++ b/src/octoprint/plugins/lasercutterprofiles/__init__.py @@ -0,0 +1,234 @@ +# coding=utf-8 +from __future__ import absolute_import + +__author__ = "Gina Häußge " +__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html" +__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import logging +import logging.handlers +import os +import flask +import socket + +import octoprint.plugin +import octoprint.util +from octoprint.util import dict_merge +import octoprint.settings +from octoprint.server import NO_CONTENT + + +from .profile import LaserCutterProfileManager, InvalidProfileError, CouldNotOverwriteError + +import copy +from octoprint.server.util.flask import restricted_access +from flask import Blueprint, request, jsonify, abort, current_app, session, make_response, url_for + + +blueprint = flask.Blueprint("plugin.lasercutterprofiles", __name__) +laserCutterProfileManager = LaserCutterProfileManager() + +@blueprint.route("/profiles", methods=["GET"]) +def laserCutterProfilesList(): + all_profiles = laserCutterProfileManager.get_all() + return jsonify(dict(profiles=_convert_profiles(all_profiles))) + +@blueprint.route("/profiles", methods=["POST"]) +@restricted_access +def laserCutterProfilesAdd(): + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) + + if not "profile" in json_data: + return make_response("No profile included in request", 400) + + base_profile = laserCutterProfileManager.get_default() + if "basedOn" in json_data and isinstance(json_data["basedOn"], basestring): + other_profile = laserCutterProfileManager.get(json_data["basedOn"]) + if other_profile is not None: + base_profile = other_profile + + if "id" in base_profile: + del base_profile["id"] + if "name" in base_profile: + del base_profile["name"] + if "default" in base_profile: + del base_profile["default"] + + new_profile = json_data["profile"] + make_default = False + if "default" in new_profile: + make_default = True + del new_profile["default"] + + profile = dict_merge(base_profile, new_profile) + try: + saved_profile = laserCutterProfileManager.save(profile, allow_overwrite=False, make_default=make_default) + except InvalidProfileError: + return make_response("Profile is invalid", 400) + except CouldNotOverwriteError: + return make_response("Profile already exists and overwriting was not allowed", 400) + except Exception as e: + return make_response("Could not save profile: %s" % e.message, 500) + else: + return jsonify(dict(profile=_convert_profile(saved_profile))) + +@blueprint.route("/profiles/", methods=["GET"]) +def laserCutterProfilesGet(identifier): + profile = laserCutterProfileManager.get(identifier) + if profile is None: + return make_response("Unknown profile: %s" % identifier, 404) + else: + return jsonify(_convert_profile(profile)) + +@blueprint.route("/profiles/", methods=["DELETE"]) +@restricted_access +def laserCutterProfilesDelete(identifier): + laserCutterProfileManager.remove(identifier) + return NO_CONTENT + +@blueprint.route("/profiles/", methods=["PATCH"]) +@restricted_access +def laserCutterProfilesUpdate(identifier): + if not "application/json" in request.headers["Content-Type"]: + return make_response("Expected content-type JSON", 400) + + try: + json_data = request.json + except JSONBadRequest: + return make_response("Malformed JSON body in request", 400) + + if not "profile" in json_data: + return make_response("No profile included in request", 400) + + profile = laserCutterProfileManager.get(identifier) + if profile is None: + profile = laserCutterProfileManager.get_default() + + new_profile = json_data["profile"] + new_profile = dict_merge(profile, new_profile) + + make_default = False + if "default" in new_profile: + make_default = True + del new_profile["default"] + + new_profile["id"] = identifier + + try: + saved_profile = laserCutterProfileManager.save(new_profile, allow_overwrite=True, make_default=make_default) + except InvalidProfileError: + return make_response("Profile is invalid", 400) + except CouldNotOverwriteError: + return make_response("Profile already exists and overwriting was not allowed", 400) + except Exception as e: + return make_response("Could not save profile: %s" % e.message, 500) + else: + return jsonify(dict(profile=_convert_profile(saved_profile))) + +def _convert_profiles(profiles): + result = dict() + for identifier, profile in profiles.items(): + result[identifier] = _convert_profile(profile) + return result + +def _convert_profile(profile): + default = laserCutterProfileManager.get_default()["id"] + current = laserCutterProfileManager.get_current_or_default()["id"] + + converted = copy.deepcopy(profile) + converted["resource"] = url_for(".laserCutterProfilesGet", identifier=profile["id"], _external=True) + converted["default"] = (profile["id"] == default) + converted["current"] = (profile["id"] == current) + return converted + + + +default_settings = { + "zAxis": False, + "working_area_width": 216, + "working_area_height": 297 +} +s = octoprint.plugin.plugin_settings("lasercutterprofiles", defaults=default_settings) + + +class LaserCutterProfilesPlugin(octoprint.plugin.SettingsPlugin, + octoprint.plugin.StartupPlugin, + octoprint.plugin.BlueprintPlugin, + octoprint.plugin.AssetPlugin, + octoprint.plugin.TemplatePlugin): + + global laserCutterProfileManager + + def __init__(self): + pass + + ##~~ StartupPlugin API + def on_startup(self, host, port): + pass + + ##~~ AssetPlugin API + + def get_assets(self): + return { + "js": ["js/lasercutterprofiles.js"], + "less": [], + "css": [] + } + + ##~~ SettingsPlugin API + + def on_settings_load(self): + return dict( + workingAreaHeight=s.get(["workingAreaHeight"]), + workingAreaWidth=s.get(["workingAreaWidth"]), + zAxis=s.getBoolean(["zAxis"]) + ) + + def on_settings_save(self, data): + if "workingAreaHeight" in data and data["workingAreaHeight"]: + s.set(["workingAreaHeight"], data["workingAreaHeight"]) + if "workingAreaWidth" in data and data["workingAreaWidth"]: + s.set(["workingAreaWidth"], data["workingAreaWidth"]) + if "zAxis" in data: + zAxis = data["zAxis"] in octoprint.settings.valid_boolean_trues + s.setBoolean(["zAxis"], zAxis) + + ##~~ TemplatePlugin API + + def get_template_vars(self): + return dict( + _settings_menu_entry="Laser cutter profile" + ) + + def get_template_folder(self): + import os + return os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates") + + ##~~ BlueprintPlugin API + + def get_blueprint(self): + global blueprint + return blueprint + +def _sanitize_name(name): + if name is None: + return None + + if "/" in name or "\\" in name: + raise ValueError("name must not contain / or \\") + + import string + valid_chars = "-_.() {ascii}{digits}".format(ascii=string.ascii_letters, digits=string.digits) + sanitized_name = ''.join(c for c in name if c in valid_chars) + sanitized_name = sanitized_name.replace(" ", "_") + return sanitized_name.lower() + +__plugin_name__ = "lasercutterprofiles" +__plugin_version__ = "0.1" +__plugin_implementations__ = [LaserCutterProfilesPlugin()] diff --git a/src/octoprint/plugins/lasercutterprofiles/profile.py b/src/octoprint/plugins/lasercutterprofiles/profile.py new file mode 100644 index 00000000..a4cb1ddd --- /dev/null +++ b/src/octoprint/plugins/lasercutterprofiles/profile.py @@ -0,0 +1,297 @@ +# coding=utf-8 +from __future__ import absolute_import + +__author__ = "Gina Häußge " +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" + + +import os +import copy +import re +import logging + +from octoprint.settings import settings +from octoprint.util import dict_merge, dict_clean, dict_contains_keys + +class SaveError(Exception): + pass + +class CouldNotOverwriteError(SaveError): + pass + +class InvalidProfileError(Exception): + pass + +class LaserCutterProfileManager(object): + + default = dict( + id = "_mrbeam_junior", + name = "Mr Beam", + model = "Junior", + volume=dict( + width = 216, + depth = 297, + height = 0, + ), + zAxis = False, + axes=dict( + x = dict(speed=5000, inverted=False), + y = dict(speed=5000, inverted=False), + z = dict(speed=1000, inverted=False) + ) + ) + + def __init__(self): + self._current = None + self._folder = settings().getBaseFolder("plugins")+"/lasercutterprofiles" + if not os.path.exists(self._folder): + os.makedirs(self._folder) + self._logger = logging.getLogger(__name__) + + def select(self, identifier): + if identifier is None or not self.exists(identifier): + self._current = self.get_default() + return False + else: + self._current = self.get(identifier) + return True + + def deselect(self): + self._current = None + + def get_all(self): + return self._load_all() + + def get(self, identifier): + try: + if identifier == "_default": + return self._load_default() + elif self.exists(identifier): + return self._load_from_path(self._get_profile_path(identifier)) + else: + return None + except InvalidProfileError: + return None + + def remove(self, identifier): + if identifier == "_default": + return False + return self._remove_from_path(self._get_profile_path(identifier)) + + def save(self, profile, allow_overwrite=False, make_default=False): + if "id" in profile and profile['id'] != '': + identifier = profile["id"] + elif "name" in profile: + identifier = profile["name"] + else: + raise InvalidProfileError("profile must contain either id or name") + + identifier = self._sanitize(identifier) + profile["id"] = identifier + profile = dict_clean(profile, self.__class__.default) + + print("save", profile) + if identifier == "_default": + default_profile = dict_merge(self._load_default(), profile) + if not self._ensure_valid_profile(default_profile): + raise InvalidProfileError() + + settings().set(["printerProfiles", "defaultProfile"], default_profile, defaults=dict(printerProfiles=dict(defaultProfile=self.__class__.default))) + settings().save() + else: + self._save_to_path(self._get_profile_path(identifier), profile, allow_overwrite=allow_overwrite) + + if make_default: + settings().set(["printerProfiles", "default"], identifier) + + return self.get(identifier) + + def get_default(self): + default = settings().get(["printerProfiles", "default"]) + if default is not None and self.exists(default): + profile = self.get(default) + if profile is not None: + return profile + + return self._load_default() + + def set_default(self, identifier): + all_identifiers = self._load_all_identifiers().keys() + if identifier is not None and not identifier in all_identifiers: + return + + settings().set(["printerProfile", "default"], identifier) + settings().save() + + def get_current_or_default(self): + if self._current is not None: + return self._current + else: + return self.get_default() + + def get_current(self): + return self._current + + def exists(self, identifier): + if identifier is None: + return False + elif identifier == "_default": + return True + else: + path = self._get_profile_path(identifier) + return os.path.exists(path) and os.path.isfile(path) + + def _load_all(self): + all_identifiers = self._load_all_identifiers() + results = dict() + for identifier, path in all_identifiers.items(): + try: + profile = self._load_from_path(path) + except InvalidProfileError: + continue + + if profile is None: + continue + + results[identifier] = dict_merge(self._load_default("_mrbeam_junior"), profile) + + if(not results.has_key("_mrbeam_junior")): + results["_mrbeam_junior"] = self._load_default("_mrbeam_junior") + if(not results.has_key("_mrbeam_senior")): + results["_mrbeam_senior"] = self._load_default("_mrbeam_senior") + return results + + def _load_all_identifiers(self): + results = dict() + for entry in os.listdir(self._folder): + if entry.startswith(".") or not entry.endswith(".profile") or entry == "_default.profile": + continue + + path = os.path.join(self._folder, entry) + if not os.path.isfile(path): + continue + + identifier = entry[:-len(".profile")] + results[identifier] = path + return results + + def _load_from_path(self, path): + if not os.path.exists(path) or not os.path.isfile(path): + return None + + import yaml + with open(path) as f: + profile = yaml.safe_load(f) + profile = self._ensure_valid_profile(profile) + if not profile: + self._logger.warn("Invalid profile: %s" % path) + raise InvalidProfileError() + return profile + + def _save_to_path(self, path, profile, allow_overwrite=False): + validated_profile = self._ensure_valid_profile(profile) + print("_save_to_path", validated_profile, path) + if not validated_profile: + raise InvalidProfileError() + + if os.path.exists(path) and not allow_overwrite: + raise SaveError("Profile %s already exists and not allowed to overwrite" % profile["id"]) + + import yaml + with open(path, "wb") as f: + try: + yaml.safe_dump(profile, f, default_flow_style=False, indent=" ", allow_unicode=True) + except Exception as e: + print (e.message) + raise SaveError("Cannot save profile %s: %s" % (profile["id"], e.message)) + + def _remove_from_path(self, path): + try: + os.remove(path) + return True + except: + return False + + def _load_default(self, defaultModel = None): + default_overrides = settings().get(["laserCutterProfiles", defaultModel]) + default = copy.deepcopy(self.__class__.default) + if(defaultModel is not None and defaultModel == "_mrbeam_senior"): + default['volume']['width'] *= 2 + default['volume']['depth'] *= 2 + default['model'] = "Senior" + default['id'] = "_mrbeam_senior" + + if(default_overrides is None): + merge = default + else: + merge = dict_merge(default, default_overrides) + + profile = self._ensure_valid_profile(merge) + if not profile: + self._logger.warn("Invalid default profile after applying overrides") + raise InvalidProfileError() + return profile + + def _get_profile_path(self, identifier): + return os.path.join(self._folder, "%s.profile" % identifier) + + def _sanitize(self, name): + if name is None: + return None + + if "/" in name or "\\" in name: + raise ValueError("name must not contain / or \\") + + import string + valid_chars = "-_.() {ascii}{digits}".format(ascii=string.ascii_letters, digits=string.digits) + sanitized_name = ''.join(c for c in name if c in valid_chars) + sanitized_name = sanitized_name.replace(" ", "_") + return sanitized_name + + def _ensure_valid_profile(self, profile): + # ensure all keys are present + if not dict_contains_keys(self.default, profile): + print("key error") + return False + + # conversion helper + def convert_value(profile, path, converter): + value = profile + for part in path[:-1]: + if not isinstance(value, dict) or not part in value: + raise RuntimeError("%s is not contained in profile" % ".".join(path)) + value = value[part] + + if not isinstance(value, dict) or not path[-1] in value: + raise RuntimeError("%s is not contained in profile" % ".".join(path)) + + value[path[-1]] = converter(value[path[-1]]) + + + # convert ints + for path in (("axes", "x", "speed"), ("axes", "y", "speed"), ("axes", "z", "speed")): + try: + convert_value(profile, path, int) + except: + print("int error") + return False + + # convert floats + for path in (("volume", "width"), ("volume", "depth"), ("volume", "height")): + try: + convert_value(profile, path, float) + except: + print("float error") + return False + + # convert booleans + for path in (("axes", "x", "inverted"), ("axes", "y", "inverted"), ("axes", "z", "inverted")): + try: + convert_value(profile, path, bool) + except: + print("bool error") + return False + + return profile + diff --git a/src/octoprint/plugins/lasercutterprofiles/static/js/lasercutterprofiles.js b/src/octoprint/plugins/lasercutterprofiles/static/js/lasercutterprofiles.js new file mode 100644 index 00000000..ceb46cce --- /dev/null +++ b/src/octoprint/plugins/lasercutterprofiles/static/js/lasercutterprofiles.js @@ -0,0 +1,268 @@ +$(function() { + + function LaserCutterProfilesViewModel(params) { + var self = this; + + self.settings = params[0]; + + self._cleanProfile = function() { + return { + id: "", + name: "", + model: "", + color: "default", + volume: { + formFactor: "rectangular", + width: 200, + depth: 200, + height: 200 + }, + heatedBed: false, + axes: { + x: {speed: 6000, inverted: false}, + y: {speed: 6000, inverted: false}, + z: {speed: 200, inverted: false}, + e: {speed: 300, inverted: false} + }, + extruder: { + count: 1, + offsets: [ + [0,0] + ], + nozzleDiameter: 0.4 + } + } + }; + + self.profiles = new ItemListHelper( + "laserCutterProfiles", + { + "name": function(a, b) { + // sorts ascending + if (a["name"].toLocaleLowerCase() < b["name"].toLocaleLowerCase()) return -1; + if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) return 1; + return 0; + } + }, + {}, + "name", + [], + [], + 10 + ); + self.defaultProfile = ko.observable(); + self.currentProfile = ko.observable(); + + self.currentProfileData = ko.observable(ko.mapping.fromJS(self._cleanProfile())); + + self.editorNew = ko.observable(false); + + self.editorName = ko.observable(); +// self.editorColor = ko.observable(); + self.editorIdentifier = ko.observable(); + self.editorModel = ko.observable(); + + self.editorVolumeWidth = ko.observable(); + self.editorVolumeDepth = ko.observable(); + self.editorVolumeHeight = ko.observable(); +// self.editorVolumeFormFactor = ko.observable(); + +// self.editorHeatedBed = ko.observable(); + self.editorZAxis = ko.observable(); + +// self.editorNozzleDiameter = ko.observable(); +// self.editorExtruders = ko.observable(); +// self.editorExtruderOffsets = ko.observableArray(); + + self.editorAxisXSpeed = ko.observable(); + self.editorAxisYSpeed = ko.observable(); + self.editorAxisZSpeed = ko.observable(); +// self.editorAxisESpeed = ko.observable(); + + self.editorAxisXInverted = ko.observable(false); + self.editorAxisYInverted = ko.observable(false); + self.editorAxisZInverted = ko.observable(false); + + self.makeDefault = function(data) { + var profile = { + id: data.id, + default: true + }; + + self.updateProfile(profile); + }; + + self.requestData = function() { + $.ajax({ + url: BASEURL + "plugin/lasercutterprofiles/profiles", + type: "GET", + dataType: "json", + success: self.fromResponse + }) + }; + + self.fromResponse = function(data) { + var items = []; + var defaultProfile = undefined; + var currentProfile = undefined; + var currentProfileData = undefined; + _.each(data.profiles, function(entry) { + if (entry.default) { + defaultProfile = entry.id; + } + if (entry.current) { + currentProfile = entry.id; + currentProfileData = ko.mapping.fromJS(entry, self.currentProfileData); + } + entry["isdefault"] = ko.observable(entry.default); + entry["iscurrent"] = ko.observable(entry.current); + items.push(entry); + }); + self.profiles.updateItems(items); + self.defaultProfile(defaultProfile); + self.currentProfile(currentProfile); + self.currentProfileData(currentProfileData); + }; + + self.addProfile = function(callback) { + var profile = self._editorData(); + $.ajax({ + url: BASEURL + "plugin/lasercutterprofiles/profiles", + type: "POST", + dataType: "json", + contentType: "application/json; charset=UTF-8", + data: JSON.stringify({profile: profile}), + success: function() { + if (callback !== undefined) { + callback(); + } + self.requestData(); + } + }); + }; + + self.removeProfile = function(data) { + $.ajax({ + url: data.resource, + type: "DELETE", + dataType: "json", + success: self.requestData + }) + }; + + self.updateProfile = function(profile, callback) { + if (profile == undefined) { + profile = self._editorData(); + } + + $.ajax({ + url: BASEURL + "plugin/lasercutterprofiles/profiles/" + profile.id, + type: "PATCH", + dataType: "json", + contentType: "application/json; charset=UTF-8", + data: JSON.stringify({profile: profile}), + success: function() { + if (callback !== undefined) { + callback(); + } + self.requestData(); + } + }); + }; + + self.showEditProfileDialog = function(data) { + var add = false; + if (data == undefined) { + data = self._cleanProfile(); + add = true; + } + + self.editorNew(add); + + self.editorIdentifier(data.id); + self.editorName(data.name); + self.editorModel(data.model); + + self.editorVolumeWidth(data.volume.width); + self.editorVolumeDepth(data.volume.depth); + self.editorVolumeHeight(data.volume.height); + + self.editorZAxis(data.zAxis); + + self.editorAxisXSpeed(data.axes.x.speed); + self.editorAxisXInverted(data.axes.x.inverted); + self.editorAxisYSpeed(data.axes.y.speed); + self.editorAxisYInverted(data.axes.y.inverted); + self.editorAxisZSpeed(data.axes.z.speed); + self.editorAxisZInverted(data.axes.z.inverted); + + var editDialog = $("#settings_laserCutterProfiles_editDialog"); + var confirmButton = $("button.btn-confirm", editDialog); + var dialogTitle = $("h3.modal-title", editDialog); + + dialogTitle.text(add ? gettext("Add Profile") : _.sprintf(gettext("Edit Profile \"%(name)s\""), {name: data.name})); + confirmButton.unbind("click"); + confirmButton.bind("click", function() { + self.confirmEditProfile(add); + }); + editDialog.modal("show"); + }; + + self.confirmEditProfile = function(add) { + var callback = function() { + $("#settings_laserCutterProfiles_editDialog").modal("hide"); + }; + + if (add) { + self.addProfile(callback); + } else { + self.updateProfile(undefined, callback); + } + }; + + self._editorData = function() { + var profile = { + id: self.editorIdentifier(), + name: self.editorName(), + model: self.editorModel(), + volume: { + width: parseFloat(self.editorVolumeWidth()), + depth: parseFloat(self.editorVolumeDepth()), + height: parseFloat(self.editorVolumeHeight()), + }, + zAxis: self.editorZAxis(), + axes: { + x: { + speed: parseInt(self.editorAxisXSpeed()), + inverted: self.editorAxisXInverted() + }, + y: { + speed: parseInt(self.editorAxisYSpeed()), + inverted: self.editorAxisYInverted() + }, + z: { + speed: parseInt(self.editorAxisZSpeed()), + inverted: self.editorAxisZInverted() + } + } + }; + + + return profile; + }; + + self.onSettingsShown = self.requestData; + self.onStartup = function(){ + console.log("lasercutter profiles onStartup"); + self.settings.laserCutterProfiles = self; + self.requestData; + }; +} + + + // view model class, identifier, parameters for constructor, container to bind to + ADDITIONAL_VIEWMODELS.push([LaserCutterProfilesViewModel, "laserCutterProfilesViewModel", + ["settingsViewModel"], + document.getElementById("laserCutterProfiles")]); + +}); diff --git a/src/octoprint/plugins/lasercutterprofiles/templates/lasercutterprofiles_settings.jinja2 b/src/octoprint/plugins/lasercutterprofiles/templates/lasercutterprofiles_settings.jinja2 new file mode 100644 index 00000000..9d95654b --- /dev/null +++ b/src/octoprint/plugins/lasercutterprofiles/templates/lasercutterprofiles_settings.jinja2 @@ -0,0 +1,135 @@ +

{{ _('Machine Profiles') }}

+
+ + + + + + + + + + + + + + + + +
{{ _('Name') }}{{ _('Model') }}{{ _('Action') }}
+  |  |  +
+ + + + + + + +
diff --git a/src/octoprint/plugins/svgtogcode/static/js/working_area.js b/src/octoprint/plugins/svgtogcode/static/js/working_area.js index 6c4554c4..c3dec03b 100644 --- a/src/octoprint/plugins/svgtogcode/static/js/working_area.js +++ b/src/octoprint/plugins/svgtogcode/static/js/working_area.js @@ -23,6 +23,8 @@ function WorkingAreaViewModel(params) { self.px2mm_factor = 1; // initial value self.hwRatio = ko.computed(function(){ // y/x = 297/216 respectively 594/432 +// var h = self.settings.laserCutterProfiles.currentProfileData().volume.depth(); +// var w = self.settings.laserCutterProfiles.currentProfileData().volume.width(); var h = self.settings.printerProfiles.currentProfileData().volume.depth(); var w = self.settings.printerProfiles.currentProfileData().volume.width(); var ratio = h / w; @@ -161,6 +163,7 @@ function WorkingAreaViewModel(params) { }; self.onStartup = function(){ + console.log("working_area_onstartup"); self.files.workingArea = self; $(window).resize(function(){ self.trigger_resize(); diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 53357fa4..ec7c1833 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -184,7 +184,7 @@ def index(): settings_entries = [ # (gettext("Printer"), None), (gettext("Serial Connection"), dict(template="dialogs/settings/serialconnection.jinja2", _div="settings_serialConnection", custom_bindings=False)), - (gettext("Machine Profiles"), dict(template="dialogs/settings/printerprofiles.jinja2", _div="settings_printerProfiles", custom_bindings=False)), +# (gettext("Machine Profiles"), dict(template="dialogs/settings/printerprofiles.jinja2", _div="settings_printerProfiles", custom_bindings=False)), # (gettext("Temperatures"), dict(template="dialogs/settings/temperatures.jinja2", _div="settings_temperature", custom_bindings=False)), (gettext("Terminal Filters"), dict(template="dialogs/settings/terminalfilters.jinja2", _div="settings_terminalFilters", custom_bindings=False)), # (gettext("Features"), None), @@ -677,7 +677,7 @@ class Server(): # enable debug logging to serial.log logging.getLogger("SERIAL").setLevel(logging.DEBUG) logging.getLogger("SERIAL").debug("Enabling serial logging") - + if __name__ == "__main__": server = Server() server.run() diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index d8777907..a62d0129 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -460,7 +460,7 @@ $(function() { // gcodeViewModel.initialize(); // ko.applyBindings(gcodeViewModel, gcode); // } - ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); +// ko.applyBindings(settingsViewModel, document.getElementById("settings_dialog")); // ko.applyBindings(navigationViewModel, document.getElementById("navbar")); // ko.applyBindings(appearanceViewModel, document.getElementsByTagName("head")[0]); // ko.applyBindings(printerStateViewModel, document.getElementById("drop_overlay")); diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index f20e2a8c..a3872f8e 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -115,6 +115,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel, printerProfilesV self.removeTerminalFilter = function(filter) { self.terminalFilters.remove(filter); + self.saveall(); }; self.onSettingsShown = function() { @@ -203,7 +204,6 @@ function SettingsViewModel(loginStateViewModel, usersViewModel, printerProfilesV self.temperature_profiles(response.temperature.profiles); self.system_actions(response.system.actions); - self.terminalFilters(response.terminalFilters); }; @@ -274,6 +274,8 @@ function SettingsViewModel(loginStateViewModel, usersViewModel, printerProfilesV }, "terminalFilters": self.terminalFilters() }); + + console.log("data", data.terminalFilters); return data; }; diff --git a/src/octoprint/templates/dialogs/settings.jinja2 b/src/octoprint/templates/dialogs/settings.jinja2 index add81d59..351b149a 100644 --- a/src/octoprint/templates/dialogs/settings.jinja2 +++ b/src/octoprint/templates/dialogs/settings.jinja2 @@ -1,7 +1,7 @@