From 1b8b0687d5f393073f68dda2145403d67a88e71c Mon Sep 17 00:00:00 2001 From: jneilliii Date: Sun, 11 Aug 2019 16:33:04 -0400 Subject: [PATCH] 0.8.6 **Added** - configurable color options for the navbar icons. - countdown timer that utilizes the backlog command with delay to allow for powering off a plug after the pi has been shutdown using the system command option. - polling option to check status based on configured interval. --- octoprint_tasmota/__init__.py | 104 ++++--- octoprint_tasmota/static/css/tasmota.css | 9 +- octoprint_tasmota/static/js/tasmota.js | 274 ++++++++++-------- .../templates/tasmota_navbar.jinja2 | 2 +- .../templates/tasmota_settings.jinja2 | 30 +- setup.py | 2 +- 6 files changed, 252 insertions(+), 169 deletions(-) diff --git a/octoprint_tasmota/__init__.py b/octoprint_tasmota/__init__.py index b3a6544..5b99bed 100644 --- a/octoprint_tasmota/__init__.py +++ b/octoprint_tasmota/__init__.py @@ -13,17 +13,17 @@ import threading class tasmotaPlugin(octoprint.plugin.SettingsPlugin, - octoprint.plugin.AssetPlugin, - octoprint.plugin.TemplatePlugin, + octoprint.plugin.AssetPlugin, + octoprint.plugin.TemplatePlugin, octoprint.plugin.SimpleApiPlugin, octoprint.plugin.StartupPlugin): - + def __init__(self): self._logger = logging.getLogger("octoprint.plugins.tasmota") self._tasmota_logger = logging.getLogger("octoprint.plugins.tasmota.debug") - + ##~~ StartupPlugin mixin - + def on_startup(self, host, port): # setup customized logger from octoprint.logging.handlers import CleaningTimedRotatingFileHandler @@ -34,20 +34,22 @@ def on_startup(self, host, port): self._tasmota_logger.addHandler(tasmota_logging_handler) self._tasmota_logger.setLevel(logging.DEBUG if self._settings.get_boolean(["debug_logging"]) else logging.INFO) self._tasmota_logger.propagate = False - + def on_after_startup(self): self._logger.info("Tasmota loaded!") - + ##~~ SettingsPlugin mixin - + def get_settings_defaults(self): return dict( singleRelay = True, debug_logging = False, - arrSmartplugs = [{'ip':'','displayWarning':True,'idx':'1','warnPrinting':False,'gcodeEnabled':False,'gcodeOnDelay':0,'gcodeOffDelay':0,'autoConnect':True,'autoConnectDelay':10.0,'autoDisconnect':True,'autoDisconnectDelay':0,'sysCmdOn':False,'sysRunCmdOn':'','sysCmdOnDelay':0,'sysCmdOff':False,'sysRunCmdOff':'','sysCmdOffDelay':0,'currentState':'unknown','btnColor':'#808080','username':'admin','password':'','icon':'icon-bolt','label':''}], + polling_enabled = False, + polling_interval = 0, + arrSmartplugs = [{'ip':'','displayWarning':True,'idx':'1','warnPrinting':False,'gcodeEnabled':False,'gcodeOnDelay':0,'gcodeOffDelay':0,'autoConnect':True,'autoConnectDelay':10.0,'autoDisconnect':True,'autoDisconnectDelay':0,'sysCmdOn':False,'sysRunCmdOn':'','sysCmdOnDelay':0,'sysCmdOff':False,'sysRunCmdOff':'','sysCmdOffDelay':0,'currentState':'unknown','username':'admin','password':'','icon':'icon-bolt','label':'','on_color':'#00FF00','off_color':'#FF0000','unknown_color':'#808080','use_backlog':False,'backlog_on_delay':0,'backlog_off_delay':0}], ) - - def on_settings_save(self, data): + + def on_settings_save(self, data): old_debug_logging = self._settings.get_boolean(["debug_logging"]) octoprint.plugin.SettingsPlugin.on_settings_save(self, data) @@ -58,16 +60,16 @@ def on_settings_save(self, data): self._tasmota_logger.setLevel(logging.DEBUG) else: self._tasmota_logger.setLevel(logging.INFO) - + def get_settings_version(self): - return 3 - + return 4 + def on_settings_migrate(self, target, current=None): if current is None or current < self.get_settings_version(): # Reset plug settings to defaults. self._logger.debug("Resetting arrSmartplugs for tasmota settings.") self._settings.set(['arrSmartplugs'], self.get_settings_defaults()["arrSmartplugs"]) - + ##~~ AssetPlugin mixin def get_assets(self): @@ -75,30 +77,35 @@ def get_assets(self): js=["js/tasmota.js"], css=["css/tasmota.css"] ) - + ##~~ TemplatePlugin mixin - + def get_template_configs(self): return [ dict(type="navbar", custom_bindings=True), dict(type="settings", custom_bindings=True) ] - + ##~~ SimpleApiPlugin mixin - - def turn_on(self, plugip, plugidx, username="admin", password=""): + + def turn_on(self, plugip, plugidx, username="admin", password="", backlog_delay=0): if self._settings.get(['singleRelay']): plugidx = '' self._tasmota_logger.debug("Turning on %s index %s." % (plugip, plugidx)) try: - webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=Power" + str(plugidx) + "%20on").read() - response = json.loads(webresponse) + if int(backlog_delay) > 0: + webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=backlog%20delay%20" + str(int(backlog_delay)*10) + "%3BPower" + str(plugidx) + "%20on%3B").read() + response = dict() + response["POWER%s" % plugidx] = "ON" + else: + webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=Power" + str(plugidx) + "%20on").read() + response = json.loads(webresponse) chk = response["POWER%s" % plugidx] - except: + except: self._tasmota_logger.error('Invalid ip or unknown error connecting to %s.' % plugip, exc_info=True) response = "Unknown error turning on %s index %s." % (plugip, plugidx) chk = "UNKNOWN" - + self._tasmota_logger.debug("Response: %s" % response) if self._settings.get(['singleRelay']): plugidx = '1' @@ -109,20 +116,25 @@ def turn_on(self, plugip, plugidx, username="admin", password=""): else: self._tasmota_logger.debug(response) self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="unknown",ip=plugip,idx=plugidx)) - - def turn_off(self, plugip, plugidx, username="admin", password=""): + + def turn_off(self, plugip, plugidx, username="admin", password="", backlog_delay=0): if self._settings.get(['singleRelay']): plugidx = '' self._tasmota_logger.debug("Turning off %s index %s." % (plugip, plugidx)) try: - webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=Power" + str(plugidx) + "%20off").read() - response = json.loads(webresponse) + if int(backlog_delay) > 0: + webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=backlog%20delay%20" + str(int(backlog_delay)*10) + "%3BPower" + str(plugidx) + "%20off%3B").read() + response = dict() + response["POWER%s" % plugidx] = "OFF" + else: + webresponse = urllib2.urlopen("http://" + plugip + "/cm?user=" + username + "&password=" + password + "&cmnd=Power" + str(plugidx) + "%20off").read() + response = json.loads(webresponse) chk = response["POWER%s" % plugidx] except: self._tasmota_logger.error('Invalid ip or unknown error connecting to %s.' % plugip, exc_info=True) response = "Unknown error turning off %s index %s." % (plugip, plugidx) chk = "UNKNOWN" - + self._tasmota_logger.debug("Response: %s" % response) if self._settings.get(['singleRelay']): plugidx = '1' @@ -133,7 +145,7 @@ def turn_off(self, plugip, plugidx, username="admin", password=""): else: self._tasmota_logger.debug(response) self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="unknown",ip=plugip,idx=plugidx)) - + def check_status(self, plugip, plugidx, username="admin", password=""): if self._settings.get(['singleRelay']): plugidx = '' @@ -148,7 +160,7 @@ def check_status(self, plugip, plugidx, username="admin", password=""): self._tasmota_logger.error('Invalid ip or unknown error connecting to %s.' % plugip, exc_info=True) response = "unknown error with %s." % plugip chk = "UNKNOWN" - + self._tasmota_logger.debug("%s index %s is %s" % (plugip, plugidx, chk)) if self._settings.get(['singleRelay']): plugidx = '1' @@ -158,8 +170,8 @@ def check_status(self, plugip, plugidx, username="admin", password=""): self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="off",ip=plugip,idx=plugidx)) else: self._tasmota_logger.debug(response) - self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="unknown",ip=plugip,idx=plugidx)) - + self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="unknown",ip=plugip,idx=plugidx)) + def get_api_commands(self): return dict(turnOn=["ip","idx"],turnOff=["ip","idx"],checkStatus=["ip","idx"],connectPrinter=[],disconnectPrinter=[],sysCommand=["cmd"]) @@ -168,18 +180,18 @@ def on_api_command(self, command, data): if not user_permission.can(): from flask import make_response return make_response("Insufficient rights", 403) - + if command == 'turnOn': if "username" in data and data["username"] != "": self._tasmota_logger.debug("Using authentication for %s." % "{ip}".format(**data)) - self.turn_on("{ip}".format(**data),"{idx}".format(**data),username="{username}".format(**data),password="{password}".format(**data)) + self.turn_on("{ip}".format(**data),"{idx}".format(**data),username="{username}".format(**data),password="{password}".format(**data),backlog_delay="{backlog_delay}".format(**data)) else: self.turn_on("{ip}".format(**data),"{idx}".format(**data)) elif command == 'turnOff': if "username" in data and data["username"] != "": self._tasmota_logger.debug("Using authentication for %s." % "{ip}".format(**data)) - self.turn_off("{ip}".format(**data),"{idx}".format(**data),username="{username}".format(**data),password="{password}".format(**data)) - else: + self.turn_off("{ip}".format(**data),"{idx}".format(**data),username="{username}".format(**data),password="{password}".format(**data),backlog_delay="{backlog_delay}".format(**data)) + else: self.turn_off("{ip}".format(**data),"{idx}".format(**data)) elif command == 'checkStatus': if "username" in data and data["username"] != "": @@ -196,30 +208,36 @@ def on_api_command(self, command, data): elif command == 'sysCommand': self._tasmota_logger.debug("Running system command %s." % "{cmd}".format(**data)) os.system("{cmd}".format(**data)) - + ##~~ Gcode processing hook - + def processGCODE(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs): if gcode: if cmd.startswith("M8") and cmd.count(" ") >= 2: plugip = cmd.split()[1] - plugidx = cmd.split()[2] + plugidx = cmd.split()[2] for plug in self._settings.get(["arrSmartplugs"]): if plug["ip"].upper() == plugip.upper() and plug["idx"] == plugidx and plug["gcodeEnabled"]: if cmd.startswith("M80"): - t = threading.Timer(int(plug["gcodeOnDelay"]),self.turn_on, [plug["ip"],plug["idx"]],{'username': plug["username"],'password': plug["password"]}) + if plug["sysCmdOn"] and plug["sysRunCmdOn"] != "": + s = threading.Timer(int(plug["sysCmdOnDelay"]), os.system, [plug["sysRunCmdOn"]]) + s.start() + t = threading.Timer(int(plug["gcodeOnDelay"]),self.turn_on, [plug["ip"],plug["idx"]],{'username': plug["username"],'password': plug["password"],'backlog_delay': plug["backlog_on_delay"]}) t.start() self._tasmota_logger.debug("Received M80 command, attempting power on of %s index %s." % (plugip,plugidx)) return elif cmd.startswith("M81"): - t = threading.Timer(int(plug["gcodeOffDelay"]),self.turn_off, [plug["ip"],plug["idx"]],{'username': plug["username"],'password': plug["password"]}) + if plug["sysCmdOff"] and plug["sysRunCmdOff"] != "": + s = threading.Timer(int(plug["sysCmdOffDelay"]), os.system, [plug["sysRunCmdOff"]]) + s.start() + t = threading.Timer(int(plug["gcodeOffDelay"]),self.turn_off, [plug["ip"],plug["idx"]],{'username': plug["username"],'password': plug["password"],'backlog_delay': plug["backlog_off_delay"]}) t.start() self._tasmota_logger.debug("Received M81 command, attempting power off of %s index %s." % (plugip,plugidx)) return else: return return - + ##~~ Softwareupdate hook diff --git a/octoprint_tasmota/static/css/tasmota.css b/octoprint_tasmota/static/css/tasmota.css index f56758c..2eb167e 100644 --- a/octoprint_tasmota/static/css/tasmota.css +++ b/octoprint_tasmota/static/css/tasmota.css @@ -1,4 +1,4 @@ -#navbar_plugin_tasmota > a > i.on { +/* #navbar_plugin_tasmota > a > i.on { color: #00FF00 !important; } @@ -8,12 +8,17 @@ #navbar_plugin_tasmota > a > i.unknown { color: #808080 !important; -} +} */ #navbar_plugin_tasmota > a > div.tasmota_label { display: none; } +#navbar_plugin_tasmota > a > i.unknown::after { + content: " ?"; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; +} } + /* TouchUI - Show Label */ #touch #navbar_plugin_tasmota > a > div.tasmota_label { padding-left: 5px; diff --git a/octoprint_tasmota/static/js/tasmota.js b/octoprint_tasmota/static/js/tasmota.js index 98f6260..f9281c6 100644 --- a/octoprint_tasmota/static/js/tasmota.js +++ b/octoprint_tasmota/static/js/tasmota.js @@ -5,10 +5,10 @@ * License: AGPLv3 */ $(function() { - function tasmotaViewModel(parameters) { - var self = this; + function tasmotaViewModel(parameters) { + var self = this; - self.settings = parameters[0]; + self.settings = parameters[0]; self.loginState = parameters[1]; self.arrSmartplugs = ko.observableArray(); @@ -17,20 +17,33 @@ $(function() { self.gcodeOffString = function(data){return 'M81 '+data.ip()+' '+data.idx();}; self.selectedPlug = ko.observable(); self.processing = ko.observableArray([]); - - self.onBeforeBinding = function() { + self.get_color = function(data){ + console.log(data); + switch(data.currentState()) { + case "on": + return data.on_color(); + break; + case "off": + return data.off_color(); + break; + default: + return data.unknown_color(); + } + }; + + self.onBeforeBinding = function() { self.arrSmartplugs(self.settings.settings.plugins.tasmota.arrSmartplugs()); - } - + } + self.onAfterBinding = function() { self.checkStatuses(); } - self.onEventSettingsUpdated = function(payload) { + self.onEventSettingsUpdated = function(payload) { self.settings.requestData(); self.arrSmartplugs(self.settings.settings.plugins.tasmota.arrSmartplugs()); } - + self.onEventPrinterStateChanged = function(payload) { if (payload.state_id == "PRINTING" || payload.state_id == "PAUSED"){ self.isPrinting(true); @@ -38,64 +51,69 @@ $(function() { self.isPrinting(false); } } - + self.addPlug = function() { self.selectedPlug({'ip':ko.observable(''), - 'idx':ko.observable('1'), - 'displayWarning':ko.observable(true), - 'warnPrinting':ko.observable(false), - 'gcodeEnabled':ko.observable(false), - 'gcodeOnDelay':ko.observable(0), - 'gcodeOffDelay':ko.observable(0), - 'autoConnect':ko.observable(true), - 'autoConnectDelay':ko.observable(10.0), - 'autoDisconnect':ko.observable(true), - 'autoDisconnectDelay':ko.observable(0), - 'sysCmdOn':ko.observable(false), - 'sysRunCmdOn':ko.observable(''), - 'sysCmdOnDelay':ko.observable(0), - 'sysCmdOff':ko.observable(false), - 'sysRunCmdOff':ko.observable(''), - 'sysCmdOffDelay':ko.observable(0), - 'currentState':ko.observable('unknown'), - 'btnColor':ko.observable('#808080'), - 'username':ko.observable('admin'), - 'password':ko.observable(''), - 'icon':ko.observable('icon-bolt'), - 'label':ko.observable('')}); + 'idx':ko.observable('1'), + 'displayWarning':ko.observable(true), + 'warnPrinting':ko.observable(false), + 'gcodeEnabled':ko.observable(false), + 'gcodeOnDelay':ko.observable(0), + 'gcodeOffDelay':ko.observable(0), + 'autoConnect':ko.observable(true), + 'autoConnectDelay':ko.observable(10.0), + 'autoDisconnect':ko.observable(true), + 'autoDisconnectDelay':ko.observable(0), + 'sysCmdOn':ko.observable(false), + 'sysRunCmdOn':ko.observable(''), + 'sysCmdOnDelay':ko.observable(0), + 'sysCmdOff':ko.observable(false), + 'sysRunCmdOff':ko.observable(''), + 'sysCmdOffDelay':ko.observable(0), + 'currentState':ko.observable('unknown'), + 'username':ko.observable('admin'), + 'password':ko.observable(''), + 'icon':ko.observable('icon-bolt'), + 'label':ko.observable(''), + 'on_color':ko.observable('#00FF00'), + 'off_color':ko.observable('#FF0000'), + 'unknown_color':ko.observable('#808080'), + 'use_backlog':ko.observable(false), + 'backlog_on_delay':ko.observable(0), + 'backlog_off_delay':ko.observable(0)}); self.settings.settings.plugins.tasmota.arrSmartplugs.push(self.selectedPlug()); $("#TasmotaEditor").modal("show"); } - + self.editPlug = function(data) { self.selectedPlug(data); $("#TasmotaEditor").modal("show"); } - + self.removePlug = function(row) { self.settings.settings.plugins.tasmota.arrSmartplugs.remove(row); } - + self.cancelClick = function(data) { self.processing.remove(data.ip()); } - + self.onDataUpdaterPluginMessage = function(plugin, data) { - if (plugin != "tasmota") { - return; - } - + if (plugin != "tasmota") { + return; + } + plug = ko.utils.arrayFirst(self.settings.settings.plugins.tasmota.arrSmartplugs(),function(item){ return ((item.ip().toUpperCase() == data.ip.toUpperCase()) && (item.idx() == data.idx)); - }) || {'ip':data.ip,'idx':data.idx,'currentState':'unknown','btnColor':'#808080','gcodeEnabled':false}; - - if(self.settings.settings.plugins.tasmota.debug_logging()){ + }) || {'ip':data.ip,'idx':data.idx,'currentState':'unknown','gcodeEnabled':false}; + + if(self.settings.settings.plugins.tasmota.debug_logging()){ console.log(self.settings.settings.plugins.tasmota.arrSmartplugs()); - console.log('msg received:'+JSON.stringify(data)); - console.log('plug data:'+ko.toJSON(plug)); + console.log('msg received:'+JSON.stringify(data)); + console.log('plug data:'+ko.toJSON(plug)); } - - if (plug.currentState != data.currentState) { + + if (plug.currentState != data.currentState) { plug.currentState(data.currentState) switch(data.currentState) { case "on": @@ -109,12 +127,12 @@ $(function() { type: 'error', hide: true }); - } - self.settings.saveData(); + } + self.settings.saveData(); } self.processing.remove(data.ip); - }; - + }; + self.toggleRelay = function(data) { self.processing.push(data.ip()); switch(data.currentState()){ @@ -128,7 +146,7 @@ $(function() { self.checkStatus(data); } } - + self.turnOn = function(data) { if(data.sysCmdOn()){ setTimeout(function(){self.sysCommand(data.sysRunCmdOn())},data.sysCmdOnDelay()*1000); @@ -140,24 +158,25 @@ $(function() { self.sendTurnOn(data); } } - + self.sendTurnOn = function(data) { - $.ajax({ - url: API_BASEURL + "plugin/tasmota", - type: "POST", - dataType: "json", - data: JSON.stringify({ - command: "turnOn", + $.ajax({ + url: API_BASEURL + "plugin/tasmota", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "turnOn", ip: data.ip(), idx: data.idx(), username: data.username(), - password: data.password() - }), - contentType: "application/json; charset=UTF-8" - }); - }; + password: data.password(), + backlog_delay: data.backlog_on_delay() + }), + contentType: "application/json; charset=UTF-8" + }); + }; - self.turnOff = function(data) { + self.turnOff = function(data) { if((data.displayWarning() || (self.isPrinting() && data.warnPrinting())) && !$("#TasmotaWarning").is(':visible')){ self.selectedPlug(data); $("#TasmotaWarning").modal("show"); @@ -173,8 +192,8 @@ $(function() { self.sendTurnOff(data); } } - }; - + }; + self.sendTurnOff = function(data) { $.ajax({ url: API_BASEURL + "plugin/tasmota", @@ -185,67 +204,68 @@ $(function() { ip: data.ip(), idx: data.idx(), username: data.username(), - password: data.password() + password: data.password(), + backlog_delay: data.backlog_off_delay() }), contentType: "application/json; charset=UTF-8" - }); + }); } - + self.checkStatus = function(data) { - $.ajax({ - url: API_BASEURL + "plugin/tasmota", - type: "POST", - dataType: "json", - data: JSON.stringify({ - command: "checkStatus", + $.ajax({ + url: API_BASEURL + "plugin/tasmota", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "checkStatus", ip: data.ip(), idx: data.idx(), username: data.username(), password: data.password() - }), - contentType: "application/json; charset=UTF-8" - }).done(function(){ + }), + contentType: "application/json; charset=UTF-8" + }).done(function(){ self.settings.saveData(); }); - }; - + }; + self.disconnectPrinter = function() { - $.ajax({ - url: API_BASEURL + "plugin/tasmota", - type: "POST", - dataType: "json", - data: JSON.stringify({ - command: "disconnectPrinter" - }), - contentType: "application/json; charset=UTF-8" - }); + $.ajax({ + url: API_BASEURL + "plugin/tasmota", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "disconnectPrinter" + }), + contentType: "application/json; charset=UTF-8" + }); } - + self.connectPrinter = function() { - $.ajax({ - url: API_BASEURL + "plugin/tasmota", - type: "POST", - dataType: "json", - data: JSON.stringify({ - command: "connectPrinter" - }), - contentType: "application/json; charset=UTF-8" - }); + $.ajax({ + url: API_BASEURL + "plugin/tasmota", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "connectPrinter" + }), + contentType: "application/json; charset=UTF-8" + }); } - + self.sysCommand = function(sysCmd) { - $.ajax({ - url: API_BASEURL + "plugin/tasmota", - type: "POST", - dataType: "json", - data: JSON.stringify({ - command: "sysCommand", + $.ajax({ + url: API_BASEURL + "plugin/tasmota", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "sysCommand", cmd: sysCmd - }), - contentType: "application/json; charset=UTF-8" - }); + }), + contentType: "application/json; charset=UTF-8" + }); } - + self.checkStatuses = function() { ko.utils.arrayForEach(self.settings.settings.plugins.tasmota.arrSmartplugs(),function(item){ if(item.ip() !== "") { @@ -255,17 +275,29 @@ $(function() { self.checkStatus(item); } }); - }; - } + if (self.settings.settings.plugins.tasmota.polling_enabled() && parseInt(self.settings.settings.plugins.tasmota.polling_interval(),10) > 0) { + if(self.settings.settings.plugins.tasmota.debug_logging()){ + console.log('Polling enabled, checking status again in ' + (parseInt(self.settings.settings.plugins.tasmota.polling_interval(),10) * 60000) + '.'); + } + if(typeof self.polling_timer !== "undefined") { + if(self.settings.settings.plugins.tasmota.debug_logging()){ + console.log('Clearing polling timer.'); + } + clearTimeout(self.polling_timer); + } + self.polling_timer = setTimeout(function() {self.checkStatuses();}, (parseInt(self.settings.settings.plugins.tasmota.polling_interval(),10) * 60000)); + }; + }; + } - // view model class, parameters for constructor, container to bind to - OCTOPRINT_VIEWMODELS.push([ - tasmotaViewModel, + // view model class, parameters for constructor, container to bind to + OCTOPRINT_VIEWMODELS.push([ + tasmotaViewModel, - // e.g. loginStateViewModel, settingsViewModel, ... - ["settingsViewModel","loginStateViewModel"], + // e.g. loginStateViewModel, settingsViewModel, ... + ["settingsViewModel","loginStateViewModel"], - // "#navbar_plugin_tasmota","#settings_plugin_tasmota" - ["#navbar_plugin_tasmota","#settings_plugin_tasmota"] - ]); + // "#navbar_plugin_tasmota","#settings_plugin_tasmota" + ["#navbar_plugin_tasmota","#settings_plugin_tasmota"] + ]); }); diff --git a/octoprint_tasmota/templates/tasmota_navbar.jinja2 b/octoprint_tasmota/templates/tasmota_navbar.jinja2 index 00b92fb..53597f1 100644 --- a/octoprint_tasmota/templates/tasmota_navbar.jinja2 +++ b/octoprint_tasmota/templates/tasmota_navbar.jinja2 @@ -1,5 +1,5 @@ -
+
-
+
+
+
+ +
+
+
+
+ + +
+