Skip to content

Commit 90aeb58

Browse files
authored
Feat bump aiobmsble to 0.24.0 (#721)
* bump aiobmsble to 0.23.0 * code optimization * add pylint to CI/CD * Update Pylint step to continue on error Allow Pylint to continue on error during linting. * Update README with RSSI sensor FAQ Added FAQ entry about RSSI sensor updates and availability. * fix _attr_pack() signature * Update README.md * remove ECO-WORTHY limitation * Add detection for Sungoldpower batteries (#723) * bump aiobmsble * Update manifest.json * bump aiobmsble to 0.23.0 (#704) * Update README with NEEY BMS v2 Added NEEY BMS v2 to the list of supported devices. * Change problem code type (#707) * change problem code type * fix test according to new problem code type * fix test * Update README.md * fix merged aiobmsble branch * bump aiobmsble to 0.24.0 * update HA to 2026.6.0 closes #713, closes #711 * remove advanced options from tests * fix TempSensor in tests * fix BLEDevice path * fix test errors * minimize test requirements * update python/ruff version * requirements to avoid warnings * patch fixture issue * mitigate lingering timers * fix flaky tests * update pre-commit * update mypy settings * fix still flaky test * update manifest * fix race condition
1 parent a0629ef commit 90aeb58

26 files changed

Lines changed: 351 additions & 234 deletions

.github/workflows/fuzzing.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: "Set up Python"
1919
uses: actions/setup-python@main
2020
with:
21-
python-version: "3.13"
21+
python-version: "3.14"
2222
cache: "pip"
2323

2424
- name: Install dependencies

.github/workflows/hassfest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ permissions:
55

66
on:
77
push:
8+
branches:
9+
- main
810
pull_request:
911
workflow_dispatch:
1012
schedule:

.github/workflows/lint.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ permissions:
55

66
on:
77
push:
8+
branches:
9+
- main
810
pull_request:
911
workflow_dispatch:
1012
schedule:
@@ -21,14 +23,18 @@ jobs:
2123
- name: "Set up Python"
2224
uses: actions/setup-python@main
2325
with:
24-
python-version: "3.13"
26+
python-version: "3.14"
2527
cache: "pip"
2628

2729
- name: "Install requirements"
2830
run: python3 -m pip install -r requirements_test.txt
2931

3032
- name: "Run Ruff"
3133
run: python3 -m ruff check .
34+
35+
- name: "Run Pylint"
36+
continue-on-error: true
37+
run: python3 -m pylint .
3238

3339
- name: "Run MyPy"
3440
run: python3 -m mypy .

.github/workflows/tests.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Run tests for integrations
22

33
on:
44
push:
5+
branches:
6+
- main
57
pull_request:
68
workflow_dispatch:
79
schedule:
@@ -20,7 +22,7 @@ jobs:
2022
- name: "Set up Python"
2123
uses: actions/setup-python@main
2224
with:
23-
python-version: "3.13"
25+
python-version: "3.14"
2426
cache: "pip"
2527

2628
- name: Install dependencies

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,5 @@ cython_debug/
162162
TODO.md
163163

164164
# macOS
165-
.DS_Store
165+
.DS_Store
166+
scripts/create_bump_aiobmsble_branch.py

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.11.0 # ruff version, needs to match installed version
3+
rev: v0.15.20 # ruff version, needs to match installed version
44
hooks:
55
- id: ruff
66
# - repo: https://github.com/pre-commit/mirrors-mypy
7-
# rev: v1.17.0 # Use the latest stable version of mypy
7+
# rev: v2.1.0 # Use the latest stable version of mypy
88
# hooks:
99
# - id: mypy
10-
# additional_dependencies: [homeassistant==2025.7.0] # takes long thus mypy disabled here
10+
# additional_dependencies: [homeassistant==2026.6.0] # takes long thus mypy disabled here
1111
# args: ["--allow-subclassing-any"] # avoid HA dependency installation
1212
# # args: [--ignore-missing-imports] # Optional: customize your mypy flags

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ This integration allows to monitor Bluetooth Low Energy (BLE) battery management
5252
- DCHOUSE batteries (show up as `DCHOUSE`…)
5353
- Ective, Startcraft, Topband batteries (show up as `$PFLAC`…, `NWJ20`…, `ZM20`…)
5454
- EG4 LL BMS
55+
- Sungold batteries
5556
- Eleksol batteries
5657
- Felicity ESS (show up as `F10`…) and FLB batteries (show up as `F07`…)
5758
- Gobel Power BMS (show up as `BMS-`…)
5859
- HumsiENK Smart BMS (show up as `HS`…)
60+
- ECO-WORTHY (show up as `ECO....`)
5961
- JBD BMS, Jiabaida, Xiaoxiang (show up as `JBD-`…)
6062
- accurat batteries, Aolithium batteries
61-
- BasenGreen, Bulltron, CHINS, DCHOUSE, ECO-WORTHY, Epoch batteries
63+
- BasenGreen, Bulltron, CHINS, Cloud Energy, DCHOUSE, ECO-WORTHY, Epoch batteries
6264
- Eleksol, Elfhub, Fritz Berger, JavaEnegy, LANPWR, Liontron, Lossigy, Norström, OGRPHY, Perfektium, Ultimatron batteries
6365
- SBL batteries (show up as `SBL-`…), Supervolt v3 batteries (show up as `SX1`…), Vatrer batteries
6466
- JK BMS, Jikong, (HW version ≥ 6 required)
@@ -164,10 +166,6 @@ This integration follows standard integration removal. No extra steps are requir
164166
165167
### Known Issues
166168

167-
<details><summary>ECO-WORTHY batteries "<code>ECOxxxx</code>"</summary>
168-
ECO-WORTHY batteries that show up as <code>ECOxxxx</code> use classic Bluetooth and do not support Bluetooth Low Energy (BLE). Thus, they unfortunately cannot be integrated.
169-
The advertisement contains <code>{"name":"ECOxxxx","service_uuids":["0000ff00-0000-1000-8000-00805f9b34fb","00000001-0000-1000-8000-00805f9b34fb"]</code>
170-
</details>
171169
<details><summary>Elektronicx, Lithtech batteries</summary>
172170
Bluetooth is turned off, when there is no current. Thus, device will get unavailable / cannot be added.
173171
</details>
@@ -261,6 +259,9 @@ Then go to the [energy dashboard configuration](https://my.home-assistant.io/red
261259
### My sensors show unknown/unavailable at startup!
262260
The polling interval is 30 seconds. So at startup it takes a few minutes to detect the battery and query the sensors. Then data will be available.
263261

262+
### Why is the RSSI sensor not updated or is unavailable?
263+
The `RSSI` value is only measured by Home Assistant when a device is not connected. Thus, you will see updates only in case a connection is lost or after a restart. The integration by default tries to maintain a permanent connection to improve data availability and avoid constant reconnect not appreciated by some BMSs.
264+
264265
### Can I set a custom polling interval?
265266
Yes, but I strongly discourage that for stability reasons. If you still want to do so, please see the default way to define a [custom interval][custint-url] by Home Assistant. Note that Bluetooth discoveries can take up to a minute in worst case. Thus, please expect side effects, when changing the default of 30 seconds!
266267

@@ -336,7 +337,7 @@ all [contributors of aiobmsble](https://github.com/patman15/aiobmsble?tab=readme
336337
[license-shield]: https://img.shields.io/github/license/patman15/BMS_BLE-HA?style=for-the-badge&color=orange&cacheSeconds=86400
337338
[releases-shield]: https://img.shields.io/github/release/patman15/BMS_BLE-HA.svg?style=for-the-badge&cacheSeconds=14400
338339
[releases]: https://github.com//patman15/BMS_BLE-HA/releases
339-
[effort-shield]: https://img.shields.io/badge/Effort%20spent-1016_hours-gold?style=for-the-badge&cacheSeconds=86400
340+
[effort-shield]: https://img.shields.io/badge/Effort%20spent-1073_hours-gold?style=for-the-badge&cacheSeconds=86400
340341
[install-shield]: https://img.shields.io/badge/dynamic/json?style=for-the-badge&color=green&label=HACS&suffix=%20Installs&cacheSeconds=15600&url=https://analytics.home-assistant.io/custom_integrations.json&query=$.bms_ble.total&cacheSeconds=14400
341342
[btproxy-url]: https://esphome.io/components/bluetooth_proxy
342343
[custint-url]: https://www.home-assistant.io/common-tasks/general/#defining-a-custom-polling-interval

custom_components/bms_ble/binary_sensor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Support for BMS_BLE binary sensors."""
22

33
from collections.abc import Callable
4+
from typing import override
45

56
from aiobmsble import BMSMode, BMSSample
67

@@ -137,15 +138,17 @@ def __init__(
137138
self._attr_unique_id = f"{DOMAIN}-{unique_id}-{descr.key}"
138139
self._attr_device_info = bms.device_info
139140
self._attr_has_entity_name = True
140-
self.entity_description: BmsBinaryEntityDescription = descr
141+
self.entity_description = descr
141142
super().__init__(bms)
142143

143144
@property
145+
@override
144146
def is_on(self) -> bool | None:
145147
"""Handle updated data from the coordinator."""
146148
return bool(self.coordinator.data.get(self.entity_description.key))
147149

148150
@property
151+
@override
149152
def extra_state_attributes(self) -> dict[str, int | str] | None:
150153
"""Return entity specific state attributes, e.g. cell voltages."""
151154
return (

custom_components/bms_ble/config_flow.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Config flow for BLE Battery Management System integration."""
22

33
from dataclasses import dataclass
4-
from typing import Any, Final
4+
from typing import Any, Final, override
55

66
from aiobmsble.basebms import BaseBMS
77
from aiobmsble.utils import bms_cls, bms_identify
@@ -83,15 +83,16 @@ async def _async_device_supported(
8383
)
8484
return str(bms_class.get_bms_module())
8585

86+
@override
8687
async def async_step_bluetooth(
8788
self, discovery_info: BluetoothServiceInfoBleak
8889
) -> ConfigFlowResult:
8990
"""Handle a flow initialized by Bluetooth discovery."""
9091
LOGGER.debug("Bluetooth device detected: %s", discovery_info)
9192

9293
address: Final = discovery_info.address
93-
await self.async_set_unique_id(address)
94-
self._abort_if_unique_id_configured()
94+
if await self.async_set_unique_id(address, raise_on_progress=False):
95+
return self.async_abort(reason="already_configured")
9596

9697
if not (bms_module := await self._async_device_supported(discovery_info)):
9798
return self.async_abort(reason="not_supported")
@@ -114,7 +115,11 @@ async def async_step_bluetooth_confirm(
114115
LOGGER.debug("confirm step for %s", self._disc_dev.name)
115116

116117
if user_input is not None:
117-
self._abort_if_unique_id_configured()
118+
if await self.async_set_unique_id(
119+
self._disc_dev.discovery_info.address, raise_on_progress=False
120+
):
121+
return self.async_abort(reason="already_configured")
122+
118123
return self.async_create_entry(
119124
title=self._disc_dev.name,
120125
data={"type": self._disc_dev.type},
@@ -127,6 +132,7 @@ async def async_step_bluetooth_confirm(
127132
description_placeholders=self.context.get("title_placeholders"),
128133
)
129134

135+
@override
130136
async def async_step_user(
131137
self, user_input: dict[str, Any] | None = None
132138
) -> ConfigFlowResult:
@@ -138,8 +144,8 @@ async def async_step_user(
138144

139145
if user_input is not None:
140146
address = str(user_input[CONF_ADDRESS])
141-
await self.async_set_unique_id(address, raise_on_progress=False)
142-
self._abort_if_unique_id_configured()
147+
if await self.async_set_unique_id(address, raise_on_progress=False):
148+
return self.async_abort(reason="already_configured")
143149
self._disc_dev = self._disc_devs[address]
144150

145151
return self.async_create_entry(
@@ -186,6 +192,7 @@ async def async_step_user(
186192

187193
@staticmethod
188194
@callback
195+
@override
189196
def async_get_options_flow(
190197
config_entry: ConfigEntry,
191198
) -> OptionsFlowWithReload:

custom_components/bms_ble/coordinator.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from collections import deque
44
from datetime import timedelta
5+
from itertools import islice
56
from time import monotonic
6-
from typing import Final
7+
from typing import Final, override
78

89
from aiobmsble import BMSSample
910
from aiobmsble.basebms import BaseBMS
@@ -81,6 +82,7 @@ def link_quality(self) -> int:
8182

8283
return self._link_q.count(True) * 100 // len(self._link_q)
8384

85+
@override
8486
async def async_shutdown(self) -> None:
8587
"""Shutdown coordinator and any connection."""
8688
LOGGER.debug("Shutting down BMS (%s)", self.name)
@@ -93,7 +95,7 @@ def _device_stale(self) -> bool:
9395
elif (
9496
not self._stale
9597
and self.link_quality <= 10
96-
and list(self._link_q)[-10:] == [False] * 10
98+
and list(islice(reversed(self._link_q), 10)) == [False] * 10
9799
):
98100
rssi: Final = self.rssi
99101
LOGGER.error(
@@ -107,6 +109,7 @@ def _device_stale(self) -> bool:
107109

108110
return self._stale
109111

112+
@override
110113
async def _async_setup(self) -> None:
111114
bms_info: Final = await self._device.device_info()
112115
self.device_info.update(
@@ -122,6 +125,7 @@ async def _async_setup(self) -> None:
122125
)
123126
)
124127

128+
@override
125129
async def _async_update_data(self) -> BMSSample:
126130
"""Return the latest data from the device."""
127131

0 commit comments

Comments
 (0)