Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
43499cbe9c | |||
ce10fc7e3b | |||
6699679ca2 | |||
988512f522 | |||
b03a045b24 | |||
6124bba2e7 | |||
554d78efb7 | |||
525a08c20e | |||
2cc132213f | |||
30ee76bf7e | |||
8b9b08f382 | |||
7612b7081c | |||
4df6a99238 | |||
acf3db65f6 | |||
18f009fa85 | |||
79bc20c81a | |||
05e2e10497 |
16
README.md
16
README.md
@ -1,10 +1,20 @@
|
||||
# OctoPrint-FanSpeedSlider
|
||||
# Fan Speed Slider Plugin
|
||||
|
||||
Adds a slider to control the speed of a parts cooling fan.
|
||||
Add a slider to control the speed of a parts cooling fan.
|
||||
|
||||

|
||||
|
||||
*Note: Slider does __not__ follow the speed of the fan. If the fan speed is set via gcode or an LCD panel on the printer, the slider will not respond to the change.*
|
||||
## Usage
|
||||
|
||||
Slide the slider, click the button. There really isn't much else to do :)
|
||||
|
||||
The default value of the slider is user configurable, this is the value that the slider will be set to upon loading OctoPrint's UI, and any time you refresh the page.
|
||||
|
||||
The minimum fan speed setting will limit how slow the fan runs, this is useful since some fans don't work below a certain speed.
|
||||
|
||||
The maximum fan speed setting will limit how fast the fan runs, this is useful if your fan is too strong, or you wish to limit the speed post-slice without having to re-slice your file.
|
||||
|
||||
*Note: Slider does __not__ follow the speed of the fan. If the fan speed is set via gcode or an LCD panel on the printer, the slider will not respond to the change. It is a __setting__, not an indicator, and functions the same way the feedrate and flowrate sliders do.*
|
||||
|
||||
## Setup
|
||||
|
||||
|
@ -1,22 +1,67 @@
|
||||
# coding=utf-8
|
||||
from __future__ import absolute_import
|
||||
|
||||
from decimal import *
|
||||
import re
|
||||
import octoprint.plugin
|
||||
|
||||
class FanSliderPlugin(octoprint.plugin.StartupPlugin,
|
||||
octoprint.plugin.TemplatePlugin,
|
||||
octoprint.plugin.SettingsPlugin,
|
||||
octoprint.plugin.AssetPlugin):
|
||||
|
||||
def get_assets(self):
|
||||
return dict(
|
||||
js=["js/fanslider.js"]
|
||||
)
|
||||
|
||||
def get_update_information(self):
|
||||
octoprint.plugin.TemplatePlugin,
|
||||
octoprint.plugin.SettingsPlugin,
|
||||
octoprint.plugin.AssetPlugin):
|
||||
|
||||
def on_after_startup(self):
|
||||
self.get_settings_updates()
|
||||
|
||||
def get_settings_defaults(self):
|
||||
return dict(
|
||||
defaultFanSpeed=100,
|
||||
minSpeed=0,
|
||||
maxSpeed=100
|
||||
)
|
||||
|
||||
def on_settings_save(self, data):
|
||||
octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
|
||||
self.get_settings_updates()
|
||||
|
||||
def get_assets(self):
|
||||
return dict(
|
||||
js=["js/fanslider.js"],
|
||||
css=["css/style.css"]
|
||||
)
|
||||
|
||||
def get_template_configs(self):
|
||||
return [
|
||||
dict(type="settings", custom_bindings=False)
|
||||
]
|
||||
|
||||
def get_settings_updates(self):
|
||||
self.defaultFanSpeed = self._settings.get(["defaultFanSpeed"])
|
||||
self.minSpeed = self._settings.get(["minSpeed"])
|
||||
self.maxSpeed = self._settings.get(["maxSpeed"])
|
||||
|
||||
getcontext().prec=5 #sets precision for "Decimal" not sure if this'll cause conflicts, ideas?
|
||||
self.minPWM = round( Decimal(self.minSpeed) * Decimal(2.55), 2 )
|
||||
self.maxPWM = round( Decimal(self.maxSpeed) * Decimal(2.55), 2 )
|
||||
|
||||
def rewrite_m106(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
|
||||
if gcode and gcode.startswith('M106'):
|
||||
fanPwm = re.search("S(\d+.\d+)", cmd)
|
||||
if fanPwm and fanPwm.group(1):
|
||||
fanPwm = fanPwm.group(1)
|
||||
if Decimal(fanPwm) < self.minPWM and Decimal(fanPwm) != 0:
|
||||
self._logger.info("fan pwm value " + str(fanPwm) + " is below threshold, increasing to " + str(self.minPWM))
|
||||
cmd = "M106 S" + str(self.minPWM)
|
||||
return cmd,
|
||||
elif Decimal(fanPwm) > self.maxPWM:
|
||||
self._logger.info("fan pwm value " + str(fanPwm) + " is above threshold, decreasing to " + str(self.maxPWM))
|
||||
cmd = "M106 S" + str(self.maxPWM)
|
||||
return cmd,
|
||||
|
||||
def get_update_information(self):
|
||||
return dict(
|
||||
fanspeedslider=dict(
|
||||
displayName="Fan Speed Slider",
|
||||
displayName="Fan Speed Control",
|
||||
displayVersion=self._plugin_version,
|
||||
|
||||
# version check: github repository
|
||||
@ -29,8 +74,15 @@ class FanSliderPlugin(octoprint.plugin.StartupPlugin,
|
||||
pip="https://github.com/ntoff/OctoPrint-FanSpeedSlider/archive/{target_version}.zip"
|
||||
)
|
||||
)
|
||||
__plugin_name__ = "Fan Speed Slider"
|
||||
__plugin_implementation__ = FanSliderPlugin()
|
||||
__plugin_hooks__ = {
|
||||
|
||||
__plugin_name__ = "Fan Speed Control"
|
||||
|
||||
def __plugin_load__():
|
||||
global __plugin_implementation__
|
||||
__plugin_implementation__ = __plugin_implementation__ = FanSliderPlugin()
|
||||
|
||||
global __plugin_hooks__
|
||||
__plugin_hooks__ = {
|
||||
"octoprint.comm.protocol.gcode.queuing": __plugin_implementation__.rewrite_m106,
|
||||
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information
|
||||
}
|
20
octoprint_fanspeedslider/static/css/style.css
Normal file
20
octoprint_fanspeedslider/static/css/style.css
Normal file
@ -0,0 +1,20 @@
|
||||
#touch body #control #control-fan-slider {
|
||||
padding: 30px 0 15px 15px;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#touch body #control #control-fan-slider button, #touch body #control #control-fan-slider input {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 20px;
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
height: auto;
|
||||
-ms-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div#settings_plugin_fanspeedslider div {
|
||||
width: 80%;
|
||||
}
|
@ -1,49 +1,64 @@
|
||||
/*
|
||||
*
|
||||
*
|
||||
* Author: ntoff
|
||||
* License: AGPLv3
|
||||
*/
|
||||
$(function() {
|
||||
|
||||
function FanSliderPluginViewModel(parameters) {
|
||||
function FanSliderPluginViewModel(parameters) {
|
||||
var self = this;
|
||||
|
||||
self.printerstate = parameters[0];
|
||||
self.loginstate = parameters[1];
|
||||
self.control = parameters[2]
|
||||
|
||||
fanSpeed = ko.observable(255);
|
||||
//convert 0 - 255 to 0 - 100% for the button
|
||||
fanPercent = ko.pureComputed(function () {
|
||||
return Math.floor(fanSpeed() /255 * 100);
|
||||
self.settings = parameters[0];
|
||||
self.control = parameters[1];
|
||||
self.loginState = parameters[2];
|
||||
|
||||
fanSpeed = ko.observable(undefined);
|
||||
|
||||
//convert percentage into PWM
|
||||
self.fanPWM = ko.pureComputed(function () {
|
||||
self.speed = fanSpeed() * 255 / 100 //don't forget to limit this to 2 decimal places at some point.
|
||||
return self.speed;
|
||||
});
|
||||
//set fan speed
|
||||
//send gcode to set fan speed
|
||||
sendFanSpeed = function () {
|
||||
self.control.sendCustomCommand({ command: "M106 S" + fanSpeed() });
|
||||
};
|
||||
self.control.sendCustomCommand({ command: "M106 S" + self.fanPWM() });
|
||||
};
|
||||
//extra classes
|
||||
$("#control > div.jog-panel").eq(0).addClass("controls");
|
||||
$("#control > div.jog-panel").eq(1).addClass("tools");
|
||||
$("#control > div.jog-panel").eq(2).addClass("general");
|
||||
|
||||
//add ID to buttons
|
||||
$("#control > div.general").find("button").eq(0).attr("id", "motors-off");
|
||||
$("#control > div.general").find("button").eq(1).attr("id", "fan-on");
|
||||
$("#control > div.general").find("button").eq(2).attr("id", "fan-off");
|
||||
|
||||
//remove original fan on/off buttons
|
||||
$("#fan-on").remove();
|
||||
$("#fan-off").remove();
|
||||
//add new fan controls
|
||||
$("#control > div.jog-panel.general").find("button").eq(0).before("<input type=\"number\" style=\"width: 90px\" data-bind=\"slider: {min: 00, max: 255, step: 1, value: fanSpeed, tooltip: 'hide'}\">\
|
||||
<button class=\"btn btn-block control-box\" data-bind=\"enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { sendFanSpeed() }\">" + gettext("Fan on") + ":<span data-bind=\"text: fanPercent() + '%'\"></span></button>\
|
||||
<button class=\"btn btn-block control-box\" data-bind=\"enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendCustomCommand({ type: 'command', commands: ['M106 S0'] }) }\">" + gettext("Fan off") + "</button>");
|
||||
|
||||
$("#control > div.jog-panel").eq(2).addClass("general");
|
||||
//If !TouchUI then remove standard buttons + add slider + new buttons
|
||||
if ($("#touch body").length ==0 ) {
|
||||
//add ID to buttons
|
||||
$("#control > div.general").find("button").eq(0).attr("id", "motors-off");
|
||||
$("#control > div.general").find("button").eq(1).attr("id", "fan-on");
|
||||
$("#control > div.general").find("button").eq(2).attr("id", "fan-off");
|
||||
//remove original fan on/off buttons
|
||||
$("#fan-on").remove();
|
||||
$("#fan-off").remove();
|
||||
//add new fan controls
|
||||
$("#control > div.jog-panel.general").find("button").eq(0).before("\
|
||||
<input type=\"number\" style=\"width: 90px\" data-bind=\"slider: {min: 00, max: 100, step: 1, value: fanSpeed, tooltip: 'hide'}\">\
|
||||
<button class=\"btn btn-block control-box\" data-bind=\"enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { sendFanSpeed() }\">" + gettext("Fan on") + ":<span data-bind=\"text: fanSpeed() + '%'\"></span></button>\
|
||||
<button class=\"btn btn-block control-box\" data-bind=\"enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { $root.sendCustomCommand({ type: 'command', commands: ['M106 S0'] }) }\">" + gettext("Fan off") + "</button>\
|
||||
");
|
||||
} else { //if TouchUI is active we only add the speed input + fan on button in a new section.
|
||||
console.log("Fan Speed Slider: NOTICE! TouchUI is active, adding simplified control.");
|
||||
$("#control > div.jog-panel.general").after("\
|
||||
<div id=\"control-fan-slider\" class=\"jog-panel filament\" data-bind=\"visible: loginState.isUser\">\
|
||||
<div>\
|
||||
<input type=\"number\" style=\"width: 150px\" data-bind=\"slider: {min: 00, max: 255, step: 1, value: fanSpeed, tooltip: 'hide'}\">\
|
||||
<button class=\"btn btn-block control-box\" data-bind=\"enable: isOperational() && !isPrinting() && loginState.isUser(), click: function() { sendFanSpeed() }\">" + gettext("Fan Speed(%)") + "</button>\
|
||||
</div>\
|
||||
</div>\
|
||||
");
|
||||
}
|
||||
//retrieve settings
|
||||
self.onBeforeBinding = function() {
|
||||
fanSpeed(self.settings.settings.plugins.fanspeedslider.defaultFanSpeed());
|
||||
}
|
||||
}
|
||||
OCTOPRINT_VIEWMODELS.push([
|
||||
FanSliderPluginViewModel,
|
||||
|
||||
["printerStateViewModel", "loginStateViewModel", "controlViewModel"]
|
||||
]);
|
||||
["settingsViewModel", "controlViewModel", "loginStateViewModel"]
|
||||
]);
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
<h3>{{ _('Fan Speed Control') }}</h3>
|
||||
<div>
|
||||
<form class="form-horizontal">
|
||||
<p>{{ _('Set the default value for the speed slider.') }}</p>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Default Value') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" min="0" max="100" class="input-mini" data-bind="value: settings.plugins.fanspeedslider.defaultFanSpeed">
|
||||
<span class="add-on">%</span>
|
||||
</div>
|
||||
<span class="help-block">{{ _('The default value the slider will be set to when opening OctoPrint\'s UI') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ _('The settings below can be used to limit the fan\'s output without having to re-slice and re-upload your gcode.') }}</p>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Minimum Speed') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" min="0" max="100" class="input-mini" data-bind="value: settings.plugins.fanspeedslider.minSpeed">
|
||||
<span class="add-on">%</span>
|
||||
</div>
|
||||
<span class="help-block">{{ _('Any value sent which is below this value will be increased to meet this speed. Useful if your fan doesn\'t work below a certain threshold.') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Maximum Speed') }}</label>
|
||||
<div class="controls">
|
||||
<div class="input-append">
|
||||
<input type="number" min="0" max="100" class="input-mini" data-bind="value: settings.plugins.fanspeedslider.maxSpeed">
|
||||
<span class="add-on">%</span>
|
||||
</div>
|
||||
<span class="help-block">{{ _('Any value sent which is above this value will be decreased to meet this speed. Useful if your fan is too strong on full speed.') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p><i>{{ _('NOTE: The min/max setting has no effect when you are printing from an SD card that is attached directly to the printer as the gcode does not pass through OctoPrint.') }}</i></p>
|
||||
</form>
|
||||
</div>
|
2
setup.py
2
setup.py
@ -14,7 +14,7 @@ plugin_package = "octoprint_fanspeedslider"
|
||||
plugin_name = "OctoPrint-FanSpeedSlider"
|
||||
|
||||
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
|
||||
plugin_version = "0.1.0"
|
||||
plugin_version = "0.1.3"
|
||||
|
||||
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
|
||||
# module
|
||||
|
Reference in New Issue
Block a user