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 | ||||
| 
 | ||||
| from datetime import datetime | ||||
| 
 | ||||
| try: | ||||
|     from Crypto.Cipher import AES | ||||
| except ImportError as e: | ||||
|  | @ -9,7 +10,6 @@ except ImportError as e: | |||
| import time | ||||
| import random | ||||
| import socket | ||||
| import sys | ||||
| import threading | ||||
| import codecs | ||||
| 
 | ||||
|  | @ -60,6 +60,7 @@ def gendevice(devtype, host, mac): | |||
|         return device(host=host, mac=mac, devtype=devtype) | ||||
|     return deviceClass(host=host, mac=mac, devtype=devtype) | ||||
| 
 | ||||
| 
 | ||||
| def discover(timeout=None, local_ip_address=None): | ||||
|     if local_ip_address is None: | ||||
|         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.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 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] | ||||
|     starttime = time.time() | ||||
| 
 | ||||
|     devices = [] | ||||
| 
 | ||||
|   timezone = int(time.timezone/-3600) | ||||
|     timezone = int(time.timezone / -3600) | ||||
|     packet = bytearray(0x30) | ||||
| 
 | ||||
|     year = datetime.now().year | ||||
|  | @ -122,9 +123,8 @@ def discover(timeout=None, local_ip_address=None): | |||
|         mac = responsepacket[0x3a:0x40] | ||||
|         devtype = responsepacket[0x34] | responsepacket[0x35] << 8 | ||||
| 
 | ||||
| 
 | ||||
|         return gendevice(devtype, host, mac) | ||||
|   else: | ||||
| 
 | ||||
|     while (time.time() - starttime) < timeout: | ||||
|         cs.settimeout(timeout - (time.time() - starttime)) | ||||
|         try: | ||||
|  | @ -140,7 +140,6 @@ def discover(timeout=None, local_ip_address=None): | |||
|     return devices | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class device: | ||||
|     def __init__(self, host, mac, devtype, timeout=10): | ||||
|         self.host = host | ||||
|  | @ -148,13 +147,15 @@ class device: | |||
|         self.devtype = devtype | ||||
|         self.timeout = timeout | ||||
|         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.iv = bytearray([0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58]) | ||||
|         self.key = bytearray( | ||||
|             [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.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_BROADCAST, 1) | ||||
|     self.cs.bind(('',0)) | ||||
|         self.cs.bind(('', 0)) | ||||
|         self.type = "Unknown" | ||||
|         self.lock = threading.Lock() | ||||
| 
 | ||||
|  | @ -166,12 +167,12 @@ class device: | |||
|             self.decrypt = self.decrypt_pycrypto | ||||
| 
 | ||||
|     def encrypt_pyaes(self, payload): | ||||
|     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)]) | ||||
|         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)]) | ||||
| 
 | ||||
|     def decrypt_pyaes(self, payload): | ||||
|     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)]) | ||||
|         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)]) | ||||
| 
 | ||||
|     def encrypt_pycrypto(self, payload): | ||||
|         aes = AES.new(bytes(self.key), AES.MODE_CBC, bytes(self.iv)) | ||||
|  | @ -255,9 +256,9 @@ class device: | |||
|         packet[0x33] = self.id[3] | ||||
| 
 | ||||
|         # pad the payload for AES encryption | ||||
|     if len(payload)>0: | ||||
|       numpad=(len(payload)//16+1)*16 | ||||
|       payload=payload.ljust(numpad, b"\x00") | ||||
|         if payload: | ||||
|             numpad = (len(payload) // 16 + 1) * 16 | ||||
|             payload = payload.ljust(numpad, b"\x00") | ||||
| 
 | ||||
|         checksum = 0xbeaf | ||||
|         for i in range(len(payload)): | ||||
|  | @ -294,7 +295,7 @@ class device: | |||
| 
 | ||||
| 
 | ||||
| class mp1(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "MP1" | ||||
| 
 | ||||
|  | @ -307,16 +308,14 @@ class mp1(device): | |||
|         packet[0x03] = 0xa5 | ||||
|         packet[0x04] = 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[0x08] = 0x02 | ||||
|         packet[0x0a] = 0x03 | ||||
|         packet[0x0d] = sid_mask | ||||
|         packet[0x0e] = sid_mask if state else 0 | ||||
| 
 | ||||
|     response = self.send_packet(0x6a, packet) | ||||
| 
 | ||||
|     err = response[0x22] | (response[0x23] << 8) | ||||
|         self.send_packet(0x6a, packet) | ||||
| 
 | ||||
|     def set_power(self, sid, state): | ||||
|         """Sets the power state of the smart power strip.""" | ||||
|  | @ -337,9 +336,10 @@ class mp1(device): | |||
| 
 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|       if type(payload[0x4]) == int: | ||||
|         if isinstance(payload[0x4], int): | ||||
|             state = payload[0x0e] | ||||
|         else: | ||||
|             state = ord(payload[0x0e]) | ||||
|  | @ -357,7 +357,7 @@ class mp1(device): | |||
| 
 | ||||
| 
 | ||||
| class sp1(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "SP1" | ||||
| 
 | ||||
|  | @ -368,7 +368,7 @@ class sp1(device): | |||
| 
 | ||||
| 
 | ||||
| class sp2(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "SP2" | ||||
| 
 | ||||
|  | @ -398,19 +398,12 @@ class sp2(device): | |||
|         packet[0] = 1 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|       if type(payload[0x4]) == int: | ||||
|         if payload[0x4] == 1 or payload[0x4] == 3 or payload[0x4] == 0xFD: | ||||
|           state = True | ||||
|         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 | ||||
|         if isinstance(payload[0x4], int): | ||||
|             return bool(payload[0x4] == 1 or payload[0x4] == 3 or payload[0x4] == 0xFD) | ||||
|         return bool(ord(payload[0x4]) == 1 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFD) | ||||
| 
 | ||||
|     def check_nightlight(self): | ||||
|         """Returns the power state of the smart plug.""" | ||||
|  | @ -418,35 +411,30 @@ class sp2(device): | |||
|         packet[0] = 1 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|       if type(payload[0x4]) == int: | ||||
|         if payload[0x4] == 2 or payload[0x4] == 3 or payload[0x4] == 0xFF: | ||||
|           state = True | ||||
|         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 | ||||
|         if isinstance(payload[0x4], int): | ||||
|             return bool(payload[0x4] == 2 or payload[0x4] == 3 or payload[0x4] == 0xFF) | ||||
|         return bool(ord(payload[0x4]) == 2 or ord(payload[0x4]) == 3 or ord(payload[0x4]) == 0xFF) | ||||
| 
 | ||||
|     def get_energy(self): | ||||
|         packet = bytearray([8, 0, 254, 1, 5, 1, 0, 0, 0, 45]) | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|       if type(payload[0x07]) == int: | ||||
|         energy = int(hex(payload[0x07] * 256 + payload[0x06])[2:]) + int(hex(payload[0x05])[2:])/100.0 | ||||
|         if isinstance(payload[0x7], int): | ||||
|             energy = int(hex(payload[0x07] * 256 + payload[0x06])[2:]) + int(hex(payload[0x05])[2:]) / 100.0 | ||||
|         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 | ||||
| 
 | ||||
| 
 | ||||
| class a1(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "A1" | ||||
| 
 | ||||
|  | @ -455,10 +443,11 @@ class a1(device): | |||
|         packet[0] = 1 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         data = {} | ||||
|         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['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0 | ||||
|             light = payload[0x8] | ||||
|  | @ -505,10 +494,11 @@ class a1(device): | |||
|         packet[0] = 1 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         data = {} | ||||
|         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['humidity'] = (payload[0x6] * 10 + payload[0x7]) / 10.0 | ||||
|             data['light'] = payload[0x8] | ||||
|  | @ -524,7 +514,7 @@ class a1(device): | |||
| 
 | ||||
| 
 | ||||
| class rm(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "RM2" | ||||
| 
 | ||||
|  | @ -533,7 +523,8 @@ class rm(device): | |||
|         packet[0] = 4 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|         return payload[0x04:] | ||||
| 
 | ||||
|  | @ -562,7 +553,8 @@ class rm(device): | |||
|         packet[0] = 0x1a | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return False | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|         if payload[0x04] == 1: | ||||
|             return True | ||||
|  | @ -573,7 +565,8 @@ class rm(device): | |||
|         packet[0] = 0x1b | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return False | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|         if payload[0x04] == 1: | ||||
|             return True | ||||
|  | @ -584,9 +577,10 @@ class rm(device): | |||
|         packet[0] = 1 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return False | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|       if type(payload[0x4]) == int: | ||||
|         if isinstance(payload[0x4], int): | ||||
|             temp = (payload[0x4] * 10 + payload[0x5]) / 10.0 | ||||
|         else: | ||||
|             temp = (ord(payload[0x4]) * 10 + ord(payload[0x5])) / 10.0 | ||||
|  | @ -595,7 +589,7 @@ class rm(device): | |||
| 
 | ||||
| # For legacy compatibility - don't use this | ||||
| class rm2(rm): | ||||
|   def __init__ (self): | ||||
|     def __init__(self): | ||||
|         device.__init__(self, None, None, None) | ||||
| 
 | ||||
|     def discover(self): | ||||
|  | @ -605,7 +599,7 @@ class rm2(rm): | |||
| 
 | ||||
| 
 | ||||
| class hysen(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "Hysen heating controller" | ||||
| 
 | ||||
|  | @ -614,13 +608,13 @@ class hysen(device): | |||
|     # Returns decrypted payload | ||||
|     # 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 | ||||
|   def send_request(self,input_payload): | ||||
|     def send_request(self, input_payload): | ||||
| 
 | ||||
|         from PyCRC.CRC16 import CRC16 | ||||
|         crc = CRC16(modbus_flag=True).calculate(bytes(input_payload)) | ||||
| 
 | ||||
|         # 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) | ||||
| 
 | ||||
|         # append CRC | ||||
|  | @ -633,41 +627,40 @@ class hysen(device): | |||
|         # check for error | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|         if err: | ||||
|       raise ValueError('broadlink_response_error',err) | ||||
|             raise ValueError('broadlink_response_error', err) | ||||
| 
 | ||||
|         response_payload = bytearray(self.decrypt(bytes(response[0x38:]))) | ||||
| 
 | ||||
|         # experimental check on CRC in response (first 2 bytes are len, and trailing bytes are crc) | ||||
|         response_payload_len = response_payload[0] | ||||
|         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])) | ||||
|     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] | ||||
|     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 | ||||
|     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 | ||||
| 
 | ||||
|     # Get current external temperature in degrees celsius | ||||
|     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 | ||||
| 
 | ||||
|     # Get full status (including timer schedule) | ||||
|     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['remote_lock'] = payload[3] & 1 | ||||
|         data['power'] = payload[4] & 1 | ||||
|         data['active'] = (payload[4] >> 4) & 1 | ||||
|         data['temp_manual'] = (payload[4] >> 6) & 1 | ||||
|     data['room_temp'] =  (payload[5] & 255)/2.0 | ||||
|     data['thermostat_temp'] =  (payload[6] & 255)/2.0 | ||||
|         data['room_temp'] = (payload[5] & 255) / 2.0 | ||||
|         data['thermostat_temp'] = (payload[6] & 255) / 2.0 | ||||
|         data['auto_mode'] = payload[7] & 15 | ||||
|         data['loop_mode'] = (payload[7] >> 4) & 15 | ||||
|         data['sensor'] = payload[8] | ||||
|  | @ -675,13 +668,13 @@ class hysen(device): | |||
|         data['dif'] = payload[10] | ||||
|         data['svh'] = payload[11] | ||||
|         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: | ||||
|             data['room_temp_adj'] = 32767 - data['room_temp_adj'] | ||||
|         data['fre'] = payload[15] | ||||
|         data['poweron'] = payload[16] | ||||
|         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['min'] = payload[20] | ||||
|         data['sec'] = payload[21] | ||||
|  | @ -689,42 +682,49 @@ class hysen(device): | |||
| 
 | ||||
|         weekday = [] | ||||
|         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 | ||||
|         weekend = [] | ||||
|         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 | ||||
|         return data | ||||
| 
 | ||||
|     # Change controller 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" ] | ||||
|     # 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 | ||||
|     # The sensor command is currently experimental | ||||
|   def set_mode(self, auto_mode, loop_mode,sensor=0): | ||||
|     mode_byte = ( (loop_mode + 1) << 4) + auto_mode | ||||
|     def set_mode(self, auto_mode, loop_mode, sensor=0): | ||||
|         mode_byte = ((loop_mode + 1) << 4) + auto_mode | ||||
|         # 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 | ||||
|   # 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 | ||||
|     # Deadzone for floor temprature (dIF) dif = 1..9. Factory default: 2C | ||||
|     # 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 | ||||
|     # 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 | ||||
|     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) | ||||
| 
 | ||||
|   # 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): | ||||
|         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) | ||||
|     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): | ||||
|     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 | ||||
|     # n.b. day=1 is Monday, ..., day=7 is Sunday | ||||
|     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 | ||||
|     # 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 } | ||||
|     # 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) | ||||
|   def set_schedule(self,weekday,weekend): | ||||
|     def set_schedule(self, weekday, weekend): | ||||
|         # 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 | ||||
|         # weekday times | ||||
|         for i in range(0, 6): | ||||
|       input_payload.append( weekday[i]['start_hour'] ) | ||||
|       input_payload.append( weekday[i]['start_minute'] ) | ||||
|             input_payload.append(weekday[i]['start_hour']) | ||||
|             input_payload.append(weekday[i]['start_minute']) | ||||
| 
 | ||||
|         # weekend times | ||||
|         for i in range(0, 2): | ||||
|       input_payload.append( weekend[i]['start_hour'] ) | ||||
|       input_payload.append( weekend[i]['start_minute'] ) | ||||
|             input_payload.append(weekend[i]['start_hour']) | ||||
|             input_payload.append(weekend[i]['start_minute']) | ||||
| 
 | ||||
|         # weekday temperatures | ||||
|         for i in range(0, 6): | ||||
|       input_payload.append( int(weekday[i]['temp'] * 2) ) | ||||
|             input_payload.append(int(weekday[i]['temp'] * 2)) | ||||
| 
 | ||||
|         # weekend temperatures | ||||
|         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) | ||||
| 
 | ||||
|  | @ -787,6 +788,7 @@ class S1C(device): | |||
|     """ | ||||
|     Its VERY VERY VERY DIRTY IMPLEMENTATION of S1C | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, *a, **kw): | ||||
|         device.__init__(self, *a, **kw) | ||||
|         self.type = 'S1C' | ||||
|  | @ -796,13 +798,14 @@ class S1C(device): | |||
|         packet[0] = 0x06  # 0x06 - get sensors info, 0x07 - probably add sensors | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         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)) | ||||
| 
 | ||||
|         payload = aes.decrypt(bytes(response[0x38:])) | ||||
|       if payload: | ||||
|         head = payload[:4] | ||||
|         count = payload[0x4] #need to fix for python 2.x | ||||
|         if not payload: | ||||
|             return None | ||||
|         count = payload[0x4] | ||||
|         sensors = payload[0x6:] | ||||
|         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()) | ||||
|             _order = ord(chr(sens[1])) | ||||
|             _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') | ||||
| 
 | ||||
|  | @ -833,7 +836,7 @@ class S1C(device): | |||
| 
 | ||||
| 
 | ||||
| class dooya(device): | ||||
|   def __init__ (self, host, mac, devtype): | ||||
|     def __init__(self, host, mac, devtype): | ||||
|         device.__init__(self, host, mac, devtype) | ||||
|         self.type = "Dooya DT360E" | ||||
| 
 | ||||
|  | @ -847,7 +850,8 @@ class dooya(device): | |||
|         packet[10] = 0x44 | ||||
|         response = self.send_packet(0x6a, packet) | ||||
|         err = response[0x22] | (response[0x23] << 8) | ||||
|     if err == 0: | ||||
|         if err != 0: | ||||
|             return None | ||||
|         payload = self.decrypt(bytes(response[0x38:])) | ||||
|         return ord(payload[4]) | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue