TP-Link smart plug HS100/HS110

Others (MiLight, Hue, Toon etc...)

Moderator: leecollings

cdk222
Posts: 3
Joined: Thursday 01 March 2018 14:55
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by cdk222 »

Hi Mike

I am receiving similar errors to MnM001 and have been spinning my wheels on it for a couple of days now:

Traceback (most recent call last):
File "./tplink-smartplug-HS110.py", line 60, in <module>
jsonData = json.loads(result)
File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

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 = '10.1.1.5'
port = 9999
cmd = '{"emeter":{"get_realtime":{}}}'

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

# 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
	
Looking at https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s i did notice a slight variation in the syntax they use for the following:

Electricity (instant and counter)
/json.htm?type=command&param=udevice&idx=IDX&nvalue=0&svalue=POWER;ENERGY

........however others including yourself have this working with your code so im not sure where I am going wrong. I have messed around with the IP & port but no luck. I can turn the plug on\off using the another HSxxx script (tplink-smartplug.py).

Please Help!!!
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

@cdk222,

First of all, make sure that ip = '10.1.1.5' is the ip address of your HS110; you've got the same ip address in baseURL - this should be your Domoticz ip. Also, remove the <> here, and try again. Then see below, if this doesn't work.

@MnM001,

I've come across a bash script for HS100 / HS110 here: https://github.com/ggeorgovassilis/linuxscripts.
On my HS110 I get:

Code: Select all

./HS100.sh 192.168.0.84 9999 on
found 0 associations
found 1 connections:
     1:	flags=82<CONNECTED,PREFERRED>
	outif en0
	src 192.168.0.10 port 63787
	dst 192.168.0.84 port 9999
	rank info not available
	TCP aux info available

Connection to 192.168.0.84 port 9999 [tcp/distinct] succeeded!
This bash script has some interesting dependencies and diagnostics, which might have a bearing.
If this (and your 'python tplink-smartplug.py -t 192.168.0.151 -c time') doesn't work, then I'm wondering if TP-Link have changed the encryption / decryption of these devices?
If so, I don't know what to suggest (I was following this website: https://www.softscheck.com/en/reverse-e ... ink-hs110/).
cdk222
Posts: 3
Joined: Thursday 01 March 2018 14:55
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by cdk222 »

Thanks Mike

I had tried numerous combinations of IP and unfortunately I left the incorrect one in the code I posted.

Have spend the day playing around with the code and found numerous updates that needed to be made for the newer version of Python. In particular the urllib commands and the HS110 'power' and 'total' syntax.

The below worked in the end for me although I need to update the value's sent to domoticz......next on the list

Thanks again

Code: Select all

#!/usr/bin/python3
#
#################################################################################################
#################################################################################################
#
# TP-Link Wi-Fi Smart Plug Protocol Energy Monitoring Client
# For use with TP-Link HS-110
#
#
# Additional Information:
#
#      https://www.domoticz.com/forum/viewtopic.php?f=56&t=13290
#      https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/
#
# Complete URL String to Send to Domoticz
#      http://DOMSERVER_IP:8080/json.htm?type=command&param=udevice&nvalue=0&idx=IDX&svalue=POWER;TOTAL
#
#################################################################################################
#################################################################################################

import socket
import argparse
import json
import urllib
import urllib.request

version = 0.1

# Check if IP is valid
def validIP(ip):
        try:
                socket.inet_pton(socket.AF_INET, ip)
        except socket.error:
                parser.error("Invalid IP Address.")
        return ip

# Predefined Smart Plug Commands
# For a full list of commands, consult tplink_commands.txt
commands = {'info'     : '{"system":{"get_sysinfo":{}}}',
                        'on'       : '{"system":{"set_relay_state":{"state":1}}}',
                        'off'      : '{"system":{"set_relay_state":{"state":0}}}',
                        'cloudinfo': '{"cnCloud":{"get_info":{}}}',
                        'wlanscan' : '{"netif":{"get_scaninfo":{"refresh":0}}}',
                        'time'     : '{"time":{"get_time":{}}}',
                        'schedule' : '{"schedule":{"get_rules":{}}}',
                        'countdown': '{"count_down":{"get_rules":{}}}',
                        'antitheft': '{"anti_theft":{"get_rules":{}}}',
                        'reboot'   : '{"system":{"reboot":{"delay":1}}}',
                        'reset'    : '{"system":{"reset":{"delay":1}}}',
                        'energy'    : '{"emeter":{"get_realtime":{}}}'
}

# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
def encrypt(string):
    key = 171
    result = b"\0\0\0"+ chr(len(string)).encode('latin-1')
    for i in string.encode('latin-1'):
        a = key ^ i
        key = a
        result += chr(a).encode('latin-1')
    return result


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

# Parse commandline arguments
parser = argparse.ArgumentParser(description="TP-Link Wi-Fi Smart Plug Client v" + str(version))
parser.add_argument("-t", "--target", metavar="<ip>", required=True, help="Target IP Address", type=validIP)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-c", "--command", metavar="<command>", help="Preset command to send. Choices are: "+", ".join(commands), choices=commands)
group.add_argument("-j", "--json", metavar="<JSON string>", help="Full JSON string of command to send")
args = parser.parse_args()

# Domoticz command stub and IDx of HS110
baseURL = 'http://<DOMOTICZ IP>:8080/json.htm?type=command&param=udevice&nvalue=0'
HSIdx = <IDX from Domoticz>

# Set target IP, port and command to send
ip = args.target
port = 9999
if args.command is None:
        cmd = args.json
else:
        cmd = commands[args.command]

# 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()

        cmdresult = decrypt(data[4:])
        jsonData = json.loads(cmdresult)
        power = jsonData['emeter']['get_realtime']['power_mw']
        total = jsonData['emeter']['get_realtime']['total_wh'] * 1000
        url = baseURL + "&idx=%s&svalue=%s;%s" % (HSIdx, power, total)
        domreq = urllib.request.Request(url)

# Troubleshooting Print Commands
#       print (json.dumps(jsonData, indent=4,sort_keys=True))
#       print(power,total)
#       print(url)
#       print("Sent:     ", cmd)
#       print("Received: ", decrypt(data[4:]))

except socket.error:
        quit("Cound not connect to host " + ip + ":" + str(port))

try:
        urllib.request.urlopen(domreq)
except Exception as e:
        print(str(e))
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

@cdk222,

Glad you seem to have got this working - maybe the difference in our experiences is because I am using Python 2.7?
cdk222
Posts: 3
Joined: Thursday 01 March 2018 14:55
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by cdk222 »

@Mike

Thats exactly what it was. Thanks for the original code
cazz
Posts: 16
Joined: Sunday 04 March 2018 18:12
Target OS: Linux
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by cazz »

Just here to say thanks MikeF for the script that you post in page 1
Have a HS100 and was curious if I can use that too read the Watt for me.
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

The HS100 is just a wifi smart plug - the HS110 includes energy monitoring.
cazz
Posts: 16
Joined: Sunday 04 March 2018 18:12
Target OS: Linux
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by cazz »

Sorry I mean HS110 :)
kawa
Posts: 1
Joined: Wednesday 14 March 2018 4:15
Target OS: Linux
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by kawa »

For those using python3 you can use the following python code:

To switch on/off (tplink-smartplug.py)

Code: Select all

#!/usr/bin/env python3
#
# TP-Link Wi-Fi Smart Plug Protocol Client
# For use with TP-Link HS-100 or HS-110
#  
# by Lubomir Stroetmann
# Copyright 2016 softScheck GmbH 
# 
# modified by Kawa
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 
#
import socket
import argparse

version = 0.5

# Check if IP is valid
def validIP(ip):
	try:
		socket.inet_pton(socket.AF_INET, ip)
	except socket.error:
		parser.error("Invalid IP Address.")
	return ip 

# Predefined Smart Plug Commands
# For a full list of commands, consult tplink_commands.txt
commands = {'info'     : '{"system":{"get_sysinfo":{}}}',
			'on'       : '{"system":{"set_relay_state":{"state":1}}}',
			'off'      : '{"system":{"set_relay_state":{"state":0}}}',
			'cloudinfo': '{"cnCloud":{"get_info":{}}}',
			'wlanscan' : '{"netif":{"get_scaninfo":{"refresh":0}}}',
			'time'     : '{"time":{"get_time":{}}}',
			'schedule' : '{"schedule":{"get_rules":{}}}',
			'countdown': '{"count_down":{"get_rules":{}}}',
			'antitheft': '{"anti_theft":{"get_rules":{}}}',
			'reboot'   : '{"system":{"reboot":{"delay":1}}}',
			'reset'    : '{"system":{"reset":{"delay":1}}}'
}

# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
def encrypt(string):
	key = 171
	result = b"\0\0\0"+ chr(len(string)).encode('utf-8')
	for i in string.encode('utf-8'):
		a = key ^ i
		key = a
		result += chr(a).encode('utf-8')
	return result

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

# Parse commandline arguments
parser = argparse.ArgumentParser(description="TP-Link Wi-Fi Smart Plug Client v" + str(version))
parser.add_argument("-t", "--target", metavar="<ip>", required=True, help="Target IP Address", type=validIP)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-c", "--command", metavar="<command>", help="Preset command to send. Choices are: "+", ".join(commands), choices=commands) 
group.add_argument("-j", "--json", metavar="<JSON string>", help="Full JSON string of command to send")
args = parser.parse_args()

# Set target IP, port and command to send
ip = args.target
port = 9999
if args.command is None:
	cmd = args.json
else:
	cmd = commands[args.command]



# 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)
#	print ("Received: ", decrypt(data[4:]))
except socket.error:
	quit("Cound not connect to host " + ip + ":" + str(port))
Calling the script by adding the following on/off actions to the virtual switch (my tplink plug has ip 192.168.1.32):

Code: Select all

script://tplink-smartplug.py -t 192.168.1.32 -c on

Code: Select all

script://tplink-smartplug.py -t 192.168.1.32 -c off

I have the following script to get the status (tplink-status.py)

Code: Select all

#!/usr/bin/env python3
#
# TP-Link Wi-Fi Smart Plug Protocol Client
# For use with TP-Link HS100
#
# modified by Kawa
#
# Gets current switch status
# and sends to Domoticz

import socket
import argparse
import json
import urllib.request, urllib.parse, urllib.error
import urllib.request, urllib.error, urllib.parse


# Check if IP is valid
def validIP(ip):
	try:
		socket.inet_pton(socket.AF_INET, ip)
	except socket.error:
		parser.error("Invalid IP Address.")
	return ip 


parser = argparse.ArgumentParser(description="TP-Link Wi-Fi Smart Plug Client")
parser.add_argument("-t", "--target", metavar="<ip>", required=True, help="Target IP Address", type=validIP)
parser.add_argument("-i", "--idx", metavar="<idx>", help="Which Domoticz IDX")
parser.add_argument("-d", "--domoticz", metavar="<Domoticz IP>", help="Full IP address with port number for Domoticz")
args = parser.parse_args()


# ip, port, and command for HS100
ip = args.target
port = 9999
cmd = '{"system":{"get_sysinfo":{}}}'
idx = args.idx
domoticzIP = args.domoticz

# Domoticz command stub and IDx of HS100
baseURL = 'http://' + domoticzIP + '/json.htm?type=command&param=udevice&idx=' + str(idx) + '&nvalue='

# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
def encrypt(string):
	key = 171
	result = b"\0\0\0"+ chr(len(string)).encode('latin-1')
	for i in string.encode('latin-1'):
		a = key ^ i
		key = a
		result += chr(a).encode('latin-1')
	return result

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

def domoticzrequest (url):
   request = urllib.request.Request(url)
   response = urllib.request.urlopen(request)
   result = response.read()
   return result;

def getcurrentstate():
   url = 'http://' + domoticzIP + '/json.htm?type=devices&rid=' + str(idx)
   response = domoticzrequest(url)
   jsonData = json.loads(response)
   status = jsonData['result'][0]['Status']
   lampState = 0
   if status == "On":
      lampState = 1

   return lampState;

# 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()

   result = decrypt(data[4:])
   jsonData = json.loads(result)
   relay_state = jsonData['system']['get_sysinfo']['relay_state']
except socket.error:
   quit("Cound not connect to host " + ip + ":" + str(port))

# Send data to Domoticz
try:
   currentState = getcurrentstate()
   if currentState != relay_state:
      url = baseURL + str(relay_state)
      domoticzrequest(url)
      
except urllib.error.URLError as e:
   print((e.code))
I have the following line in crontab:
(the plug has ip 192.168.1.32 and the virtual switch has idx 5 on domoticz running on 192.168.1.50:8080

Code: Select all

*       *       *       *       *       /config/scripts/tplink-status.py -t 192.168.1.32 -i 5 -d 192.168.1.50:8080 > /var/log/tplink.log
Mdieli
Posts: 12
Joined: Saturday 14 February 2015 11:18
Target OS: Linux
Domoticz version: BETA
Location: The Netherlands
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Mdieli »

I just received 2 HS110 plugs and I am trying to setup the Domoticz "link".
I want to be able to switch them ofcource. But more important, I want them also to report power consumption with a short (15 - 60 seconds) interval.

I found (at least) 2 different scripts to enable switching (tplink-smartplug.py and hs100.sh) and 1 script to retrieve power consumption and send it to domoticz (tplink-status.py)

With the hs100.sh script, i have no problem to switch it on/off.

However, I am having trouble to get the python scripts to work.

Problem 1
When excuting:

Code: Select all

./tplink-smartplug.py -t 192.168.1.165 -j '{"emeter":{"get_realtime":{}}}'
I receive NO response at all. (I'm 100% sure about the IP: I tried it with another IP, and then I receive the error that it is the wrong IP)

Code: Select all

Sent:      {"emeter":{"get_realtime":{}}}
Received:
The same happens when I try to switch on/off

Code: Select all

./tplink-smartplug.py -t 192.168.1.165 -c on

Code: Select all

Sent:      {"system":{"set_relay_state":{"state":1}}}
Received:
Problem 2
When executing:

Code: Select all

./tplink-status.py -t 192.168.1.165 -i 1891 -d 192.168.1.150:8080
I receive the following error:

Code: Select all

Traceback (most recent call last):
  File "./tplink-status.py", line 97, in <module>
    currentState = getcurrentstate()
  File "./tplink-status.py", line 73, in getcurrentstate
    jsonData = json.loads(response)
  File "/usr/lib/python3.5/json/__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'
The IPs, port and IDX are 100% correct btw.

Anyone who can point me in the right direction?
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

I've not been using my HS110 for a while, but I've just reinstalled it (as a Switch - Electric (Instant + Counter)) in Domoticz, and I'm running this python script (which I previously published in this thread - 3 October 2016):
Spoiler: show

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.0.xx'
port = 9999
cmd = '{"emeter":{"get_realtime":{}}}'

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

# 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
and it's working OK:

Image

Note that I'm using python 2.7 (on a RPi 2), and there may be differences with python 3.x (e.g., around urllib / urllib2); also, I'm not using authorisation in Domoticz. You could try uncommenting some of the print statements, as a debugging aid.

Mike
Mdieli
Posts: 12
Joined: Saturday 14 February 2015 11:18
Target OS: Linux
Domoticz version: BETA
Location: The Netherlands
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Mdieli »

MikeF wrote: Saturday 23 June 2018 13:30 ...
Thanks for the script, it is a lot like the one I allready had.

After some investigation, I found that the post @ softcheck.com was eddited and a commend was added:
UPDATE 19.06.2018:
TP-Link has updated their firmware. If you install this firmware upgrade, tplink-smartplug.py does not work anymore.
https://www.softscheck.com/en/reverse-e ... ink-hs110/

I'm note sure if the 0 (zero) response I receive from the switch is an indication that I have the latest firmware. Also not sure how to check this if updates are pushed by the app automatically.
According the app, the current firmware version I have is 1.4.3
Mdieli
Posts: 12
Joined: Saturday 14 February 2015 11:18
Target OS: Linux
Domoticz version: BETA
Location: The Netherlands
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Mdieli »

I found that updates are done via the app indeed.
I checked, and the app says there is 1 update availabl for the switch. So not the latest firmware yet I guess.
Does anyone have the same firmware as I and have it working? (1.4.3)
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

I wasn't aware of this... I don't (and won't!) use Kasa with my HS110.

I am wary of applying 'latest versions' of apps / firmware, in case they remove /disrupt features I was previously using - e.g., iOS 11.4 removes support for Airport Express.

... and Gizmocuz has just announced new Domoticz stable, 4.9700! :)
Mdieli
Posts: 12
Joined: Saturday 14 February 2015 11:18
Target OS: Linux
Domoticz version: BETA
Location: The Netherlands
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Mdieli »

In the meantime, I found out there was/is a tool for the PC to upgrade the firmware in the past. I tried to up/downgrade to a previous one but no result.

Fortunate, I saw an post on the git page from Home Assistant, there was one person that also had troubles but found out it was an little API change. Heb changed the python script from HA a bit and has it working again. Unfortunate, I'm not a coder. :(
MikeF
Posts: 350
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi / ODroid
Domoticz version: V2022.2
Location: UK
Contact:

Re: TP-Link smart plug HS100/HS110

Post by MikeF »

Can you post that script? I'll have a look.
Mdieli
Posts: 12
Joined: Saturday 14 February 2015 11:18
Target OS: Linux
Domoticz version: BETA
Location: The Netherlands
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Mdieli »

This is the code that should work with HA:

Code: Select all

"""
Support for TPLink HS100/HS110/HS200 smart switch.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.tplink/
"""
import logging
import time

import voluptuous as vol

from homeassistant.components.switch import (
    SwitchDevice, PLATFORM_SCHEMA, ATTR_CURRENT_POWER_W, ATTR_TODAY_ENERGY_KWH)
from homeassistant.const import (CONF_HOST, CONF_NAME, ATTR_VOLTAGE)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['pyHS100==0.3.2']

_LOGGER = logging.getLogger(__name__)

ATTR_TOTAL_ENERGY_KWH = 'total_energy_kwh'
ATTR_CURRENT_A = 'current_a'

CONF_LEDS = 'enable_leds'

DEFAULT_NAME = 'TP-Link Switch'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_LEDS): cv.boolean,
})


# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the TPLink switch platform."""
    from pyHS100 import SmartPlug
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)
    leds_on = config.get(CONF_LEDS)

    add_devices([SmartPlugSwitch(SmartPlug(host), name, leds_on)], True)


class SmartPlugSwitch(SwitchDevice):
    """Representation of a TPLink Smart Plug switch."""

    def __init__(self, smartplug, name, leds_on):
        """Initialize the switch."""
        self.smartplug = smartplug
        self._name = name
        if leds_on is not None:
            self.smartplug.led = leds_on
        self._state = None
        self._available = True
        # Set up emeter cache
        self._emeter_params = {}

    @property
    def name(self):
        """Return the name of the Smart Plug, if any."""
        return self._name

    @property
    def available(self) -> bool:
        """Return if switch is available."""
        return self._available

    @property
    def is_on(self):
        """Return true if switch is on."""
        return self._state

    def turn_on(self, **kwargs):
        """Turn the switch on."""
        self.smartplug.turn_on()

    def turn_off(self):
        """Turn the switch off."""
        self.smartplug.turn_off()

    @property
    def device_state_attributes(self):
        """Return the state attributes of the device."""
        return self._emeter_params

    def update(self):
        """Update the TP-Link switch's state."""
        from pyHS100 import SmartDeviceException
        try:
            self._available = True
            self._state = self.smartplug.state == \
                self.smartplug.SWITCH_STATE_ON

            if self._name is None:
                self._name = self.smartplug.alias

            if self.smartplug.has_emeter:
                emeter_readings = self.smartplug.get_emeter_realtime()
                for value in emeter_readings :
                  try :
                      name, unit = value.split("_",1)
                      div = 1000
                  except ValueError:
                      name = value.split("_",1)[0]
                      div = 1

                  if name == "power":
                      self._emeter_params[ATTR_CURRENT_POWER_W] \
                          = "%.1f W" % (float(emeter_readings[value])/div)
                  if name == "total":
                      self._emeter_params[ATTR_TODAY_ENERGY_KWH] \
                          = "%.2f kW" % (float(emeter_readings[value])/div)
                  if name == "voltage":
                      self._emeter_params[ATTR_VOLTAGE] \
                          = "%.2f V" % (float(emeter_readings[value])/div)
                  if name == "current":
                      self._emeter_params[ATTR_CURRENT_A] \
                          = "%.1f A" % (float(emeter_readings[value])/div)

                try:
                    emeter_statics = self.smartplug.get_emeter_daily()
                    self._emeter_params[ATTR_TODAY_ENERGY_KWH] \
                        = "%.2f kW" % (float(emeter_statics[int(time.strftime("%e"))]/1000))
                except KeyError:
                    # Device returned no daily history
                    pass

        except (SmartDeviceException, OSError) as ex:
            _LOGGER.warning("Could not read state for %s: %s", self.name, ex)
            self._available = False

This is the file in HA. I'm not sure if this fix has been implemented there yet:
https://github.com/home-assistant/home- ... /tplink.py
Olfer
Posts: 21
Joined: Thursday 17 November 2016 10:28
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Germany
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Olfer »

Hi guys,

"UPDATE 04.07.2018:
We updated our tool. It now supports TP-Link HS100, HS105 and HS110 with the latest firmware. Hostnames instead of IP addresses are also supported and the script can now be imported as a Python module. A big thank you to the persons who opened issues and pull requests on GitHub."

https://www.softscheck.com/en/reverse-e ... ink-hs110/

Cheers!


Update from my side: version 0.2 (latest) seems to work for me! You can download it from GitHub from. Softscheck, see link above.
Domoticz on RPi3 with Aeontec Z-Stick gen5. Beta.
Z-Wave, Zigbee, WiFi and MQTT (mainly Tasmota), Neo Cool Cam, Devolo, Fibaro, Shelly, Hue, Osram, Blitzwolf, TP-Link, Alexa. Latest: Somfy IO integration
Hofland
Posts: 5
Joined: Friday 23 February 2018 14:57
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Hofland »

I received my HS110 today and did a firmware update.
I can sucessfully read the data with the Softscheck tool.

Has anyone created a script based on the latest Softscheck tool to send the HS110 energy data to Domoticz?
Olfer
Posts: 21
Joined: Thursday 17 November 2016 10:28
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Germany
Contact:

Re: TP-Link smart plug HS100/HS110

Post by Olfer »

Hi,

You'll have to...

* replace the encrypt and decrypt functions with new ones
* add the 'from struct import pack' line
* alter the json query to... ']['power_mw'] / 1000
* alter the json query to...']['total_wh']

Sry I'm not at home.. This description is all I can deliver for now. Also I'm just getting integer values for power. If someone could help.... Would be great! Thanks in advance!
Domoticz on RPi3 with Aeontec Z-Stick gen5. Beta.
Z-Wave, Zigbee, WiFi and MQTT (mainly Tasmota), Neo Cool Cam, Devolo, Fibaro, Shelly, Hue, Osram, Blitzwolf, TP-Link, Alexa. Latest: Somfy IO integration
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 1 guest