Files
hs110-influx-2/scanner.py
2020-10-04 22:50:23 +02:00

145 lines
3.7 KiB
Python

#!/usr/bin/env python3
#
# TP-Link Wi-Fi Smart Plug Influx Reader
# For use with TP-Link HS-110
#
# by Dennis Gunia
#
# TpLink Protocol code (reqCmd(), encrypt(), decrpyt()) based on https://github.com/softScheck/tplink-smartplug
# by Lubomir Stroetmann
# Copyright 2016 softScheck GmbH
#
# 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 libs
import yaml
import socket
import sys
import pytz
import json
from struct import pack
from influxdb import InfluxDBClient
from datetime import datetime
# list of influx data points
points = []
# read config file
with open(r'./config.yml') as file:
config = yaml.load(file, Loader=yaml.FullLoader)
# connect to influxdb
try:
client = InfluxDBClient(host=config['influx']['ip'], port=config['influx']['port'], username=config['influx']['user'], password=config['influx']['password'], database=config['influx']['db'])
client.get_list_database()
except:
exit("Cannot connect to influxdb")
# Encryption and Decryption of TP-Link Smart Home Protocol
# XOR Autokey Cipher with starting key = 171
# Python 3.x Version
if sys.version_info[0] > 2:
def encrypt(string):
key = 171
result = pack('>I', len(string))
for i in string:
a = key ^ ord(i)
key = a
result += bytes([a])
return result
def decrypt(string):
key = 171
result = ""
for i in string:
a = key ^ i
key = i
result += chr(a)
return result
# Python 2.x Version
else:
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
# send command to tplink device
def reqCmd(cmd, ip):
sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_tcp.settimeout(20)
sock_tcp.connect((ip, 9999))
sock_tcp.settimeout(None)
sock_tcp.send(encrypt(cmd))
data = sock_tcp.recv(2048)
sock_tcp.close()
return json.loads(decrypt(data[4:]))
# read single plug and add to data array
def readPlug(ip):
try:
# create utc timetsamp
dateTimeObj = datetime.now(tz=pytz.utc).isoformat()
# retrieve data from plug
dInfo = reqCmd('{"system":{"get_sysinfo":{}}}', ip)['system']['get_sysinfo']
dData = reqCmd('{"emeter":{"get_realtime":{}}}', ip)['emeter']['get_realtime']
# create point and add to points array
json_body = {
"measurement": config['influx']['measurement'],
"tags": {
"mac": dInfo['mac'],
"alias": dInfo['alias'],
"ip": ip
},
"time": dateTimeObj,
"fields": {
"volts": dData['voltage_mv'],
"amps": dData['current_ma'],
"watts": dData['power_mw'],
"wh": dData['total_wh']
}
}
points.append(json_body)
print("Read plug " + ip )
except socket.error:
print("Could not connect to plug " + ip )
# read all plugs
for deviceIp in config['plugs']:
readPlug(deviceIp)
# write to influxdb
try:
client.write_points(points)
except:
exit("error writing influxdb")
finally:
print("done!")