├── .gitignore ├── octoprint_BetterHeaterTimeout ├── static │ ├── css │ │ └── BetterHeaterTimeout.css │ ├── less │ │ └── BetterHeaterTimeout.less │ └── js │ │ └── BetterHeaterTimeout.js ├── templates │ └── BetterHeaterTimeout_settings.jinja2 └── __init__.py ├── babel.cfg ├── requirements.txt ├── MANIFEST.in ├── .editorconfig ├── extras ├── README.txt └── BetterHeaterTimeout.md ├── translations └── README.txt ├── README.md └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | .idea 4 | *.iml 5 | build 6 | dist 7 | *.egg* 8 | .DS_Store 9 | *.zip 10 | -------------------------------------------------------------------------------- /octoprint_BetterHeaterTimeout/static/css/BetterHeaterTimeout.css: -------------------------------------------------------------------------------- 1 | /* TODO: Have your plugin's CSS files generated to here. */ 2 | -------------------------------------------------------------------------------- /octoprint_BetterHeaterTimeout/static/less/BetterHeaterTimeout.less: -------------------------------------------------------------------------------- 1 | // TODO: Put your plugin's LESS here, have it generated to ../css. 2 | -------------------------------------------------------------------------------- /babel.cfg: -------------------------------------------------------------------------------- 1 | [python: */**.py] 2 | [jinja2: */**.jinja2] 3 | extensions=jinja2.ext.autoescape, jinja2.ext.with_ 4 | 5 | [javascript: */**.js] 6 | extract_messages = gettext, ngettext 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ### 2 | # This file is only here to make sure that something like 3 | # 4 | # pip install -e . 5 | # 6 | # works as expected. Requirements can be found in setup.py. 7 | ### 8 | 9 | . 10 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include octoprint_BetterHeaterTimeout/templates * 3 | recursive-include octoprint_BetterHeaterTimeout/translations * 4 | recursive-include octoprint_BetterHeaterTimeout/static * 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [**.py] 13 | indent_style = tab 14 | 15 | [**.js] 16 | indent_style = space 17 | indent_size = 4 18 | -------------------------------------------------------------------------------- /extras/README.txt: -------------------------------------------------------------------------------- 1 | Currently Cookiecutter generates the following helpful extras to this folder: 2 | 3 | BetterHeaterTimeout.md 4 | Data file for plugins.octoprint.org. Fill in the missing TODOs once your 5 | plugin is ready for release and file a PR as described at 6 | http://plugins.octoprint.org/help/registering/ to get it published. 7 | 8 | This folder may be safely removed if you don't need it. 9 | -------------------------------------------------------------------------------- /octoprint_BetterHeaterTimeout/static/js/BetterHeaterTimeout.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | function BetterHeaterTimeoutViewModel(parameters) { 3 | var self = this; 4 | 5 | self.onDataUpdaterPluginMessage = (plugin, { event, payload }) => { 6 | if(plugin !== "BetterHeaterTimeout") return; 7 | 8 | if(event === "HeaterTimeout") 9 | return new PNotify({ 10 | title: "HeaterTimeout", 11 | text: `Heater '${payload.heater}' disabled after ${payload.time_elapsed} seconds.`, 12 | }); 13 | } 14 | } 15 | 16 | OCTOPRINT_VIEWMODELS.push({ 17 | construct: BetterHeaterTimeoutViewModel, 18 | dependencies: [], 19 | elements: [] 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /translations/README.txt: -------------------------------------------------------------------------------- 1 | Your plugin's translations will reside here. The provided setup.py supports a 2 | couple of additional commands to make managing your translations easier: 3 | 4 | babel_extract 5 | Extracts any translateable messages (marked with Jinja's `_("...")` or 6 | JavaScript's `gettext("...")`) and creates the initial `messages.pot` file. 7 | babel_refresh 8 | Reruns extraction and updates the `messages.pot` file. 9 | babel_new --locale= 10 | Creates a new translation folder for locale ``. 11 | babel_compile 12 | Compiles the translations into `mo` files, ready to be used within 13 | OctoPrint. 14 | babel_pack --locale= [ --author= ] 15 | Packs the translation for locale `` up as an installable 16 | language pack that can be manually installed by your plugin's users. This is 17 | interesting for languages you can not guarantee to keep up to date yourself 18 | with each new release of your plugin and have to depend on contributors for. 19 | 20 | If you want to bundle translations with your plugin, create a new folder 21 | `octoprint_BetterHeaterTimeout/translations`. When that folder exists, 22 | an additional command becomes available: 23 | 24 | babel_bundle --locale= 25 | Moves the translation for locale `` to octoprint_BetterHeaterTimeout/translations, 26 | effectively bundling it with your plugin. This is interesting for languages 27 | you can guarantee to keep up to date yourself with each new release of your 28 | plugin. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OctoPrint-BetterHeaterTimeout 2 | 3 | Turns off heaters after specified time being on and unused. 4 | 5 | *Note: this plugin has not been tested with versions under 1.3.10; they may not work!* 6 | 7 | ## Advantages: 8 | - Supports configuring timeout length 9 | - Supports before/after gcode 10 | - Supports notification via Web UI 11 | - Fires a custom event _*_ when the heaters timeout, so one can be notified or otherwise hook on the event. 12 | 13 | _*May not be (officially) supported; see [OctoPrint#2965](https://github.com/foosel/OctoPrint/issues/2965) for more info._ 14 | 15 | ## Setup 16 | 17 | Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wiki/Plugin:-Plugin-Manager) 18 | or manually using this URL: 19 | 20 | https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout/archive/master.zip 21 | 22 | If you want to trigger on the custom event, use the event name `HeaterTimeout`. 23 | The payload values are `heater`, `time_elapsed`, and `timeout`. 24 | 25 | 26 | ## Configuration 27 | 28 | The checkbox enables/disables the timeout, and the number input changes the timeout length. 29 | 30 | ### After target temp changes vs after heating starts 31 | 32 | If set to the former, changing the target temp will reset the timeout. 33 | 34 | ### Before/after GCODE 35 | 36 | GCODE commands to run before/after the heaters are disabled. 37 | You can use the placeholders `$heater`, `$time_elapsed`. and `$timeout`. 38 | I think the names are pretty self-explanatory. 39 | 40 | **Examples:** 41 | ``` 42 | M117 $heater timed out ; display that on the screen 43 | ``` 44 | ``` 45 | M300 S100 P200 ; chirp 46 | ``` 47 | -------------------------------------------------------------------------------- /octoprint_BetterHeaterTimeout/templates/BetterHeaterTimeout_settings.jinja2: -------------------------------------------------------------------------------- 1 |
2 |

{{ _('Timeout') }}

3 |
4 | 8 |
9 |
10 | 11 | s 12 |
13 | 20 |
21 |
22 |
23 | 26 |
27 |
28 | 29 | s 30 |
31 |
32 |
33 |

{{ _('GCODEs') }}

34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | 42 |
43 | 44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /extras/BetterHeaterTimeout.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: plugin 3 | 4 | id: BetterHeaterTimeout 5 | title: OctoPrint-BetterHeaterTimeout 6 | description: Turns off heaters after specified time being on and unused 7 | author: T6 8 | license: AGPLv3 9 | 10 | # TODO 11 | date: today's date in format YYYY-MM-DD, e.g. 2015-04-21 12 | 13 | homepage: https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout 14 | source: https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout 15 | archive: https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout/archive/master.zip 16 | 17 | # TODO 18 | # Set this to true if your plugin uses the dependency_links setup parameter to include 19 | # library versions not yet published on PyPi. SHOULD ONLY BE USED IF THERE IS NO OTHER OPTION! 20 | #follow_dependency_links: false 21 | 22 | # TODO 23 | tags: 24 | - a list 25 | - of tags 26 | - that apply 27 | - to your plugin 28 | - (take a look at the existing plugins for what makes sense here) 29 | 30 | # TODO 31 | screenshots: 32 | - url: url of a screenshot, /assets/img/... 33 | alt: alt-text of a screenshot 34 | caption: caption of a screenshot 35 | - url: url of another screenshot, /assets/img/... 36 | alt: alt-text of another screenshot 37 | caption: caption of another screenshot 38 | - ... 39 | 40 | # TODO 41 | featuredimage: url of a featured image for your plugin, /assets/img/... 42 | 43 | # TODO 44 | # You only need the following if your plugin requires specific OctoPrint versions or 45 | # specific operating systems to function - you can safely remove the whole 46 | # "compatibility" block if this is not the case. 47 | 48 | compatibility: 49 | 50 | # List of compatible versions 51 | # 52 | # A single version number will be interpretated as a minimum version requirement, 53 | # e.g. "1.3.1" will show the plugin as compatible to OctoPrint versions 1.3.1 and up. 54 | # More sophisticated version requirements can be modelled too by using PEP440 55 | # compatible version specifiers. 56 | # 57 | # You can also remove the whole "octoprint" block. Removing it will default to all 58 | # OctoPrint versions being supported. 59 | 60 | octoprint: 61 | - 1.2.0 62 | 63 | # List of compatible operating systems 64 | # 65 | # Valid values: 66 | # 67 | # - windows 68 | # - linux 69 | # - macos 70 | # - freebsd 71 | # 72 | # There are also two OS groups defined that get expanded on usage: 73 | # 74 | # - posix: linux, macos and freebsd 75 | # - nix: linux and freebsd 76 | # 77 | # You can also remove the whole "os" block. Removing it will default to all 78 | # operating systems being supported. 79 | 80 | os: 81 | - linux 82 | - windows 83 | - macos 84 | - freebsd 85 | 86 | --- 87 | 88 | **TODO**: Longer description of your plugin, configuration examples etc. This part will be visible on the page at 89 | http://plugins.octoprint.org/plugin/BetterHeaterTimeout/ 90 | -------------------------------------------------------------------------------- /octoprint_BetterHeaterTimeout/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import 3 | 4 | import octoprint.plugin 5 | 6 | class BetterHeaterTimeoutPlugin( 7 | octoprint.plugin.SettingsPlugin, 8 | octoprint.plugin.TemplatePlugin, 9 | octoprint.plugin.AssetPlugin, 10 | octoprint.printer.PrinterCallback, 11 | ): 12 | def initialize(self): 13 | self._printer.register_callback(self); 14 | self._temp_statuses = dict(); 15 | 16 | def on_printer_add_temperature(self, data): 17 | if not self._settings.get(["enabled"]) or not self._printer.is_ready(): 18 | self._temp_statuses = dict(); 19 | 20 | time = data["time"] 21 | 22 | for key in data: 23 | if key == "time": 24 | continue 25 | 26 | target = data[key]["target"] 27 | 28 | if not target: 29 | if key in self._temp_statuses: 30 | del self._temp_statuses[key] 31 | else: 32 | if key in self._temp_statuses: 33 | if self._settings.get(["since_change"]) and target != self._temp_statuses[key]["temp"]: 34 | self._temp_statuses[key] = dict( 35 | start=time, 36 | temp=target, 37 | ) 38 | else: 39 | time_elapsed = time - self._temp_statuses[key]["start"] 40 | timeout = self._settings.get_float(["bedTimeout" if key == "bed" else "timeout"]) 41 | if time_elapsed >= timeout: 42 | def send_gcode_lines(setting_name): 43 | self._printer.commands(self._settings.get([setting_name]) \ 44 | .replace("$heater", key) \ 45 | .replace("$time_elapsed", str(time_elapsed)) \ 46 | .replace("$timeout", str(timeout)) \ 47 | .split("\n")) 48 | 49 | send_gcode_lines("before_gcode"); 50 | self._printer.set_temperature(key, 0); 51 | payload = dict( 52 | heater=key, 53 | time_elapsed=time_elapsed, 54 | timeout=timeout, 55 | ) 56 | self._event_bus.fire("HeaterTimeout", payload) 57 | self._plugin_manager.send_plugin_message(self._identifier, dict(event="HeaterTimeout", payload=payload)) 58 | send_gcode_lines("after_gcode"); 59 | else: 60 | self._temp_statuses[key] = dict( 61 | start=time, 62 | temp=target, 63 | ) 64 | 65 | def get_template_configs(self): 66 | return [dict(type="settings", custom_bindings=False)] 67 | 68 | def get_assets(self): 69 | return dict(js=['js/BetterHeaterTimeout.js']) 70 | 71 | def get_settings_defaults(self): 72 | return dict( 73 | bedTimeout=600, 74 | timeout=600, 75 | enabled=True, 76 | since_change=True, 77 | before_gcode='', 78 | after_gcode='M117 $heater timed out', 79 | ) 80 | 81 | def get_settings_version(self): 82 | return 2 83 | 84 | def get_update_information(self): 85 | return dict( 86 | BetterHeaterTimeout=dict( 87 | displayName="BetterHeaterTimeout", 88 | displayVersion=self._plugin_version, 89 | 90 | type="github_release", 91 | user="tjjfvi", 92 | repo="OctoPrint-BetterHeaterTimeout", 93 | current=self._plugin_version, 94 | 95 | pip="https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout/archive/{target_version}.zip" 96 | ) 97 | ) 98 | 99 | 100 | __plugin_name__ = "BetterHeaterTimeout" 101 | __plugin_pythoncompat__ = ">=2.7,<4" 102 | 103 | def __plugin_load__(): 104 | global __plugin_implementation__ 105 | __plugin_implementation__ = BetterHeaterTimeoutPlugin() 106 | 107 | global __plugin_hooks__ 108 | __plugin_hooks__ = { 109 | "octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information 110 | } 111 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | ######################################################################################################################## 4 | ### Do not forget to adjust the following variables to your own plugin. 5 | 6 | # The plugin's identifier, has to be unique 7 | plugin_identifier = "BetterHeaterTimeout" 8 | 9 | # The plugin's python package, should be "octoprint_", has to be unique 10 | plugin_package = "octoprint_BetterHeaterTimeout" 11 | 12 | # The plugin's human readable name. Can be overwritten within OctoPrint's internal data via __plugin_name__ in the 13 | # plugin module 14 | plugin_name = "OctoPrint-BetterHeaterTimeout" 15 | 16 | # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module 17 | plugin_version = "1.3.0" 18 | 19 | # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin 20 | # module 21 | plugin_description = """Turns off heaters after specified time being on and unused""" 22 | 23 | # The plugin's author. Can be overwritten within OctoPrint's internal data via __plugin_author__ in the plugin module 24 | plugin_author = "T6" 25 | 26 | # The plugin's author's mail address. 27 | plugin_author_email = "t6@t6.fyi" 28 | 29 | # The plugin's homepage URL. Can be overwritten within OctoPrint's internal data via __plugin_url__ in the plugin module 30 | plugin_url = "https://github.com/tjjfvi/OctoPrint-BetterHeaterTimeout" 31 | 32 | # The plugin's license. Can be overwritten within OctoPrint's internal data via __plugin_license__ in the plugin module 33 | plugin_license = "AGPLv3" 34 | 35 | # Any additional requirements besides OctoPrint should be listed here 36 | plugin_requires = [] 37 | 38 | ### -------------------------------------------------------------------------------------------------------------------- 39 | ### More advanced options that you usually shouldn't have to touch follow after this point 40 | ### -------------------------------------------------------------------------------------------------------------------- 41 | 42 | # Additional package data to install for this plugin. The subfolders "templates", "static" and "translations" will 43 | # already be installed automatically if they exist. Note that if you add something here you'll also need to update 44 | # MANIFEST.in to match to ensure that python setup.py sdist produces a source distribution that contains all your 45 | # files. This is sadly due to how python's setup.py works, see also http://stackoverflow.com/a/14159430/2028598 46 | plugin_additional_data = [] 47 | 48 | # Any additional python packages you need to install with your plugin that are not contained in .* 49 | plugin_additional_packages = [] 50 | 51 | # Any python packages within .* you do NOT want to install with your plugin 52 | plugin_ignored_packages = [] 53 | 54 | # Additional parameters for the call to setuptools.setup. If your plugin wants to register additional entry points, 55 | # define dependency links or other things like that, this is the place to go. Will be merged recursively with the 56 | # default setup parameters as provided by octoprint_setuptools.create_plugin_setup_parameters using 57 | # octoprint.util.dict_merge. 58 | # 59 | # Example: 60 | # plugin_requires = ["someDependency==dev"] 61 | # additional_setup_parameters = {"dependency_links": ["https://github.com/someUser/someRepo/archive/master.zip#egg=someDependency-dev"]} 62 | additional_setup_parameters = {} 63 | 64 | ######################################################################################################################## 65 | 66 | from setuptools import setup 67 | 68 | try: 69 | import octoprint_setuptools 70 | except: 71 | print("Could not import OctoPrint's setuptools, are you sure you are running that under " 72 | "the same python installation that OctoPrint is installed under?") 73 | import sys 74 | sys.exit(-1) 75 | 76 | setup_parameters = octoprint_setuptools.create_plugin_setup_parameters( 77 | identifier=plugin_identifier, 78 | package=plugin_package, 79 | name=plugin_name, 80 | version=plugin_version, 81 | description=plugin_description, 82 | author=plugin_author, 83 | mail=plugin_author_email, 84 | url=plugin_url, 85 | license=plugin_license, 86 | requires=plugin_requires, 87 | additional_packages=plugin_additional_packages, 88 | ignored_packages=plugin_ignored_packages, 89 | additional_data=plugin_additional_data 90 | ) 91 | 92 | if len(additional_setup_parameters): 93 | from octoprint.util import dict_merge 94 | setup_parameters = dict_merge(setup_parameters, additional_setup_parameters) 95 | 96 | setup(**setup_parameters) 97 | --------------------------------------------------------------------------------