TP-Link WiFi Switches

Forum rules

Questions about hardware that can be controlled by Indigo (but not through the interfaces and plugins listed). If Indigo doesn't support some bit of hardware you're interested in, and you don't find a 3rd Party Plugin for it, add it to this forum. Be sure to include links to as much information as you can find about it.

Note: adding it here does not mean we're going to add it - in fact it's possible one of our 3rd party developers may decide to write a plugin for it. We add hardware/features based on a lot of different factors beyond just having a request for it.

Posted on
Tue Aug 20, 2019 3:21 am
ChopOMatic offline
Posts: 110
Joined: Sep 12, 2014

Re: TP-Link WiFi Switches

Definite interest here. SWEET.




Sent from my iPad using Tapatalk

Posted on
Tue Aug 20, 2019 8:08 am
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

Post Deleted. See new post dates 21-August-2019
Last edited by berkinet on Wed Aug 21, 2019 1:44 pm, edited 1 time in total.

Posted on
Wed Aug 21, 2019 1:43 pm
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

Ok. all better, I think. Let's try this again.
ChopOMatic wrote:
Definite interest here. SWEET.

Here ya go...
  1. Edit the script (see below) and put it anywhere you want, though the Indigo Scripts folder would be a good place, and name it tpmonitor.py.
  2. Create Indigo variables for: state (you should already have this), watts, amps and volts.
  3. Then, create a trigger of Type: Indigo Server Startup and in the action execute the script.
  4. And, finally, start it manually (Execute Actions)

I have also revised the TP-Link shell script to support the ability to change update frequency on the fly. The revised code is at the bottom of this post.

Here are the constants you can set in the script
  • Set target IP, port and command to send
      ip = "192.168.5.113" # the ip address of your plug
      port = 9999 # normally leave at the default 9999
  • Set the polling frequency
      offUpFreq = 30 # interval in seconds between updates when the plug is off
        You can try setting this longer. But, you may incur dropped connections
      onUpFreq = 2 # interval in seconds between updates when the plug is on
  • If you will run this script in Indigo, define variables for the following data elements and enter the variable names here. Change the example names as desired
      indigoStateVarName = "tplinkState"
      indigoWattsVarName = "tplinkWatts"
      indigoAmpsVarName = "tplinkAmps"
      indigoVoltsVarName = "tplinkVolts"
Code: Select all
#!/usr/bin/env python -u

# Set target IP, port and command to send
ip = "192.168.5.113" # the ip address of your plug
port = 9999          # normally leave at the default 9999

# Set the polling frequency
offUpFreq = 30   # interval in secs between updates when the plug is off should be <= 30
onUpFreq  =  2   # interval in secs between updates when the plug is on

# Specify the variable names for the following data elements
indigoStateVarName =  "tplinkState"
indigoWattsVarName =  "tplinkWatts"
indigoAmpsVarName  =  "tplinkAmps"
indigoVoltsVarName =  "tplinkVolts"

######################################
# No changes needed below this point #
######################################
import socket
import json
import sys
from struct import pack
from threading import Event
import binascii
import subprocess
import signal

# Make sure we are not already running
cmd = '/usr/bin/pgrep -f tpmonitor.py'
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
myPID, err = process.communicate()
# indigo.server.log("Got pid = %s" % (my_pid), type="TP-Link", isError=True)
if len(myPID.splitlines()) >0:
   indigo.server.log("Quiting. tpmonitor.py is already running. pid = %s" % (myPID), type="TP-Link", isError=True)
   return
indigo.server.log("PID of TPLink Monitor is: " + myPID, type="TP-Link")

# Create a way to exit the timed loop...
def reset(signo, _frame):
   indigo.server.log("Polling wait interup received", type="TP-Link")
   exit.set()
# ...and a trigger to invoke the exit
signal.signal(getattr(signal, 'SIGUSR1'), reset)

# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
def encrypt(string):
   key = 171
   result = pack('>I', len(string))
   for i in string:
      a = key ^ ord(i)
      key = a
      result += chr(a)
   return result

def decrypt(string):
   key = 171
   result = ""
   for i in string:
      a = key ^ ord(i)
      key = ord(i)
      result += chr(a)
   return result

# Get our variable references from Indigo
try:
   indigoState = indigo.variables[indigoStateVarName]
   indigoWatts = indigo.variables[indigoWattsVarName]
   indigoAmps  = indigo.variables[indigoAmpsVarName]
   indigoVolts = indigo.variables[indigoVoltsVarName]
except Exception as e:
   indigo.server.log("Fatal error looking uo Indigo variables: %s" % (str(e)), type="TP-Link", isError=True)
   return

# set variables for the connection the the TPLink plug and the reply processing
cmdEm = '{"emeter":{"get_realtime":{}}}'
enccmdEm = encrypt(cmdEm)
cmdSt = '{"system":{"get_sysinfo":{}}}'
enccmdSt = encrypt(cmdSt)
lastState = 2  # 2 is not a legal state. Thus a state change will be forced on the first run
lastWatts = 0
errCount = 0
failFlag = False
failMsg = "Empty"
updateFreq = offUpFreq

# Open a socket and get ready to send and receive data
try:
   sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
   sock_tcp.settimeout(10)
   sock_tcp.connect((ip, port))   
except Exception as e:
   indigo.server.log("Untrapped Exception attempting to open connection to %s:%s. %s|%s" % (ip, str(port), str(sys.exc_info()[0]), str(e)), type="TP-Link", isError=True)
   return

indigo.server.log("Connected to TPLink Wi-Fi Smartplug at %s." % ip, type="TP-Link", isError=False)

while True:
   try:
      # indigo.server.log("Starting TCP Read", type="TP-Link", isError=True)
      sock_tcp.send(enccmdSt)
      sRcvd = sock_tcp.recv(8096)
      
      if not sRcvd:
         indigo.server.log("No sRcvd received", type="TP-Link", isError=True)
      else:
         # indigo.server.log("Received sRcvd: |%s|" % (binascii.hexlify(bytearray(sRcvd))), type="TP-Link", isError=True)
         sData = json.loads(decrypt(sRcvd[4:]))
         state = sData['system']['get_sysinfo']['relay_state']

      sock_tcp.send(enccmdEm)
      eRcvd = sock_tcp.recv(8096)
      if not eRcvd:
         indigo.server.log("No eRcvd received", type="TP-Link", isError=True)
      else:
         # indigo.server.log("Received eRcvd: |%s|" % (binascii.hexlify(bytearray(eRcvd))), type="TP-Link", isError=True)
         eData = json.loads(decrypt(eRcvd[4:]))
         curWatts = eData['emeter']['get_realtime']['power_mw']
         curVolts = eData['emeter']['get_realtime']['voltage_mv']
         curAmps  = eData['emeter']['get_realtime']['current_ma']

      if abs(curWatts - lastWatts) > 5000 or state != lastState:
         startFreq = updateFreq
         if state == 1:
            updateFreq = onUpFreq
         else:
            updateFreq = offUpFreq
         if startFreq != updateFreq:
            indigo.server.log("Polling frequency reset to %s secs." % (str(updateFreq)), type="TP-Link", isError=False)

         lastWatts = curWatts # do this here so we get a delta against the last reported watts
         # indigo.server.log(u"Info log message: %s, %s, %s, %s" % (str(state), str(curWatts/1000), str(float(curAmps)/1000), str(curVolts/1000)))
         indigo.variable.updateValue(indigoState, value=str(state))
         indigo.variable.updateValue(indigoWatts, value=str(curWatts/1000))
         indigo.variable.updateValue(indigoAmps, value=str(float(curAmps)/1000))
         indigo.variable.updateValue(indigoVolts, value=str(curVolts/1000))
      
      lastState = state
      errCount = 0
   
   except socket.timeout:
      errCount += 1
      if errCount < 10:
         indigo.server.log("Timeout (" + str(errCount) + " fails) waiting for host " + ip + ":" + str(port), isError=True)
      else:
         failMsg = "Timeout waiting for host "
         failFlag = True

   except socket.error:
      errCount += 1
      if errCount < 10:
         indigo.server.log("Could not connect to host (" + str(errCount) + " fails)  " + ip + ":" + str(port), isError=True)
      else:
         failMsg = "Could not connect to host"
         failFlag = True

   except (KeyError, ValueError) as e:
      errCount += 1
      if errCount < 10:
         indigo.server.log("No valid response received (" + str(errCount) + " fails)  " + ip + ":" + str(port) + " | " + str(e), isError=True)
      else:
         failMsg = "No valid response received"
         failFlag = True
   
   except Exception as e:
      errCount += 1
      if errCount < 10:
         indigo.server.log("Untrapped exception(" + str(errCount) + " fails)  " + ip + ":" + str(port) + " | " + str(e), isError=True)
      else:
         failMsg = "Untrapped Exception" + str(sys.exc_info()[0]) + " | " + str(e)
         failFlag = True

   if failFlag:
      indigo.server.log("Quitting: %s %s:%s" % (failMsg, ip, str(port)), type="TP-Link", isError=True)
      break

   # Start a short, interuptable, event to create a delay between loop invocations
   exit = Event()
   exit.wait(updateFreq)
   exit.clear()

sock_tcp.close()
indigo.server.log("Received quit signal, shutting down connection to %s:%s" % (ip, port), type="TP-Link", isError=True)
return



and the shell script to control the smart plug
Code: Select all
#!/bin/zsh
 
TPLINK=/usr/local/bin/tplink_smartplug.py
PLUGADDR=192.168.5.113

#### Do not change anything below this line ####
FUNC=$1

# get the pid of the Indigo TPLink monitor so we can tell it when to update
PID=`pgrep -f tpmonitor.py|head -1`
alias tpOn='$TPLINK -t $PLUGADDR -c on >/dev/null 2>&1'
alias tpOff='$TPLINK -t $PLUGADDR -c off >/dev/null 2>&1'
alias tpStatus='kill -SIGUSR1 $PID;$TPLINK -t $PLUGADDR -c info | grep Received | sed -e "s/Received..//" | json_pp | grep relay_state | cut -c26 | tr -d "\n"'

case $FUNC in
   on)
      tpOn
      tpStatus
      ;;
   off)
      tpOff
      tpStatus
      ;;
   toggle)
      STATE=`tpStatus`
      case $STATE in
         0)
            tpOn
            tpStatus
            ;;
         1)
            tpOff
            tpStatus
            ;;
         *)
            echo Unknown state $STATE received from plug
                    exit 1
            ;;
      esac
      ;;
   status)
      tpStatus
      ;;
   *)
           echo Error: on, off or status required
           exit 1
      ;;
esac

exit 0


Posted on
Wed Aug 21, 2019 2:04 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: TP-Link WiFi Switches

Is there some reason not to use the TP-Link plugin? I have a couple of the HS105 WiFi Plug Mini's and they seem to work well.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Aug 21, 2019 2:17 pm
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

jay (support) wrote:
Is there some reason not to use the TP-Link plugin? I have a couple of the HS105 WiFi Plug Mini's and they seem to work well.

I have no idea. I was just trying to help @ChopOMatic. Though, the HS110 supports energy reporting., However, I suspect if the protocols differ, it should still be possible to extend the existing plugin, or use it as the base for a new plugin.

On the other hand, take a look at the third post in this thread...
Back on Fri Dec 29, 2017, jay (support) wrote:
No need for a plugin really - just execute a run shell script action that points to that script.

Posted on
Wed Aug 21, 2019 2:40 pm
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

jay (support) wrote:
Is there some reason not to use the TP-Link plugin? I have a couple of the HS105 WiFi Plug Mini's and they seem to work well.

So yes, the TPLink plugin works with the HS110, and probably the HS100 as well. In fact, it its based on the same code @ChopOMatic and @FlyingDiver were trying to get working when I first found this thread. Unfortunately, based on their discussion, and your older remark, I just assumed there wasn't a plugin.

OTOH, The plugin just supports on, off, toggle and status and script I just posted adds support for energy reporting for the HS110. I'll get in touch with @jtburgess and see if he is interested in expanding his work.

And, on top of getting the energy support working, , I learned how to signal a script/plugin to break out of a timed wait to do an instant update, and change the polling frequency while the device is on. So, the script just polls once every 30 seconds until the device is turned on, at which point it polls immediately and then every 2 seconds (adjustable) as long as the device is on.

Posted on
Wed Aug 21, 2019 3:51 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: TP-Link WiFi Switches

berkinet wrote:
[size=85]On the other hand, take a look at the third post in this thread...
Back on Fri Dec 29, 2017, jay (support) wrote:
No need for a plugin really - just execute a run shell script action that points to that script.


That was a year before the plugin was created...

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Aug 21, 2019 6:26 pm
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

jay (support) wrote:
...That was a year before the plugin was created...

Yes, I know. I was just answering your question “Is there some reason not to use the TP-Link plugin? “. I had seen your post, and then posts from others offering ideas and help so I didn’t think to look to see if there was already a plugin.

Posted on
Thu Aug 22, 2019 8:51 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: TP-Link WiFi Switches

berkinet wrote:
jay (support) wrote:
...That was a year before the plugin was created...

Yes, I know. I was just answering your question “Is there some reason not to use the TP-Link plugin? “. I had seen your post, and then posts from others offering ideas and help so I didn’t think to look to see if there was already a plugin.


I wasn't targeting you specifically with this post, but rather anyone who was trying to use a script rather than the plugin by pointing out that there was a plugin. My specific response about the age of the post was to reinforce that it's usually a really good idea to look for available plugins before attempting to use a different solution posted 2 years earlier.

Hopefully, if you issue a pull request with your additions to the plugin jtburgess will accept them and create a new release. If you hear from him that he's not interested in maintaining the plugin, let us know and we can fork his repo (his license will allow this) into our open source repository (managed by FlyingDiver) and can get a new release generated.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Thu Aug 22, 2019 9:10 am
berkinet offline
User avatar
Posts: 3290
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France

Re: TP-Link WiFi Switches

jay (support) wrote:
...Hopefully, if you issue a pull request with your additions to the plugin jtburgess will accept them and create a new release.....
We are already in contact and I have alpha code running doing periodic polling.

I would really like to implement dual frequency polling: slow when the device is off, like once a minute (see note on that below) and faster when it is on. Then if the device is turned on, the wait loop can be interrupted (event() worked great for this) via some internal signal and then reset for a shorter interval as long as the plug is on. The problem is that this requires a separate poling queue for each device instance, and, therefore, separate, per-device, signalling. Back burner for now.

I did run into an odd problem with the current script (posted here earlier). If the polling wait was set at 60 seconds, the socket would shutdown. I tried turning on TCP keepalive, but that didn't help, at least not how I did it. At 30 seconds, there is no problem. I did not try to figure out what the actual limit was - on my to-do list. The reason for the long delay is that most controls commands will probably come through Indigo, and those are synchronous. Button presses on the device and use of the kasa app are the other possibilities, and risking missing one by 30 to 60 seconds seemed to be a fair trade off for minimising packet activity.

And, it is potentially possible to receive broadcasts from the devices. They actually "phone home" on every state change, and probably on energy reading changes as well. That is how the kasa app keeps state. The url to call is defined in the device and is, theoretically at least, changeable. Though, I have been unsuccessful in my attempts. It is also possible to send the device a special DNS resolver address that would resolve the home URL to something local. But, that means setting up some equipment just for the plug. Unfortunately, the packets are encrypted. But, if there were a way to get the plug to connect locally, those connections could be used as triggers to do an update.

Posted on
Thu Aug 22, 2019 7:49 pm
jtburgess offline
User avatar
Posts: 77
Joined: Jan 17, 2018
Location: NJ

Re: TP-Link WiFi Switches

I’m in contact with both Berkinet and Ramias, who has made separate changes to support a power strip device.
I’ll merge both sets of changes and put out a new release, — after verifying both Berkinet and Ramias are OK with the merger.
Stay tuned. This will be a long process.
If there are any other known TPLink device types, now would be the time to let us all know, so I can incorporate them in the next release.

Posted on
Fri Aug 23, 2019 9:33 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: TP-Link WiFi Switches

That's fantastic news, we're very happy to see a group of you guys collaborating on this plugin. This is the model that we'd hoped for when we started encouraging the use of Github.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Fri Aug 23, 2019 9:52 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: TP-Link WiFi Switches

jtburgess wrote:
If there are any other known TPLink device types, now would be the time to let us all know, so I can incorporate them in the next release.


In looking at their smart home site, they also appear to have a couple of switches, plugs (including in-wall) and a variety of bulbs including color. Does anyone have any of those?

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Sat Aug 24, 2019 8:46 pm
jtburgess offline
User avatar
Posts: 77
Joined: Jan 17, 2018
Location: NJ

Re: TP-Link WiFi Switches

jay (support) wrote:
In looking at their smart home site, they also appear to have a couple of switches, plugs (including in-wall) and a variety of bulbs including color. Does anyone have any of those?

Good idea Jay.
I/we can create stubs for other device types that no-one is currently using (untested, obviously), to allow for future code.

Posted on
Mon Aug 26, 2019 2:40 am
ChopOMatic offline
Posts: 110
Joined: Sep 12, 2014

Re: TP-Link WiFi Switches

I just ordered one of the bulbs to play with. Let me know what info I can provide to help.


Sent from my iPad using Tapatalk

Who is online

Users browsing this forum: No registered users and 4 guests