From 1a1169f1a9d7b9075e2cebfb10c3ae769237e6d0 Mon Sep 17 00:00:00 2001 From: Felipe Martins Diel <41558831+felipediel@users.noreply.github.com> Date: Mon, 16 Mar 2020 05:49:41 -0300 Subject: [PATCH] Add support for 0x5f36 devices and RM4 series (#317) * Add support for 0x5f36 devices This type of device requires a header in the payload. The rest is the same. * Improve request header assignment * Change code sending header I just found out that this device uses a different header for sending codes. This update addresses this issue. * Improve authentication Use the error code to check if the authentication was successful. * Use default value when devtype is None * Use generic remote type if devtype is None * Extend support to RM4 series I just realized that RM4 devices use the same header. I will take the opportunity to extend support to these devices as well. * Add device type 0x62be and create rm4 class The rm4 class will improve code scalability. Just add the RM4 type to this class and it will just work. * Remove comma --- broadlink/__init__.py | 81 ++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/broadlink/__init__.py b/broadlink/__init__.py index e2207ec..4bf9447 100644 --- a/broadlink/__init__.py +++ b/broadlink/__init__.py @@ -44,9 +44,14 @@ def gendevice(devtype, host, mac): 0x27a6, # RM2 Pro PP 0x278f, # RM Mini Shate 0x27c2, # RM Mini 3 - 0x27d1, #new RM Mini3 - 0x27de, # RM Mini 3 (C) + 0x27d1, # new RM Mini3 + 0x27de # RM Mini 3 (C) ], + rm4: [0x51da, # RM4b + 0x5f36, # RM Mini 3 + 0x610f, # RM4c + 0x62be # RM4c + ], a1: [0x2714], # A1 mp1: [0x4EB5, # MP1 0x4EF7 # Honyar oem mp1 @@ -146,7 +151,7 @@ class device: def __init__(self, host, mac, devtype, timeout=10): self.host = host self.mac = mac.encode() if isinstance(mac, str) else mac - self.devtype = devtype + self.devtype = devtype if devtype is not None else 0x272a self.timeout = timeout self.count = random.randrange(0xffff) self.iv = bytearray( @@ -204,11 +209,11 @@ class device: payload[0x36] = ord('1') response = self.send_packet(0x65, payload) - - payload = self.decrypt(response[0x38:]) - - if not payload: + + if any(response[0x22:0x24]): return False + + payload = self.decrypt(response[0x38:]) key = payload[0x04:0x14] if len(key) % 16 != 0: @@ -233,8 +238,8 @@ class device: packet[0x05] = 0xa5 packet[0x06] = 0xaa packet[0x07] = 0x55 - packet[0x24] = 0x2a - packet[0x25] = 0x27 + packet[0x24] = self.devtype & 0xff + packet[0x25] = self.devtype >> 8 packet[0x26] = command packet[0x28] = self.count & 0xff packet[0x29] = self.count >> 8 @@ -251,8 +256,8 @@ class device: # pad the payload for AES encryption if payload: - payload += bytearray(((len(payload)-1)//16+1)*16 - len(payload)) - + payload += bytearray(16 - len(payload)%16) + checksum = adler32(payload, 0xbeaf) & 0xffff packet[0x34] = checksum & 0xff packet[0x35] = checksum >> 8 @@ -571,76 +576,88 @@ class rm(device): def __init__(self, host, mac, devtype): device.__init__(self, host, mac, devtype) self.type = "RM2" + self._request_header = bytes() + self._code_sending_header = bytes() def check_data(self): - packet = bytearray(16) - packet[0] = 4 + packet = bytearray(self._request_header) + packet.append(0x04) response = self.send_packet(0x6a, packet) err = response[0x22] | (response[0x23] << 8) if err != 0: return None payload = self.decrypt(bytes(response[0x38:])) - return payload[0x04:] + return payload[len(self._request_header) + 4:] def send_data(self, data): - packet = bytearray([0x02, 0x00, 0x00, 0x00]) + packet = bytearray(self._code_sending_header) + packet += bytes([0x02, 0x00, 0x00, 0x00]) packet += data self.send_packet(0x6a, packet) def enter_learning(self): - packet = bytearray(16) - packet[0] = 3 + packet = bytearray(self._request_header) + packet.append(0x03) self.send_packet(0x6a, packet) def sweep_frequency(self): - packet = bytearray(16) - packet[0] = 0x19 + packet = bytearray(self._request_header) + packet.append(0x19) self.send_packet(0x6a, packet) def cancel_sweep_frequency(self): - packet = bytearray(16) - packet[0] = 0x1e + packet = bytearray(self._request_header) + packet.append(0x1e) self.send_packet(0x6a, packet) def check_frequency(self): - packet = bytearray(16) - packet[0] = 0x1a + packet = bytearray(self._request_header) + packet.append(0x1a) response = self.send_packet(0x6a, packet) err = response[0x22] | (response[0x23] << 8) if err != 0: return False payload = self.decrypt(bytes(response[0x38:])) - if payload[0x04] == 1: + if payload[len(self._request_header) + 4] == 1: return True return False def find_rf_packet(self): - packet = bytearray(16) - packet[0] = 0x1b + packet = bytearray(self._request_header) + packet.append(0x1b) response = self.send_packet(0x6a, packet) err = response[0x22] | (response[0x23] << 8) if err != 0: return False payload = self.decrypt(bytes(response[0x38:])) - if payload[0x04] == 1: + if payload[len(self._request_header) + 4] == 1: return True return False def check_temperature(self): - packet = bytearray(16) - packet[0] = 1 + packet = bytearray(self._request_header) + packet.append(0x01) response = self.send_packet(0x6a, packet) err = response[0x22] | (response[0x23] << 8) if err != 0: return False payload = self.decrypt(bytes(response[0x38:])) - if isinstance(payload[0x4], int): - temp = (payload[0x4] * 10 + payload[0x5]) / 10.0 + temp_pos = len(self._request_header) + 4 + if isinstance(payload[temp_pos], int): + temp = (payload[temp_pos] * 10 + payload[temp_pos+1]) / 10.0 else: - temp = (ord(payload[0x4]) * 10 + ord(payload[0x5])) / 10.0 + temp = (ord(payload[temp_pos]) * 10 + ord(payload[temp_pos+1])) / 10.0 return temp +class rm4(rm): + def __init__(self, host, mac, devtype): + device.__init__(self, host, mac, devtype) + self.type = "RM4" + self._request_header = b'\x04\x00' + self._code_sending_header = b'\xd0\x00' + + # For legacy compatibility - don't use this class rm2(rm): def __init__(self):