From 5685ece018481b70436a9db5e58c99acdfb177f9 Mon Sep 17 00:00:00 2001 From: cheetahdotcat <99863633+cheetahdotcat@users.noreply.github.com> Date: Sun, 6 Feb 2022 16:41:57 +0000 Subject: [PATCH] added example --- examples/mqttdump.py | 269 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 examples/mqttdump.py diff --git a/examples/mqttdump.py b/examples/mqttdump.py new file mode 100644 index 0000000..e60dc47 --- /dev/null +++ b/examples/mqttdump.py @@ -0,0 +1,269 @@ +#!/bin/python3 + +# Mi365 Scooter Library +# MiAuth - Authenticate and interact with Xiaomi devices over BLE +# Copyright (C) 2021 Daljeet Nandha + modified by @catSIXe +# +# 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 . +# +import sys +import json +import os +import argparse +import time +from bluepy import btle + +from mim365mi.m365scooter import M365Scooter + +from message import * + +import struct + +parser = argparse.ArgumentParser() +parser.add_argument("mac", help="mac address of target device") +parser.add_argument("-c", "--command", help="send command (w/o checksum) to uart and print reply") +parser.add_argument("-s", "--serial", action='store_true', help="retrieve serial number") +parser.add_argument("-v", "--version", action='store_true', help="retrieve firmware version") +parser.add_argument("-d", "--debug", action='store_true', help="activate debug log") + +parser.add_argument("-r", "--register", action='store_true', + help="register with device / create token (caution: will lose bond to all other apps)") +parser.add_argument("-t", "--token_file", default="./mi_token", + help="path to mi token file (default: ./mi_token)") + +args = parser.parse_args() + + +from miauth.mi.micrypto import MiCrypto + +from paho.mqtt import client as mqtt_client +#broker = '65.108.182.254' +port = 1883 +broker = '127.0.0.1' +#port = 1880 +topic = "m365/test/" +# generate client ID with pub prefix randomly +client_id = f'john-xina' + +def connect_mqtt(): + def on_connect(client, userdata, flags, rc): + if rc == 0: + print("Connected to MQTT Broker!") + else: + print("Failed to connect, return code %d\n", rc) + + client = mqtt_client.Client(client_id) + client.username_pw_set('scooter', 'nigg2939294924958583') + client.on_connect = on_connect + client.connect(broker, port) + return client + +def main(): + mqtt = connect_mqtt() + mc = M365Scooter(btle.Peripheral(), args.mac, debug=args.debug) + + def lol(reg, payload): + payloadO = payload + payload = int.from_bytes(payload, signed=True, byteorder='little') + + if reg == '23010d': #Motor phase A current + reg = 'esc_phase_a_current' + if reg == '23010e': #Motor phase B current + reg = 'esc_phase_b_current' + if reg == '23010f': #Motor phase C current + reg = 'esc_phase_c_current' + if reg == '230175': #drive mode + reg = 'drive_mode' + if reg == '230122': #batt percent + reg = 'bms_level' + if reg == '230150': #current + reg = 'esc_current_ma' + payload = payload * 10 + if reg == '230148': #bms volt + reg = 'bms_volt' + payload = payload/1e2 + if reg == '230147': #esc volt + reg = 'esc_volt' + payload = payload/1e2 + if reg == '230126': #speed + reg = 'speed' + + if reg == '230129': #Total mileage, m + reg = 'total_km' + payload = payload/1e3 + if reg == '23012F': #Current mileage + reg = 'current_m' + if reg == '230132': #Total run time + reg = 'total_uptime' + if reg == '23013a': #triptime + reg = 'triptime1' + + if reg == '23013e': #frame_temp + reg = 'frame_temp1' + payload = payload/1e1 + if reg == '2301bb': #frame_temp + reg = 'frame_temp2' + payload = payload/1e1 + if reg == '250140': # cell voltages + reg = 'bms_cell_voltages' + payload = [] + for i in range(0, 10): + payload.append(int.from_bytes(payloadO[ (i*2) : (i*2)+2 ], signed=True, byteorder='little') /1e3) + payload = json.dumps(payload) + if reg == '25013b': # battery health + reg = 'bms_health' + if reg == '250130': # battery status + reg = 'bms_status' + + if reg == '25011c': # Charge count + reg = 'bms_chargecount' + if reg == '25011b': # Charge full cycles + reg = 'bms_chargefullcount' + if reg == '250110': # Serial number + reg = 'bms_serial' + payload = payloadO.decode() + if reg == '250133': # bms current x10mA + reg = 'bms_current_ma' + payload = payload * 10 + if reg == '250135': # bms temps high and low bytes, -20C offset + reg = 'bms_temp1' + payload = payloadO[ 1 ] - 20 + mqtt.publish(topic + reg, payload) + + reg = 'bms_temp2' + payload = payloadO[ 0 ] - 20 + print(reg, payload) + mqtt.publish(topic + reg, payload) + mc.handleData(lol) + + print("Connecting") + mc.connect() + + if args.register: + print("Registering") + mc.register() + mc.save_token(args.token_file) + print("Saved token to:", args.token_file) + + if not mc.token: + if not os.path.isfile(args.token_file): + sys.exit("""No authentication token found, register with '-r' or specify path to token file with '-t '.Caution: After registration this device will lose coupling to all other apps (remove/add device in Mi Home app if needed). """) + + print("Loading token from:", args.token_file) + mc.load_token(args.token_file) + + print("Logging in...") + mc.login() + + print("Retrieving serial number") + + mc.comm_simplex("55aa032001 10 0e") + #print("Serial number:", resp.decode()) + time.sleep(3) + + #print("Retrieving firmware version") + #resp = mc.comm("55aa032001 1a 10") + #print("Firmware version:", f"{resp[0]}.{resp[1]}") + + cmd = str(battery_info._raw_bytes.hex()) + print("Sending command:", cmd) + def scooterPowerManagement(reboot=False): + mc.comm_simplex(Message() \ + .set_direction(0x20) \ + .set_read_write(ReadWrite.WRITE) \ + .set_attribute(0x78 if reboot else 0x79) \ + .set_payload(b'\x01\x00') \ + .build()._raw_bytes.hex()) + + def on_message(client, userdata, msg): + print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic") + if msg.topic == topic +'cmds/poweroff': + scooterPowerManagement(True) + if msg.topic == topic +'cmds/reboot': + scooterPowerManagement(True) + + mqtt.subscribe(topic+'cmds/#') + mqtt.on_message = on_message + mqtt.loop_start() + i = 0 + while True: + i+=1 + # ESC + if i % 10 == 0: + mc.comm_simplex('55AA 03 20 01 29 04') # Total mileage, m + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 2F 02') # Current mileage + time.sleep(0.05) + + mc.comm_simplex('55AA 03 20 01 32 04') # Total run time + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 34 04') # ? some run time + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 3A 04') # ? trip time + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 3B 04') # ? trip time + time.sleep(0.05) + + mc.comm_simplex('55AA 03 20 01 22 02') # BMS Percent + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 26 02') # Speed + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 47 02') # ESC supply voltage (measured by ESC) + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 48 02') # Battery voltage (from BMS) + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 50 02') # Battery current (from BMS) + time.sleep(0.05) + if i % 10 == 0: + mc.comm_simplex('55AA 03 20 01 75 02') + time.sleep(0.05) + # ESC Temperatures + if i % 10 == 0: + mc.comm_simplex('55AA 03 20 01 BB 02') #Frame temperature + time.sleep(0.05) + mc.comm_simplex('55AA 03 20 01 3E 02') # Frame temperature + time.sleep(0.05) + + # BMS + if i % 10 == 0: + mc.comm_simplex('55AA 03 22 01 40 14') # Cell Voltage 1 - 10 + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 35 02') # bTemperature1:bTemperature2, Deg C, 0 is -20 + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 30 02') # Status + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 33 02') #Current, x10mA, positive - discharging, negative - charging + time.sleep(0.05) + if i % 180 == 0: + mc.comm_simplex('55AA 03 22 01 10 0E') #Serial number + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 17 02') #Firmware version + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 18 02') #Factory capacity + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 20 02') #Manufacture date + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 1B 02') #Charge full cycles + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 1C 02') #Charge count + time.sleep(0.05) + mc.comm_simplex('55AA 03 22 01 3B 02') #Health, % + time.sleep(0.05) + + print("Disconnecting") + mc.disconnect() + + +if __name__ == "__main__": + main() \ No newline at end of file