1414import sqlite3
1515from datetime import datetime
1616from struct import unpack
17+ from builtins import bytes
1718
1819class tplinksmartplugPlugin (octoprint .plugin .SettingsPlugin ,
1920 octoprint .plugin .AssetPlugin ,
2021 octoprint .plugin .TemplatePlugin ,
2122 octoprint .plugin .SimpleApiPlugin ,
2223 octoprint .plugin .StartupPlugin ,
23- octoprint .plugin .ProgressPlugin ):
24+ octoprint .plugin .ProgressPlugin ,
25+ octoprint .plugin .EventHandlerPlugin ):
2426
2527 def __init__ (self ):
2628 self ._logger = logging .getLogger ("octoprint.plugins.tplinksmartplug" )
@@ -55,12 +57,14 @@ def on_after_startup(self):
5557 def get_settings_defaults (self ):
5658 return dict (
5759 debug_logging = False ,
58- arrSmartplugs = [{'ip' :'' ,'label' :'' ,'icon' :'icon-bolt' ,'displayWarning' :True ,'warnPrinting' :False ,'thermal_runaway' :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' ,'useCountdownRules' :False ,'countdownOnDelay' :0 ,'countdownOffDelay' :0 ,'emeter' :{'get_realtime' :{}}}],
60+ arrSmartplugs = [{'ip' :'' ,'label' :'' ,'icon' :'icon-bolt' ,'displayWarning' :True ,'warnPrinting' :False ,'thermal_runaway' :False ,'event_on_error' : False , 'event_on_disconnect' : 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' ,'useCountdownRules' :False ,'countdownOnDelay' :1 ,'countdownOffDelay' :1 ,'emeter' :{'get_realtime' :{}}}],
5961 pollingInterval = 15 ,
6062 pollingEnabled = False ,
6163 thermal_runaway_monitoring = False ,
6264 thermal_runaway_max_bed = 0 ,
63- thermal_runaway_max_extruder = 0
65+ thermal_runaway_max_extruder = 0 ,
66+ event_on_error_monitoring = False ,
67+ event_on_disconnect_monitoring = False
6468 )
6569
6670 def on_settings_save (self , data ):
@@ -76,7 +80,7 @@ def on_settings_save(self, data):
7680 self ._tplinksmartplug_logger .setLevel (logging .INFO )
7781
7882 def get_settings_version (self ):
79- return 9
83+ return 10
8084
8185 def on_settings_migrate (self , target , current = None ):
8286 if current is None or current < 5 :
@@ -115,6 +119,14 @@ def on_settings_migrate(self, target, current=None):
115119 arrSmartplugs_new .append (plug )
116120 self ._settings .set (["arrSmartplugs" ],arrSmartplugs_new )
117121
122+ if current is not None and current < 10 :
123+ arrSmartplugs_new = []
124+ for plug in self ._settings .get (['arrSmartplugs' ]):
125+ plug ["event_on_error" ] = False
126+ plug ["event_on_disconnect" ] = False
127+ arrSmartplugs_new .append (plug )
128+ self ._settings .set (["arrSmartplugs" ],arrSmartplugs_new )
129+
118130 ##~~ AssetPlugin mixin
119131
120132 def get_assets (self ):
@@ -143,16 +155,20 @@ def turn_on(self, plugip):
143155 self ._tplinksmartplug_logger .debug ("Turning on %s." % plugip )
144156 plug = self .plug_search (self ._settings .get (["arrSmartplugs" ]),"ip" ,plugip )
145157 self ._tplinksmartplug_logger .debug (plug )
146- if plug ["useCountdownRules" ]:
147- self .sendCommand (json .loads ('{"count_down":{"delete_all_rules":null}}' ),plug ["ip" ])
148- chk = self .lookup (self .sendCommand (json .loads ('{"count_down":{"add_rule":{"enable":1,"delay":%s,"act":1,"name":"turn on"}}}' % plug ["countdownOnDelay" ]),plug ["ip" ]),* ["count_down" ,"add_rule" ,"err_code" ])
158+ if "/" in plugip :
159+ plug_ip , plug_num = plugip .split ("/" )
160+ else :
161+ plug_ip = plugip
162+ plug_num = - 1
163+ if plug ["useCountdownRules" ] and int (plug ["countdownOnDelay" ]) > 0 :
164+ self .sendCommand (json .loads ('{"count_down":{"delete_all_rules":null}}' ),plug_ip , plug_num )
165+ chk = self .lookup (self .sendCommand (json .loads ('{"count_down":{"add_rule":{"enable":1,"delay":%s,"act":1,"name":"turn on"}}}' % plug ["countdownOnDelay" ]),plug_ip ,plug_num ),* ["count_down" ,"add_rule" ,"err_code" ])
166+ if chk == 0 :
167+ c = threading .Timer (int (plug ["countdownOnDelay" ])+ 5 ,self ._plugin_manager .send_plugin_message ,[self ._identifier , dict (check_status = True ,ip = plugip )])
168+ c .start ()
149169 else :
150170 turn_on_cmnd = dict (system = dict (set_relay_state = dict (state = 1 )))
151- plug_ip = plugip .split ("/" )
152- if len (plug_ip ) == 2 :
153- chk = self .lookup (self .sendCommand (turn_on_cmnd ,plug_ip [0 ],plug_ip [1 ]),* ["system" ,"set_relay_state" ,"err_code" ])
154- else :
155- chk = self .lookup (self .sendCommand (turn_on_cmnd ,plug_ip [0 ]),* ["system" ,"set_relay_state" ,"err_code" ])
171+ chk = self .lookup (self .sendCommand (turn_on_cmnd ,plug_ip ,plug_num ),* ["system" ,"set_relay_state" ,"err_code" ])
156172
157173 self ._tplinksmartplug_logger .debug (chk )
158174 if chk == 0 :
@@ -168,9 +184,17 @@ def turn_off(self, plugip):
168184 self ._tplinksmartplug_logger .debug ("Turning off %s." % plugip )
169185 plug = self .plug_search (self ._settings .get (["arrSmartplugs" ]),"ip" ,plugip )
170186 self ._tplinksmartplug_logger .debug (plug )
171- if plug ["useCountdownRules" ]:
172- self .sendCommand (json .loads ('{"count_down":{"delete_all_rules":null}}' ),plug ["ip" ])
173- chk = self .lookup (self .sendCommand (json .loads ('{"count_down":{"add_rule":{"enable":1,"delay":%s,"act":0,"name":"turn off"}}}' % plug ["countdownOffDelay" ]),plug ["ip" ]),* ["count_down" ,"add_rule" ,"err_code" ])
187+ if "/" in plugip :
188+ plug_ip , plug_num = plugip .split ("/" )
189+ else :
190+ plug_ip = plugip
191+ plug_num = - 1
192+ if plug ["useCountdownRules" ] and int (plug ["countdownOffDelay" ]) > 0 :
193+ self .sendCommand (json .loads ('{"count_down":{"delete_all_rules":null}}' ),plug_ip ,plug_num )
194+ chk = self .lookup (self .sendCommand (json .loads ('{"count_down":{"add_rule":{"enable":1,"delay":%s,"act":0,"name":"turn off"}}}' % plug ["countdownOffDelay" ]),plug_ip ,plug_num ),* ["count_down" ,"add_rule" ,"err_code" ])
195+ if chk == 0 :
196+ c = threading .Timer (int (plug ["countdownOnDelay" ])+ 5 ,self ._plugin_manager .send_plugin_message ,[self ._identifier , dict (check_status = True ,ip = plugip )])
197+ c .start ()
174198
175199 if plug ["sysCmdOff" ]:
176200 t = threading .Timer (int (plug ["sysCmdOffDelay" ]),os .system ,args = [plug ["sysRunCmdOff" ]])
@@ -181,11 +205,7 @@ def turn_off(self, plugip):
181205
182206 if not plug ["useCountdownRules" ]:
183207 turn_off_cmnd = dict (system = dict (set_relay_state = dict (state = 0 )))
184- plug_ip = plugip .split ("/" )
185- if len (plug_ip ) == 2 :
186- chk = self .lookup (self .sendCommand (turn_off_cmnd ,plug_ip [0 ],plug_ip [1 ]),* ["system" ,"set_relay_state" ,"err_code" ])
187- else :
188- chk = self .lookup (self .sendCommand (turn_off_cmnd ,plug_ip [0 ]),* ["system" ,"set_relay_state" ,"err_code" ])
208+ chk = self .lookup (self .sendCommand (turn_off_cmnd ,plug_ip ,plug_num ),* ["system" ,"set_relay_state" ,"err_code" ])
189209
190210 self ._tplinksmartplug_logger .debug (chk )
191211 if chk == 0 :
@@ -203,8 +223,10 @@ def check_status(self, plugip):
203223 response = self .sendCommand (check_status_cmnd , plug_ip [0 ], plug_ip [1 ])
204224 else :
205225 response = self .sendCommand (check_status_cmnd , plug_ip [0 ])
206-
207- if "ENE" in self .lookup (response , * ["system" ,"get_sysinfo" ,"feature" ]):
226+
227+ self ._tplinksmartplug_logger .debug (self .deep_get (response ,["system" ,"get_sysinfo" ,"feature" ], default = "" ))
228+ if "ENE" in self .deep_get (response ,["system" ,"get_sysinfo" ,"feature" ], default = "" ):
229+ # if "ENE" in self.lookup(response, *["system","get_sysinfo","feature"]):
208230 emeter_data_cmnd = dict (emeter = dict (get_realtime = dict ()))
209231 if len (plug_ip ) == 2 :
210232 check_emeter_data = self .sendCommand (emeter_data_cmnd , plug_ip [0 ], plug_ip [1 ])
@@ -290,6 +312,28 @@ def on_api_command(self, command, data):
290312 response = dict (ip = data .ip , currentState = "unknown" )
291313 return flask .jsonify (response )
292314
315+ ##~~ EventHandlerPlugin mixin
316+
317+ def on_event (self , event , payload ):
318+ if event == "PrinterStateChanged" :
319+ self ._tplinksmartplug_logger .debug (payload ["state_id" ])
320+ if event == "Error" and self ._settings .getBoolean (["event_on_error_monitoring" ]) == True :
321+ self ._tplinksmartplug_logger .debug ("powering off due to %s event." % event )
322+ for plug in self ._settings .get (['arrSmartplugs' ]):
323+ if plug ["event_on_error" ] == True :
324+ self ._tplinksmartplug_logger .debug ("powering off %s due to %s event." % (plug ["ip" ], event ))
325+ response = self .turn_off (plug ["ip" ])
326+ if response ["currentState" ] == "off" :
327+ self ._plugin_manager .send_plugin_message (self ._identifier , response )
328+ if event == "Disconnected" and self ._settings .getBoolean (["event_on_disconnect_monitoring" ]) == True :
329+ self ._tplinksmartplug_logger .debug ("powering off due to %s event." % event )
330+ for plug in self ._settings .get (['arrSmartplugs' ]):
331+ if plug ["event_on_disconnect" ] == True :
332+ self ._tplinksmartplug_logger .debug ("powering off %s due to %s event." % (plug ["ip" ], event ))
333+ response = self .turn_off (plug ["ip" ])
334+ if response ["currentState" ] == "off" :
335+ self ._plugin_manager .send_plugin_message (self ._identifier , response )
336+
293337 ##~~ Utilities
294338
295339 def _get_device_id (self , plugip ):
@@ -336,23 +380,42 @@ def plug_search(self, list, key, value):
336380 if item [key ] == value :
337381 return item
338382
383+ # def encrypt(self, string):
384+ # key = 171
385+ # result = "\0\0\0"+chr(len(string))
386+ # for i in string:
387+ # a = key ^ ord(i)
388+ # key = a
389+ # result += chr(a)
390+ # return result
391+
392+ # def decrypt(self, string):
393+ # key = 171
394+ # result = ""
395+ # for i in string:
396+ # a = key ^ ord(i)
397+ # key = ord(i)
398+ # result += chr(a)
399+ # return result
400+
339401 def encrypt (self , string ):
340402 key = 171
341- result = "\0 \0 \0 " + chr ( len (string ))
342- for i in string :
343- a = key ^ ord ( i )
403+ result = b "\0 \0 \0 " + bytes ([ len (string )] )
404+ for i in bytes ( string . encode ( 'latin-1' )):
405+ a = key ^ i
344406 key = a
345- result += chr ( a )
407+ result += bytes ([ a ] )
346408 return result
347409
410+
348411 def decrypt (self , string ):
349- key = 171
350- result = ""
351- for i in string :
352- a = key ^ ord ( i )
353- key = ord ( i )
354- result += chr ( a )
355- return result
412+ key = 171
413+ result = b ""
414+ for i in bytes ( string ):
415+ a = key ^ i
416+ key = i
417+ result += bytes ([ a ] )
418+ return result . decode ( 'latin-1' )
356419
357420 def sendCommand (self , cmd , plugip , plug_num = - 1 ):
358421 commands = {'info' : '{"system":{"get_sysinfo":{}}}' ,
@@ -367,7 +430,8 @@ def sendCommand(self, cmd, plugip, plug_num = -1):
367430 'reboot' : '{"system":{"reboot":{"delay":1}}}' ,
368431 'reset' : '{"system":{"reset":{"delay":1}}}'
369432 }
370-
433+ if re .search ('/\d+$' , plugip ):
434+ self ._tplinksmartplug_logger .exception ("Internal error passing unsplit %s" , plugip )
371435 # try to connect via ip address
372436 try :
373437 socket .inet_aton (plugip )
@@ -383,7 +447,7 @@ def sendCommand(self, cmd, plugip, plug_num = -1):
383447 self ._tplinksmartplug_logger .debug ("Invalid hostname %s." % plugip )
384448 return {"system" :{"get_sysinfo" :{"relay_state" :3 }},"emeter" :{"err_code" : True }}
385449
386- if plug_num >= 0 :
450+ if int ( plug_num ) >= 0 :
387451 plug_ip_num = plugip + "/" + plug_num
388452 cmd ["context" ] = dict (child_ids = [self ._get_device_id (plug_ip_num )])
389453
@@ -464,10 +528,10 @@ def processGCODE(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwar
464528 def check_temps (self , parsed_temps ):
465529 thermal_runaway_triggered = False
466530 for k , v in parsed_temps .items ():
467- if k == "B" and v [1 ] > 0 and v [ 0 ] > int (self ._settings .get (["thermal_runaway_max_bed" ])):
531+ if k == "B" and v [0 ] > int (self ._settings .get (["thermal_runaway_max_bed" ])):
468532 self ._tplinksmartplug_logger .debug ("Max bed temp reached, shutting off plugs." )
469533 thermal_runaway_triggered = True
470- if k .startswith ("T" ) and v [1 ] > 0 and v [ 0 ] > int (self ._settings .get (["thermal_runaway_max_extruder" ])):
534+ if k .startswith ("T" ) and v [0 ] > int (self ._settings .get (["thermal_runaway_max_extruder" ])):
471535 self ._tplinksmartplug_logger .debug ("Extruder max temp reached, shutting off plugs." )
472536 thermal_runaway_triggered = True
473537 if thermal_runaway_triggered == True :
@@ -500,6 +564,7 @@ def get_update_information(self):
500564 )
501565
502566__plugin_name__ = "TP-Link Smartplug"
567+ __plugin_pythoncompat__ = ">=2.7,<4"
503568
504569def __plugin_load__ ():
505570 global __plugin_implementation__
0 commit comments