• Have you tried out dark mode?! Scroll to the bottom of any page to find a sun or moon icon to turn dark mode on or off!

diy solar

diy solar

Monitoring Bluetti Systems

chromedshark

New Member
Joined
Apr 3, 2022
Messages
34
TLDR: I'm looking for packet captures to confirm message checksum behavior and to verify behavior for other systems than a single AC300 + B300.

The AC300 (and other larger Bluetti devices) support both bluetooth and wifi for control. Bluetooth control does not require a pin, so anybody nearby could connect and control your device. Wifi control requires internet, and connects to Bluetti's server over an unencrypted connection. I want secure local control of my device(s), which means reversing the internet protocol and running my own compatible server (with eventual monitoring and a web interface for control).

The Bluetti devices speak unencrypted MQTT v3.1.1 to iot.bluettipower.com on port 18760, with some non-standard extra message types used during the connection process. The device publishes to the topic "PUB/AC300/[device_serial_number_here]", and subscribes to a similarly named "SUB/..." topic. The payload appears to be a totally custom protocol revolving around reading from and updating 512 byte "pages" of data. The device regularly publishes updates to certain pages at a variety of offsets, and it can be polled more frequently for any data in any of the pages. The device is controlled by publishing updates to these pages through the "SUB/..." topic. All messages have a 2 byte checksum, and if it's wrong it will be ignored by the device.

I have managed to locate all the information the app displays, as well as figure out how to control everything the app does. There appears to be some detailed battery information that I haven't yet bothered to reverse, and there's a lot of extra fields that I have no clue on their purpose. I have some rough code up on Github, but my eventual goal is to re-write it all in Go so that it's easier to distribute and run. My eventual plan is a single solution MQTT server, metrics exporter (Prometheus), web api, and simple web interface for control and monitoring. The simplest deploy would be a raspberry pi running as an access point to simplify the DNS override, but if you wanted to you could deploy it wherever you wanted on your network. That said I've still got a ways to go to get there.

The one big thing holding me back right now is the message checksum. It's definitely a standard CRC16, but there's some kind of "prefix" that they're using that I haven't been able to figure out. I was able to bypass it for my device using a nice tool someone wrote, but if it's different per device then I'll need to write some code to calculate it before I can release anything. If I could get some packet captures of other people's devices, that would help immensely in figuring this out. WARNING: A full packet capture will include device passwords, which it might be possible to abuse, so I would recommend starting your capture after the device is connected to wifi. Additionally, if you go to the "About Device" section in the app, your device will send the current wifi ESSID it's connected to, as well as your wifi password, so I would stay away from that.
 
TLDR: I'm looking for packet captures to confirm message checksum behavior and to verify behavior for other systems than a single AC300 + B300.

The AC300 (and other larger Bluetti devices) support both bluetooth and wifi for control. Bluetooth control does not require a pin, so anybody nearby could connect and control your device. Wifi control requires internet, and connects to Bluetti's server over an unencrypted connection. I want secure local control of my device(s), which means reversing the internet protocol and running my own compatible server (with eventual monitoring and a web interface for control).

The Bluetti devices speak unencrypted MQTT v3.1.1 to iot.bluettipower.com on port 18760, with some non-standard extra message types used during the connection process. The device publishes to the topic "PUB/AC300/[device_serial_number_here]", and subscribes to a similarly named "SUB/..." topic. The payload appears to be a totally custom protocol revolving around reading from and updating 512 byte "pages" of data. The device regularly publishes updates to certain pages at a variety of offsets, and it can be polled more frequently for any data in any of the pages. The device is controlled by publishing updates to these pages through the "SUB/..." topic. All messages have a 2 byte checksum, and if it's wrong it will be ignored by the device.

I have managed to locate all the information the app displays, as well as figure out how to control everything the app does. There appears to be some detailed battery information that I haven't yet bothered to reverse, and there's a lot of extra fields that I have no clue on their purpose. I have some rough code up on Github, but my eventual goal is to re-write it all in Go so that it's easier to distribute and run. My eventual plan is a single solution MQTT server, metrics exporter (Prometheus), web api, and simple web interface for control and monitoring. The simplest deploy would be a raspberry pi running as an access point to simplify the DNS override, but if you wanted to you could deploy it wherever you wanted on your network. That said I've still got a ways to go to get there.

The one big thing holding me back right now is the message checksum. It's definitely a standard CRC16, but there's some kind of "prefix" that they're using that I haven't been able to figure out. I was able to bypass it for my device using a nice tool someone wrote, but if it's different per device then I'll need to write some code to calculate it before I can release anything. If I could get some packet captures of other people's devices, that would help immensely in figuring this out. WARNING: A full packet capture will include device passwords, which it might be possible to abuse, so I would recommend starting your capture after the device is connected to wifi. Additionally, if you go to the "About Device" section in the app, your device will send the current wifi ESSID it's connected to, as well as your wifi password, so I would stay away from that.
Hi there, thanks for your work so far !

I'm considering an AC200Max, which only has bluetooth. Any pointers on how to get started with something like a Pi with bluetooth so I could get battery status etc and ideally control ?
 
The general process is to start by getting some form of monitoring that allows you to use the app on your phone and observe what is communicated. Once you have enough data you can start building your own client implementation. I think both iOS and Android have developer tools for doing this, but I’ve not used either of them. I eventually would like to reverse the bluetooth protocol, just to see if it provides more information, but I didn’t want to start there because it can be a pain to program for.

(In theory the faster way to figure it out is to decompile the Android app, but I wasn’t sure on the legality of that, and I didn’t want to have any legal concerns with releasing the result of my reversing.)
 
I eventually would like to reverse the bluetooth protocol

Hi, not sure if you've had a go at doing this yet

I've captured bluetooth packets between my iOS device and my AC200Max while using the Bluetti app to turn DC and AC outlets on and off

service FF01 is notify
service FF02 is write
service FF03 and FF04 are also advertised, but they weren't involved in my testing

The app sends the following 2 commands repeatedly ? Perhaps to get battery stats etc ?
Code:
 Write Command - FF02 - Value: 0103 000A 0037 241E
Code:
 Write Command - FF02 - Value: 0103 0BB9 003D 57DA

In response to the above commands, I get packets returned on FF01 such as
Code:
0103 6E41 4332 3030 4D00 0000 0000 0003
4002 0100 0000 0000 001D A200 061C 7B00
0100 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 6400 0000 0000
0100 0000 0000 0000 0000 0000 0000 0000
0000 00C0 0000 0000 0000 0000 0003 E4

which I'm not sure how to decode... could they be encrypted ?


In better news, I did find the commands for DC on/off and AC on/off:

DC on:
Code:
 Write Command - FF02 - Value: 0106 0BC0 0001 4A12

DC off:
Code:
 Write Command - FF02 - Value: 0106 0BC0 0000 8BD2

AC on:
Code:
 Write Command - FF02 - Value: 0106 0BBF 0001 7BCA

AC off:
Code:
 Write Command - FF02 - Value: 0106 0BBF 0000 BA0A


If you had any pointers on how to extract the received info from the Bluetti, that would be great

Thanks !
 
Oh wow, nice work! It looks like it works almost exactly like what I'm seeing over MQTT, so that's great news!

Code:
Write Command - FF02 - Value: 0103 000A 0037 241E
  1. The MQTT packets start with "0101", but the bluetooth packets appear to start with just "01". This just appears to be a fixed prefix to each message.
  2. The next byte, 0x03, tells you what kind of command we're looking at. 0x03 is the "range read" command (0x06 and 0x10 are other types with different behavior).
  3. The next byte, 0x00, tells you what "page" we're reading from. Page 0x00 includes all the status information about the power station (power input/output, software version, battery state, inverter mode, etc.). Page 0x0B is where all the configurable things are (output on/off, schedule, the current time, etc.). Page 0x13 on the AC300 has wifi information, but presumably isn't used on the AC200Max.
  4. The next byte, 0x0A, tells you what "offset" we're reading from. Each page seems to consist of 512 bytes, and this protocol works in 2 byte offsets (and sizes). An offset of 0x0A means that it starts at 2 * 10 bytes into the page and then reads from there.
  5. The next two bytes, 0x0037, are used as a 16-bit unsigned integer for the length. Just like the offset, this needs to be multiplied by 2 to get the actual size in bytes it's requesting (110 bytes in this case).
  6. The final two bytes, 0x241E, are the checksum. I can calculate this checksum with the code I already have, which is a good sign that I should be able to use it for any devices they have. When I add "01" to the beginning and checksum everything except the last two bytes, I also get 0x241E (which suggests that I'm probably running the checksum on the wrong stuff if changing the payload does not change the resulting checksum).
Code:
Notify Command - FF01 - Value: 0103 6E41 4332 3030 4D00 0000 0000 0003
  1. We can skip the first byte, 0x01, since that's in every message.
  2. The next byte, 0x03, tells us this is a response to a "range read" command.
  3. The next byte, 0x6E, is the length in bytes of the data we requested, and is twice the length we used above (2 * 55 == 110 bytes).
  4. There should be 112 bytes remaining in the data - 2 extra for the checksum - but you seem to be missing some bytes in what you posted. That said, we can decode at least part of it - the first 12 bytes are used for the device type (string, null-terminated, fixed length). If we decode that we get "AC200M".
If I make this request to my AC300, here's what I get in response:
Code:
Request: 0101 0300 0a00 3724 1e

Response:
0       01 01 03 6e 41 43 33 30 30 00 00 00 00 00 00 00 
8       03 fa db 3b 06 5c 01 f2 00 00 00 00 00 00 27 09 
10      00 06 26 a4 00 06 8c 27 00 01 c1 22 00 0d 00 00 
18      00 00 00 00 00 00 00 00 00 57 00 00 00 00 00 00 
20      00 00 00 68 00 00 00 26 00 01 00 01 00 01 00 01 
28      00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 
30      00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
38      00 00 f5 93

I'm currently in the process of re-architecting the code on the evented-architecture branch, but both branches include parsing code (data_pages.rb and payload_parser.rb are just different forms of the same underlying rules).

Code:
Write Command - FF02 - Value: 0106 0BC0 0001 4A12
  1. Skip the first byte.
  2. The next byte, 0x06, tells you that we're doing a single field update.
  3. The next byte, 0x0B, tells you that we're updating a single field on the 0x0B page, the config page as I've been calling it.
  4. The next byte, 0xC0, tells you what field offset we're updating. 0xC0 is the DC output field, 0xBF is the AC output field, 0xF5 might be the screen sleep time for you (it is on the AC300).
  5. The next two bytes are the value to set it to, 0x0000 or 0x0001.
  6. The final two bytes are the checksum.
The third message type, 0x10, should allow you to set an entire range (for example to turn off both the AC output and DC output at the same time). You'll have to test this, though, to see if this works over bluetooth.

Code:
0110 0BBF 0002 0400 0000 00CB AB
  1. Skip the first byte.
  2. The next byte, 0x10, tells you that we're doing a "range update".
  3. The next byte, 0x0B, is the page.
  4. The next byte, 0xBF, is the offset.
  5. The next two bytes, 0x0002, are used as a 16-bit unsigned integer for the length.
  6. The next byte, 0x04, is the actual byte length of the payload. Don't ask me why they have the length twice in their protocol.
  7. The next 4 bytes are the payload - two 16-bit unsigned integers for the 0xBF and 0xC0 fields.
  8. The last two bytes are the checksum.
If you can make your own requests, I'd be interested to see what they return for the full contents of page 0x00 and 0x0B. Below are some pre-checksummed requests that you might be able to use to do that. Also, if you have any further questions about this I'm happy to help.

Code:
Read data from the pages with offsets and sizes I expect to work
Write Command - FF02 - VALUE: 0103 0000 0046 c438
Write Command - FF02 - VALUE: 0103 0046 007f e5ff
Write Command - FF02 - VALUE: 0103 0bb9 003d 57da
 
:ROFLMAO: I just re-ran the checksum reverse engineering script and it says the Bluetooth messages are using standard CRC-16/MODBUS. It looks like the extra 0x01 byte at the beginning of the MQTT packet is ignored for the checksum, but it was throwing me off completely. Well that makes things a lot easier.
 
@chromedshark this is fantastic !

Here are the responses from the pre-checksummed requests you posted:

Code:
ATT Send           Write Request FF02 - Value: 0103 0000 0046 C438  
ATT Receive        Write Response  
ATT Receive        FF01 - Value: 0103 8C00 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0041 4332 3030 4D00 0000 0000 0003…  
ATT Receive        FF01 - Value: 4002 0100 0000 0000 001D A200 061C 7B00…  
ATT Receive        FF01 - Value: 0100 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 6400 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 00C0 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0085 0C

ATT Send           Write Request FF02 - Value: 0103 0046 007F E5FF  
ATT Receive        Write Response  
ATT Receive        FF01 - Value: 0103 FE00 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0315 0300 0200 6400 2800…  
ATT Receive        FF01 - Value: 0300 6400 0000 6400 0000 0002 5801 4D01…  
ATT Receive        FF01 - Value: 5101 4E01 5401 5201 5201 4F01 4E01 4E01…  
ATT Receive        FF01 - Value: 5101 5101 4E00 3C00 3B00 3B00 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…
 
ATT Send           Write Request FF02 - Value: 0103 0BB9 003D 57DA  
ATT Receive        Write Response
ATT Receive        FF01 - Value: 0103 7A00 0000 0000 0000 0000 0000 0100…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0016 0509 0815 0300 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 0000 0000 0000 0000 0000 0000…  
ATT Receive        FF01 - Value: 0000 0000 02B7 0B


Does that provide any useful info ?

Let me know if you want any other packet dumps as well
 
Here are the responses from the pre-checksummed requests you posted:
It looks like you’re still missing data. The “AC200M” should show up at offset 10, or 20 bytes in, and does in your first post. In what you just posted, however, it’s somehow at offset 8 (16 bytes in), so we’re somehow missing 4 bytes. There’s “…” at the end of every line. Is it possible that whatever tool you’re using is truncating each line by 4 bytes? Based on looking around online, that matches up with the Bluetooth protocol supporting 20 bytes of data per packet (each line has only 16 bytes).
 
If I assume each line that ends with an ellipsis is missing 4 bytes at the end, it does appear to match up with the existing parser research I've done. If you were to plug it into the wall and turn on the AC / DC output and hook up some loads to it, then we could use that to confirm that all the numbers are indeed showing up in the right spots.

The pack voltages look odd because they have some missing data in them that I had to fill with zeros, but it's cool to see that the battery information section seems similar between units.

Code:
{"device_type"=>"AC200M", "dc_input_power"=>0, "ac_input_power"=>0, "ac_output_power"=>0, "dc_output_power"=>0, "total_battery_percent"=>100, "ac_output_on"=>false, "dc_output_on"=>false}
{"inverter_mode"=>"Stop", "inverter_voltage"=>0.0, "inverter_frequency"=>0.0, "ac_output_current_probable"=>0.0, "ac_input_voltage"=>0.0, "ac_dc_inverter_power"=>0, "ac_input_frequency"=>0.0, "dc_input_voltage_possible"=>0.0, "dc_input_power_possible"=>0, "dc_input_current_possible"=>0.0, "pack_voltage_probable"=>0.5379e3, "pack_battery_percent"=>100, "packs"=>[{"pack_num"=>0, "voltages"=>[3.33, 2.56, 0.0, 0.81, 3.34, 3.4, 3.38, 3.38, 3.35, 3.34, 3.34, 2.56, 0.0, 0.81, 3.37, 3.34]}]}
{"ups_mode"=>"Unavailable", "ac_output_on"=>false, "dc_output_on"=>false, "grid_charge_on"=>false, "time_control_on"=>false, "battery_range_start"=>0, "battery_range_end"=>0, "device_time"=>"05-09-22 08:21:03", "time_one_mode"=>"Unavailable", "time_one_start"=>"00:00", "time_one_end"=>"00:00", "time_two_mode"=>"Unavailable", "time_two_start"=>"00:00", "time_two_end"=>"00:00", "time_three_mode"=>"Unavailable", "time_three_start"=>"00:00", "time_three_end"=>"00:00", "time_four_mode"=>"Unavailable", "time_four_start"=>"00:00", "time_four_end"=>"00:00", "time_five_mode"=>"Unavailable", "time_five_start"=>"00:00", "time_five_end"=>"00:00", "time_six_mode"=>"Unavailable", "time_six_start"=>"00:00", "time_six_end"=>"00:00", "auto_sleep"=>"30s"}
 
It looks like you’re still missing data. The “AC200M” should show up at offset 10, or 20 bytes in, and does in your first post. In what you just posted, however, it’s somehow at offset 8 (16 bytes in), so we’re somehow missing 4 bytes. There’s “…” at the end of every line. Is it possible that whatever tool you’re using is truncating each line by 4 bytes? Based on looking around online, that matches up with the Bluetooth protocol supporting 20 bytes of data per packet (each line has only 16 bytes).
Good point with the “…”
I’m using Apple Packet Logger; I’ll dig around and see if there’s some way of stopping it from truncating
 
If you were to plug it into the wall and turn on the AC / DC output and hook up some loads to it, then we could use that
My first packet log I actually did just that

Can you PM me an email address and I’ll send you the whole thing ?
 
Ok here are the hopefully full length responses

Code:
ATT Send           Write Request FF02 - Value: 0103 0000 0046 C438 
ATT Receive        Write Response 
ATT Receive        FF01 - Value: 01038c0000000000000000000000000000000000
ATT Receive        FF01 - Value: 00000041433230304d00000000000003f9b4d964 
ATT Receive        FF01 - Value: 4002010000000000001da200061c7b00068a8f00 
ATT Receive        FF01 - Value: 0100000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000064000000000000000000 
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000 
ATT Receive        FF01 - Value: 000000c000000000000000000000000000000000 
ATT Receive        FF01 - Value: 000000850c

ATT Send           Write Request FF02 - Value: 0103 0046 007F E5FF 
ATT Receive        Write Response 
ATT Receive        FF01 - Value: 0103fe0000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000 
ATT Receive        FF01 - Value: 0000000000000315030002006400280001000115
ATT Receive        FF01 - Value: 03006400000064000000000258014d0152015001 
ATT Receive        FF01 - Value: 51014e015401520152014f014e014e014d015001
ATT Receive        FF01 - Value: 510151014e003c003b003b000000000000000000 
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000dcbf
 
ATT Send           Write Request FF02 - Value: 0103 0BB9 003D 57DA 
ATT Receive        Write Response
ATT Receive        FF01 - Value: 01037a0000000000000000000000010000000000 
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000 
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000001605090815030000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000000000000000000000000000000000000
ATT Receive        FF01 - Value: 0000000002b70b
 
Quick update. I've finally put together a script that logs (raw) data by polling the device over Bluetooth. I have a single AC300 with a B300, so I'm looking for data from other devices and configurations. DM me and I can send you an email address to send logs to if you'd like to help out.

Next step - a solution for integrating a Bluetti power station into Home Assistant using MQTT.
 
I'll have to see how far I make it today, but the first version that supports reporting to an MQTT broker has been released (v0.2.1).

Notes:
  • I've tested it on Ubuntu with Python 3.7, but it should work on newer versions of Python.
  • Theoretically it supports monitoring multiple devices simultaneously, but I have only tested it with a single device, so that functionality might not work.
  • This release currently only supports anonymous MQTT, no TLS, and the standard port. Home Assistant only recommends Mosquitto as a broker, and that's what I've tested with.
  • I'm currently not parsing out and publishing a lot of the detailed data in the mid range (0x46 - 0x79). I've been working on getting this ready for release, and I want to take a second pass at the analysis of it and make sure everything is properly identified.
  • The current implementation does not scan all attached battery packs for data, but it's on my list.
Next Releases:
 
v0.3.0 has been released with support for sending commands over MQTT. I changed the topics slightly from v0.2.1 to make the implementation easier: previously they looked like bluetti/state/AC3001234567890123/ac_output_power but now they look like bluetti/state/AC300-1234567890123/ac_output_power.

I'll put some better documentation together eventually, but for now there are 6 properties that can be changed. If your device doesn't support setting one of these properties and you attempt to change them, I haven't tested what will happen.
  • bluetti/command/DEVICE_ID/ups_mode - supports setting to CUSTOMIZED, PV_PRIORITY, STANDARD, or TIME_CONTROL
  • bluetti/command/DEVICE_ID/ac_output_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/dc_output_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/grid_charge_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/time_control_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/auto_sleep_mode - supports setting to THIRTY_SECONDS, ONE_MINUTE, FIVE_MINUTES, or NEVER
 
v0.3.0 has been released with support for sending commands over MQTT. I changed the topics slightly from v0.2.1 to make the implementation easier: previously they looked like bluetti/state/AC3001234567890123/ac_output_power but now they look like bluetti/state/AC300-1234567890123/ac_output_power.

I'll put some better documentation together eventually, but for now there are 6 properties that can be changed. If your device doesn't support setting one of these properties and you attempt to change them, I haven't tested what will happen.
  • bluetti/command/DEVICE_ID/ups_mode - supports setting to CUSTOMIZED, PV_PRIORITY, STANDARD, or TIME_CONTROL
  • bluetti/command/DEVICE_ID/ac_output_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/dc_output_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/grid_charge_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/time_control_on - supports setting to ON or OFF
  • bluetti/command/DEVICE_ID/auto_sleep_mode - supports setting to THIRTY_SECONDS, ONE_MINUTE, FIVE_MINUTES, or NEVER

Fantastic work !

Just wondering
- what's been updated with 0.5.1
- can we specify an MQTT username/password yet ?

Edit
- and perhaps a non-standard MQTT port ?

Thanks again
 
Last edited:
Ahh, sorry, I’ve started keeping a changelog now so that this information is easier to find. Version 0.5.1 adds a debugging feature that provides more information for diagnosing issues.

Regarding your feature requests for username, password, and port control, those can be found in version 0.6.0 (just released).

Bash:
bluetti-mqtt --broker 192.168.1.1 --username a-username --password a-password 00:11:22:33:44:55

I’m still working on Home Assistant MQTT discovery, but that’s my current highest priority feature.
 
I've spent four days working on a way to capture logs. First using a program to capture the app screen, running the images through a ocr, and then uploading them to a spreadsheet. Then realized it takes to long to correct the ocr and downloaded android studio to manipulate the apk file. Came to the conclusion I'm not smart enough to modify the apk file. Noticed a debug mode, plugged in my old phone pulled up the app and started pulling those same 20 character hex response. Then found the same device connector manager.

However, I'm definitely going to look into installing and messing around with what you've created. Amazing work!

I just wish I could have found this post sooner!
 
Just signed up here to share what I've learned so far and hopefully can help with development.
Recently purchased the EB3A and was hoping for some data logging, which lead me to this thread and the bluetti_mqtt project.
Struggled for a bit to get it working for the EB3A but can highlight the major differences:
  • Regex device name matching had to be modified to include "EB3A"
  • The simple commands work fine without modifications, but there are some bigger differences in functionality that isn't shared so really only tried ac_output_on and dc_output_on mqtt commands
  • MidStatusPageParser and ControlPageParser were returning unexpected results so temporarily had to remove from polling
  • LowerStatusPageParser has a different offset and length: QueryRangeCommand(0x00, 0x0A, 0x35) (This might be a firmware and/or protocol version thing)
Can possibly make a pull request or fork but in general the code might need some restructuring to continue support for all devices and limiting functionality that only applies to each device and unfortunately this is my only device to test with. @chromedshark if I can help with development in any way I'd love to!

@Andyv12 I also went down the apk rabbit hole, mainly to fix the LowerStatusPageParser to start getting the values I was looking for (input/output power). Decompiled and started diving into the code. Found a bunch of interesting things including the debug mode you mentioned. It's triggered with a quick series of taps on the left/right bottom corners of the login screen, but there is a boot time limit that I couldn't get around on my regular phone or in an emulator. Ended up just re-writing the way it's launched and recompiled the apk. Attached is a screenshot of this page, but it's basically only used to set the bluetti mqtt server that the devices report and I would assume how the wifi enabled devices receive commands remotely. I was also able to modify the app to report to my local mqtt server (LOT url in the screenshot, it used to be tcp://iot.bluettipower.com:18760 as mentioned in the first post). While it did connect locally, my device (or the app in this case) wasn't sending any periodic info, presumably because the EB3A is bluetooth only, and can't receive commands from the web. However for others, this might be an interesting alternative route, but since de/recompiling and sharing a modified app is not okay, you'll have to research this on your own.
The main files in the apk of interest to us are the ProtocolAddr, ConnectConstants, and ProtocalParse (there's also "v2" versions of each of these, v2 seeming to have more advanced or elevated features). ProtocolAddr ended up being the most useful, and I could work backwards from these to find the appropriate values for QueryRangeCommand and UpdateFieldCommand in bluetti_mqtt.

For example, to control the led light on my unit,

From ProtocolAddr:
LED_CONTROL = 3034;

0B DA in hex, so UpdateFieldCommand(0x0B, 0xDA, value)

'value' can be
1 - Low
2 - High
3 - SOS
4 - OFF

Was initially testing these with the BLE Scanner app. The easiest way I could find to generate the byte array in string format for the write characteristic was this:
Python:
from bluetti_mqtt.commands import DeviceCommand, UpdateFieldCommand
value = 1
cmd = UpdateFieldCommand(0x0B, 0xDA, value)
bytes(cmd).hex()
'01060bda00016bd5'

Due to the different app/firmware/protocol versions, it'll be quite the task to support all devices, but I hope we can take this info and start adding support for new devices like the EB3A. For now I'm at least happy to have input and output values coming in to my mqtt broker and home automation dashboard.

Apologies for the lengthy post, but hopefully some or any of this is helpful!
 

Attachments

  • bluetti_app_dev_menu.png
    bluetti_app_dev_menu.png
    69.6 KB · Views: 18
Thanks for doing this - I'm going to start playing with this - my end goal is putting this information into Home Assistant - would love it if I could figure out how to get this running on a esp32 w/ ESPHome
 
Good work/info! I am also interested in this and eventually integrating an EcoFlow Delta Pro into either HA or SmartThings. I have both. I gave WireShark a try a few days ago to capture some wifi packets off my AP which the Ecoflow is connected to and watched the comm between the EF app on my phone to the DP. The first test was just to see if I could capture so did not dive into decoding to much.

From googling around I understand that if you ask EF support for access to their server api they will send you token to do so. I did find some Python code for another EF model but according to what I read googling around is that EF Support changed up the api and broke the code. It looks like they also use MQTT to communicate. Anyway, I suspect that eventually they will actually publish the api and allow customers to do their own thing.

I did ask EF support if there was a browser based url/page I could access to view/manage the EF but they said not as of now. I suspect this will be coming in the near future also.
 
Last edited:
@chromedshark
nice work.
do you plan a HACS -Integration for home assistant?
I have the supervisor container version installed with the store access available. I have 4 different servers (RPI3, Linux, Win2012 Server, etc) running docker managed by Portainer. No going back to native OS installs if I can avoid it.

Update: I see now that HACS is a seperate install from the supervisor add-on store. I sure wish the UI was a little more simplfied and an all-in-one "install all" container/script provided.
 
Last edited:
Found what I was looking for. moving future talk on EF/HA integration to the software forum.

 

diy solar

diy solar
Back
Top