first commit
This commit is contained in:
144
scanner.py
Normal file
144
scanner.py
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/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!")
|
||||
Reference in New Issue
Block a user