about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelipe Martins Diel <41558831+felipediel@users.noreply.github.com>2020-03-16T08·49-0300
committerGitHub <noreply@github.com>2020-03-16T08·49+0100
commit1a1169f1a9d7b9075e2cebfb10c3ae769237e6d0 (patch)
treee7b91e5178b4e290046d6b1879f063d68be7c1d1
parentaf95fa2446b7d8bf804c827e7bf4a8a9e67b4026 (diff)
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
-rw-r--r--broadlink/__init__.py81
1 files changed, 49 insertions, 32 deletions
diff --git a/broadlink/__init__.py b/broadlink/__init__.py
index e2207ec42d08..4bf94473a255 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):