Option to automatically send temperature fine adjustments

As suggested by a loyal Patron ;)
This commit is contained in:
Gina Häußge 2017-07-04 16:39:03 +02:00
parent 8ad165c625
commit b2c1bb4b53
7 changed files with 129 additions and 17 deletions

View file

@ -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)

View file

@ -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"])

View file

@ -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

View file

@ -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([]);

View file

@ -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) {

View file

@ -1,5 +1,5 @@
<form class="form-horizontal">
<h3>{{ _('Temperature Graph') }}</h3>
<h3>{{ _('Graph') }}</h3>
<div class="control-group">
<label class="control-label">{{ _('Graph cutoff') }}</label>
<div class="controls">
@ -10,7 +10,26 @@
<span class="help-block">{{ _('Needs a restart of OctoPrint to become active.') }}</span>
</div>
</div>
<h3>{{ _('Temperature Presets') }}</h3>
<h3>{{ _('Fine adjustments') }}</h3>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: temperature_sendAutomatically"> {{ _('Send temperature fine adjustments automatically') }}
</label>
<span class="help-block">{{ _('Enable this to have temperature fine adjustments you do via the + or - button be sent to the printer automatically.') }}</span>
</div>
</div>
<div class="control-group" data-bind="visible: temperature_sendAutomatically">
<label class="control-label">{{ _('Sending delay') }}</label>
<div class="controls">
<div class="input-append">
<input type="number" class="input-mini" min="0" max="30" data-bind="value: temperature_sendAutomaticallyAfter">
<span class="add-on">sec</span>
</div>
<span class="help-block">{{ _('OctoPrint will use this delay to limit the number of sent temperature commands should you perform multiple fine adjustments in a short time.') }}</span>
</div>
</div>
<h3>{{ _('Presets') }}</h3>
<div class="row-fluid">
<div class="offset4 span3"><h4>{{ _('Extruder') }}</h4></div>
<div class="span3"><h4>{{ _('Bed') }}</h4></div>

View file

@ -24,10 +24,10 @@
<td class="temperature_target">
<form class="form-inline" style="margin:0" data-bind="submit: function(element) { $root.setTarget($data, element) }">
<div class="input-prepend input-append">
<button type="button" class="btn btn-input-dec" data-bind="click: $root.decrementTarget, enable: $root.isOperational() && $root.loginState.isUser()" title="{{ _('-1') }}"><i class="fa fa-minus"></i></button>
<button type="button" class="btn btn-input-dec" data-bind="click: $root.decrementTarget, enable: $root.isOperational() && $root.loginState.isUser()" title="{{ _('Fine adjust: -1°C') }}"><i class="fa fa-minus"></i></button>
<input type="number" min="0" max="999" class="input-mini input-nospin" style="width: 30px" data-bind="attr: {placeholder: cleanTemperature(target())}, value: newTarget, valueUpdate: 'input', enable: $root.isOperational() && $root.loginState.isUser(), event: { focus: function(d, e) {$root.handleFocus(e, 'target', $data) } }">
<span class="add-on">&deg;C</span>
<button type="button" class="btn btn-input-inc" data-bind="click: $root.incrementTarget, enable: $root.isOperational() && $root.loginState.isUser()" title="{{ _('+1') }}"><i class="fa fa-plus"></i></button>
<button type="button" class="btn btn-input-inc" data-bind="click: $root.incrementTarget, enable: $root.isOperational() && $root.loginState.isUser()" title="{{ _('Fine adjust: +1°C') }}"><i class="fa fa-plus"></i></button>
</div>
<div class="btn-group">
<button type="submit" data-bind="enable: $root.isOperational() && $root.loginState.isUser() && $data.newTargetValid()" class="btn btn-primary" title="{{ _('Set') }}"><i class="fa fa-check"></i></button>