Three parts of communication between python and Xiaomi gateway:
The understanding of the following content needs to be used in conjunction with the green meter gateway LAN communication protocol
1. Monitor the multicast information sent by the gateway: (there are life signals and event information of the gateway and connected equipment)
2. Read the required information
3. Control connection device (involving token encryption part)
1. upd broadcast monitors the multicast information of Xiaomi gateway
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 import socket 5 6 def get_gateway_heart(): 7 SENDERIP = "0.0.0.0" 8 MYPORT = 9898 9 MYGROUP = '224.0.0.50' 10 11 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 12 #allow multiple sockets to use the same PORT number 13 sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 14 #Bind to the port that we know will receive multicast data 15 sock.bind((SENDERIP,MYPORT)) 16 #tell the kernel that we are a multicast socket 17 #sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) 18 #Tell the kernel that we want to add ourselves to a multicast group 19 #The address for the multicast group is the third param 20 status = sock.setsockopt(socket.IPPROTO_IP, 21 socket.IP_ADD_MEMBERSHIP, 22 socket.inet_aton(MYGROUP) + socket.inet_aton(SENDERIP)); 23 24 #sock.setblocking(0) 25 #ts = time.time() 26 data, addr = sock.recvfrom(1024) 27 data_str=str(data,encoding='utf-8') 28 # sock.close() 29 return data_str 30 31 32 if __name__=='__main__': 33 while True: 34 tmp=get_gateway_heart() 35 print(tmp)
2. The calculation method of converting the initial password vector of Xiaomi gateway to string
1 from binascii import b2a_hex, a2b_hex 2 import sys 3 4 s='17996d093d28ddb3ba695a2e6f58562e' #Initial vector 5 m=a2b_hex(s) 6 print(m) 7 8 #Initial vector after conversion 9 #b'\x17\x99m\t=(\xdd\xb3\xbaiZ.oXV.'
3. The method of encrypting token
1 from Crypto.Cipher import AES 2 from binascii import b2a_hex, a2b_hex 3 4 class prpcrypt(): 5 def __init__(self, key='cy5zmrpqws05vsqj'): 6 self.key = key # 7 self.mode = AES.MODE_CBC 8 9 # Encryption function, if the text is less than 16 bits, use space to complement it to 16 bits, 10 # If more than 16 is not a multiple of 16 at that time, then make up for the multiple of 16. 11 def encrypt(self, text): #text is the content to encrypt 12 cryptor = AES.new(self.key, self.mode,b'\x17\x99m\t=(\xdd\xb3\xbaiZ.oXV.') 13 # Here key key Length must be 16( AES-128), 14 # 24(AES-192),Or 32. AES-256)Bytes length 15 # at present AES-128 Enough for current use 16 length = 16 17 count = len(text) 18 if count < length: 19 add = (length - count) 20 # \0 backspace 21 text = text + ('\0' * add) 22 elif count > length: 23 add = (length - (count % length)) 24 text = text + ('\0' * add) 25 self.ciphertext = cryptor.encrypt(text) 26 # Because the strings obtained during AES encryption are not necessarily ascii character sets, there may be problems when they are output to the terminal or saved 27 # Therefore, the encrypted string is converted into hexadecimal string 28 return str(b2a_hex(self.ciphertext),encoding='utf-8') 29 #return self.ciphertext 30 31 # After decryption, remove the complemented space strip() Remove b'0000000000000000' 32 def decrypt(self, text): 33 cryptor = AES.new(self.key, self.mode, b'\x17\x99m\t=(\xdd\xb3\xbaiZ.oXV.') 34 plain_text = cryptor.decrypt(a2b_hex(text)) 35 return plain_text.rstrip('\0') 36 37 38 if __name__ == '__main__': 39 pc = prpcrypt('0987654321qwerty') # Initialization key 40 e = pc.encrypt('1234567890abcdef') # encryption 41 # d = pc.decrypt(e) # Decrypt 42 print("encryption:", e) 43 #print("Decrypt:", d)
4. Obtain the token and encrypt the acquired token
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket import json from xm_gw.encrypty import prpcrypt def get_token(): #adopt get_id_list Get token ip_port_single = ("192.168.31.150", 9898) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) comd = {'cmd': 'get_id_list'} order = json.dumps(comd) s.sendto(bytes(order, encoding="utf-8"), ip_port_single) data,addr=s.recvfrom(1024) data_str=str(data,encoding='utf-8') token=json.loads(data_str).get('token') s.close() return token def get_token_encrypty(): tok = get_token() # Get the present token,What to encrypt k = prpcrypt() key_encrypt = k.encrypt(tok) return key_encrypt if __name__=='__main__': tok=get_token() tok_encry=get_token_encrypty() print(tok) print(tok_encry)
5. Establish gateway communication and execute control command
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import socket 4 import json 5 from xm_gw import udp_token_key 6 7 8 class udp_gw(): 9 def __init__(self, ip_gateway='192.168.8.100'): 10 self.ip_port_zu43 = ('224.0.0.50', 4321) 11 self.ip_port_single = (ip_gateway, 9898) 12 self.ip_port_zu9898=('224.0.0.50', 9898) 13 14 def whois(self): 15 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) 16 comd = {'cmd': 'whois'} 17 order = json.dumps(comd) 18 s.sendto(bytes(order, encoding="utf-8"), self.ip_port_zu43) 19 data_bytes, addr = s.recvfrom(1024) 20 data_dic = json.loads(str(data_bytes, encoding='utf-8')) 21 s.close() 22 return data_dic 23 24 def get_id_list(self): 25 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) 26 comd = {'cmd': 'get_id_list'} 27 order = json.dumps(comd) 28 s.sendto(bytes(order, encoding="utf-8"), self.ip_port_single) 29 data_bytes, addr = s.recvfrom(1024) 30 data_dic = json.loads(str(data_bytes, encoding='utf-8')) 31 s.close() 32 return data_dic 33 34 def read_sid(self, sid): 35 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) 36 comd = {'cmd': 'read', 'sid': sid} 37 order = json.dumps(comd) 38 s.sendto(bytes(order, encoding="utf-8"), self.ip_port_single) 39 data_bytes, addr = s.recvfrom(1024) 40 data_dic = json.loads(str(data_bytes, encoding='utf-8')) 41 s.close() 42 return data_dic 43 44 def write_plug(self, status): 45 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) 46 key_encrypt = udp_token_key.get_token_encrypty() 47 comd = {"cmd": "write", "model": "plug", "sid": "158d0001b84d9a", "short_id": 46384, 48 "data": {"status": status, 'key': key_encrypt}} 49 order = json.dumps(comd) 50 s.sendto(bytes(order, encoding="utf-8"), self.ip_port_single) 51 data_bytes, addr = s.recvfrom(1024) 52 data_dic = json.loads(str(data_bytes, encoding='utf-8')) 53 s.close() 54 return data_dic 55 56 def read_all_sid(self): 57 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) 58 ls = json.loads(self.get_id_list().get('data')) 59 ls_sensor_state = [] 60 for sid in ls: 61 comd = {'cmd': 'read', 'sid': sid} 62 order = json.dumps(comd) 63 s.sendto(bytes(order, encoding="utf-8"), self.ip_port_single) 64 data_bytes, addr = s.recvfrom(1024) 65 data_dic = json.loads(str(data_bytes, encoding='utf-8')) 66 # print(data_dic) 67 ls_sensor_state.append(data_dic) 68 s.close() 69 return ls_sensor_state 70 71 def get_dict_model_sid(self): 72 dic_gw=self.whois() 73 ls=self.read_all_sid() 74 dic_model_sid = {} 75 for dic in ls: 76 model = dic.get('model') 77 sid = dic.get('sid') 78 dic_model_sid[model] = sid 79 dic_model_sid['gateway'] = dic_gw.get('sid') 80 return dic_model_sid 81 82 83 if __name__=='__main__': 84 import time 85 #{'plug': '158d0001b84d9a', 'switch': '158d0001c10bd7', 'sensor_ht': '158d0001e87bd9', 86 # 'magnet': '158d0001bb3daf', 'motion': '158d0001c2f110', 'gateway': '7811dcb38599'} 87 gw=udp_gw('192.168.31.150') 88 tmp = gw.read_sid('158d0001b84d9a') 89 # print(tmp1) 90 # time.sleep(5) 91 # gw.write_plug('off') 92 # time.sleep(5) 93 # tmp=gw.read_sid('158d0001b84d9a') 94 95 print(tmp)