Skip to content

Commit 188ae11

Browse files
authored
cleanup JBD protocol (#109)
* clean protocol * make errors timeout * adapted tests
1 parent 3a29080 commit 188ae11

File tree

2 files changed

+35
-40
lines changed

2 files changed

+35
-40
lines changed

custom_components/bms_ble/plugins/jbd_bms.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(self, ble_device: BLEDevice, reconnect: bool = False) -> None:
5151
"""Intialize private BMS members."""
5252
super().__init__(LOGGER, self._notification_handler, ble_device, reconnect)
5353
self._data: bytearray = bytearray()
54-
self._data_final: bytearray | None = None
54+
self._data_final: bytearray = bytearray()
5555

5656
@staticmethod
5757
def matcher_dict_list() -> list[dict[str, Any]]:
@@ -123,7 +123,6 @@ def _notification_handler(self, _sender, data: bytearray) -> None:
123123
LOGGER.debug(
124124
"%s: incorrect frame end (length: %i).", self.name, len(self._data)
125125
)
126-
self._data_event.set()
127126
return
128127

129128
crc: Final[int] = BMS._crc(self._data[2 : frame_end - 2])
@@ -134,10 +133,9 @@ def _notification_handler(self, _sender, data: bytearray) -> None:
134133
int.from_bytes(self._data[frame_end - 2 : frame_end], "big"),
135134
crc,
136135
)
137-
self._data_final = None # reset invalid data
138-
else:
139-
self._data_final = self._data
136+
return
140137

138+
self._data_final = self._data
141139
self._data_event.set()
142140

143141
@staticmethod
@@ -194,8 +192,6 @@ async def _async_update(self) -> BMSsample:
194192
await self._client.write_gatt_char(BMS.uuid_tx(), data=cmd)
195193
await asyncio.wait_for(self._wait_event(), timeout=BAT_TIMEOUT)
196194

197-
if self._data_final is None:
198-
continue
199195
if (
200196
len(self._data_final) != BMS.INFO_LEN + self._data_final[3]
201197
or len(self._data_final) < BMS.INFO_LEN + exp_len

tests/test_jbd_bms.py

+32-33
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from bleak.backends.characteristic import BleakGATTCharacteristic
77
from bleak.exc import BleakError
88
from bleak.uuids import normalize_uuid_str
9+
import pytest
910

1011
from custom_components.bms_ble.plugins.jbd_bms import BMS
1112

@@ -62,34 +63,6 @@ async def write_gatt_char(
6263
self._notify_callback("MockJBDBleakClient", notify_data)
6364

6465

65-
class MockInvalidBleakClient(MockJBDBleakClient):
66-
"""Emulate a JBD BMS BleakClient returning wrong data."""
67-
68-
def _response(
69-
self, char_specifier: BleakGATTCharacteristic | int | str | UUID, data: Buffer
70-
) -> bytearray:
71-
if (
72-
isinstance(char_specifier, str)
73-
and normalize_uuid_str(char_specifier) == normalize_uuid_str("ff02")
74-
and bytearray(data)[0] == self.HEAD_CMD
75-
):
76-
if bytearray(data)[1:3] == self.CMD_INFO:
77-
return bytearray( # wrong end
78-
b"\xdd\x03\x00\x1D\x06\x18\xFE\xE1\x01\xF2\x01\xF4\x00\x2A\x2C\x7C\x00\x00\x00"
79-
b"\x00\x00\x00\x80\x64\x03\x04\x03\x0B\x8B\x0B\x8A\x0B\x84\xf8\x84\xdd"
80-
)
81-
82-
return ( # wrong CRC
83-
bytearray(b"\xdd\x03\x00\x1d") + bytearray(31) + bytearray(b"\x77")
84-
)
85-
86-
return bytearray()
87-
88-
async def disconnect(self) -> bool:
89-
"""Mock disconnect to raise BleakError."""
90-
raise BleakError
91-
92-
9366
class MockOversizedBleakClient(MockJBDBleakClient):
9467
"""Emulate a JBD BMS BleakClient returning wrong data length."""
9568

@@ -166,19 +139,45 @@ async def test_update(monkeypatch, reconnect_fixture) -> None:
166139
await bms.disconnect()
167140

168141

169-
async def test_invalid_response(monkeypatch) -> None:
142+
@pytest.fixture(
143+
name="wrong_response",
144+
params=[
145+
bytearray( # wrong end
146+
b"\xdd\x03\x00\x1D\x06\x18\xFE\xE1\x01\xF2\x01\xF4\x00\x2A\x2C\x7C\x00\x00\x00"
147+
b"\x00\x00\x00\x80\x64\x03\x04\x03\x0B\x8B\x0B\x8A\x0B\x84\xf8\x84\xdd"
148+
),
149+
bytearray(b"\xdd\x04\x00\x1d")
150+
+ bytearray(31)
151+
+ bytearray(b"\x77"), # wrong CRC
152+
],
153+
)
154+
def response(request) -> bytearray:
155+
"""Return all possible BMS variants."""
156+
return request.param
157+
158+
159+
async def test_invalid_response(monkeypatch, wrong_response) -> None:
170160
"""Test data update with BMS returning invalid data (wrong CRC)."""
171161

162+
monkeypatch.setattr(
163+
"custom_components.bms_ble.plugins.jbd_bms.BAT_TIMEOUT",
164+
0.1,
165+
)
166+
167+
monkeypatch.setattr(
168+
"tests.test_jbd_bms.MockJBDBleakClient._response",
169+
lambda _s, _c_, d: wrong_response,
170+
)
171+
172172
monkeypatch.setattr(
173173
"custom_components.bms_ble.plugins.basebms.BleakClient",
174-
MockInvalidBleakClient,
174+
MockJBDBleakClient,
175175
)
176176

177177
bms = BMS(generate_ble_device("cc:cc:cc:cc:cc:cc", "MockBLEdevice", None, -73))
178178

179-
result = await bms.async_update()
180-
181-
assert result == {}
179+
with pytest.raises(TimeoutError):
180+
_result = await bms.async_update()
182181

183182
await bms.disconnect()
184183

0 commit comments

Comments
 (0)