aiobmsble 0.1.0__tar.gz → 0.2.1__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 (54) hide show
  1. {aiobmsble-0.1.0/aiobmsble.egg-info → aiobmsble-0.2.1}/PKG-INFO +23 -14
  2. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/README.md +15 -10
  3. aiobmsble-0.2.1/aiobmsble/__init__.py +99 -0
  4. aiobmsble-0.2.1/aiobmsble/__main__.py +93 -0
  5. aiobmsble-0.2.1/aiobmsble/basebms.py +529 -0
  6. aiobmsble-0.2.1/aiobmsble/bms/__init__.py +1 -0
  7. aiobmsble-0.2.1/aiobmsble/bms/abc_bms.py +164 -0
  8. aiobmsble-0.2.1/aiobmsble/bms/ant_bms.py +196 -0
  9. aiobmsble-0.2.1/aiobmsble/bms/braunpwr_bms.py +167 -0
  10. aiobmsble-0.2.1/aiobmsble/bms/cbtpwr_bms.py +168 -0
  11. aiobmsble-0.2.1/aiobmsble/bms/cbtpwr_vb_bms.py +184 -0
  12. aiobmsble-0.2.1/aiobmsble/bms/daly_bms.py +164 -0
  13. aiobmsble-0.2.1/aiobmsble/bms/dpwrcore_bms.py +207 -0
  14. aiobmsble-0.2.1/aiobmsble/bms/dummy_bms.py +89 -0
  15. aiobmsble-0.2.1/aiobmsble/bms/ecoworthy_bms.py +151 -0
  16. aiobmsble-0.2.1/aiobmsble/bms/ective_bms.py +177 -0
  17. aiobmsble-0.2.1/aiobmsble/bms/ej_bms.py +233 -0
  18. aiobmsble-0.2.1/aiobmsble/bms/felicity_bms.py +139 -0
  19. aiobmsble-0.2.1/aiobmsble/bms/jbd_bms.py +203 -0
  20. aiobmsble-0.2.1/aiobmsble/bms/jikong_bms.py +301 -0
  21. aiobmsble-0.2.1/aiobmsble/bms/neey_bms.py +214 -0
  22. aiobmsble-0.2.1/aiobmsble/bms/ogt_bms.py +214 -0
  23. aiobmsble-0.2.1/aiobmsble/bms/pro_bms.py +144 -0
  24. aiobmsble-0.2.1/aiobmsble/bms/redodo_bms.py +127 -0
  25. aiobmsble-0.2.1/aiobmsble/bms/renogy_bms.py +149 -0
  26. aiobmsble-0.2.1/aiobmsble/bms/renogy_pro_bms.py +105 -0
  27. aiobmsble-0.2.1/aiobmsble/bms/roypow_bms.py +186 -0
  28. aiobmsble-0.2.1/aiobmsble/bms/seplos_bms.py +245 -0
  29. aiobmsble-0.2.1/aiobmsble/bms/seplos_v2_bms.py +205 -0
  30. aiobmsble-0.2.1/aiobmsble/bms/tdt_bms.py +199 -0
  31. aiobmsble-0.2.1/aiobmsble/bms/tianpwr_bms.py +138 -0
  32. aiobmsble-0.2.1/aiobmsble/utils.py +163 -0
  33. {aiobmsble-0.1.0 → aiobmsble-0.2.1/aiobmsble.egg-info}/PKG-INFO +23 -14
  34. aiobmsble-0.2.1/aiobmsble.egg-info/SOURCES.txt +46 -0
  35. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/aiobmsble.egg-info/requires.txt +4 -2
  36. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/pyproject.toml +14 -13
  37. aiobmsble-0.2.1/tests/test_basebms.py +571 -0
  38. aiobmsble-0.2.1/tests/test_examples.py +86 -0
  39. aiobmsble-0.2.1/tests/test_fuzzing.py +61 -0
  40. aiobmsble-0.2.1/tests/test_main.py +150 -0
  41. aiobmsble-0.2.1/tests/test_plugins.py +42 -0
  42. aiobmsble-0.2.1/tests/test_utils.py +191 -0
  43. aiobmsble-0.1.0/aiobmsble/__init__.py +0 -54
  44. aiobmsble-0.1.0/aiobmsble/__main__.py +0 -69
  45. aiobmsble-0.1.0/aiobmsble/basebms.py +0 -313
  46. aiobmsble-0.1.0/aiobmsble/utils.py +0 -73
  47. aiobmsble-0.1.0/aiobmsble.egg-info/SOURCES.txt +0 -15
  48. aiobmsble-0.1.0/tests/test_ogt_bms.py +0 -296
  49. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/LICENSE +0 -0
  50. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/MANIFEST.in +0 -0
  51. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/aiobmsble.egg-info/dependency_links.txt +0 -0
  52. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/aiobmsble.egg-info/entry_points.txt +0 -0
  53. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/aiobmsble.egg-info/top_level.txt +0 -0
  54. {aiobmsble-0.1.0 → aiobmsble-0.2.1}/setup.cfg +0 -0
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiobmsble
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Summary: Asynchronous Python library to query battery management systems via Bluetooth Low Energy.
5
5
  Author: Patrick Loschmidt
6
6
  Maintainer: Patrick Loschmidt
7
7
  License-Expression: Apache-2.0
8
8
  Project-URL: Homepage, https://github.com/patman15/aiobmsble/
9
- Project-URL: Documentation, https://github.com/patman15/aiobmsble/
9
+ Project-URL: Documentation, https://github.com/patman15/aiobmsble/README
10
+ Project-URL: Source Code, https://github.com/patman15/aiobmsble/
11
+ Project-URL: Bug Reports, https://github.com/patman15/aiobmsble/issues
10
12
  Keywords: BMS,BLE,battery,bluetooth
11
13
  Classifier: Programming Language :: Python :: 3
12
14
  Classifier: Operating System :: OS Independent
@@ -16,8 +18,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
18
  Requires-Python: >=3.12
17
19
  Description-Content-Type: text/markdown
18
20
  License-File: LICENSE
19
- Requires-Dist: bleak~=0.22.3
20
- Requires-Dist: bleak-retry-connector~=3.8.1
21
+ Requires-Dist: bleak~=1.1.0
22
+ Requires-Dist: bleak-retry-connector~=4.0.2
21
23
  Requires-Dist: asyncio
22
24
  Requires-Dist: logging
23
25
  Requires-Dist: statistics
@@ -26,6 +28,8 @@ Provides-Extra: dev
26
28
  Requires-Dist: pytest; extra == "dev"
27
29
  Requires-Dist: pytest-asyncio; extra == "dev"
28
30
  Requires-Dist: pytest-cov; extra == "dev"
31
+ Requires-Dist: pytest-xdist; extra == "dev"
32
+ Requires-Dist: hypothesis; extra == "dev"
29
33
  Requires-Dist: mypy; extra == "dev"
30
34
  Requires-Dist: ruff; extra == "dev"
31
35
  Dynamic: license-file
@@ -35,9 +39,8 @@ Dynamic: license-file
35
39
  # Aiobmsble
36
40
  Requires Python 3 and uses [asyncio](https://pypi.org/project/asyncio/) and [bleak](https://pypi.org/project/bleak/)
37
41
  > [!IMPORTANT]
38
- > At the moment the library is under development and not all BMS classes have been ported over from the [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/)!
42
+ > At the moment the library is under development and there might be missing functionality compared to the [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/)!
39
43
  > Please do not (yet) report missing BMS support or bugs here. Instead please raise an issue at the integration till the library reached at least development status *beta*.
40
- > Plan is to support all BMSs that are listed [here](https://github.com/patman15/BMS_BLE-HA/edit/main/README.md#supported-devices).
41
44
 
42
45
  ## Asynchronous Library to Query Battery Management Systems via Bluetooth LE
43
46
  This library is intended to query data from battery management systems that use Bluetooth LE. It is developed to support [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/) that was written to make BMS data available to Home Assistant. While the integration depends on Home Assistant, this library can be used stand-alone in any Python environment (with necessary dependencies installed).
@@ -55,6 +58,7 @@ This example can also be found as an [example](/examples/minimal.py) in the resp
55
58
  """Example of using the aiobmsble library to find a BLE device by name and print its senosr data."""
56
59
 
57
60
  import asyncio
61
+ import logging
58
62
  from typing import Final
59
63
 
60
64
  from bleak import BleakScanner
@@ -62,30 +66,35 @@ from bleak.backends.device import BLEDevice
62
66
  from bleak.exc import BleakError
63
67
 
64
68
  from aiobmsble import BMSsample
65
- from aiobmsble.bms.ogt_bms import BMS # use the right BMS class for your device
69
+ from aiobmsble.bms.dummy_bms import BMS # use the right BMS class for your device
66
70
 
67
71
  NAME: Final[str] = "BT Device Name" # Replace with the name of your BLE device
68
72
 
73
+ # Configure logging
74
+ logging.basicConfig(level=logging.INFO)
75
+ logger: logging.Logger = logging.getLogger(__name__)
76
+
69
77
 
70
78
  async def main(dev_name) -> None:
71
- """Main function to find a BLE device by name and update its sensor data."""
79
+ """Find a BLE device by name and update its sensor data."""
72
80
 
73
81
  device: BLEDevice | None = await BleakScanner.find_device_by_name(dev_name)
74
82
  if device is None:
75
- print(f"Device '{dev_name}' not found.")
83
+ logger.error("Device '%s' not found.", dev_name)
76
84
  return
77
85
 
78
- print(f"Found device: {device.name} ({device.address})")
86
+ logger.info("Found device: %s (%s)", device.name, device.address)
79
87
  bms = BMS(ble_device=device, reconnect=True)
80
88
  try:
81
- print("Updating BMS data...")
89
+ logger.info("Updating BMS data...")
82
90
  data: BMSsample = await bms.async_update()
83
- print("BMS data: ", repr(data).replace(", ", ",\n\t"))
91
+ logger.info("BMS data: %s", repr(data).replace(", ", ",\n\t"))
84
92
  except BleakError as ex:
85
- print(f"Failed to update BMS: {ex}")
93
+ logger.error("Failed to update BMS: %s", type(ex).__name__)
86
94
 
87
95
 
88
- asyncio.run(main(NAME))
96
+ if __name__ == "__main__":
97
+ asyncio.run(main(NAME)) # pragma: no cover
89
98
  ```
90
99
 
91
100
  ## Installation
@@ -3,9 +3,8 @@
3
3
  # Aiobmsble
4
4
  Requires Python 3 and uses [asyncio](https://pypi.org/project/asyncio/) and [bleak](https://pypi.org/project/bleak/)
5
5
  > [!IMPORTANT]
6
- > At the moment the library is under development and not all BMS classes have been ported over from the [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/)!
6
+ > At the moment the library is under development and there might be missing functionality compared to the [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/)!
7
7
  > Please do not (yet) report missing BMS support or bugs here. Instead please raise an issue at the integration till the library reached at least development status *beta*.
8
- > Plan is to support all BMSs that are listed [here](https://github.com/patman15/BMS_BLE-HA/edit/main/README.md#supported-devices).
9
8
 
10
9
  ## Asynchronous Library to Query Battery Management Systems via Bluetooth LE
11
10
  This library is intended to query data from battery management systems that use Bluetooth LE. It is developed to support [BMS_BLE-HA integration](https://github.com/patman15/BMS_BLE-HA/) that was written to make BMS data available to Home Assistant. While the integration depends on Home Assistant, this library can be used stand-alone in any Python environment (with necessary dependencies installed).
@@ -23,6 +22,7 @@ This example can also be found as an [example](/examples/minimal.py) in the resp
23
22
  """Example of using the aiobmsble library to find a BLE device by name and print its senosr data."""
24
23
 
25
24
  import asyncio
25
+ import logging
26
26
  from typing import Final
27
27
 
28
28
  from bleak import BleakScanner
@@ -30,30 +30,35 @@ from bleak.backends.device import BLEDevice
30
30
  from bleak.exc import BleakError
31
31
 
32
32
  from aiobmsble import BMSsample
33
- from aiobmsble.bms.ogt_bms import BMS # use the right BMS class for your device
33
+ from aiobmsble.bms.dummy_bms import BMS # use the right BMS class for your device
34
34
 
35
35
  NAME: Final[str] = "BT Device Name" # Replace with the name of your BLE device
36
36
 
37
+ # Configure logging
38
+ logging.basicConfig(level=logging.INFO)
39
+ logger: logging.Logger = logging.getLogger(__name__)
40
+
37
41
 
38
42
  async def main(dev_name) -> None:
39
- """Main function to find a BLE device by name and update its sensor data."""
43
+ """Find a BLE device by name and update its sensor data."""
40
44
 
41
45
  device: BLEDevice | None = await BleakScanner.find_device_by_name(dev_name)
42
46
  if device is None:
43
- print(f"Device '{dev_name}' not found.")
47
+ logger.error("Device '%s' not found.", dev_name)
44
48
  return
45
49
 
46
- print(f"Found device: {device.name} ({device.address})")
50
+ logger.info("Found device: %s (%s)", device.name, device.address)
47
51
  bms = BMS(ble_device=device, reconnect=True)
48
52
  try:
49
- print("Updating BMS data...")
53
+ logger.info("Updating BMS data...")
50
54
  data: BMSsample = await bms.async_update()
51
- print("BMS data: ", repr(data).replace(", ", ",\n\t"))
55
+ logger.info("BMS data: %s", repr(data).replace(", ", ",\n\t"))
52
56
  except BleakError as ex:
53
- print(f"Failed to update BMS: {ex}")
57
+ logger.error("Failed to update BMS: %s", type(ex).__name__)
54
58
 
55
59
 
56
- asyncio.run(main(NAME))
60
+ if __name__ == "__main__":
61
+ asyncio.run(main(NAME)) # pragma: no cover
57
62
  ```
58
63
 
59
64
  ## Installation
@@ -0,0 +1,99 @@
1
+ """Package for battery management systems (BMS) via Bluetooth LE."""
2
+
3
+ from collections.abc import Callable
4
+ from enum import IntEnum
5
+ from typing import Any, Literal, NamedTuple, TypedDict
6
+
7
+ type BMSvalue = Literal[
8
+ "battery_charging",
9
+ "battery_mode",
10
+ "battery_level",
11
+ "current",
12
+ "power",
13
+ "temperature",
14
+ "voltage",
15
+ "cycles",
16
+ "cycle_capacity",
17
+ "cycle_charge",
18
+ "delta_voltage",
19
+ "problem",
20
+ "runtime",
21
+ "balance_current",
22
+ "cell_count",
23
+ "cell_voltages",
24
+ "design_capacity",
25
+ "pack_count",
26
+ "temp_sensors",
27
+ "temp_values",
28
+ "problem_code",
29
+ ]
30
+
31
+ type BMSpackvalue = Literal[
32
+ "pack_voltages",
33
+ "pack_currents",
34
+ "pack_battery_levels",
35
+ "pack_cycles",
36
+ ]
37
+
38
+
39
+ class BMSmode(IntEnum):
40
+ """Enumeration of BMS modes."""
41
+
42
+ UNKNOWN = -1
43
+ BULK = 0x00
44
+ ABSORPTION = 0x01
45
+ FLOAT = 0x02
46
+
47
+
48
+ class BMSsample(TypedDict, total=False):
49
+ """Dictionary representing a sample of battery management system (BMS) data."""
50
+
51
+ battery_charging: bool # True: battery charging
52
+ battery_mode: BMSmode # BMS charging mode
53
+ battery_level: int | float # [%]
54
+ current: float # [A] (positive: charging)
55
+ power: float # [W] (positive: charging)
56
+ temperature: int | float # [°C]
57
+ voltage: float # [V]
58
+ cycle_capacity: int | float # [Wh]
59
+ cycles: int # [#]
60
+ delta_voltage: float # [V]
61
+ problem: bool # True: problem detected
62
+ runtime: int # [s]
63
+ # detailed information
64
+ balance_current: float # [A]
65
+ cell_count: int # [#]
66
+ cell_voltages: list[float] # [V]
67
+ cycle_charge: int | float # [Ah]
68
+ design_capacity: int # [Ah]
69
+ pack_count: int # [#]
70
+ temp_sensors: int # [#]
71
+ temp_values: list[int | float] # [°C]
72
+ problem_code: int # BMS specific code, 0 no problem
73
+ # battery pack data
74
+ pack_voltages: list[float] # [V]
75
+ pack_currents: list[float] # [A]
76
+ pack_battery_levels: list[int | float] # [%]
77
+ pack_cycles: list[int] # [#]
78
+
79
+
80
+ class BMSdp(NamedTuple):
81
+ """Representation of one BMS data point."""
82
+
83
+ key: BMSvalue # the key of the value to be parsed
84
+ pos: int # position within the message
85
+ size: int # size in bytes
86
+ signed: bool # signed value
87
+ fct: Callable[[int], Any] = lambda x: x # conversion function (default do nothing)
88
+ idx: int = -1 # array index containing the message to be parsed
89
+
90
+
91
+ class MatcherPattern(TypedDict, total=False):
92
+ """Optional patterns that can match Bleak advertisement data."""
93
+
94
+ local_name: str # name pattern that supports Unix shell-style wildcards
95
+ service_uuid: str # 128-bit UUID that the device must advertise
96
+ service_data_uuid: str # service data for the service UUID
97
+ manufacturer_id: int # required manufacturer ID
98
+ manufacturer_data_start: list[int] # required starting bytes of manufacturer data
99
+ connectable: bool # True if active connections to the device are required
@@ -0,0 +1,93 @@
1
+ """Example function for package usage."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ import logging
6
+ from typing import Final
7
+
8
+ from bleak import BleakScanner
9
+ from bleak.backends.device import BLEDevice
10
+ from bleak.backends.scanner import AdvertisementData
11
+ from bleak.exc import BleakError
12
+
13
+ from aiobmsble import BMSsample
14
+ from aiobmsble.basebms import BaseBMS
15
+ from aiobmsble.utils import bms_identify
16
+
17
+ logging.basicConfig(
18
+ format="%(levelname)s: %(message)s",
19
+ level=logging.INFO,
20
+ )
21
+ logger: logging.Logger = logging.getLogger(__package__)
22
+
23
+
24
+ async def scan_devices() -> dict[str, tuple[BLEDevice, AdvertisementData]]:
25
+ """Scan for BLE devices and return results."""
26
+ logger.info("starting scan...")
27
+ scan_result: dict[str, tuple[BLEDevice, AdvertisementData]] = (
28
+ await BleakScanner.discover(return_adv=True)
29
+ )
30
+ logger.info(scan_result)
31
+ logger.info("%i BT devices in range.", len(scan_result))
32
+ return scan_result
33
+
34
+
35
+ async def detect_bms() -> None:
36
+ """Query a Bluetooth device based on the provided arguments."""
37
+
38
+ scan_result: dict[str, tuple[BLEDevice, AdvertisementData]] = await scan_devices()
39
+ for ble_dev, advertisement in scan_result.values():
40
+ logger.info(
41
+ "%s\nBT device '%s' (%s)\n\t%s",
42
+ "-" * 72,
43
+ ble_dev.name,
44
+ ble_dev.address,
45
+ repr(advertisement).replace(", ", ",\n\t"),
46
+ )
47
+
48
+ if bms_cls := bms_identify(advertisement):
49
+ logger.info("Found matching BMS type: %s", bms_cls.device_id())
50
+ bms: BaseBMS = bms_cls(ble_device=ble_dev, reconnect=True)
51
+
52
+ try:
53
+ logger.info("Updating BMS data...")
54
+ data: BMSsample = await bms.async_update()
55
+ logger.info("BMS data: %s", repr(data).replace(", '", ",\n\t'"))
56
+ except (BleakError, TimeoutError) as exc:
57
+ logger.error("Failed to update BMS: %s", type(exc).__name__)
58
+
59
+ logger.info("done.")
60
+
61
+
62
+ def setup_logging(args: argparse.Namespace) -> None:
63
+ """Configure logging based on command line arguments."""
64
+ loglevel: Final[int] = logging.DEBUG if args.verbose else logging.INFO
65
+
66
+ if args.logfile:
67
+ file_handler = logging.FileHandler(args.logfile)
68
+ file_handler.setLevel(loglevel)
69
+ file_handler.setFormatter(
70
+ logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
71
+ )
72
+ logger.addHandler(file_handler)
73
+
74
+ logger.setLevel(loglevel)
75
+
76
+
77
+ def main() -> None:
78
+ """Entry point for the script to run the BMS detection."""
79
+ parser = argparse.ArgumentParser(
80
+ description="Reference script for 'aiobmsble' to show all recognized BMS in range."
81
+ )
82
+ parser.add_argument("-l", "--logfile", type=str, help="Path to the log file")
83
+ parser.add_argument(
84
+ "-v", "--verbose", action="store_true", help="Enable debug logging"
85
+ )
86
+
87
+ setup_logging(parser.parse_args())
88
+
89
+ asyncio.run(detect_bms())
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main() # pragma: no cover