Skip to content

Commit

Permalink
0.9.0
Browse files Browse the repository at this point in the history
### Notes
- **Previously configured plugs will be erased upon upgrade to account for new data structure.**

### Added
- Button labeling.
- Button icons configurable via fontawesome class names found [here](http://fontawesome.io/3.2.1/cheatsheet/).
- Spinning icon while awaiting response from server.

### Changed
- Improved settings layout, less clutter.
  • Loading branch information
jneilliii authored Jan 30, 2018
1 parent 769296b commit 69fbbac
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 99 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Work inspired by [OctoPrint-PSUControl](https://github.com/kantlivelong/OctoPrin

![screenshot](settings.png)

![screenshot](plugeditor.png)

## Setup

Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wiki/Plugin:-Plugin-Manager)
Expand All @@ -20,7 +22,12 @@ or manually using this URL:
Once installed go into settings and enter the ip address for your TP-Link Smartplug device. Adjust additional settings as needed.

## Settings Explained

- **IP**
- IP or hostname of plug to control.
- **Label**
- Label to use for title attribute on hover over button in navbar.
- **Icon Class**
- Class name from [fontawesome](http://fontawesome.io/3.2.1/cheatsheet/) to use for icon on button.
- **Warn**
- The left checkbox will always warn when checked.
- The right checkbox will only warn when printer is printing.
Expand Down
13 changes: 13 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.9.0] - 2018-01-30
### Notes
- **Previously configured plugs will be erased upon upgrade to account for new data structure.**

### Added
- Button labeling.
- Button icons configurable via fontawesome class names found [here](http://fontawesome.io/3.2.1/cheatsheet/).
- Spinning icon while awaiting response from server.

### Changed
- Improved settings layout, less clutter.

## [0.8.0] - 2018-01-29
### Added
- Status polling of all configured plugs.
Expand Down Expand Up @@ -92,6 +104,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Initial release.

[0.9.0]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.9.0
[0.8.0]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.8.0
[0.7.3]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.7.3
[0.7.2]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.7.2
Expand Down
7 changes: 4 additions & 3 deletions octoprint_tplinksmartplug/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def on_after_startup(self):
def get_settings_defaults(self):
return dict(
debug_logging = False,
arrSmartplugs = [{'ip':'','displayWarning':True,'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'}],
arrSmartplugs = [{'ip':'','label':'','icon':'icon-bolt','displayWarning':True,'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'}],
pollingInterval = 15,
pollingEnabled = False
)
Expand All @@ -60,7 +60,7 @@ def on_settings_save(self, data):
self._tplinksmartplug_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():
Expand All @@ -72,7 +72,8 @@ def on_settings_migrate(self, target, current=None):

def get_assets(self):
return dict(
js=["js/tplinksmartplug.js"]
js=["js/tplinksmartplug.js"],
css=["css/tplinksmartplug.css"]
)

##~~ TemplatePlugin mixin
Expand Down
16 changes: 16 additions & 0 deletions octoprint_tplinksmartplug/static/css/tplinksmartplug.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#navbar_plugin_tplinksmartplug > a > i.on {
color: #00FF00 !important;
}

#navbar_plugin_tplinksmartplug > a > i.off {
color: #FF0000 !important;
}

#navbar_plugin_tplinksmartplug > a > i.unknown {
color: #808080 !important;
}

#TPLinkPlugEditor table th, #TPLinkPlugEditor table td {
border-top: none;
vertical-align: top;
}
31 changes: 22 additions & 9 deletions octoprint_tplinksmartplug/static/js/tplinksmartplug.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ $(function() {

self.arrSmartplugs = ko.observableArray();
self.isPrinting = ko.observable(false);
self.selectedPlug = ko.observable();
self.processing = ko.observableArray([]);

self.onBeforeBinding = function() {
self.arrSmartplugs(self.settings.settings.plugins.tplinksmartplug.arrSmartplugs());
Expand All @@ -23,7 +25,6 @@ $(function() {
}

self.onEventSettingsUpdated = function(payload) {
self.settings.requestData();
self.arrSmartplugs(self.settings.settings.plugins.tplinksmartplug.arrSmartplugs());
}

Expand All @@ -35,8 +36,19 @@ $(function() {
}
}

self.cancelClick = function(data) {
self.processing.remove(data.ip());
}

self.editPlug = function(data) {
self.selectedPlug(data);
$("#TPLinkPlugEditor").modal("show");
}

self.addPlug = function() {
self.settings.settings.plugins.tplinksmartplug.arrSmartplugs.push({'ip':ko.observable(''),
self.selectedPlug({'ip':ko.observable(''),
'label':ko.observable(''),
'icon':ko.observable('icon-bolt'),
'displayWarning':ko.observable(true),
'warnPrinting':ko.observable(false),
'gcodeEnabled':ko.observable(false),
Expand All @@ -54,6 +66,8 @@ $(function() {
'sysCmdOffDelay':ko.observable(0),
'currentState':ko.observable('unknown'),
'btnColor':ko.observable('#808080')});
self.settings.settings.plugins.tplinksmartplug.arrSmartplugs.push(self.selectedPlug);
$("#TPLinkPlugEditor").modal("show");
}

self.removePlug = function(row) {
Expand All @@ -73,13 +87,10 @@ $(function() {
plug.currentState(data.currentState)
switch(data.currentState) {
case "on":
plug.btnColor("#00FF00");
break;
case "off":
plug.btnColor("#FF0000");
break;
default:
plug.btnColor("#808080");
new PNotify({
title: 'TP-Link Smartplug Error',
text: 'Status ' + plug.currentState() + ' for ' + plug.ip() + '. Double check IP Address\\Hostname in TPLinkSmartplug Settings.',
Expand All @@ -89,9 +100,11 @@ $(function() {
self.settings.saveData();
}
}
self.processing.remove(data.ip);
};

self.toggleRelay = function(data) {
self.processing.push(data.ip());
switch(data.currentState()){
case "on":
self.turnOff(data);
Expand Down Expand Up @@ -125,11 +138,11 @@ $(function() {
};

self.turnOff = function(data) {
var dlg_id = "#tplinksmartplug_poweroff_confirmation_dialog_" + data.ip().replace( /(:|\.|[|])/g, "\\$1" );
if((data.displayWarning() || (self.isPrinting() && data.warnPrinting())) && !$(dlg_id).is(':visible')){
$(dlg_id).modal("show");
if((data.displayWarning() || (self.isPrinting() && data.warnPrinting())) && !$("#TPLinkSmartPlugWarning").is(':visible')){
self.selectedPlug(data);
$("#TPLinkSmartPlugWarning").modal("show");
} else {
$(dlg_id).modal("hide");
$("#TPLinkSmartPlugWarning").modal("hide");
if(data.sysCmdOff()){
setTimeout(function(){self.sysCommand(data.sysRunCmdOff())},data.sysCmdOffDelay()*1000);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
<!-- ko foreach: settings.settings.plugins.tplinksmartplug.arrSmartplugs -->
<!-- ko if: $data.ip().length > 0 -->
<a class="pull-right" href=#" data-bind="click: $root.toggleRelay,visible: $root.loginState.loggedIn(),style: {color: $data.btnColor}, attr: {title: $data.ip}" style="display: none;float: left;"><i class="icon-bolt"></i></a>
<div data-bind="attr: {id: 'tplinksmartplug_poweroff_confirmation_dialog_' + $data.ip()}" class="modal hide fade">
<a class="pull-right" href=#" data-bind="click: $root.toggleRelay,visible: $root.loginState.loggedIn(),attr: {title: $data.label}" style="display: none;float: left;"><i class="icon" data-bind="css: [currentState(), icon(),($root.processing().includes(ip()) ? 'icon-spin' : '')].join(' ')"></i></a>
<!-- /ko -->
<div id="TPLinkSmartPlugWarning" data-bind="with: selectedPlug" class="modal hide fade">
<div class="modal-header">
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
<h3>TPLink Smartplug</h3>
<h3>Tasmota-MQTT</h3>
</div>
<div class="modal-body">
<p>
<!--ko text: $data.ip()--><!--/ko--> is currently <!--ko text: $data.currentState()--><!--/ko-->.
<!--ko text: ip()--><!--/ko--> is currently <!--ko text: currentState()--><!--/ko-->.
</p>
<p>
<p>
{{ _('Are you sure you want to proceed?') }}
</p>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Cancel') }}</a>
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true" data-bind="click: $root.cancelClick">{{ _('Cancel') }}</a>
<a href="#" class="btn btn-danger" data-bind="click: $root.turnOff">{{ _('Proceed') }}</a>
</div>
</div>
<!-- /ko -->
<!-- /ko -->
158 changes: 82 additions & 76 deletions octoprint_tplinksmartplug/templates/tplinksmartplug_settings.jinja2
Original file line number Diff line number Diff line change
@@ -1,90 +1,96 @@
<div class="tplinksettings">
<h4>TP-Link Smartplug Settings</h4>
<table class="table table-condensed" data-bind='visible: settings.settings.plugins.tplinksmartplug.arrSmartplugs().length > 0'>
<tbody data-bind='foreach: settings.settings.plugins.tplinksmartplug.arrSmartplugs'>
<tr data-bind='attr: {title: ip}' class="info">
<td colspan="6"><strong>IP or Hostname</strong> <input data-bind='value: ip, uniqueName: true' /></td>
<td style="text-align:center"><a href="#" class="icon-trash" data-bind="click: $root.removePlug, visible: $root.settings.settings.plugins.tplinksmartplug.arrSmartplugs().length > 1"></a></td>
</tr>
<tr data-bind='visible: ip().length > 0, attr: {title: ip}' class="info">
<td style="text-align:center">Warn</td>
<td style="text-align:center">GCODE</td>
<td style="text-align:center">postConnect</td>
<td style="text-align:center">preDisconnect</td>
<td style="text-align:center">Cmd On</td>
<td style="text-align:center">Cmd Off</td>
<td></td>
</tr>
<tr data-bind='visible: ip().length > 0, attr: {title: ip}'>
<td style="text-align:center"><input type="checkbox" data-bind="checked: displayWarning, uniqueName: true" title="Enable warning prompt." /><input type="checkbox" data-bind="checked: warnPrinting, uniqueName: true" title="Warn while printing." /></td>
<td style="text-align:center"><input type="checkbox" data-bind="checked: gcodeEnabled, uniqueName: true"/></td>
<td style="text-align:center"><input type="checkbox" data-bind="checked: autoConnect, uniqueName: true"/></td>
<td style="text-align:center"><input type="checkbox" data-bind="checked: autoDisconnect, uniqueName: true"/></td>
<td style="text-align:center"><input type="checkbox" data-bind="checked: sysCmdOn, uniqueName: true"/></td>
<td style="text-align:center"><input type="checkbox" data-bind="checked: sysCmdOff, uniqueName: true"/></td>
<td style="text-align:center"></td>
</tr>
<tr data-bind='visible: gcodeEnabled() && ip().length > 0, attr: {title: ip}'>
<td></td>
<td colspan="2">GCODE On Delay</td>
<td colspan="4"><input data-bind="value: gcodeOnDelay, uniqueName: true" size="3" /></td>
<h4>TP-Link Smartplug Settings</h4>
<table class="table table-condensed" data-bind='visible: settings.settings.plugins.tplinksmartplug.arrSmartplugs().length > 0'>
<thead>
<tr>
<td>{{ _('Plug') }}</td>
<td style="text-align:center">{{ _('Options') }}</td>
<td style="text-align:center"><a href="#" class="btn btn-mini icon-plus" data-bind="click: addPlug"></a></td>
</tr>
</thead>
<tbody data-bind="foreach: settings.settings.plugins.tplinksmartplug.arrSmartplugs">
<tr data-bind="attr: {title: ip}">
<td><span data-bind="text: label" /></td>
<td style="text-align:center">
<i class="icon" data-bind="css: {'icon-check': displayWarning(),'icon-check-empty': !displayWarning()}" title="Warning Prompt" />
<i class="icon" data-bind="css: {'icon-check': warnPrinting(),'icon-check-empty': !warnPrinting()}" title="Warn While Printing" />
<i class="icon" data-bind="css: {'icon-check': autoConnect(),'icon-check-empty': !autoConnect()}" title="Auto Connect" />
<i class="icon" data-bind="css: {'icon-check': autoDisconnect(),'icon-check-empty': !autoDisconnect()}" title="Auto Disconnect" />
<i class="icon" data-bind="css: {'icon-check': gcodeEnabled(),'icon-check-empty': !gcodeEnabled()}" title="GCODE Trigger" />
<i class="icon" data-bind="css: {'icon-check': sysCmdOn(),'icon-check-empty': !sysCmdOn()}" title="Run System Command On" />
<i class="icon" data-bind="css: {'icon-check': sysCmdOff(),'icon-check-empty': !sysCmdOff()}" title="Run System Command Off" /></td>
<td style="text-align:center">
<div class="btn-group">
<a href="#" class="btn btn-mini icon-pencil" data-bind="click: $root.editPlug"></a>
<a href="#" class="btn btn-mini icon-trash" data-bind="click: $root.removePlug, visible: $root.settings.settings.plugins.tplinksmartplug.arrSmartplugs().length > 1"></a>
</div>
</td>
</tr>
</tbody>
</table>

<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.settings.plugins.tplinksmartplug.debug_logging"> Enable debug logging.
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.settings.plugins.tplinksmartplug.pollingEnabled" /> Enable polling of status.
</label>
</div>
</div>
<div class="control-group" data-bind="visible: settings.settings.plugins.tplinksmartplug.pollingEnabled">
<div class="controls">
<label class="control-label">{{ _('Minutes between checks') }}</label>
<input type="number" min="0" data-bind="value: settings.settings.plugins.tplinksmartplug.pollingInterval" />
</div>
</div>

<div id="TPLinkPlugEditor" data-bind="with: selectedPlug" class="modal hide fade">
<div class="modal-header">
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
<h3>TPLink Smartplug Editor</h3>
</div>
<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" data-bind="value: ip" /><div></td>
<td><div class="controls"><label class="control-label">{{ _('Label') }}</label><input type="text" class="input input-small" data-bind="value: label" /></div></td>
<td><div class="controls"><label class="control-label"><a href="http://fontawesome.io/3.2.1/cheatsheet/" target="_blank">{{ _('Icon Class') }}</a></label><input type="text" class="input-block-level" data-bind="value: icon" /><div></td>
</tr>
<tr data-bind='visible: gcodeEnabled() && ip().length > 0, attr: {title: ip}'>
<tr>
<td><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: displayWarning"/> Warning Prompt</label></div></td>
<td><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: warnPrinting"/> Warn While Printing</label></div></td>
<td></td>
<td colspan="2">GCODE Off Delay</td>
<td colspan="4"><input data-bind="value: gcodeOffDelay, uniqueName: true" size="3" /></td>
</tr>
<tr data-bind='visible: autoConnect() && ip().length > 0, attr: {title: ip}'>
<td></td>
<td colspan="2">Auto Connect Delay</td>
<td colspan="4"><input data-bind="value: autoConnectDelay, uniqueName: true" size="3" /></td>
<tr>
<td><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: autoConnect"/> Auto Connect</label><input type="text" data-bind="value: autoConnectDelay,visible: autoConnect" class="input input-small" /></div></td>
<td><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: autoDisconnect"/> Auto Disconnect</label><input type="text" data-bind="value: autoDisconnectDelay,visible: autoDisconnect" class="input input-small" /></div></td>
</tr>
<tr data-bind='visible: autoDisconnect() && ip().length > 0, attr: {title: ip}'>
<tr>
<td><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: gcodeEnabled"/> GCODE Trigger</label></div></td>
<td></td>
<td colspan="2">Auto Disconnect Delay</td>
<td colspan="4"><input data-bind="value: autoDisconnectDelay, uniqueName: true" size="3" /></td>
</tr>
<tr data-bind='visible: sysCmdOn() && ip().length > 0, attr: {title: ip}'>
<td></td>
<td colspan="2">System Command On</td>
<td colspan="4"><input data-bind="value: sysRunCmdOn, uniqueName: true" /></td>
</tr>
<tr data-bind='visible: sysCmdOn() && ip().length > 0, attr: {title: ip}'>
<tr data-bind="visible: gcodeEnabled">
<td><div class="controls"><label class="control-label">{{ _('GCODE On Delay') }}</label><input type="text" data-bind="value: gcodeOnDelay" class="input input-small" /></div></td>
<td><div class="controls"><label class="control-label">{{ _('GCODE Off Delay') }}</label><input type="text" data-bind="value: gcodeOffDelay" class="input input-small" /></div></td>
<td></td>
<td colspan="2">System Command On Delay</td>
<td colspan="4"><input data-bind="value: sysCmdOnDelay, uniqueName: true" size="3" /></td>
</tr>
<tr data-bind='visible: sysCmdOff() && ip().length > 0, attr: {title: ip}'>
<td></td>
<td colspan="2">System Command Off</td>
<td colspan="4"><input data-bind="value: sysRunCmdOff, uniqueName: true" /></td>
<tr>
<td colspan="2" style="vertical-align: bottom"><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: sysCmdOn"/> Run System Command After On</label><input type="text" data-bind="value: sysRunCmdOn,visible: sysCmdOn" class="input-block-level" /></div></td>
<td style="vertical-align: bottom"><div class="controls" data-bind="visible: sysRunCmdOn"><label class="control-label">{{ _('Delay') }}</label><input type="text" data-bind="value: sysCmdOnDelay" class="input input-small" /></div></td>
</tr>
<tr data-bind='visible: sysCmdOff() && ip().length > 0, attr: {title: ip}'>
<td></td>
<td colspan="2">System Command Off Delay</td>
<td colspan="4"><input data-bind="value: sysCmdOffDelay, uniqueName: true" size="3" /></td>
<tr>
<td colspan="2" style="vertical-align: bottom"><div class="controls"><label class="checkbox"><input type="checkbox" data-bind="checked: sysCmdOff"/> Run System Command Before Off</label><input type="text" data-bind="value: sysRunCmdOff,visible: sysCmdOff" class="input-block-level" /></div></td>
<td style="vertical-align: bottom"><div class="controls" data-bind="visible: sysRunCmdOff"><label class="control-label">{{ _('Delay') }}</label><input type="text" data-bind="value: sysCmdOffDelay" class="input input-small" /></div></td>
</tr>
</tbody>
</table>
<button data-bind='click: addPlug' class="btn btn-primary">Add Plug</button>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.settings.plugins.tplinksmartplug.debug_logging"> Enable debug logging.
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: settings.settings.plugins.tplinksmartplug.pollingEnabled" /> Enable polling of status.
</label>
</div>
</table>
</div>
<div class="control-group" data-bind="visible: settings.settings.plugins.tplinksmartplug.pollingEnabled">
<div class="controls">
<label class="control-label">{{ _('Minutes between checks') }}</label>
<input type="number" min="0" data-bind="value: settings.settings.plugins.tplinksmartplug.pollingInterval" />
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Close') }}</a>
</div>
</div>
Binary file added plugeditor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed settings_general.png
Binary file not shown.
Binary file removed settings_off.png
Binary file not shown.
Binary file removed settings_on.png
Binary file not shown.
Loading

0 comments on commit 69fbbac

Please sign in to comment.