diff --git a/src/octoprint/plugin/__init__.py b/src/octoprint/plugin/__init__.py index c0853599..d6214d62 100644 --- a/src/octoprint/plugin/__init__.py +++ b/src/octoprint/plugin/__init__.py @@ -264,13 +264,17 @@ class PluginSettings(object): :returns: The retrieved settings value. :rtype: object - .. method:: get_int(path) + .. method:: get_int(path, min=None, max=None) - Like :func:`get` but tries to convert the retrieved value to ``int``. + Like :func:`get` but tries to convert the retrieved value to ``int``. If ``min`` is provided and the retrieved + value is less than it, it will be returned instead of the value. Likewise for ``max`` - it will be returned if + the value is greater than it. - .. method:: get_float(path) + .. method:: get_float(path, min=None, max=None) - Like :func:`get` but tries to convert the retrieved value to ``float``. + Like :func:`get` but tries to convert the retrieved value to ``float``. If ``min`` is provided and the retrieved + value is less than it, it will be returned instead of the value. Likewise for ``max`` - it will be returned if + the value is greater than it. .. method:: get_boolean(path) @@ -286,13 +290,19 @@ class PluginSettings(object): :param boolean force: If set to True, the modified configuration will even be written back to disk if the value didn't change. - .. method:: set_int(path, value, force=False) + .. method:: set_int(path, value, force=False, min=None, max=None) Like :func:`set` but ensures the value is an ``int`` through attempted conversion before setting it. + If ``min`` and/or ``max`` are provided, it will also be ensured that the value is greater than or equal + to ``min`` and less than or equal to ``max``. If that is not the case, the limit value (``min`` if less than + that, ``max`` if greater than that) will be set instead. - .. method:: set_float(path, value, force=False) + .. method:: set_float(path, value, force=False, min=None, max=None) Like :func:`set` but ensures the value is an ``float`` through attempted conversion before setting it. + If ``min`` and/or ``max`` are provided, it will also be ensured that the value is greater than or equal + to ``min`` and less than or equal to ``max``. If that is not the case, the limit value (``min`` if less than + that, ``max`` if greater than that) will be set instead. .. method:: set_boolean(path, value, force=False) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index a30d9a45..65eefc9d 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -168,7 +168,9 @@ def getSettings(): }, "temperature": { "profiles": s.get(["temperature", "profiles"]), - "cutoff": s.getInt(["temperature", "cutoff"]) + "cutoff": s.getInt(["temperature", "cutoff"]), + "sendAutomatically": s.getBoolean(["temperature", "sendAutomatically"]), + "sendAutomaticallyAfter": s.getInt(["temperature", "sendAutomaticallyAfter"], min=0, max=30), }, "system": { "actions": s.get(["system", "actions"]), @@ -388,6 +390,8 @@ def _saveSettings(data): if "temperature" in data.keys(): if "profiles" in data["temperature"]: s.set(["temperature", "profiles"], data["temperature"]["profiles"]) if "cutoff" in data["temperature"]: s.setInt(["temperature", "cutoff"], data["temperature"]["cutoff"]) + if "sendAutomatically" in data["temperature"]: s.setBoolean(["temperature", "sendAutomatically"], data["temperature"]["sendAutomatically"]) + if "sendAutomaticallyAfter" in data["temperature"]: s.setInt(["temperature", "sendAutomaticallyAfter"], data["temperature"]["sendAutomaticallyAfter"], min=0, max=30) if "terminalFilters" in data.keys(): s.set(["terminalFilters"], data["terminalFilters"]) diff --git a/src/octoprint/settings.py b/src/octoprint/settings.py index 396bc0bc..221d35e7 100644 --- a/src/octoprint/settings.py +++ b/src/octoprint/settings.py @@ -231,7 +231,9 @@ default_settings = { {"name": "ABS", "extruder" : 210, "bed" : 100 }, {"name": "PLA", "extruder" : 180, "bed" : 60 } ], - "cutoff": 30 + "cutoff": 30, + "sendAutomatically": False, + "sendAutomaticallyAfter": 1, }, "printerProfiles": { "default": None @@ -1275,23 +1277,43 @@ class Settings(object): return None def getInt(self, path, **kwargs): + minimum = kwargs.pop("min", None) + maximum = kwargs.pop("max", None) + value = self.get(path, **kwargs) if value is None: return None try: - return int(value) + intValue = int(value) + + if minimum is not None and intValue < minimum: + return minimum + elif maximum is not None and intValue > maximum: + return maximum + else: + return intValue except ValueError: self._logger.warn("Could not convert %r to a valid integer when getting option %r" % (value, path)) return None def getFloat(self, path, **kwargs): + minimum = kwargs.pop("min", None) + maximum = kwargs.pop("max", None) + value = self.get(path, **kwargs) if value is None: return None try: - return float(value) + floatValue = float(value) + + if minimum is not None and floatValue < minimum: + return minimum + elif maximum is not None and floatValue > maximum: + return maximum + else: + return floatValue except ValueError: self._logger.warn("Could not convert %r to a valid integer when getting option %r" % (value, path)) return None @@ -1447,8 +1469,16 @@ class Settings(object): self.set(path, None, **kwargs) return + minimum = kwargs.pop("min", None) + maximum = kwargs.pop("max", None) + try: intValue = int(value) + + if minimum is not None and intValue < minimum: + intValue = minimum + if maximum is not None and intValue > maximum: + intValue = maximum except ValueError: self._logger.warn("Could not convert %r to a valid integer when setting option %r" % (value, path)) return @@ -1460,8 +1490,16 @@ class Settings(object): self.set(path, None, **kwargs) return + minimum = kwargs.pop("min", None) + maximum = kwargs.pop("max", None) + try: floatValue = float(value) + + if minimum is not None and floatValue < minimum: + floatValue = minimum + if maximum is not None and floatValue > maximum: + floatValue = maximum except ValueError: self._logger.warn("Could not convert %r to a valid integer when setting option %r" % (value, path)) return diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index a18bb272..15a34ac6 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -195,6 +195,8 @@ $(function() { self.temperature_profiles = ko.observableArray(undefined); self.temperature_cutoff = ko.observable(undefined); + self.temperature_sendAutomatically = ko.observable(undefined); + self.temperature_sendAutomaticallyAfter = ko.observable(undefined); self.system_actions = ko.observableArray([]); diff --git a/src/octoprint/static/js/app/viewmodels/temperature.js b/src/octoprint/static/js/app/viewmodels/temperature.js index 6b7ac5d6..43deff3a 100644 --- a/src/octoprint/static/js/app/viewmodels/temperature.js +++ b/src/octoprint/static/js/app/viewmodels/temperature.js @@ -454,11 +454,14 @@ $(function() { self.incrementTarget = function(item) { var value = item.newTarget(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) value = item.target(); + if (value === undefined || (typeof(value) == "string" && value.trim() == "")) { + value = item.target(); + } try { value = parseInt(value); if (value > 999) return; item.newTarget(value + 1); + self.autosendTarget(item); } catch (ex) { // do nothing } @@ -466,33 +469,69 @@ $(function() { self.decrementTarget = function(item) { var value = item.newTarget(); - if (value === undefined || (typeof(value) == "string" && value.trim() == "")) value = item.target(); + if (value === undefined || (typeof(value) == "string" && value.trim() == "")) { + value = item.target(); + } try { value = parseInt(value); if (value <= 0) return; item.newTarget(value - 1); + self.autosendTarget(item); } catch (ex) { // do nothing } }; + var _sendTimeout = {}; + + self.autosendTarget = function(item) { + if (!self.settingsViewModel.temperature_sendAutomatically()) return; + var delay = self.settingsViewModel.temperature_sendAutomaticallyAfter() * 1000; + + var name = item.name(); + if (_sendTimeout[name]) { + window.clearTimeout(_sendTimeout[name]); + } + _sendTimeout[name] = window.setTimeout(function() { + self.setTarget(item); + delete _sendTimeout[name]; + }, delay); + }; + + self.clearAutosendTarget = function(item) { + var name = item.name(); + if (_sendTimeout[name]) { + window.clearTimeout(_sendTimeout[name]); + delete _sendTimeout[name]; + } + }; + self.setTarget = function(item, form) { var value = item.newTarget(); - $(form).find("input").blur(); + if (form !== undefined) { + $(form).find("input").blur(); + } if (value === undefined || (typeof(value) == "string" && value.trim() == "")) return OctoPrintClient.createRejectedDeferred(); + + self.clearAutosendTarget(item); return self.setTargetToValue(item, value); }; self.setTargetFromProfile = function(item, profile) { if (!profile) return OctoPrintClient.createRejectedDeferred(); + + self.clearAutosendTarget(item); return self.setTargetToValue(item, (item.key() == "bed" ? profile.bed : profile.extruder)); }; self.setTargetToZero = function(item) { + self.clearAutosendTarget(item); return self.setTargetToValue(item, 0); }; self.setTargetToValue = function(item, value) { + self.clearAutosendTarget(item); + try { value = parseInt(value); } catch (ex) { diff --git a/src/octoprint/templates/dialogs/settings/temperatures.jinja2 b/src/octoprint/templates/dialogs/settings/temperatures.jinja2 index 5f4411a6..727eb0ed 100644 --- a/src/octoprint/templates/dialogs/settings/temperatures.jinja2 +++ b/src/octoprint/templates/dialogs/settings/temperatures.jinja2 @@ -1,5 +1,5 @@