Page 1 of 1

Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Sun Oct 01, 2017 9:04 pm
by fips
Although there are plenty of smart plugs currently available on the market, my choice was, as usual, hugely constrained by the requirement of maximum hackability of such a device. So I've decided to pick up the TP-Link HS110 smart plug, mainly because of this article: Reverse Engineering the TP-Link HS110, which describes many aspects of the plug in great detail, and also shows how to operate the plug using just a simple Python script (tplink-smartplug.py), so the integration with the home automation system of your choice (Domoticz in my case) is going be pretty straightforward.

Image

First thing you should do is to reserve an IP address for your smart plug on your router. For this, you'll need plug's MAC address, which is written on the backside of the device (alternatively you can figure out the MAC address inside the companion Kasa application).

Then you can use tplink-smartplug.py to turn on/off the plug simply like so:

Code: Select all

python ./tplink-smartplug.py -t <IP> -c on
which gives the below response:

Code: Select all

Sent:      {"system":{"set_relay_state":{"state":1}}}
Received:  {"system":{"set_relay_state":{"err_code":0}}}
You can also read the real-time power consumption info via (HS110 only):

Code: Select all

python ./tplink-smartplug.py -t <IP> -j '{"emeter":{"get_realtime":{}}}'
which gives:

Code: Select all

Sent:      {"emeter":{"get_realtime":{}}}
Received:  {"emeter":{"get_realtime":{"current":0.041124,"voltage":237.504900,"power":9.341753,"total":0.001000,"err_code":0}}}
The nice thing about this plug is that it is operated simply via sending JSON requests, so it can be easily commanded and queried for various information. For example, I'm going to use the above real-time power consumption info for monitoring when my washing machine finishes its job (I'll keep this for next time though).

The final step is to integrate the plug with Domoticz (in my case running on a Raspberry Pi). For this you will need to copy the control script into '~/domoticz/scripts/' and make it executable. This can be achieved via:

Code: Select all

cp ./tplink-smartplug.py ~/domoticz/scripts/
sudo chmod +x ~/domoticz/scripts/tplink-smartplug.py
Then you can open Domoticz in your web browser, go to "Setup/Hardware" and add a new virtual hardware of the "Dummy" type. After that click on "Create Virtual Sensors" of the newly created virtual hardware, the bellow popup will appear:

Image

Name the newly created sensor, and select "Switch" as its type. After that you will need to edit the switch and add the on/off actions:

Code: Select all

script:///home/pi/domoticz/scripts/tplink-smartplug.py -t <IP> -c on
script:///home/pi/domoticz/scripts/tplink-smartplug.py -t <IP> -c off
Image

That's it! Your switch is now ready.

Related articles:
Home automation with Domoticz, ESP8266 and BME280

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Wed Apr 04, 2018 8:44 pm
by Flo00719
Hello,
how to get info on consumption in domoticz? because I've been on it for 3 days and no tutorial works correctly even by hacking the code while I get the consumer information when I type "{"emeter":{"get_realtime":{}}}"
thanks

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Wed Apr 04, 2018 9:59 pm
by fips
Hi,

Unfortunately, I haven't tried to propagate the consumption info from HS110 into Domoticz yet, although it's on my list for some time. However looking at this thread it seems it shouldn't be that difficult (there's even a Python script included). A small annoyance is that Demoticz is unable to query HS110 consumption info directly, so as a workaround it is necessary to setup a script that is scheduled to run periodically either by itself or by the operating system, lets say every minute. The script queries the HS110 state and parses the resulting JSON in order to extract the consumption info (that's basically what tplink-smartplug.py does), the consumption info is then inserted into another HTTP request and sent to Domoticz, which updates the virtual sensor of the type 'Electric (Instant+Counter)'.

Hope it helps.

(I might look into this later when I find some spare time. I'll definitely update this thread if there's any progress).

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Thu Apr 05, 2018 3:59 pm
by Flo00719
Thank you for answering me so quickly.
So I'll explain everything I did and you can tell me if I made a mistake but I followed this tutorial.
I created the virtual button as below:
[img]
https://www.dropbox.com/s/m2sdtfmtj2i0r ... e.PNG?dl=0
[/img]

I created a file called "test.py" and I copied it to him:

Code: Select all

#!/usr/bin/env python
# 
# TP-Link Wi-Fi Smart Plug Protocol Client
# For use with TP-Link HS110: energy monitor
# 
# Gets current power (W) and cumulative energy (kWh)
# and sends to Domoticz

import socket
import argparse
import json
import urllib
import urllib2

# ip, port, and command for HS110
ip = '192.168.1.29'
port = 9999
cmd = '{"emeter":{"get_realtime":{}}}'

# Domoticz command stub and IDx of HS110
baseURL = 'http://192.168.1.24:8080/json.htm?type=command&param=udevice&nvalue=0'
HSIdx = 28

# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
def encrypt(string):
	key = 171
	result = "\0\0\0\0"
	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

def domoticzrequest (url):
   request = urllib2.Request(url)
#   request.add_header("Authorization", "Basic %s" % base64string)
   response = urllib2.urlopen(request)
   return None;

# Send command and receive reply 
try:
	sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock_tcp.connect((ip, port))
	sock_tcp.send(encrypt(cmd))
	data = sock_tcp.recv(2048)
	sock_tcp.close()
	
#	print "Sent:     ", cmd
	result = decrypt(data[4:])
	jsonData = json.loads(result)
#	print "Received: "
#	print json.dumps(jsonData, indent=4, sort_keys=True)
	power = jsonData['emeter']['get_realtime']['power']
	total = jsonData['emeter']['get_realtime']['total'] * 1000
#	print power, total
except socket.error:
	quit("Cound not connect to host " + ip + ":" + str(port))

# Send data to Domoticz
try:
    url = baseURL + "&idx=%s&svalue=%s;%s" % (HSIdx, power, total)
    domoticzrequest(url)
except urllib2.URLError, e:
	print e.code
so I replaced the ip address of the plug with the associated port 9999 and I changed the ip address of my domoticz with the associated port 8080.

After I did:

Code: Select all

crontab -e
*/5**** sudo python /home/pi/domoticz/scripts/python/test.py >/dev/null 2>&1
I do not see where I made the mistake. Thank you for giving me feedback on what you thought.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Thu Apr 05, 2018 10:43 pm
by fips
I went through your steps quickly and it looks good to me. I would try to run the script directly (without crontab) to see if it produces any error (you can also uncomment the print statements to see how the script progresses).

Code: Select all

python /home/pi/domoticz/scripts/python/test.py
If there are no errors it means Domoticz has received the HTTP request and it should show current consumption info or print an error to the log.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Fri Apr 06, 2018 2:13 pm
by Flo00719
Thank you for watching. I had already done this command because I searched a lot before asking for help and I get this error:

Code: Select all

Traceback (most recent call last):
  File "/home/pi/domoticz/scripts/python/test.py", line 74, in <module>    
    print e.code
AttributeError: 'URLError' object has no attribute 'code'
And here I admit that I'm blocking.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Fri Apr 06, 2018 3:02 pm
by fips
Try to replace this:

Code: Select all

except urllib2.URLError, e:
   print e.code
With this:

Code: Select all

except urllib2.HTTPError, e:
    print e.code
except urllib2.URLError, e:
    print e.args
This should give better info about the error.

(I'm going to try your script over the weekend...)

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Fri Apr 06, 2018 5:08 pm
by Flo00719
I get:

Code: Select all

(error(110, 'connection timed out') ,)

I am nevertheless some of my addresses because the switch via domoticz works.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Fri Apr 06, 2018 8:34 pm
by fips
I believe you are very close to success. There seems to be a networking problem when sending data to Domoticz ('connection timed out' is quite strange on a local network, I would try to run the script a couple of times to see if the problem persists).

I have tested your script (with changed IPs, ports, device index, and uncommented print) and it seems to work fine for me, it prints:

Code: Select all

Sent:      {"emeter":{"get_realtime":{}}}
Received:
{
    "emeter": {
        "get_realtime": {
            "current": 0.050626,
            "err_code": 0,
            "power": 5.156992,
            "total": 47.638,
            "voltage": 239.229237
        }
    }
}
5.156992 47638.0
and the information is successfully passed to Domoticz:
Image

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Mon Apr 09, 2018 3:17 pm
by Flo00719
I tried to leave that code and at the execution of

Code: Select all

python /home/pi/domoticz/scripts/python/test.py
I have an error 401. I am short of idea but thanks for the help

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Tue Apr 10, 2018 9:21 pm
by fips
HTTP Error 401 means UNAUTHORIZED, which is strange if you are running locally...

As a last resort, try to paste this url to your web browser:

Code: Select all

http://192.168.1.24:8080/json.htm?type=command&param=udevice&nvalue=0&idx=28&svalue=123;456
(double check '192.168.1.24:8080' and idx=28 are the correct values for your configuration)

This should send the data directly to Domoticz and update the electricity panel, the web browser should print out this:

Code: Select all

{
   "status" : "OK",
   "title" : "Update Device"
}
If this does not work than the problem is not in the script but rather in your network or Domoticz configuration.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Wed Apr 11, 2018 8:24 pm
by Flo00719
Hello, thank you for continuing to help me :) actually I had the:

Code: Select all

{
   "status" : "OK",
   "title" : "Update Device"
}
And domoticz it is well updated
I do not see where the error is in the script

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Mon May 28, 2018 2:35 am
by viriathus
Hey guys,

Did we resolve the 401 from the script? I'm getting the same problem. Works fine if I paste the link in a browser when I am already authenticated in domoticz, but not from the script which is not.

Thanks.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Mon May 28, 2018 8:06 pm
by fips
Hi Viriathus,

I guess I haven't encountered the 401 myself because I don't use any authentication (I use Domoticz only locally, so there is no need for me). However, once I enabled the authentication via defining Username/Password, the 401 started to show up. I looked though the System Setup and found out that you can specify Networks: [ ... ] - local networks for which Domotics won't request login. After setting this I no longer get the 401 from the script.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Mon May 28, 2018 9:28 pm
by viriathus
Thank you... I also found that the following worked for me in the end:

Code: Select all

import base64

baseURL = 'http://127.0.0.1:8080/json.htm?type=command&param=udevice&nvalue=0'
username='<username>'
password='<password>'

def domoticzrequest (url):
   base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
   request = urllib2.Request(url)
   request.add_header("Authorization", "Basic %s" % base64string)  
   response = urllib2.urlopen(request)
   print response.read()

   return None;

Cheers.

Re: Controlling the TP-Link HS110 smart plug with Domoticz

Posted: Mon May 28, 2018 10:34 pm
by fips
Thanks for sharing this. Actually, adding authentication right to the HTTP request seems to be the preferred solution. I was thinking about something similar.