code clean up (#243)
This commit is contained in:
parent
c9a1c106a7
commit
a75f98720e
1 changed files with 774 additions and 770 deletions
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
|
@ -9,7 +10,6 @@ except ImportError as e:
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import socket
|
import socket
|
||||||
import sys
|
|
||||||
import threading
|
import threading
|
||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ def gendevice(devtype, host, mac):
|
||||||
return device(host=host, mac=mac, devtype=devtype)
|
return device(host=host, mac=mac, devtype=devtype)
|
||||||
return deviceClass(host=host, mac=mac, devtype=devtype)
|
return deviceClass(host=host, mac=mac, devtype=devtype)
|
||||||
|
|
||||||
|
|
||||||
def discover(timeout=None, local_ip_address=None):
|
def discover(timeout=None, local_ip_address=None):
|
||||||
if local_ip_address is None:
|
if local_ip_address is None:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
@ -69,13 +70,13 @@ def discover(timeout=None, local_ip_address=None):
|
||||||
cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
cs.bind((local_ip_address,0))
|
cs.bind((local_ip_address, 0))
|
||||||
port = cs.getsockname()[1]
|
port = cs.getsockname()[1]
|
||||||
starttime = time.time()
|
starttime = time.time()
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
|
|
||||||
timezone = int(time.timezone/-3600)
|
timezone = int(time.timezone / -3600)
|
||||||
packet = bytearray(0x30)
|
packet = bytearray(0x30)
|
||||||
|
|
||||||
year = datetime.now().year
|
year = datetime.now().year
|
||||||
|
@ -122,9 +123,8 @@ def discover(timeout=None, local_ip_address=None):
|
||||||
mac = responsepacket[0x3a:0x40]
|
mac = responsepacket[0x3a:0x40]
|
||||||
devtype = responsepacket[0x34] | responsepacket[0x35] << 8
|
devtype = responsepacket[0x34] | responsepacket[0x35] << 8
|
||||||
|
|
||||||
|
|
||||||
return gendevice(devtype, host, mac)
|
return gendevice(devtype, host, mac)
|
||||||
else:
|
|
||||||
while (time.time() - starttime) < timeout:
|
while (time.time() - starttime) < timeout:
|
||||||
cs.settimeout(timeout - (time.time() - starttime))
|
cs.settimeout(timeout - (time.time() - starttime))
|
||||||
try:
|
try:
|
||||||
|
@ -140,7 +140,6 @@ def discover(timeout=None, local_ip_address=None):
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class device:
|
class device:
|
||||||
def __init__(self, host, mac, devtype, timeout=10):
|
def __init__(self, host, mac, devtype, timeout=10):
|
||||||
self.host = host
|
self.host = host
|
||||||
|
@ -148,13 +147,15 @@ class device:
|
||||||
self.devtype = devtype
|
self.devtype = devtype
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.count = random.randrange(0xffff)
|
self.count = random.randrange(0xffff)
|
||||||
self.key = bytearray([0x09, 0x76, 0x28, 0x34, 0x3f, 0xe9, 0x9e, 0x23, 0x76, 0x5c, 0x15, 0x13, 0xac, 0xcf, 0x8b, 0x02])
|
self.key = bytearray(
|
||||||
self.iv = bytearray([0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58])
|
[0x09, 0x76, 0x28, 0x34, 0x3f, 0xe9, 0x9e, 0x23, 0x76, 0x5c, 0x15, 0x13, 0xac, 0xcf, 0x8b, 0x02])
|
||||||
|
self.iv = bytearray(
|
||||||
|
[0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58])
|
||||||
self.id = bytearray([0, 0, 0, 0])
|
self.id = bytearray([0, 0, 0, 0])
|
||||||
self.cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.cs = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
self.cs.bind(('',0))
|
self.cs.bind(('', 0))
|
||||||
self.type = "Unknown"
|
self.type = "Unknown"
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
|
|
||||||
|
@ -166,12 +167,12 @@ class device:
|
||||||
self.decrypt = self.decrypt_pycrypto
|
self.decrypt = self.decrypt_pycrypto
|
||||||
|
|
||||||
def encrypt_pyaes(self, payload):
|
def encrypt_pyaes(self, payload):
|
||||||
aes = pyaes.AESModeOfOperationCBC(self.key, iv = bytes(self.iv))
|
aes = pyaes.AESModeOfOperationCBC(self.key, iv=bytes(self.iv))
|
||||||
return b"".join([aes.encrypt(bytes(payload[i:i+16])) for i in range(0, len(payload), 16)])
|
return b"".join([aes.encrypt(bytes(payload[i:i + 16])) for i in range(0, len(payload), 16)])
|
||||||
|
|
||||||
def decrypt_pyaes(self, payload):
|
def decrypt_pyaes(self, payload):
|
||||||
aes = pyaes.AESModeOfOperationCBC(self.key, iv = bytes(self.iv))
|
aes = pyaes.AESModeOfOperationCBC(self.key, iv=bytes(self.iv))
|
||||||
return b"".join([aes.decrypt(bytes(payload[i:i+16])) for i in range(0, len(payload), 16)])
|
return b"".join([aes.decrypt(bytes(payload[i:i + 16])) for i in range(0, len(payload), 16)])
|
||||||
|
|
||||||
def encrypt_pycrypto(self, payload):
|
def encrypt_pycrypto(self, payload):
|
||||||
aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv))
|
aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv))
|
||||||
|
@ -255,9 +256,9 @@ class device:
|
||||||
packet[0x33] = self.id[3]
|
packet[0x33] = self.id[3]
|
||||||
|
|
||||||
# pad the payload for AES encryption
|
# pad the payload for AES encryption
|
||||||
if len(payload)>0:
|
if payload:
|
||||||
numpad=(len(payload)//16+1)*16
|
numpad = (len(payload) // 16 + 1) * 16
|
||||||
payload=payload.ljust(numpad, b"\x00")
|
payload = payload.ljust(numpad, b"\x00")
|
||||||
|
|
||||||
checksum = 0xbeaf
|
checksum = 0xbeaf
|
||||||
for i in range(len(payload)):
|
for i in range(len(payload)):
|
||||||
|
@ -294,7 +295,7 @@ class device:
|
||||||
|
|
||||||
|
|
||||||
class mp1(device):
|
class mp1(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "MP1"
|
self.type = "MP1"
|
||||||
|
|
||||||
|
@ -307,16 +308,14 @@ class mp1(device):
|
||||||
packet[0x03] = 0xa5
|
packet[0x03] = 0xa5
|
||||||
packet[0x04] = 0x5a
|
packet[0x04] = 0x5a
|
||||||
packet[0x05] = 0x5a
|
packet[0x05] = 0x5a
|
||||||
packet[0x06] = 0xb2 + ((sid_mask<<1) if state else sid_mask)
|
packet[0x06] = 0xb2 + ((sid_mask << 1) if state else sid_mask)
|
||||||
packet[0x07] = 0xc0
|
packet[0x07] = 0xc0
|
||||||
packet[0x08] = 0x02
|
packet[0x08] = 0x02
|
||||||
packet[0x0a] = 0x03
|
packet[0x0a] = 0x03
|
||||||
packet[0x0d] = sid_mask
|
packet[0x0d] = sid_mask
|
||||||
packet[0x0e] = sid_mask if state else 0
|
packet[0x0e] = sid_mask if state else 0
|
||||||
|
|
||||||
response = self.send_packet(0x6a, packet)
|
self.send_packet(0x6a, packet)
|
||||||
|
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
|
||||||
|
|
||||||
def set_power(self, sid, state):
|
def set_power(self, sid, state):
|
||||||
"""Sets the power state of the smart power strip."""
|
"""Sets the power state of the smart power strip."""
|
||||||
|
@ -337,9 +336,10 @@ class mp1(device):
|
||||||
|
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
state = payload[0x0e]
|
state = payload[0x0e]
|
||||||
else:
|
else:
|
||||||
state = ord(payload[0x0e])
|
state = ord(payload[0x0e])
|
||||||
|
@ -357,7 +357,7 @@ class mp1(device):
|
||||||
|
|
||||||
|
|
||||||
class sp1(device):
|
class sp1(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "SP1"
|
self.type = "SP1"
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ class sp1(device):
|
||||||
|
|
||||||
|
|
||||||
class sp2(device):
|
class sp2(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "SP2"
|
self.type = "SP2"
|
||||||
|
|
||||||
|
@ -398,19 +398,12 @@ class sp2(device):
|
||||||
packet[0] = 1
|
packet[0] = 1
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
if payload[0x4] == 1 or payload[0x4] == 3 or payload[0x4] == 0xFD:
|
return bool(payload[0x4] == 1 or payload[0x4] == 3 or payload[0x4] == 0xFD)
|
||||||
state = True
|
return bool(ord(payload[0x4]) == 1 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFD)
|
||||||
else:
|
|
||||||
state = False
|
|
||||||
else:
|
|
||||||
if ord(payload[0x4]) == 1 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFD:
|
|
||||||
state = True
|
|
||||||
else:
|
|
||||||
state = False
|
|
||||||
return state
|
|
||||||
|
|
||||||
def check_nightlight(self):
|
def check_nightlight(self):
|
||||||
"""Returns the power state of the smart plug."""
|
"""Returns the power state of the smart plug."""
|
||||||
|
@ -418,35 +411,30 @@ class sp2(device):
|
||||||
packet[0] = 1
|
packet[0] = 1
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
if payload[0x4] == 2 or payload[0x4] == 3 or payload[0x4] == 0xFF:
|
return bool(payload[0x4] == 2 or payload[0x4] == 3 or payload[0x4] == 0xFF)
|
||||||
state = True
|
return bool(ord(payload[0x4]) == 2 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFF)
|
||||||
else:
|
|
||||||
state = False
|
|
||||||
else:
|
|
||||||
if ord(payload[0x4]) == 2 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFF:
|
|
||||||
state = True
|
|
||||||
else:
|
|
||||||
state = False
|
|
||||||
return state
|
|
||||||
|
|
||||||
def get_energy(self):
|
def get_energy(self):
|
||||||
packet = bytearray([8, 0, 254, 1, 5, 1, 0, 0, 0, 45])
|
packet = bytearray([8, 0, 254, 1, 5, 1, 0, 0, 0, 45])
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x07]) == int:
|
if isinstance(payload[0x7], int):
|
||||||
energy = int(hex(payload[0x07] * 256 + payload[0x06])[2:]) + int(hex(payload[0x05])[2:])/100.0
|
energy = int(hex(payload[0x07] * 256 + payload[0x06])[2:]) + int(hex(payload[0x05])[2:]) / 100.0
|
||||||
else:
|
else:
|
||||||
energy = int(hex(ord(payload[0x07]) * 256 + ord(payload[0x06]))[2:]) + int(hex(ord(payload[0x05]))[2:])/100.0
|
energy = int(hex(ord(payload[0x07]) * 256 + ord(payload[0x06]))[2:]) + int(
|
||||||
|
hex(ord(payload[0x05]))[2:]) / 100.0
|
||||||
return energy
|
return energy
|
||||||
|
|
||||||
|
|
||||||
class a1(device):
|
class a1(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "A1"
|
self.type = "A1"
|
||||||
|
|
||||||
|
@ -455,10 +443,11 @@ class a1(device):
|
||||||
packet[0] = 1
|
packet[0] = 1
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
data = {}
|
data = {}
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
data['temperature'] = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
data['temperature'] = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
||||||
data['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0
|
data['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0
|
||||||
light = payload[0x8]
|
light = payload[0x8]
|
||||||
|
@ -505,10 +494,11 @@ class a1(device):
|
||||||
packet[0] = 1
|
packet[0] = 1
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
data = {}
|
data = {}
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
data['temperature'] = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
data['temperature'] = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
||||||
data['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0
|
data['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0
|
||||||
data['light'] = payload[0x8]
|
data['light'] = payload[0x8]
|
||||||
|
@ -524,7 +514,7 @@ class a1(device):
|
||||||
|
|
||||||
|
|
||||||
class rm(device):
|
class rm(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "RM2"
|
self.type = "RM2"
|
||||||
|
|
||||||
|
@ -533,7 +523,8 @@ class rm(device):
|
||||||
packet[0] = 4
|
packet[0] = 4
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
return payload[0x04:]
|
return payload[0x04:]
|
||||||
|
|
||||||
|
@ -562,7 +553,8 @@ class rm(device):
|
||||||
packet[0] = 0x1a
|
packet[0] = 0x1a
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return False
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if payload[0x04] == 1:
|
if payload[0x04] == 1:
|
||||||
return True
|
return True
|
||||||
|
@ -573,7 +565,8 @@ class rm(device):
|
||||||
packet[0] = 0x1b
|
packet[0] = 0x1b
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return False
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if payload[0x04] == 1:
|
if payload[0x04] == 1:
|
||||||
return True
|
return True
|
||||||
|
@ -584,9 +577,10 @@ class rm(device):
|
||||||
packet[0] = 1
|
packet[0] = 1
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return False
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
if type(payload[0x4]) == int:
|
if isinstance(payload[0x4], int):
|
||||||
temp = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
temp = (payload[0x4] * 10 + payload[0x5]) / 10.0
|
||||||
else:
|
else:
|
||||||
temp = (ord(payload[0x4]) * 10 + ord(payload[0x5])) / 10.0
|
temp = (ord(payload[0x4]) * 10 + ord(payload[0x5])) / 10.0
|
||||||
|
@ -595,7 +589,7 @@ class rm(device):
|
||||||
|
|
||||||
# For legacy compatibility - don't use this
|
# For legacy compatibility - don't use this
|
||||||
class rm2(rm):
|
class rm2(rm):
|
||||||
def __init__ (self):
|
def __init__(self):
|
||||||
device.__init__(self, None, None, None)
|
device.__init__(self, None, None, None)
|
||||||
|
|
||||||
def discover(self):
|
def discover(self):
|
||||||
|
@ -605,7 +599,7 @@ class rm2(rm):
|
||||||
|
|
||||||
|
|
||||||
class hysen(device):
|
class hysen(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "Hysen heating controller"
|
self.type = "Hysen heating controller"
|
||||||
|
|
||||||
|
@ -614,13 +608,13 @@ class hysen(device):
|
||||||
# Returns decrypted payload
|
# Returns decrypted payload
|
||||||
# New behaviour: raises a ValueError if the device response indicates an error or CRC check fails
|
# New behaviour: raises a ValueError if the device response indicates an error or CRC check fails
|
||||||
# The function prepends length (2 bytes) and appends CRC
|
# The function prepends length (2 bytes) and appends CRC
|
||||||
def send_request(self,input_payload):
|
def send_request(self, input_payload):
|
||||||
|
|
||||||
from PyCRC.CRC16 import CRC16
|
from PyCRC.CRC16 import CRC16
|
||||||
crc = CRC16(modbus_flag=True).calculate(bytes(input_payload))
|
crc = CRC16(modbus_flag=True).calculate(bytes(input_payload))
|
||||||
|
|
||||||
# first byte is length, +2 for CRC16
|
# first byte is length, +2 for CRC16
|
||||||
request_payload = bytearray([len(input_payload) + 2,0x00])
|
request_payload = bytearray([len(input_payload) + 2, 0x00])
|
||||||
request_payload.extend(input_payload)
|
request_payload.extend(input_payload)
|
||||||
|
|
||||||
# append CRC
|
# append CRC
|
||||||
|
@ -633,41 +627,40 @@ class hysen(device):
|
||||||
# check for error
|
# check for error
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err:
|
if err:
|
||||||
raise ValueError('broadlink_response_error',err)
|
raise ValueError('broadlink_response_error', err)
|
||||||
|
|
||||||
response_payload = bytearray(self.decrypt(bytes(response[0x38:])))
|
response_payload = bytearray(self.decrypt(bytes(response[0x38:])))
|
||||||
|
|
||||||
# experimental check on CRC in response (first 2 bytes are len, and trailing bytes are crc)
|
# experimental check on CRC in response (first 2 bytes are len, and trailing bytes are crc)
|
||||||
response_payload_len = response_payload[0]
|
response_payload_len = response_payload[0]
|
||||||
if response_payload_len + 2 > len(response_payload):
|
if response_payload_len + 2 > len(response_payload):
|
||||||
raise ValueError('hysen_response_error','first byte of response is not length')
|
raise ValueError('hysen_response_error', 'first byte of response is not length')
|
||||||
crc = CRC16(modbus_flag=True).calculate(bytes(response_payload[2:response_payload_len]))
|
crc = CRC16(modbus_flag=True).calculate(bytes(response_payload[2:response_payload_len]))
|
||||||
if (response_payload[response_payload_len] == crc & 0xFF) and (response_payload[response_payload_len+1] == (crc >> 8) & 0xFF):
|
if (response_payload[response_payload_len] == crc & 0xFF) and (
|
||||||
|
response_payload[response_payload_len + 1] == (crc >> 8) & 0xFF):
|
||||||
return response_payload[2:response_payload_len]
|
return response_payload[2:response_payload_len]
|
||||||
else:
|
raise ValueError('hysen_response_error', 'CRC check on response failed')
|
||||||
raise ValueError('hysen_response_error','CRC check on response failed')
|
|
||||||
|
|
||||||
|
|
||||||
# Get current room temperature in degrees celsius
|
# Get current room temperature in degrees celsius
|
||||||
def get_temp(self):
|
def get_temp(self):
|
||||||
payload = self.send_request(bytearray([0x01,0x03,0x00,0x00,0x00,0x08]))
|
payload = self.send_request(bytearray([0x01, 0x03, 0x00, 0x00, 0x00, 0x08]))
|
||||||
return payload[0x05] / 2.0
|
return payload[0x05] / 2.0
|
||||||
|
|
||||||
# Get current external temperature in degrees celsius
|
# Get current external temperature in degrees celsius
|
||||||
def get_external_temp(self):
|
def get_external_temp(self):
|
||||||
payload = self.send_request(bytearray([0x01,0x03,0x00,0x00,0x00,0x08]))
|
payload = self.send_request(bytearray([0x01, 0x03, 0x00, 0x00, 0x00, 0x08]))
|
||||||
return payload[18] / 2.0
|
return payload[18] / 2.0
|
||||||
|
|
||||||
# Get full status (including timer schedule)
|
# Get full status (including timer schedule)
|
||||||
def get_full_status(self):
|
def get_full_status(self):
|
||||||
payload = self.send_request(bytearray([0x01,0x03,0x00,0x00,0x00,0x16]))
|
payload = self.send_request(bytearray([0x01, 0x03, 0x00, 0x00, 0x00, 0x16]))
|
||||||
data = {}
|
data = {}
|
||||||
data['remote_lock'] = payload[3] & 1
|
data['remote_lock'] = payload[3] & 1
|
||||||
data['power'] = payload[4] & 1
|
data['power'] = payload[4] & 1
|
||||||
data['active'] = (payload[4] >> 4) & 1
|
data['active'] = (payload[4] >> 4) & 1
|
||||||
data['temp_manual'] = (payload[4] >> 6) & 1
|
data['temp_manual'] = (payload[4] >> 6) & 1
|
||||||
data['room_temp'] = (payload[5] & 255)/2.0
|
data['room_temp'] = (payload[5] & 255) / 2.0
|
||||||
data['thermostat_temp'] = (payload[6] & 255)/2.0
|
data['thermostat_temp'] = (payload[6] & 255) / 2.0
|
||||||
data['auto_mode'] = payload[7] & 15
|
data['auto_mode'] = payload[7] & 15
|
||||||
data['loop_mode'] = (payload[7] >> 4) & 15
|
data['loop_mode'] = (payload[7] >> 4) & 15
|
||||||
data['sensor'] = payload[8]
|
data['sensor'] = payload[8]
|
||||||
|
@ -675,13 +668,13 @@ class hysen(device):
|
||||||
data['dif'] = payload[10]
|
data['dif'] = payload[10]
|
||||||
data['svh'] = payload[11]
|
data['svh'] = payload[11]
|
||||||
data['svl'] = payload[12]
|
data['svl'] = payload[12]
|
||||||
data['room_temp_adj'] = ((payload[13] << 8) + payload[14])/2.0
|
data['room_temp_adj'] = ((payload[13] << 8) + payload[14]) / 2.0
|
||||||
if data['room_temp_adj'] > 32767:
|
if data['room_temp_adj'] > 32767:
|
||||||
data['room_temp_adj'] = 32767 - data['room_temp_adj']
|
data['room_temp_adj'] = 32767 - data['room_temp_adj']
|
||||||
data['fre'] = payload[15]
|
data['fre'] = payload[15]
|
||||||
data['poweron'] = payload[16]
|
data['poweron'] = payload[16]
|
||||||
data['unknown'] = payload[17]
|
data['unknown'] = payload[17]
|
||||||
data['external_temp'] = (payload[18] & 255)/2.0
|
data['external_temp'] = (payload[18] & 255) / 2.0
|
||||||
data['hour'] = payload[19]
|
data['hour'] = payload[19]
|
||||||
data['min'] = payload[20]
|
data['min'] = payload[20]
|
||||||
data['sec'] = payload[21]
|
data['sec'] = payload[21]
|
||||||
|
@ -689,42 +682,49 @@ class hysen(device):
|
||||||
|
|
||||||
weekday = []
|
weekday = []
|
||||||
for i in range(0, 6):
|
for i in range(0, 6):
|
||||||
weekday.append({'start_hour':payload[2*i + 23], 'start_minute':payload[2*i + 24],'temp':payload[i + 39]/2.0})
|
weekday.append(
|
||||||
|
{'start_hour': payload[2 * i + 23], 'start_minute': payload[2 * i + 24], 'temp': payload[i + 39] / 2.0})
|
||||||
|
|
||||||
data['weekday'] = weekday
|
data['weekday'] = weekday
|
||||||
weekend = []
|
weekend = []
|
||||||
for i in range(6, 8):
|
for i in range(6, 8):
|
||||||
weekend.append({'start_hour':payload[2*i + 23], 'start_minute':payload[2*i + 24],'temp':payload[i + 39]/2.0})
|
weekend.append(
|
||||||
|
{'start_hour': payload[2 * i + 23], 'start_minute': payload[2 * i + 24], 'temp': payload[i + 39] / 2.0})
|
||||||
|
|
||||||
data['weekend'] = weekend
|
data['weekend'] = weekend
|
||||||
return data
|
return data
|
||||||
|
|
||||||
# Change controller mode
|
# Change controller mode
|
||||||
# auto_mode = 1 for auto (scheduled/timed) mode, 0 for manual mode.
|
# auto_mode = 1 for auto (scheduled/timed) mode, 0 for manual mode.
|
||||||
# Manual mode will activate last used temperature. In typical usage call set_temp to activate manual control and set temp.
|
# Manual mode will activate last used temperature.
|
||||||
|
# In typical usage call set_temp to activate manual control and set temp.
|
||||||
# loop_mode refers to index in [ "12345,67", "123456,7", "1234567" ]
|
# loop_mode refers to index in [ "12345,67", "123456,7", "1234567" ]
|
||||||
# E.g. loop_mode = 0 ("12345,67") means Saturday and Sunday follow the "weekend" schedule
|
# E.g. loop_mode = 0 ("12345,67") means Saturday and Sunday follow the "weekend" schedule
|
||||||
# loop_mode = 2 ("1234567") means every day (including Saturday and Sunday) follows the "weekday" schedule
|
# loop_mode = 2 ("1234567") means every day (including Saturday and Sunday) follows the "weekday" schedule
|
||||||
# The sensor command is currently experimental
|
# The sensor command is currently experimental
|
||||||
def set_mode(self, auto_mode, loop_mode,sensor=0):
|
def set_mode(self, auto_mode, loop_mode, sensor=0):
|
||||||
mode_byte = ( (loop_mode + 1) << 4) + auto_mode
|
mode_byte = ((loop_mode + 1) << 4) + auto_mode
|
||||||
# print 'Mode byte: 0x'+ format(mode_byte, '02x')
|
# print 'Mode byte: 0x'+ format(mode_byte, '02x')
|
||||||
self.send_request(bytearray([0x01,0x06,0x00,0x02,mode_byte,sensor]))
|
self.send_request(bytearray([0x01, 0x06, 0x00, 0x02, mode_byte, sensor]))
|
||||||
|
|
||||||
# Advanced settings
|
# Advanced settings
|
||||||
# Sensor mode (SEN) sensor = 0 for internal sensor, 1 for external sensor, 2 for internal control temperature, external limit temperature. Factory default: 0.
|
# Sensor mode (SEN) sensor = 0 for internal sensor, 1 for external sensor,
|
||||||
|
# 2 for internal control temperature, external limit temperature. Factory default: 0.
|
||||||
# Set temperature range for external sensor (OSV) osv = 5..99. Factory default: 42C
|
# Set temperature range for external sensor (OSV) osv = 5..99. Factory default: 42C
|
||||||
# Deadzone for floor temprature (dIF) dif = 1..9. Factory default: 2C
|
# Deadzone for floor temprature (dIF) dif = 1..9. Factory default: 2C
|
||||||
# Upper temperature limit for internal sensor (SVH) svh = 5..99. Factory default: 35C
|
# Upper temperature limit for internal sensor (SVH) svh = 5..99. Factory default: 35C
|
||||||
# Lower temperature limit for internal sensor (SVL) svl = 5..99. Factory default: 5C
|
# Lower temperature limit for internal sensor (SVL) svl = 5..99. Factory default: 5C
|
||||||
# Actual temperature calibration (AdJ) adj = -0.5. Prescision 0.1C
|
# Actual temperature calibration (AdJ) adj = -0.5. Prescision 0.1C
|
||||||
# Anti-freezing function (FrE) fre = 0 for anti-freezing function shut down, 1 for anti-freezing function open. Factory default: 0
|
# Anti-freezing function (FrE) fre = 0 for anti-freezing function shut down,
|
||||||
|
# 1 for anti-freezing function open. Factory default: 0
|
||||||
# Power on memory (POn) poweron = 0 for power on memory off, 1 for power on memory on. Factory default: 0
|
# Power on memory (POn) poweron = 0 for power on memory off, 1 for power on memory on. Factory default: 0
|
||||||
def set_advanced(self, loop_mode, sensor, osv, dif, svh, svl, adj, fre, poweron):
|
def set_advanced(self, loop_mode, sensor, osv, dif, svh, svl, adj, fre, poweron):
|
||||||
input_payload = bytearray([0x01,0x10,0x00,0x02,0x00,0x05,0x0a, loop_mode, sensor, osv, dif, svh, svl, (int(adj*2)>>8 & 0xff), (int(adj*2) & 0xff), fre, poweron])
|
input_payload = bytearray([0x01, 0x10, 0x00, 0x02, 0x00, 0x05, 0x0a, loop_mode, sensor, osv, dif, svh, svl,
|
||||||
|
(int(adj * 2) >> 8 & 0xff), (int(adj * 2) & 0xff), fre, poweron])
|
||||||
self.send_request(input_payload)
|
self.send_request(input_payload)
|
||||||
|
|
||||||
# For backwards compatibility only. Prefer calling set_mode directly. Note this function invokes loop_mode=0 and sensor=0.
|
# For backwards compatibility only. Prefer calling set_mode directly.
|
||||||
|
# Note this function invokes loop_mode=0 and sensor=0.
|
||||||
def switch_to_auto(self):
|
def switch_to_auto(self):
|
||||||
self.set_mode(auto_mode=1, loop_mode=0)
|
self.set_mode(auto_mode=1, loop_mode=0)
|
||||||
|
|
||||||
|
@ -733,16 +733,17 @@ class hysen(device):
|
||||||
|
|
||||||
# Set temperature for manual mode (also activates manual mode if currently in automatic)
|
# Set temperature for manual mode (also activates manual mode if currently in automatic)
|
||||||
def set_temp(self, temp):
|
def set_temp(self, temp):
|
||||||
self.send_request(bytearray([0x01,0x06,0x00,0x01,0x00,int(temp * 2)]) )
|
self.send_request(bytearray([0x01, 0x06, 0x00, 0x01, 0x00, int(temp * 2)]))
|
||||||
|
|
||||||
# Set device on(1) or off(0), does not deactivate Wifi connectivity. Remote lock disables control by buttons on thermostat.
|
# Set device on(1) or off(0), does not deactivate Wifi connectivity.
|
||||||
|
# Remote lock disables control by buttons on thermostat.
|
||||||
def set_power(self, power=1, remote_lock=0):
|
def set_power(self, power=1, remote_lock=0):
|
||||||
self.send_request(bytearray([0x01,0x06,0x00,0x00,remote_lock,power]) )
|
self.send_request(bytearray([0x01, 0x06, 0x00, 0x00, remote_lock, power]))
|
||||||
|
|
||||||
# set time on device
|
# set time on device
|
||||||
# n.b. day=1 is Monday, ..., day=7 is Sunday
|
# n.b. day=1 is Monday, ..., day=7 is Sunday
|
||||||
def set_time(self, hour, minute, second, day):
|
def set_time(self, hour, minute, second, day):
|
||||||
self.send_request(bytearray([0x01,0x10,0x00,0x08,0x00,0x02,0x04, hour, minute, second, day ]))
|
self.send_request(bytearray([0x01, 0x10, 0x00, 0x08, 0x00, 0x02, 0x04, hour, minute, second, day]))
|
||||||
|
|
||||||
# Set timer schedule
|
# Set timer schedule
|
||||||
# Format is the same as you get from get_full_status.
|
# Format is the same as you get from get_full_status.
|
||||||
|
@ -750,28 +751,28 @@ class hysen(device):
|
||||||
# {'start_hour':17, 'start_minute':30, 'temp': 22 }
|
# {'start_hour':17, 'start_minute':30, 'temp': 22 }
|
||||||
# Each one specifies the thermostat temp that will become effective at start_hour:start_minute
|
# Each one specifies the thermostat temp that will become effective at start_hour:start_minute
|
||||||
# weekend is similar but only has 2 (e.g. switch on in morning and off in afternoon)
|
# weekend is similar but only has 2 (e.g. switch on in morning and off in afternoon)
|
||||||
def set_schedule(self,weekday,weekend):
|
def set_schedule(self, weekday, weekend):
|
||||||
# Begin with some magic values ...
|
# Begin with some magic values ...
|
||||||
input_payload = bytearray([0x01,0x10,0x00,0x0a,0x00,0x0c,0x18])
|
input_payload = bytearray([0x01, 0x10, 0x00, 0x0a, 0x00, 0x0c, 0x18])
|
||||||
|
|
||||||
# Now simply append times/temps
|
# Now simply append times/temps
|
||||||
# weekday times
|
# weekday times
|
||||||
for i in range(0, 6):
|
for i in range(0, 6):
|
||||||
input_payload.append( weekday[i]['start_hour'] )
|
input_payload.append(weekday[i]['start_hour'])
|
||||||
input_payload.append( weekday[i]['start_minute'] )
|
input_payload.append(weekday[i]['start_minute'])
|
||||||
|
|
||||||
# weekend times
|
# weekend times
|
||||||
for i in range(0, 2):
|
for i in range(0, 2):
|
||||||
input_payload.append( weekend[i]['start_hour'] )
|
input_payload.append(weekend[i]['start_hour'])
|
||||||
input_payload.append( weekend[i]['start_minute'] )
|
input_payload.append(weekend[i]['start_minute'])
|
||||||
|
|
||||||
# weekday temperatures
|
# weekday temperatures
|
||||||
for i in range(0, 6):
|
for i in range(0, 6):
|
||||||
input_payload.append( int(weekday[i]['temp'] * 2) )
|
input_payload.append(int(weekday[i]['temp'] * 2))
|
||||||
|
|
||||||
# weekend temperatures
|
# weekend temperatures
|
||||||
for i in range(0, 2):
|
for i in range(0, 2):
|
||||||
input_payload.append( int(weekend[i]['temp'] * 2) )
|
input_payload.append(int(weekend[i]['temp'] * 2))
|
||||||
|
|
||||||
self.send_request(input_payload)
|
self.send_request(input_payload)
|
||||||
|
|
||||||
|
@ -787,6 +788,7 @@ class S1C(device):
|
||||||
"""
|
"""
|
||||||
Its VERY VERY VERY DIRTY IMPLEMENTATION of S1C
|
Its VERY VERY VERY DIRTY IMPLEMENTATION of S1C
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *a, **kw):
|
def __init__(self, *a, **kw):
|
||||||
device.__init__(self, *a, **kw)
|
device.__init__(self, *a, **kw)
|
||||||
self.type = 'S1C'
|
self.type = 'S1C'
|
||||||
|
@ -796,13 +798,14 @@ class S1C(device):
|
||||||
packet[0] = 0x06 # 0x06 - get sensors info, 0x07 - probably add sensors
|
packet[0] = 0x06 # 0x06 - get sensors info, 0x07 - probably add sensors
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv))
|
aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv))
|
||||||
|
|
||||||
payload = aes.decrypt(bytes(response[0x38:]))
|
payload = aes.decrypt(bytes(response[0x38:]))
|
||||||
if payload:
|
if not payload:
|
||||||
head = payload[:4]
|
return None
|
||||||
count = payload[0x4] #need to fix for python 2.x
|
count = payload[0x4]
|
||||||
sensors = payload[0x6:]
|
sensors = payload[0x6:]
|
||||||
sensors_a = [bytearray(sensors[i * 83:(i + 1) * 83]) for i in range(len(sensors) // 83)]
|
sensors_a = [bytearray(sensors[i * 83:(i + 1) * 83]) for i in range(len(sensors) // 83)]
|
||||||
|
|
||||||
|
@ -812,7 +815,7 @@ class S1C(device):
|
||||||
_name = str(bytes(sens[4:26]).decode())
|
_name = str(bytes(sens[4:26]).decode())
|
||||||
_order = ord(chr(sens[1]))
|
_order = ord(chr(sens[1]))
|
||||||
_type = ord(chr(sens[3]))
|
_type = ord(chr(sens[3]))
|
||||||
_serial = bytes(codecs.encode(sens[26:30],"hex")).decode()
|
_serial = bytes(codecs.encode(sens[26:30], "hex")).decode()
|
||||||
|
|
||||||
type_str = S1C_SENSORS_TYPES.get(_type, 'Unknown')
|
type_str = S1C_SENSORS_TYPES.get(_type, 'Unknown')
|
||||||
|
|
||||||
|
@ -833,7 +836,7 @@ class S1C(device):
|
||||||
|
|
||||||
|
|
||||||
class dooya(device):
|
class dooya(device):
|
||||||
def __init__ (self, host, mac, devtype):
|
def __init__(self, host, mac, devtype):
|
||||||
device.__init__(self, host, mac, devtype)
|
device.__init__(self, host, mac, devtype)
|
||||||
self.type = "Dooya DT360E"
|
self.type = "Dooya DT360E"
|
||||||
|
|
||||||
|
@ -847,7 +850,8 @@ class dooya(device):
|
||||||
packet[10] = 0x44
|
packet[10] = 0x44
|
||||||
response = self.send_packet(0x6a, packet)
|
response = self.send_packet(0x6a, packet)
|
||||||
err = response[0x22] | (response[0x23] << 8)
|
err = response[0x22] | (response[0x23] << 8)
|
||||||
if err == 0:
|
if err != 0:
|
||||||
|
return None
|
||||||
payload = self.decrypt(bytes(response[0x38:]))
|
payload = self.decrypt(bytes(response[0x38:]))
|
||||||
return ord(payload[4])
|
return ord(payload[4])
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue