Initial commit
commit
774ad13e95
@ -0,0 +1,103 @@
|
|||||||
|
#
|
||||||
|
# MiAuth - Authenticate and interact with Xiaomi devices over BLE
|
||||||
|
# Copyright (C) 2021 Daljeet Nandha
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import time
|
||||||
|
|
||||||
|
from bluepy import btle
|
||||||
|
|
||||||
|
from miauth.mi.micommand import MiCommand
|
||||||
|
from miauth.mi.micrypto import MiCrypto
|
||||||
|
from miauth.uuid import UUID
|
||||||
|
from miauth.mi.miclient import MiClient
|
||||||
|
|
||||||
|
class M365Scooter(MiClient):
|
||||||
|
def __init__(self, p, mac, debug=False):
|
||||||
|
super().__init__(p, mac, debug)
|
||||||
|
self.cachedData = {}
|
||||||
|
self.dataUpdated = None
|
||||||
|
|
||||||
|
def handleData(self, callback):
|
||||||
|
self.dataUpdated = callback
|
||||||
|
|
||||||
|
def main_handler(self, data):
|
||||||
|
if len(data) == 0:
|
||||||
|
return
|
||||||
|
#if self.debug:
|
||||||
|
#print("<-", data.hex())
|
||||||
|
frm = data[0] + 0x100 * data[1]
|
||||||
|
|
||||||
|
if self.get_state() in [MiClient.State.RECV_INFO,
|
||||||
|
MiClient.State.RECV_KEY]:
|
||||||
|
self.receive_handler(frm, data)
|
||||||
|
elif self.get_state() in [MiClient.State.SEND_KEY,
|
||||||
|
MiClient.State.SEND_DID]:
|
||||||
|
self.send_handler(frm, data)
|
||||||
|
elif self.get_state() == MiClient.State.CONFIRM:
|
||||||
|
self.confirm_handler(frm)
|
||||||
|
elif self.get_state() == MiClient.State.COMM:
|
||||||
|
# TODO: check if correct number of frames received
|
||||||
|
self.received_data += data
|
||||||
|
#print('attaching data', len(self.received_data), len(data), data.hex())
|
||||||
|
if len(data) != 0:
|
||||||
|
try:
|
||||||
|
recvData = MiCrypto.decrypt_uart(
|
||||||
|
self.keys['dev_key'],
|
||||||
|
self.keys['dev_iv'],
|
||||||
|
self.received_data
|
||||||
|
) #[3:-4]
|
||||||
|
self.received_data = b''
|
||||||
|
if recvData[0:3].hex() in self.cachedData.keys():
|
||||||
|
if recvData[3:-4].hex() != self.cachedData[ recvData[0:3].hex() ].hex():
|
||||||
|
self.dataUpdated(recvData[0:3].hex(), recvData[3:-4])
|
||||||
|
else:
|
||||||
|
self.dataUpdated(recvData[0:3].hex(), recvData[3:-4])
|
||||||
|
self.cachedData[ recvData[0:3].hex() ] = recvData[3:-4]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def comm_simplex(self, cmd):
|
||||||
|
if self.get_state() != MiClient.State.COMM:
|
||||||
|
raise Exception("Not in COMM state. Retry maybe.")
|
||||||
|
|
||||||
|
if type(cmd) not in [bytearray, bytes]:
|
||||||
|
cmd = bytes.fromhex(cmd)
|
||||||
|
|
||||||
|
if cmd[:2] != b'\x55\xAA':
|
||||||
|
if cmd[:2] == b'\x5a\xa5':
|
||||||
|
raise Exception("Command must start with 55 AA (M365 PROTOCOl)!\
|
||||||
|
You sent a Nb command, try Nb pairing instead.")
|
||||||
|
else:
|
||||||
|
raise Exception("Command must start with 55 AA (M365 PROTOCOl)!")
|
||||||
|
|
||||||
|
self.received_data = b''
|
||||||
|
if not self.keys:
|
||||||
|
self.bt_write(self.ch_tx, cmd)
|
||||||
|
|
||||||
|
while self.p.waitForNotifications(3.0):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not self.received_data:
|
||||||
|
raise Exception("No answer received. Try login first.")
|
||||||
|
|
||||||
|
return self.received_data
|
||||||
|
|
||||||
|
res = MiCrypto.encrypt_uart(self.keys['app_key'], self.keys['app_iv'], cmd, it=self.uart_it)
|
||||||
|
self.bt_write(self.ch_tx, res)
|
||||||
|
self.uart_it += 1
|
||||||
|
|
||||||
|
|
||||||
|
#
|
@ -0,0 +1,14 @@
|
|||||||
|
from setuptools import find_packages, setup
|
||||||
|
setup(
|
||||||
|
name='mim365mi',
|
||||||
|
version='1.0.0',
|
||||||
|
url='https://github.com/catSIXe/m365-mi',
|
||||||
|
license='GNU AGPL v3',
|
||||||
|
author='catSIXe',
|
||||||
|
description='Authenticate and interact with Xiaomi M365 Scooters over BLE(the new Protocol)',
|
||||||
|
packages=find_packages(include=['mim365mi', 'mim365mi.*']),
|
||||||
|
install_requires=[
|
||||||
|
'miauth==0.9.2'
|
||||||
|
],
|
||||||
|
python_requires=">=3.6",
|
||||||
|
)
|
Loading…
Reference in New Issue