bluetti-bt-connect-lib 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. bluetti_bt_connect_lib-1.5.0/LICENSE +22 -0
  2. bluetti_bt_connect_lib-1.5.0/PKG-INFO +280 -0
  3. bluetti_bt_connect_lib-1.5.0/README.md +252 -0
  4. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/__init__.py +21 -0
  5. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/base_devices/__init__.py +3 -0
  6. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/base_devices/base_device_v1.py +50 -0
  7. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/base_devices/base_device_v2.py +46 -0
  8. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/base_devices/bluetti_device.py +255 -0
  9. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/bluetooth/__init__.py +3 -0
  10. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/bluetooth/device_reader.py +306 -0
  11. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/bluetooth/device_recognizer.py +90 -0
  12. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/bluetooth/device_writer.py +78 -0
  13. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/const.py +2 -0
  14. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/__init__.py +67 -0
  15. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac180.py +43 -0
  16. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac180p.py +20 -0
  17. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac180t.py +21 -0
  18. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac200l.py +34 -0
  19. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac200m.py +27 -0
  20. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac200pl.py +27 -0
  21. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac2a.py +18 -0
  22. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac2p.py +38 -0
  23. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac300.py +48 -0
  24. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac500.py +35 -0
  25. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac50b.py +14 -0
  26. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac60.py +18 -0
  27. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac60p.py +18 -0
  28. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac70.py +41 -0
  29. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ac70p.py +21 -0
  30. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ap300.py +15 -0
  31. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/eb3a.py +29 -0
  32. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/el100v2.py +15 -0
  33. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/el30v2.py +27 -0
  34. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep2000.py +156 -0
  35. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep500.py +35 -0
  36. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep500p.py +35 -0
  37. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep600.py +73 -0
  38. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep760.py +27 -0
  39. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/ep800.py +5 -0
  40. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/handsfree1.py +22 -0
  41. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/pr100v2.py +15 -0
  42. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/devices/pr30v2.py +15 -0
  43. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/__init__.py +8 -0
  44. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/charging_mode.py +8 -0
  45. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/display_mode.py +9 -0
  46. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/eco_mode.py +9 -0
  47. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/frequency_mode.py +8 -0
  48. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/led_mode.py +9 -0
  49. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/output_mode.py +10 -0
  50. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/split_phase_mode.py +7 -0
  51. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/ups_mode.py +9 -0
  52. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/enums/working_mode.py +9 -0
  53. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/exceptions.py +21 -0
  54. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/BoolField.py +16 -0
  55. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/BoolFieldNonZero.py +23 -0
  56. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/DecimalArrayField.py +14 -0
  57. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/DecimalField.py +32 -0
  58. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/DeviceField.py +22 -0
  59. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/EnumField.py +22 -0
  60. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/FieldName.py +131 -0
  61. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/FieldUnit.py +90 -0
  62. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SInt32Field.py +45 -0
  63. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SIntField.py +45 -0
  64. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SelectField.py +19 -0
  65. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SerialNumberField.py +15 -0
  66. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/StringField.py +9 -0
  67. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SwapStringField.py +17 -0
  68. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/SwitchField.py +11 -0
  69. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/UIntField.py +31 -0
  70. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/VersionField.py +16 -0
  71. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/WriteableStringField.py +42 -0
  72. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/WriteableUIntField.py +28 -0
  73. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/fields/__init__.py +20 -0
  74. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/registers/DeviceRegister.py +50 -0
  75. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/registers/ReadableRegisters.py +34 -0
  76. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/registers/WriteableRegister.py +19 -0
  77. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/registers/WriteableRegisters.py +38 -0
  78. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/registers/__init__.py +4 -0
  79. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/__init__.py +0 -0
  80. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_detect.py +38 -0
  81. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_parse.py +70 -0
  82. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_read.py +58 -0
  83. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_readall.py +73 -0
  84. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_scan.py +68 -0
  85. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/bluetti_write.py +96 -0
  86. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/scripts/types.py +17 -0
  87. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/utils/__init__.py +0 -0
  88. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/utils/bleak_client_mock.py +114 -0
  89. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/utils/device_builder.py +24 -0
  90. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/utils/device_info.py +18 -0
  91. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib/utils/privacy.py +4 -0
  92. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/PKG-INFO +280 -0
  93. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/SOURCES.txt +97 -0
  94. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/dependency_links.txt +1 -0
  95. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/entry_points.txt +7 -0
  96. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/requires.txt +7 -0
  97. bluetti_bt_connect_lib-1.5.0/bluetti_bt_connect_lib.egg-info/top_level.txt +1 -0
  98. bluetti_bt_connect_lib-1.5.0/setup.cfg +4 -0
  99. bluetti_bt_connect_lib-1.5.0/setup.py +80 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Patrick762
4
+ Copyright (c) 2026 Elliott Monaghan (modifications for Bluetti BT Connect)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.4
2
+ Name: bluetti-bt-connect-lib
3
+ Version: 1.5.0
4
+ Summary: Bluetti BT Connect - Bluetooth library for Bluetti power stations (fork of bluetti-bt-lib by Patrick762)
5
+ Home-page: https://github.com/Elliottmonaghan/bluetti-bt-connect-lib
6
+ Author: Elliott Monaghan
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: async_timeout
14
+ Requires-Dist: bleak
15
+ Requires-Dist: bleak_retry_connector
16
+ Requires-Dist: crcmod
17
+ Requires-Dist: typing_extensions; python_version < "3.12"
18
+ Dynamic: author
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: license-file
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+
29
+ # bluetti-bt-connect-lib
30
+
31
+ > **This is a fork** of [bluetti-bt-lib](https://github.com/Patrick762/bluetti-bt-lib) by [Patrick762](https://github.com/Patrick762), extended with additional device support, register corrections, and new writable fields for the Bluetti EP2000. All credit for the original protocol reverse-engineering, architecture, and core library design goes to Patrick762 and the project's other contributors. This fork exists to track device-specific fixes and additions on a faster iteration cycle; where possible, improvements are intended to be contributed back upstream.
32
+ >
33
+ > Original repository: https://github.com/Patrick762/bluetti-bt-lib
34
+ >
35
+ > Additional credit to [atiweb/hassio-bluetti-bt](https://github.com/atiweb/hassio-bluetti-bt), a separate fork of the original project, cross-referenced for two specific fixes: correcting `consumption_power_all`, `pv_input_power_all`, and `grid_power_all` from incorrectly-assumed 32-bit fields to plain 16-bit fields (validated against real captured data), and the pattern for proper Modbus response validation (CRC checking and exception-response detection) now used in `device_reader.py`.
36
+
37
+ Inofficial Library for basic communication to bluetti powerstations.
38
+ Core functions based on https://github.com/warhammerkid/bluetti_mqtt
39
+
40
+ ## Disclaimer
41
+ This library is provided without any warranty or support by Bluetti. I do not take responsibility for any problems it may cause in all cases. Use it at your own risk.
42
+
43
+ ## ⚠️ EP2000: grid/mode controls carry real risk - read before using
44
+
45
+ **AC Output, Charge From Grid, Grid Export, Working Mode, and all four grid import/export power and current limits remain writable in this build.** Before you rely on any of them, please understand what we found while investigating this device.
46
+
47
+ **The core problem:** writes into this device's grid-related register block can get a clean, protocol-valid acknowledgment from the device - and then the change does not actually take effect. This was confirmed at the raw byte level, ruling out a simple wrong-address or scaling issue. In practice this means: **you may set a limit in Home Assistant, see it accepted with no error, and the device may still be running on its old setting.** Nothing in the app or the integration will reliably tell you a write didn't stick - the failure is silent. Do not assume a control has taken effect on the device just because Home Assistant shows no error; if you change one of these settings, verify the result independently (in the Bluetti app, or against real grid behavior) before relying on it.
48
+
49
+ **Why we believe this happens:** a research pass across comparable BLE-connected solar/battery projects (EcoFlow, Renogy, Growatt, Deye/Sunsynk, Anker SOLIX, Marstek, several open-source BMS forks) found that grid-facing settings - export/import limits, working mode, grid protection - are consistently gated behind some form of vendor authentication across the entire industry, not just on Bluetti hardware: a cloud-account round-trip, a licensed BLE encryption handshake keyed to the device's serial number, or a support-issued password. The EP2000 fits this pattern - there's a genuine "Pro Mode" authentication layer here, and while we confirmed the universal technician password Bluetti documents publicly ("88888888"), simply knowing that password did not make writes persist, which points at a session or encryption-level gate underneath it that hasn't been reverse-engineered.
50
+
51
+ **Why we haven't removed these controls outright:** unlike the grid-compliance layer itself, some of these fields (notably the AC Output and Grid Export switches, and Max Grid Export Current) were live-tested and confirmed to actually take effect on this specific unit at various points during development. Reliability may vary by field, by firmware version, and possibly by unit - which is exactly why blanket trust in any of them is the wrong approach. Treat every write as unverified until you've checked its real-world effect yourself.
52
+
53
+ **Grid export and grid-protection parameters are regulated for interconnection safety in most jurisdictions** (anti-islanding, voltage/frequency ride-through). Changing them, even successfully, may have real compliance implications depending on where you live and how your system is set up. This is worth knowing regardless of whether a given write actually persists.
54
+
55
+ **If you're an advanced user or researcher**: the full research writeup and the exact register-level evidence behind the above are referenced in this version's release notes. The realistic next step for confirming or defeating the authentication gate isn't more register-guessing - it's capturing the official app's authenticated write sequence directly (Android Bluetooth HCI snoop log, or hooking the app's write call with Frida).
56
+
57
+ ## Projects using this library
58
+
59
+ - [Bluetti BT Connect - Home Assistant Integration](https://github.com/Elliottmonaghan/bluetti-bt-connect) (this fork's companion integration)
60
+ - [Original Home Assistant Integration](https://github.com/Patrick762/hassio-bluetti-bt)
61
+ - [UPS Server (NUT compatible)](https://github.com/Patrick762/nut-server-bluetti)
62
+
63
+ ## Supported Powerstations and data
64
+
65
+ Validated
66
+
67
+ |Device Name|total_battery_percent|dc_input_power|ac_input_power|dc_output_power|ac_output_power|
68
+ |-----------|---------------------|--------------|--------------|---------------|---------------|
69
+ |AC70 |✅ |✅ |✅ |✅ |✅ |
70
+ |AC180 |✅ |✅ |✅ |✅ |✅ |
71
+ |EB3A |✅ |✅ |✅ |✅ |✅ |
72
+ |EP600 |✅ |PV |Grid |❌ |AC Phases |
73
+ |EP2000 |✅ |PV |Grid |❌ |AC Phases, writable grid/mode fields (⚠️ see warning above)|
74
+ |Handsfree 1|✅ |✅ |✅ |✅ |✅ |
75
+
76
+ Added and mostly validated by contributors (some are moved here from the HA Integration https://github.com/Patrick762/hassio-bluetti-bt):
77
+
78
+
79
+ |Device Name|Contributor |total_battery_percent|dc_input_power|ac_input_power|dc_output_power|ac_output_power|
80
+ |-----------|-----------------------------------------------------------------------------------|---------------------|--------------|--------------|---------------|---------------|
81
+ |AC2A |[@ruanmed](https://github.com/ruanmed) |✅ |✅ |✅ |✅ |✅ |
82
+ |AC50B |[@goetzc](https://github.com/goetzc) |✅ |❌ |✅ |✅ |✅ |
83
+ |AC60 |[@mzpwr](https://github.com/mzpwr) |✅ |✅ |✅ |✅ |✅ |
84
+ |AC60P |[@mzpwr](https://github.com/mzpwr) |✅ |✅ |✅ |✅ |✅ |
85
+ |AC70P |[@matthewpucc](https://github.com/matthewpucc) |✅ |✅ |✅ |✅ |✅ |
86
+ |AC180P |@Patrick762 |✅ |✅ |✅ |✅ |✅ |
87
+ |AC200L |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
88
+ |AC200M |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
89
+ |AC200PL |[@0x4E4448](https://github.com/0x4E4448) |✅ |✅ |✅ |✅ |✅ |
90
+ |AC300 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
91
+ |AC500 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
92
+ |AP300 |[@seaburger](https://github.com/seaburger), [@sidieje](https://github.com/sidieje) |✅ |✅ |✅ |✅ |✅ |
93
+ |EL30V2 |[@dgudim](https://github.com/dgudim) |✅ |✅ |✅ |✅ |✅ |
94
+ |EL100V2 |[@seaburger](https://github.com/seaburger) |✅ |✅ |✅ |✅ |✅ |
95
+ |EP500 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
96
+ |EP500P |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
97
+ |EP760 |[@Apfuntimes](https://github.com/Apfuntimes) |✅ |PV |Grid |❌ |AC Phases |
98
+ |EP800 |[@jhagenk](https://github.com/jhagenk) |✅ |❌ |❌ |❌ |❌ |
99
+ |PR30V2 |@gentoo90 |✅ |✅ |✅ |✅ |✅ |
100
+ |PR100V2 |shares PR30V2 register layout (pending validation) |✅ |✅ |✅ |✅ |✅ |
101
+
102
+ ## Controls
103
+
104
+ Validated:
105
+
106
+ |Device Name|ctrl_ac|ctrl_dc|
107
+ |-----------|-------|-------|
108
+ |EB3A |✅ |✅ |
109
+
110
+ Added and mostly validated by contributors:
111
+ |Device Name|Contributor |ctrl_ac|ctrl_dc|ctrl_ups_mode|soc_range_start|soc_range_end|
112
+ |-----------|---------------------------------------------------------|-------|-------|-------------|---------------|-------------|
113
+ |AC200L |bluetti-mqtt, [@seaburger](https://github.com/seaburger) |✅ |✅ |✅ |❌ |❌ |
114
+ |EL30V2 |[@x3ccd4828](https://github.com/x3ccd4828) |✅ |✅ |❌ |❌ |❌ |
115
+
116
+ ## Battery pack data
117
+
118
+ |Device Name|voltage|battery_soc|cell_voltages|
119
+ |-----------|-------|-----------|-------------|
120
+ |AC300 |✅ |✅ |✅ |
121
+
122
+ ## Installation
123
+
124
+ ```bash
125
+ pip install bluetti-bt-connect-lib
126
+ ```
127
+
128
+ ## Commands for testing
129
+
130
+ Commands included in this library should only be used for testing.
131
+
132
+ ### Scan for supported devices
133
+
134
+ ```bash
135
+ usage: bluetti-scan [-h] [-r REGEX] [-s SCAN_TIME]
136
+
137
+ Detect bluetti devices by bluetooth name
138
+
139
+ options:
140
+ -h, --help show this help message and exit
141
+ -r REGEX, --regex REGEX
142
+ Custom regex to match device name
143
+ -s SCAN_TIME, --scan-time SCAN_TIME
144
+ How long to scan for devices (seconds)
145
+ ```
146
+
147
+ Example output: `['EB3A', '00:00:00:00:00:00']`
148
+
149
+ ### Detect device type by mac address
150
+
151
+ ```bash
152
+ usage: bluetti-detect [-h] mac
153
+
154
+ Detect bluetti devices
155
+
156
+ positional arguments:
157
+ mac Mac-address of the powerstation
158
+
159
+ options:
160
+ -h, --help show this help message and exit
161
+ ```
162
+
163
+ Example:
164
+
165
+ ```bash
166
+ bluetti-detect 00:00:00:00:00:00
167
+ ```
168
+
169
+ Example output: `Device type is 'EB3A' with iot version 1 and serial 0000000000000. Full name: EB3A0000000000000`
170
+
171
+ ### Read device data for supported devices
172
+
173
+ ```bash
174
+ usage: bluetti-read [-h] [-m MAC] [-t TYPE] [-e ENCRYPTION]
175
+
176
+ Detect bluetti devices
177
+
178
+ options:
179
+ -h, --help show this help message and exit
180
+ -m MAC, --mac MAC Mac-address of the powerstation
181
+ -t TYPE, --type TYPE Type of the powerstation (AC70 f.ex.)
182
+ -e ENCRYPTION, --encryption ENCRYPTION
183
+ Add this if encryption is needed
184
+ ```
185
+
186
+ Example:
187
+
188
+ ```bash
189
+ bluetti-read -m 00:00:00:00:00:00 -t EB3A
190
+ ```
191
+
192
+ Example output:
193
+ ```bash
194
+ FieldName.DEVICE_TYPE: EB3A
195
+ FieldName.DEVICE_SN: 0000000000000
196
+ FieldName.BATTERY_SOC: 92%
197
+ FieldName.DC_INPUT_POWER: 0W
198
+ FieldName.AC_INPUT_POWER: 0W
199
+ FieldName.AC_OUTPUT_POWER: 0W
200
+ FieldName.DC_OUTPUT_POWER: 0W
201
+ FieldName.CTRL_AC: False
202
+ FieldName.CTRL_DC: True
203
+ FieldName.CTRL_LED_MODE: LedMode.OFF
204
+ FieldName.CTRL_POWER_OFF: False
205
+ FieldName.CTRL_ECO: False
206
+ FieldName.CTRL_ECO_TIME_MODE: EcoMode.HOURS1
207
+ FieldName.CTRL_CHARGING_MODE: ChargingMode.STANDARD
208
+ FieldName.CTRL_POWER_LIFTING: False
209
+ ```
210
+
211
+ ### Write to supported device
212
+
213
+ INFO: Devices with encryption are currently not supported!
214
+
215
+ ```bash
216
+ usage: bluetti-write [-h] [-m MAC] [-t TYPE] [--on ON] [--off OFF] [-v VALUE] [-e ENCRYPTION] field
217
+
218
+ Write to bluetti device
219
+
220
+ positional arguments:
221
+ field Field name (ctrl_dc f.ex.)
222
+
223
+ options:
224
+ -h, --help show this help message and exit
225
+ -m MAC, --mac MAC Mac-address of the powerstation
226
+ -t TYPE, --type TYPE Type of the powerstation (AC70 f.ex.)
227
+ --on ON Value to write
228
+ --off OFF Value to write
229
+ -v VALUE, --value VALUE
230
+ Value to write (integer, see enum for value)
231
+ -e ENCRYPTION, --encryption ENCRYPTION
232
+ Add this if encryption is needed
233
+ ```
234
+
235
+ Example:
236
+
237
+ ```bash
238
+ bluetti-write -m 00:00:00:00:00:00 -t EB3A --on on ctrl_ac
239
+ ```
240
+
241
+ ## Adding fields
242
+
243
+ To add new fields, you can use the `bluetti-detect` command to first find out which version of iot protocol is used and if it uses encryption.
244
+
245
+ After you got this information, you can use the `bluetti-readall` command to read every registry and save the data to a json file. You should also note all values you see in the app to later compare the data.
246
+
247
+ Here's how to use the `bluetti-readall` command:
248
+
249
+ ```bash
250
+ usage: bluetti-readall [-h] [-m MAC] [-v VERSION] [-e ENCRYPTION]
251
+
252
+ Detect bluetti devices
253
+
254
+ options:
255
+ -h, --help show this help message and exit
256
+ -m MAC, --mac MAC Mac-address of the powerstation
257
+ -v VERSION, --version VERSION
258
+ IoT protocol version
259
+ -e ENCRYPTION, --encryption ENCRYPTION
260
+ Add this if encryption is needed
261
+ ```
262
+
263
+ With the separate tool at [bluetti-bt-raw-reader](https://github.com/Patrick762/bluetti-bt-raw-reader) you can view those values in a more understandable way.
264
+
265
+ You can also share the output with me using [this form](https://forms.gle/ewp7DYigtaN3ZLc68)
266
+
267
+
268
+ To test added fields with the created json file, use `bluetti-parse`:
269
+
270
+ ```bash
271
+ usage: bluetti-parse [-h] file
272
+
273
+ Parse readall output files
274
+
275
+ positional arguments:
276
+ file JSON file of the powerstation readall output
277
+
278
+ options:
279
+ -h, --help show this help message and exit
280
+ ```
@@ -0,0 +1,252 @@
1
+ # bluetti-bt-connect-lib
2
+
3
+ > **This is a fork** of [bluetti-bt-lib](https://github.com/Patrick762/bluetti-bt-lib) by [Patrick762](https://github.com/Patrick762), extended with additional device support, register corrections, and new writable fields for the Bluetti EP2000. All credit for the original protocol reverse-engineering, architecture, and core library design goes to Patrick762 and the project's other contributors. This fork exists to track device-specific fixes and additions on a faster iteration cycle; where possible, improvements are intended to be contributed back upstream.
4
+ >
5
+ > Original repository: https://github.com/Patrick762/bluetti-bt-lib
6
+ >
7
+ > Additional credit to [atiweb/hassio-bluetti-bt](https://github.com/atiweb/hassio-bluetti-bt), a separate fork of the original project, cross-referenced for two specific fixes: correcting `consumption_power_all`, `pv_input_power_all`, and `grid_power_all` from incorrectly-assumed 32-bit fields to plain 16-bit fields (validated against real captured data), and the pattern for proper Modbus response validation (CRC checking and exception-response detection) now used in `device_reader.py`.
8
+
9
+ Inofficial Library for basic communication to bluetti powerstations.
10
+ Core functions based on https://github.com/warhammerkid/bluetti_mqtt
11
+
12
+ ## Disclaimer
13
+ This library is provided without any warranty or support by Bluetti. I do not take responsibility for any problems it may cause in all cases. Use it at your own risk.
14
+
15
+ ## ⚠️ EP2000: grid/mode controls carry real risk - read before using
16
+
17
+ **AC Output, Charge From Grid, Grid Export, Working Mode, and all four grid import/export power and current limits remain writable in this build.** Before you rely on any of them, please understand what we found while investigating this device.
18
+
19
+ **The core problem:** writes into this device's grid-related register block can get a clean, protocol-valid acknowledgment from the device - and then the change does not actually take effect. This was confirmed at the raw byte level, ruling out a simple wrong-address or scaling issue. In practice this means: **you may set a limit in Home Assistant, see it accepted with no error, and the device may still be running on its old setting.** Nothing in the app or the integration will reliably tell you a write didn't stick - the failure is silent. Do not assume a control has taken effect on the device just because Home Assistant shows no error; if you change one of these settings, verify the result independently (in the Bluetti app, or against real grid behavior) before relying on it.
20
+
21
+ **Why we believe this happens:** a research pass across comparable BLE-connected solar/battery projects (EcoFlow, Renogy, Growatt, Deye/Sunsynk, Anker SOLIX, Marstek, several open-source BMS forks) found that grid-facing settings - export/import limits, working mode, grid protection - are consistently gated behind some form of vendor authentication across the entire industry, not just on Bluetti hardware: a cloud-account round-trip, a licensed BLE encryption handshake keyed to the device's serial number, or a support-issued password. The EP2000 fits this pattern - there's a genuine "Pro Mode" authentication layer here, and while we confirmed the universal technician password Bluetti documents publicly ("88888888"), simply knowing that password did not make writes persist, which points at a session or encryption-level gate underneath it that hasn't been reverse-engineered.
22
+
23
+ **Why we haven't removed these controls outright:** unlike the grid-compliance layer itself, some of these fields (notably the AC Output and Grid Export switches, and Max Grid Export Current) were live-tested and confirmed to actually take effect on this specific unit at various points during development. Reliability may vary by field, by firmware version, and possibly by unit - which is exactly why blanket trust in any of them is the wrong approach. Treat every write as unverified until you've checked its real-world effect yourself.
24
+
25
+ **Grid export and grid-protection parameters are regulated for interconnection safety in most jurisdictions** (anti-islanding, voltage/frequency ride-through). Changing them, even successfully, may have real compliance implications depending on where you live and how your system is set up. This is worth knowing regardless of whether a given write actually persists.
26
+
27
+ **If you're an advanced user or researcher**: the full research writeup and the exact register-level evidence behind the above are referenced in this version's release notes. The realistic next step for confirming or defeating the authentication gate isn't more register-guessing - it's capturing the official app's authenticated write sequence directly (Android Bluetooth HCI snoop log, or hooking the app's write call with Frida).
28
+
29
+ ## Projects using this library
30
+
31
+ - [Bluetti BT Connect - Home Assistant Integration](https://github.com/Elliottmonaghan/bluetti-bt-connect) (this fork's companion integration)
32
+ - [Original Home Assistant Integration](https://github.com/Patrick762/hassio-bluetti-bt)
33
+ - [UPS Server (NUT compatible)](https://github.com/Patrick762/nut-server-bluetti)
34
+
35
+ ## Supported Powerstations and data
36
+
37
+ Validated
38
+
39
+ |Device Name|total_battery_percent|dc_input_power|ac_input_power|dc_output_power|ac_output_power|
40
+ |-----------|---------------------|--------------|--------------|---------------|---------------|
41
+ |AC70 |✅ |✅ |✅ |✅ |✅ |
42
+ |AC180 |✅ |✅ |✅ |✅ |✅ |
43
+ |EB3A |✅ |✅ |✅ |✅ |✅ |
44
+ |EP600 |✅ |PV |Grid |❌ |AC Phases |
45
+ |EP2000 |✅ |PV |Grid |❌ |AC Phases, writable grid/mode fields (⚠️ see warning above)|
46
+ |Handsfree 1|✅ |✅ |✅ |✅ |✅ |
47
+
48
+ Added and mostly validated by contributors (some are moved here from the HA Integration https://github.com/Patrick762/hassio-bluetti-bt):
49
+
50
+
51
+ |Device Name|Contributor |total_battery_percent|dc_input_power|ac_input_power|dc_output_power|ac_output_power|
52
+ |-----------|-----------------------------------------------------------------------------------|---------------------|--------------|--------------|---------------|---------------|
53
+ |AC2A |[@ruanmed](https://github.com/ruanmed) |✅ |✅ |✅ |✅ |✅ |
54
+ |AC50B |[@goetzc](https://github.com/goetzc) |✅ |❌ |✅ |✅ |✅ |
55
+ |AC60 |[@mzpwr](https://github.com/mzpwr) |✅ |✅ |✅ |✅ |✅ |
56
+ |AC60P |[@mzpwr](https://github.com/mzpwr) |✅ |✅ |✅ |✅ |✅ |
57
+ |AC70P |[@matthewpucc](https://github.com/matthewpucc) |✅ |✅ |✅ |✅ |✅ |
58
+ |AC180P |@Patrick762 |✅ |✅ |✅ |✅ |✅ |
59
+ |AC200L |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
60
+ |AC200M |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
61
+ |AC200PL |[@0x4E4448](https://github.com/0x4E4448) |✅ |✅ |✅ |✅ |✅ |
62
+ |AC300 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
63
+ |AC500 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
64
+ |AP300 |[@seaburger](https://github.com/seaburger), [@sidieje](https://github.com/sidieje) |✅ |✅ |✅ |✅ |✅ |
65
+ |EL30V2 |[@dgudim](https://github.com/dgudim) |✅ |✅ |✅ |✅ |✅ |
66
+ |EL100V2 |[@seaburger](https://github.com/seaburger) |✅ |✅ |✅ |✅ |✅ |
67
+ |EP500 |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
68
+ |EP500P |bluetti-mqtt |✅ |✅ |✅ |✅ |✅ |
69
+ |EP760 |[@Apfuntimes](https://github.com/Apfuntimes) |✅ |PV |Grid |❌ |AC Phases |
70
+ |EP800 |[@jhagenk](https://github.com/jhagenk) |✅ |❌ |❌ |❌ |❌ |
71
+ |PR30V2 |@gentoo90 |✅ |✅ |✅ |✅ |✅ |
72
+ |PR100V2 |shares PR30V2 register layout (pending validation) |✅ |✅ |✅ |✅ |✅ |
73
+
74
+ ## Controls
75
+
76
+ Validated:
77
+
78
+ |Device Name|ctrl_ac|ctrl_dc|
79
+ |-----------|-------|-------|
80
+ |EB3A |✅ |✅ |
81
+
82
+ Added and mostly validated by contributors:
83
+ |Device Name|Contributor |ctrl_ac|ctrl_dc|ctrl_ups_mode|soc_range_start|soc_range_end|
84
+ |-----------|---------------------------------------------------------|-------|-------|-------------|---------------|-------------|
85
+ |AC200L |bluetti-mqtt, [@seaburger](https://github.com/seaburger) |✅ |✅ |✅ |❌ |❌ |
86
+ |EL30V2 |[@x3ccd4828](https://github.com/x3ccd4828) |✅ |✅ |❌ |❌ |❌ |
87
+
88
+ ## Battery pack data
89
+
90
+ |Device Name|voltage|battery_soc|cell_voltages|
91
+ |-----------|-------|-----------|-------------|
92
+ |AC300 |✅ |✅ |✅ |
93
+
94
+ ## Installation
95
+
96
+ ```bash
97
+ pip install bluetti-bt-connect-lib
98
+ ```
99
+
100
+ ## Commands for testing
101
+
102
+ Commands included in this library should only be used for testing.
103
+
104
+ ### Scan for supported devices
105
+
106
+ ```bash
107
+ usage: bluetti-scan [-h] [-r REGEX] [-s SCAN_TIME]
108
+
109
+ Detect bluetti devices by bluetooth name
110
+
111
+ options:
112
+ -h, --help show this help message and exit
113
+ -r REGEX, --regex REGEX
114
+ Custom regex to match device name
115
+ -s SCAN_TIME, --scan-time SCAN_TIME
116
+ How long to scan for devices (seconds)
117
+ ```
118
+
119
+ Example output: `['EB3A', '00:00:00:00:00:00']`
120
+
121
+ ### Detect device type by mac address
122
+
123
+ ```bash
124
+ usage: bluetti-detect [-h] mac
125
+
126
+ Detect bluetti devices
127
+
128
+ positional arguments:
129
+ mac Mac-address of the powerstation
130
+
131
+ options:
132
+ -h, --help show this help message and exit
133
+ ```
134
+
135
+ Example:
136
+
137
+ ```bash
138
+ bluetti-detect 00:00:00:00:00:00
139
+ ```
140
+
141
+ Example output: `Device type is 'EB3A' with iot version 1 and serial 0000000000000. Full name: EB3A0000000000000`
142
+
143
+ ### Read device data for supported devices
144
+
145
+ ```bash
146
+ usage: bluetti-read [-h] [-m MAC] [-t TYPE] [-e ENCRYPTION]
147
+
148
+ Detect bluetti devices
149
+
150
+ options:
151
+ -h, --help show this help message and exit
152
+ -m MAC, --mac MAC Mac-address of the powerstation
153
+ -t TYPE, --type TYPE Type of the powerstation (AC70 f.ex.)
154
+ -e ENCRYPTION, --encryption ENCRYPTION
155
+ Add this if encryption is needed
156
+ ```
157
+
158
+ Example:
159
+
160
+ ```bash
161
+ bluetti-read -m 00:00:00:00:00:00 -t EB3A
162
+ ```
163
+
164
+ Example output:
165
+ ```bash
166
+ FieldName.DEVICE_TYPE: EB3A
167
+ FieldName.DEVICE_SN: 0000000000000
168
+ FieldName.BATTERY_SOC: 92%
169
+ FieldName.DC_INPUT_POWER: 0W
170
+ FieldName.AC_INPUT_POWER: 0W
171
+ FieldName.AC_OUTPUT_POWER: 0W
172
+ FieldName.DC_OUTPUT_POWER: 0W
173
+ FieldName.CTRL_AC: False
174
+ FieldName.CTRL_DC: True
175
+ FieldName.CTRL_LED_MODE: LedMode.OFF
176
+ FieldName.CTRL_POWER_OFF: False
177
+ FieldName.CTRL_ECO: False
178
+ FieldName.CTRL_ECO_TIME_MODE: EcoMode.HOURS1
179
+ FieldName.CTRL_CHARGING_MODE: ChargingMode.STANDARD
180
+ FieldName.CTRL_POWER_LIFTING: False
181
+ ```
182
+
183
+ ### Write to supported device
184
+
185
+ INFO: Devices with encryption are currently not supported!
186
+
187
+ ```bash
188
+ usage: bluetti-write [-h] [-m MAC] [-t TYPE] [--on ON] [--off OFF] [-v VALUE] [-e ENCRYPTION] field
189
+
190
+ Write to bluetti device
191
+
192
+ positional arguments:
193
+ field Field name (ctrl_dc f.ex.)
194
+
195
+ options:
196
+ -h, --help show this help message and exit
197
+ -m MAC, --mac MAC Mac-address of the powerstation
198
+ -t TYPE, --type TYPE Type of the powerstation (AC70 f.ex.)
199
+ --on ON Value to write
200
+ --off OFF Value to write
201
+ -v VALUE, --value VALUE
202
+ Value to write (integer, see enum for value)
203
+ -e ENCRYPTION, --encryption ENCRYPTION
204
+ Add this if encryption is needed
205
+ ```
206
+
207
+ Example:
208
+
209
+ ```bash
210
+ bluetti-write -m 00:00:00:00:00:00 -t EB3A --on on ctrl_ac
211
+ ```
212
+
213
+ ## Adding fields
214
+
215
+ To add new fields, you can use the `bluetti-detect` command to first find out which version of iot protocol is used and if it uses encryption.
216
+
217
+ After you got this information, you can use the `bluetti-readall` command to read every registry and save the data to a json file. You should also note all values you see in the app to later compare the data.
218
+
219
+ Here's how to use the `bluetti-readall` command:
220
+
221
+ ```bash
222
+ usage: bluetti-readall [-h] [-m MAC] [-v VERSION] [-e ENCRYPTION]
223
+
224
+ Detect bluetti devices
225
+
226
+ options:
227
+ -h, --help show this help message and exit
228
+ -m MAC, --mac MAC Mac-address of the powerstation
229
+ -v VERSION, --version VERSION
230
+ IoT protocol version
231
+ -e ENCRYPTION, --encryption ENCRYPTION
232
+ Add this if encryption is needed
233
+ ```
234
+
235
+ With the separate tool at [bluetti-bt-raw-reader](https://github.com/Patrick762/bluetti-bt-raw-reader) you can view those values in a more understandable way.
236
+
237
+ You can also share the output with me using [this form](https://forms.gle/ewp7DYigtaN3ZLc68)
238
+
239
+
240
+ To test added fields with the created json file, use `bluetti-parse`:
241
+
242
+ ```bash
243
+ usage: bluetti-parse [-h] file
244
+
245
+ Parse readall output files
246
+
247
+ positional arguments:
248
+ file JSON file of the powerstation readall output
249
+
250
+ options:
251
+ -h, --help show this help message and exit
252
+ ```
@@ -0,0 +1,21 @@
1
+ """Bluetti BT Lib exports."""
2
+
3
+ __version__ = "1.5.0"
4
+ """Single source of truth for the package version.
5
+
6
+ setup.py reads this when LIB_VERSION is not set, so an install straight from
7
+ git reports a real version rather than falling back to 0.0.0. The release
8
+ workflow checks the git tag against it and refuses to publish on a mismatch.
9
+ """
10
+
11
+ from .base_devices import BluettiDevice
12
+ from .bluetooth import (
13
+ DeviceReader,
14
+ DeviceReaderConfig,
15
+ DeviceWriter,
16
+ DeviceRecognizerResult,
17
+ recognize_device,
18
+ )
19
+ from .enums import *
20
+ from .fields import DeviceField, FieldName, FieldUnit, get_unit
21
+ from .utils.device_builder import build_device
@@ -0,0 +1,3 @@
1
+ from .bluetti_device import *
2
+ from .base_device_v1 import *
3
+ from .base_device_v2 import *
@@ -0,0 +1,50 @@
1
+ from typing import List
2
+
3
+ from . import BluettiDevice
4
+ from ..fields import DeviceField
5
+ from ..fields import FieldName, StringField, UIntField, SerialNumberField
6
+ from ..registers import ReadableRegisters, WriteableRegister
7
+
8
+
9
+ class BaseDeviceV1(BluettiDevice):
10
+ def __init__(
11
+ self,
12
+ additional_fields: List[DeviceField] = [],
13
+ pack_fields: List[DeviceField] = [],
14
+ max_packs: int = 0,
15
+ **kwargs,
16
+ ):
17
+ super().__init__(
18
+ [
19
+ StringField(FieldName.DEVICE_TYPE, 10, 6),
20
+ SerialNumberField(FieldName.DEVICE_SN, 17),
21
+ UIntField(FieldName.BATTERY_SOC, 43, min=0, max=100),
22
+ UIntField(FieldName.DC_INPUT_POWER, 36),
23
+ UIntField(FieldName.AC_INPUT_POWER, 37),
24
+ UIntField(FieldName.AC_OUTPUT_POWER, 38),
25
+ UIntField(FieldName.DC_OUTPUT_POWER, 39),
26
+ ]
27
+ + additional_fields,
28
+ pack_fields,
29
+ max_packs,
30
+ **kwargs,
31
+ )
32
+
33
+ def get_full_registers_range(self) -> List[ReadableRegisters]:
34
+ return [ReadableRegisters(i, 10) for i in range(1, 8000, 10)]
35
+
36
+ def get_device_type_registers(self) -> List[ReadableRegisters]:
37
+ return [
38
+ ReadableRegisters(10, 6),
39
+ ]
40
+
41
+ def get_device_sn_registers(self) -> List[ReadableRegisters]:
42
+ return [
43
+ ReadableRegisters(17, 4),
44
+ ]
45
+
46
+ def get_iot_version(self) -> int:
47
+ return 1
48
+
49
+ def get_pack_selector(self, pack: int) -> WriteableRegister:
50
+ return WriteableRegister(3006, pack)