diff --git a/changelog.md b/changelog.md index 51cc70e..b0e5903 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,10 @@ 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.7] - 2018-11-03 +### Added +- Added energy monitoring support for the HS-110 devices. Plugs' statuses will be checked on startup, on toggle of on/off, on print progress, and on polling interval configured in settings. + ## [0.9.6] - 2018-08-05 ### Added - Added countdown timer option to plug settings to allow using the plug's built in functions for delayed power on/off. @@ -135,6 +139,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Initial release. +[0.9.7]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.9.7 [0.9.6]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.9.6 [0.9.5]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.9.5 [0.9.4]: https://github.com/jneilliii/OctoPrint-TPLinkSmartplug/tree/0.9.4 diff --git a/octoprint_tplinksmartplug/__init__.py b/octoprint_tplinksmartplug/__init__.py index 40ef78a..47cb22e 100644 --- a/octoprint_tplinksmartplug/__init__.py +++ b/octoprint_tplinksmartplug/__init__.py @@ -16,7 +16,8 @@ class tplinksmartplugPlugin(octoprint.plugin.SettingsPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SimpleApiPlugin, - octoprint.plugin.StartupPlugin): + octoprint.plugin.StartupPlugin, + octoprint.plugin.ProgressPlugin): def __init__(self): self._logger = logging.getLogger("octoprint.plugins.tplinksmartplug") @@ -73,7 +74,7 @@ def on_settings_migrate(self, target, current=None): def get_assets(self): return dict( - js=["js/tplinksmartplug.js"], + js=["js/tplinksmartplug.js","js/knockout-bootstrap.min.js"], css=["css/tplinksmartplug.css"] ) @@ -85,6 +86,12 @@ def get_template_configs(self): dict(type="settings", custom_bindings=True) ] + def on_print_progress(self, storage, path, progress): + self._tplinksmartplug_logger.debug("Checking statuses during print progress (%s)." % progress) + for plug in self._settings.get(["arrSmartplugs"]): + if plug["emeter"] and plug["emeter"] != {}: + self.check_status(plug["ip"]) + ##~~ SimpleApiPlugin mixin def turn_on(self, plugip): @@ -131,16 +138,14 @@ def check_status(self, plugip): self._tplinksmartplug_logger.debug("Checking status of %s." % plugip) if plugip != "": today = datetime.today() - response = self.sendCommand('{"system":{"get_sysinfo":{}},"emeter":{"get_realtime":{},"get_daystat":{"month":%d,"year":%d}}}' % (today.month, today.year),plugip) - self._tplinksmartplug_logger.info("%s %s" % (response["emeter"], plugip)) - - if response["emeter"]["err_code"] != 0: - self._tplinksmartplug_logger.info("energy error: %s" % response["emeter"]["err_msg"]) - self._plugin_manager.send_plugin_message(self._identifier, dict(emeter=dict(),ip=plugip)) - else: + check_status_cmnd = '{"system":{"get_sysinfo":{}},"emeter":{"get_realtime":{},"get_daystat":{"month":%d,"year":%d}}}' % (today.month, today.year) + self._tplinksmartplug_logger.info(check_status_cmnd) + response = self.sendCommand(check_status_cmnd, plugip) + self._tplinksmartplug_logger.info(response) + if not self.lookup(response, *["emeter","err_code"]): self._plugin_manager.send_plugin_message(self._identifier, dict(emeter=response["emeter"],ip=plugip)) - chk = response["system"]["get_sysinfo"]["relay_state"] + chk = self.lookup(response,*["system","get_sysinfo","relay_state"]) if chk == 1: self._plugin_manager.send_plugin_message(self._identifier, dict(currentState="on",ip=plugip)) elif chk == 0: @@ -166,6 +171,11 @@ def on_api_command(self, command, data): ##~~ Utilities + def lookup(self, dic, key, *keys): + if keys: + return self.lookup(dic.get(key, {}), *keys) + return dic.get(key) + def plug_search(self, list, key, value): for item in list: if item[key] == value: @@ -216,7 +226,7 @@ def sendCommand(self, cmd, plugip): self._tplinksmartplug_logger.debug("Hostname %s is valid." % plugip) except (socket.herror, socket.gaierror): self._tplinksmartplug_logger.debug("Invalid hostname %s." % plugip) - return {"system":{"get_sysinfo":{"relay_state":3}}} + return {"system":{"get_sysinfo":{"relay_state":3}},"emeter":{"err_code": True}} try: sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -230,7 +240,7 @@ def sendCommand(self, cmd, plugip): return json.loads(self.decrypt(data[4:])) except socket.error: self._tplinksmartplug_logger.debug("Could not connect to %s." % plugip) - return {"system":{"get_sysinfo":{"relay_state":3}}} + return {"system":{"get_sysinfo":{"relay_state":3}},"emeter":{"err_code": True}} ##~~ Gcode processing hook diff --git a/octoprint_tplinksmartplug/static/js/knockout-bootstrap.min.js b/octoprint_tplinksmartplug/static/js/knockout-bootstrap.min.js new file mode 100644 index 0000000..bf01bd5 --- /dev/null +++ b/octoprint_tplinksmartplug/static/js/knockout-bootstrap.min.js @@ -0,0 +1,7 @@ +/*! knockout-bootstrap version: 0.3.1 +* 2015-04-18 +* Author: Bill Pullen +* Website: http://billpull.github.com/knockout-bootstrap +* MIT License http://www.opensource.org/licenses/mit-license.php +*/ +function setupKoBootstrap(a,b){"use strict";var c=function(a){return function(){return a()+a()+"-"+a()+"-"+a()+"-"+a()+"-"+a()+a()+a()}}(function(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)});b.fn.outerHtml||(b.fn.outerHtml=function(){if(0===this.length)return!1;var a=this[0],c=a.tagName.toLowerCase();if(a.outerHTML)return a.outerHTML;var d=b.map(a.attributes,function(a){return a.name+'="'+a.value+'"'});return"<"+c+(d.length>0?" "+d.join(" "):"")+">"+a.innerHTML+""+c+">"}),a.bindingHandlers.typeahead={init:function(c,d,e){var f=b(c),g=e(),h=function(a){return function(c,d){var e,f;e=[],f=new RegExp(c,"i"),b.each(a,function(a,b){f.test(b)&&e.push({value:b})}),d(e)}},i={source:h(a.utils.unwrapObservable(d()))};g.typeaheadOptions&&b.each(g.typeaheadOptions,function(b,c){i[b]=a.utils.unwrapObservable(c)}),f.attr("autocomplete","off").typeahead({hint:!0,highlight:!0,minLength:1},i)}},a.bindingHandlers.progress={init:function(d,e,f,g){var h=b(d),i=b("
",{"class":"progress-bar","data-bind":"style: { width:"+e()+" }"});h.attr("id",c()).addClass("progress progress-info").append(i),a.applyBindingsToDescendants(g,h[0])}},a.bindingHandlers.alert={init:function(c,d){var e=b(c),f=a.utils.unwrapObservable(d()),g=b("",{type:"button","class":"close","data-dismiss":"alert"}).html("×"),h=b("").html(f.message);e.addClass("alert alert-"+f.priority).append(g).append(h)}},a.bindingHandlers.tooltip={update:function(c,d){var e,f,g;if(f=a.utils.unwrapObservable(d()),e=b(c),a.isObservable(f.title)){var h=!1;e.on("show.bs.tooltip",function(){h=!0}),e.on("hide.bs.tooltip",function(){h=!1});var i=f.animation||!0;f.title.subscribe(function(){h&&(e.data("bs.tooltip").options.animation=!1,e.tooltip("fixTitle").tooltip("show"),e.data("bs.tooltip").options.animation=i)})}g=e.data("bs.tooltip"),g?b.extend(g.options,f):e.tooltip(f)}},a.bindingHandlers.popover={init:function(c,d,e,f){var g=b(c),h=a.utils.unwrapObservable(d()),i=h.template||!1,j=h.options||{title:"popover"},k=h.data||!1;return i!==!1&&(j.content=k?"":b("#"+i).html(),j.html=!0),g.on("shown.bs.popover",function(c){var d=b(c.target).data(),e=d["bs.popover"].$tip,g=d["bs.popover"].options||{},h=b(c.target),j=h.position(),l={x:h.outerWidth(),y:h.outerHeight()};a.cleanNode(e[0]),k?a.applyBindings({template:i,data:k},e[0]):a.applyBindings(f,e[0]);var m={x:e.outerWidth(),y:e.outerHeight()};switch(e.find('button[data-dismiss="popover"]').click(function(){h.popover("hide")}),g.placement){case"right":e.css({left:l.x+j.left,top:l.y/2+j.top-m.y/2});break;case"left":e.css({left:j.left-m.x,top:l.y/2+j.top-m.y/2});break;case"top":e.css({left:j.left+(l.x/2-m.x/2),top:j.top-m.y});break;case"bottom":e.css({left:j.left+(l.x/2-m.x/2),top:j.top+l.y})}}),g.popover(j),{controlsDescendantBindings:!0}}},a.bindingHandlers.modal={init:function(c,d,e,f){var g=b(c),h=a.utils.unwrapObservable(d()),i=h.template||!1,j=h.options||{},k=h.data||!1,l=h.fade||!1,m=h.openModal||!1;j.show=!1;var n={"class":"modal"+(l?" fade":""),"tab-index":"-1",role:"dialog","aria-hidden":"true"};k&&(n["data-bind"]="template: { name: template, if: data, data: data }");var o=b("",n);k||o.html(b("#"+i).html());var p=o.html();return o.modal(j),g.on("click",function(){o.html(p),a.cleanNode(o[0]),k?a.applyBindings({template:i,data:k},o[0]):a.applyBindings(f,o[0]),o.modal("show"),m&&m(),b(".modal-backdrop").css({height:b(window).height(),position:"fixed"})}),{controlsDescendantBindings:!0}}}}!function(a){"use strict";"function"==typeof define&&define.amd?define(["require","exports","knockout","jquery"],function(b,c,d,e){a(d,e)}):a(window.ko,jQuery)}(setupKoBootstrap); \ No newline at end of file diff --git a/octoprint_tplinksmartplug/static/js/tplinksmartplug.js b/octoprint_tplinksmartplug/static/js/tplinksmartplug.js index c025307..7f2982d 100644 --- a/octoprint_tplinksmartplug/static/js/tplinksmartplug.js +++ b/octoprint_tplinksmartplug/static/js/tplinksmartplug.js @@ -15,12 +15,19 @@ $(function() { self.isPrinting = ko.observable(false); self.selectedPlug = ko.observable(); self.processing = ko.observableArray([]); - self.energy_data = function(data){ - var output = data.label() + '\n'; +/* self.energy_data = function(data){ + var output = data.label() + '