Skip to content

Commit

Permalink
0.9.21 (#178)
Browse files Browse the repository at this point in the history
* use threading.timer for print progress status checks
* added error classes for ip address entry errors
* fix unexpected idle power off
* auto power on when file is uploaded if enabled
* UI tweaks

Co-authored-by: Bryan Smith <[email protected]>
  • Loading branch information
jneilliii and BryanSmithDev authored Jun 19, 2020
1 parent 40a87bd commit fbb327d
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 34 deletions.
87 changes: 59 additions & 28 deletions octoprint_tplinksmartplug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ def on_startup(self, host, port):
db.commit()
db.close()

def on_after_startup(self):
self._logger.info("TPLinkSmartplug loaded!")
if self._settings.get(["pollingEnabled"]):
self.poll_status = RepeatedTimer(int(self._settings.get(["pollingInterval"]))*60, self.check_statuses)
self.poll_status.start()

self.abortTimeout = self._settings.get_int(["abortTimeout"])
self._tplinksmartplug_logger.debug("abortTimeout: %s" % self.abortTimeout)

Expand All @@ -131,13 +137,7 @@ def on_startup(self, host, port):
self.idleTimeoutWaitTemp = self._settings.get_int(["idleTimeoutWaitTemp"])
self._tplinksmartplug_logger.debug("idleTimeoutWaitTemp: %s" % self.idleTimeoutWaitTemp)

self._start_idle_timer()

def on_after_startup(self):
self._logger.info("TPLinkSmartplug loaded!")
if self._settings.get(["pollingEnabled"]):
self.poll_status = RepeatedTimer(int(self._settings.get(["pollingInterval"]))*60, self.check_statuses)
self.poll_status.start()
self._reset_idle_timer()

##~~ SettingsPlugin mixin

Expand All @@ -152,6 +152,7 @@ def get_settings_defaults(self):
thermal_runaway_max_extruder = 0,
event_on_error_monitoring = False,
event_on_disconnect_monitoring = False,
event_on_upload_monitoring = False,
cost_rate = 0,
abortTimeout = 30,
powerOffWhenIdle = False,
Expand Down Expand Up @@ -183,8 +184,8 @@ def on_settings_save(self, data):
self._plugin_manager.send_plugin_message(self._identifier, dict(powerOffWhenIdle=self.powerOffWhenIdle, type="timeout", timeout_value=self._timeout_value))

if self.powerOffWhenIdle == True:
self._tplinksmartplug_logger.debug("Settings saved, Automatic Power Off Endabled, starting idle timer...")
self._start_idle_timer()
self._tplinksmartplug_logger.debug("Settings saved, Automatic Power Off Enabled, starting idle timer...")
self._reset_idle_timer()

new_debug_logging = self._settings.get_boolean(["debug_logging"])
new_polling_value = self._settings.get_boolean(["pollingEnabled"])
Expand All @@ -205,7 +206,7 @@ def on_settings_save(self, data):
self.poll_status.start()

def get_settings_version(self):
return 11
return 12

def on_settings_migrate(self, target, current=None):
if current is None or current < 5:
Expand Down Expand Up @@ -259,6 +260,13 @@ def on_settings_migrate(self, target, current=None):
arrSmartplugs_new.append(plug)
self._settings.set(["arrSmartplugs"],arrSmartplugs_new)

if current is not None and current < 12:
arrSmartplugs_new = []
for plug in self._settings.get(['arrSmartplugs']):
plug["event_on_upload"] = False
arrSmartplugs_new.append(plug)
self._settings.set(["arrSmartplugs"],arrSmartplugs_new)

##~~ AssetPlugin mixin

def get_assets(self):
Expand All @@ -270,15 +278,15 @@ def get_assets(self):
##~~ TemplatePlugin mixin

def get_template_configs(self):
#templates_to_load = [dict(type="navbar", custom_bindings=True),dict(type="settings", custom_bindings=True),dict(type="sidebar", icon="plug", custom_bindings=True, data_bind="visible: show_sidebar()", template="tplinksmartplug_sidebar.jinja2", template_header="tplinksmartplug_sidebar_header.jinja2"),dict(type="tab", custom_bindings=True)]
templates_to_load = [dict(type="navbar", custom_bindings=True),dict(type="settings", custom_bindings=True),dict(type="sidebar", icon="plug", custom_bindings=True, data_bind="visible: arrSmartplugs().length > 0", template="tplinksmartplug_sidebar.jinja2", template_header="tplinksmartplug_sidebar_header.jinja2"),dict(type="tab", custom_bindings=True, data_bind="visible: show_sidebar()", template="tplinksmartplug_tab.jinja2")]
return templates_to_load

##~~ ProgressPlugin mixin

def on_print_progress(self, storage, path, progress):
self._tplinksmartplug_logger.debug("Checking statuses during print progress (%s)." % progress)
self.check_statuses()
_print_progress_timer = threading.Timer(1,self.check_statuses)
_print_progress_timer.start()
self._plugin_manager.send_plugin_message(self._identifier, dict(updatePlot=True))

if self.powerOffWhenIdle == True and not (self._skipIdleTimer == True):
Expand Down Expand Up @@ -353,6 +361,7 @@ def turn_off(self, plugip):

self._tplinksmartplug_logger.debug(chk)
if chk == 0:
self._stop_idle_timer()
return self.check_status(plugip)

def check_statuses(self):
Expand Down Expand Up @@ -472,8 +481,14 @@ def on_api_command(self, command, data):
#SELECT * FROM energy_data WHERE ip = '192.168.0.102' LIMIT 0,30
elif command == 'enableAutomaticShutdown':
self.powerOffWhenIdle = True
self._reset_idle_timer()
elif command == 'disableAutomaticShutdown':
self.powerOffWhenIdle = False
self._stop_idle_timer()
if self._abort_timer is not None:
self._abort_timer.cancel()
self._abort_timer = None
self._timeout_value = None
elif command == 'abortAutomaticShutdown':
if self._abort_timer is not None:
self._abort_timer.cancel()
Expand Down Expand Up @@ -515,17 +530,10 @@ def on_event(self, event, payload):
response = self.turn_off(plug["ip"])
if response["currentState"] == "off":
self._plugin_manager.send_plugin_message(self._identifier, response)
# Disconnected Event
if event == Events.DISCONNECTED and self._settings.getBoolean(["event_on_disconnect_monitoring"]) == True:
self._tplinksmartplug_logger.debug("powering off due to %s event." % event)
for plug in self._settings.get(['arrSmartplugs']):
if plug["event_on_disconnect"] == True:
self._tplinksmartplug_logger.debug("powering off %s due to %s event." % (plug["ip"], event))
response = self.turn_off(plug["ip"])
if response["currentState"] == "off":
self._plugin_manager.send_plugin_message(self._identifier, response)
# Client Opened Event
if event == Events.CLIENT_OPENED:
if self._settings.get_boolean(["powerOffWhenIdle"]):
self._reset_idle_timer()
self._plugin_manager.send_plugin_message(self._identifier, dict(powerOffWhenIdle=self.powerOffWhenIdle, type="timeout", timeout_value=self._timeout_value))
return
# Cancelled Print Interpreted Event
Expand Down Expand Up @@ -594,6 +602,27 @@ def on_event(self, event, payload):
if self._timelapse_active and event == Events.MOVIE_DONE or event == Events.MOVIE_FAILED:
self._tplinksmartplug_logger.debug("Timelapse generation finished: %s. Return Code: %s" % (payload.get("movie_basename", ""), payload.get("returncode", "completed")))
self._timelapse_active = False
# File Uploaded Event
if event == Events.UPLOAD and self._settings.getBoolean(["event_on_upload_monitoring"]):
self._tplinksmartplug_logger.debug("File uploaded: %s. Turning enabled plugs on." % payload.get("name", ""))
self._tplinksmartplug_logger.debug(payload)
for plug in self._settings.get(['arrSmartplugs']):
self._tplinksmartplug_logger.debug(plug)
if plug["event_on_upload"] == True and not self._printer.is_ready():
self._tplinksmartplug_logger.debug("powering on %s due to %s event." % (plug["ip"], event))
response = self.turn_on(plug["ip"])
if response["currentState"] == "on":
self._tplinksmartplug_logger.debug("power on successful for %s attempting connection in %s seconds" % (plug["ip"], plug.get("autoConnectDelay","0")))
self._plugin_manager.send_plugin_message(self._identifier, response)
if payload.get("path", False) != False and payload.get("target") == "local":
time.sleep(int(plug.get("autoConnectDelay","0"))+1)
if self._printer.is_ready() != False:
self._tplinksmartplug_logger.debug("printer connected starting print of %s" % (payload.get("path", "")))
self._printer.select_file(payload.get("path"), False, printAfterSelect=True)


if event in [Events.FILE_ADDED, Events.POWER_ON, Events.POWER_OFF, Events.UPLOAD, Events.FILE_SELECTED, Events.PRINT_STARTED, ]:
self._logger.info("%s: %s" % (event, payload));

##~~ Idle Timeout

Expand Down Expand Up @@ -880,7 +909,14 @@ def gcode_turn_on(self, plug):
self._plugin_manager.send_plugin_message(self._identifier, chk)

def processGCODE(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
if gcode:
if self.powerOffWhenIdle and not (gcode in self._idleIgnoreCommandsArray):
self._waitForHeaters = False
self._reset_idle_timer()

if gcode not in ["M80","M81"]:
return

if gcode == "M80":
if cmd.startswith("M80"):
plugip = re.sub(r'^M80\s?', '', cmd)
self._tplinksmartplug_logger.debug("Received M80 command, attempting power on of %s." % plugip)
Expand All @@ -899,11 +935,6 @@ def processGCODE(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwar
t = threading.Timer(int(plug["gcodeOffDelay"]),self.gcode_turn_off,[plug])
t.start()
return
elif self.powerOffWhenIdle and not (gcode in self._idleIgnoreCommandsArray):
self._waitForHeaters = False
self._reset_idle_timer()
else:
return

def processAtCommand(self, comm_instance, phase, command, parameters, tags=None, *args, **kwargs):
self._logger.info(command)
Expand Down Expand Up @@ -976,7 +1007,7 @@ def __plugin_load__():

global __plugin_hooks__
__plugin_hooks__ = {
"octoprint.comm.protocol.gcode.sent": __plugin_implementation__.processGCODE,
"octoprint.comm.protocol.gcode.queuing": __plugin_implementation__.processGCODE,
"octoprint.comm.protocol.atcommand.sending": __plugin_implementation__.processAtCommand,
"octoprint.comm.protocol.temperatures.received": __plugin_implementation__.monitor_temperatures,
"octoprint.plugin.softwareupdate.check_config": __plugin_implementation__.get_update_information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
</tr>
</thead>
<tbody data-bind="sortable: { data: settings.settings.plugins.tplinksmartplug.arrSmartplugs, options: { cancel: '.unsortable', handle: '.movePlug', axis:'y'} }">
<tr data-bind="attr: {title: ip}">
<tr data-bind="attr: {title: ip}, css: {error: ip().match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/[0-9]+)?$/) == null}">
<td><span data-bind="text: label" /></td>
<td style="text-align:center">
<i class="far" data-toggle="tooltip" data-bind="css: {'fa-check-square': automaticShutdownEnabled(),'fa-square': !automaticShutdownEnabled()}, tooltip: {}" title="{{ _('Automatic Power Off Enabled') }}"></i>
<i class="far" data-toggle="tooltip" data-bind="css: {'fa-check-square': event_on_upload(),'fa-square': !event_on_upload()}, tooltip: {}" title="{{ _('Automatic Power On Enabled') }}"></i>
<i class="far" data-toggle="tooltip" data-bind="css: {'fa-check-square': displayWarning(),'fa-square': !displayWarning()}, tooltip: {}" title="{{ _('Warning Prompt') }}"></i>
<i class="far" data-toggle="tooltip" data-bind="css: {'fa-check-square': warnPrinting(),'fa-square': !warnPrinting()}, tooltip: {}" title="{{ _('Warn While Printing') }}"></i>
<i class="far" data-toggle="tooltip" data-bind="css: {'fa-check-square': thermal_runaway(),'fa-square': !thermal_runaway()}, tooltip: {}" title="{{ _('Thermal Runaway') }}"></i>
Expand Down Expand Up @@ -75,7 +76,7 @@
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" title="{{ _('When enabled if printer becomes disconnected plugs with the option enabled will be automatically powered off.') }}" data-toggle="tooltip" data-bind="checked: settings.settings.plugins.tplinksmartplug.event_on_disconnect_monitoring, tooltip: {}" /> {{ _('Disconnect Event Monitoring') }}
<input type="checkbox" title="{{ _('When enabled if a file is uploaded plugs with the option enabled will be automatically powered on.') }}" data-toggle="tooltip" data-bind="checked: settings.settings.plugins.tplinksmartplug.event_on_upload_monitoring, tooltip: {}" /> {{ _('Upload Event Monitoring') }}
</label>
</div>
</div>
Expand Down Expand Up @@ -180,7 +181,7 @@
<div class="modal-body">
<table class="table table-condensed">
<tr>
<td><div class="controls"><label class="control-label">{{ _('IP') }}</label><input type="text" class="input-block-level" title="{{ _('IP address of plug on network. For multiple plug devices append /0, /1, /# that corresponds to the 0 based plug index.') }}" data-toggle="tooltip" data-bind="value: ip, tooltip: {}" /><div></td>
<td><div class="control-group" data-bind="css: {error: ip().match(/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/[0-9]+)?$/) == null}"><div class="controls"><label class="control-label">{{ _('IP') }}</label><input type="text" class="input-block-level" title="{{ _('IP address of plug on network. For multiple plug devices append /0, /1, /# that corresponds to the 0 based plug index.') }}" data-toggle="tooltip" data-bind="textInput: ip, tooltip: {}" /><div></div></td>
<td><div class="controls"><label class="control-label">{{ _('Icon') }}</label><input type="text" class="input-block-level" title="{{ _('Icon to use for button in navbar.') }}" data-toggle="tooltip" data-bind="value: icon, iconpicker: icon,iconpickerOptions: {hideOnSelect: true, collision: true}, tooltip: {}" /><div></td>
<td><div class="controls"><label class="control-label">{{ _('Label') }}</label><input type="text" class="input-block-level" title="{{ _('Label to display on hovering mouse over button in navbar.') }}" data-toggle="tooltip" data-bind="value: label, tooltip: {}" /></div></td>
</tr>
Expand All @@ -192,7 +193,7 @@
<tr>
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically power off this plug when Power Off on Idle is enabled and configured timeouts and temperatures are reached.') }}" data-toggle="tooltip" data-bind="checked: automaticShutdownEnabled, enable: $root.settings.settings.plugins.tplinksmartplug.powerOffWhenIdle(), tooltip: {}" disabled /> {{ _('Off on Idle') }}</label></div></td>
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically power off this plug when Error Event Monitoring is enabled.') }}" data-toggle="tooltip" data-bind="checked: event_on_error, enable: $root.settings.settings.plugins.tplinksmartplug.event_on_error_monitoring(), tooltip: {}" disabled /> {{ _('Off on Error') }}</label></div></td>
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically power off this plug when Disconnect Event Monitoring is enabled.') }}" data-toggle="tooltip" data-bind="checked: event_on_disconnect, enable: $root.settings.settings.plugins.tplinksmartplug.event_on_disconnect_monitoring(), tooltip: {}" disabled/> {{ _('Off on Disconnect') }}</label></div></td>
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically power on this plug when Upload Event Monitoring is enabled.') }}" data-toggle="tooltip" data-bind="checked: event_on_upload, enable: $root.settings.settings.plugins.tplinksmartplug.event_on_upload_monitoring(), tooltip: {}" disabled/> {{ _('On with Upload') }}</label></div></td>
</tr>
<tr>
<td><div class="controls"><label class="checkbox"><input type="checkbox" title="{{ _('Automatically connect to printer after powerering on this plug and waiting for configured delay.') }}" data-toggle="tooltip" data-bind="checked: autoConnect, tooltip: {}"/> {{ _('Auto Connect') }}</label><div class="input-append" data-toggle="tooltip" data-bind="tooltip: {}" title="{{ _('Amount of time to wait before attempting to connect to printer.') }}"><input type="number" data-bind="value: autoConnectDelay,enable: autoConnect" class="input input-mini" disabled /><span class="add-on">{{ _('secs') }}</span></div></div></td>
Expand Down Expand Up @@ -229,6 +230,6 @@
</table>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Close') }}</a>
<small>Make sure to click save in the main settings after making changes here.</small> <a href="#" class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Close') }}</a>
</div>
</div>
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
plugin_name = "OctoPrint-TPLinkSmartplug"

# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "0.9.20"
plugin_version = "0.9.21"

# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
Expand Down

0 comments on commit fbb327d

Please sign in to comment.