Skip to content

Commit

Permalink
start/stop notify
Browse files Browse the repository at this point in the history
  • Loading branch information
patman15 committed Jun 12, 2024
1 parent 0279bba commit a850bbe
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 17 deletions.
2 changes: 1 addition & 1 deletion custom_components/bms_ble/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(
update_interval=timedelta(seconds=UPDATE_INTERVAL),
always_update=False, # only update when sensor value has changed
)

self._mac = ble_device.address
LOGGER.debug(
"Initializing coordinator for %s (%s) as %s",
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bms_ble/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@
"issue_tracker": "https://github.com/patman15/BMS_BLE-HA/issues",
"loggers": ["bms_ble", "ogt_bms", "daly_bms", "jikong_bms"],
"requirements": [],
"version": "1.3.0b1"
"version": "1.3.0b2"
}
43 changes: 30 additions & 13 deletions custom_components/bms_ble/plugins/jikong_bms.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __init__(self, ble_device: BLEDevice, reconnect: bool = False) -> None:
self._data_event = asyncio.Event()
self._connected = False # flag to indicate active BLE connection
self._char_write_handle: int | None = None
self._char_notify_handle: int | None = None
self._FIELDS: list[tuple[str, int, int, bool, Callable[[int], int | float]]] = [
(ATTR_TEMPERATURE, 144, 2, True, lambda x: float(x / 10)),
(ATTR_VOLTAGE, 150, 4, False, lambda x: float(x / 1000)),
Expand Down Expand Up @@ -168,8 +169,7 @@ async def _connect(self) -> None:
services=[UUID_SERVICE],
)
await self._client.connect()
char_notify_handle: int | None = None
self._char_write_handle = None

for service in self._client.services:
for char in service.characteristics:
value: bytearray = bytearray()
Expand All @@ -185,27 +185,27 @@ async def _connect(self) -> None:
)
if char.uuid == UUID_CHAR:
if "notify" in char.properties:
char_notify_handle = char.handle
self._char_notify_handle = char.handle
if (
"write" in char.properties
or "write-without-response" in char.properties
):
self._char_write_handle = char.handle
if char_notify_handle is None or self._char_write_handle is None:
if self._char_notify_handle is None or self._char_write_handle is None:
LOGGER.debug(
"(%s) Failed to detect characteristics", self._ble_device.name
)
await self._client.disconnect()
return

LOGGER.debug(
"(%s) Using characteristics handle #%i (notify), #%i (write)",
self._ble_device.name,
char_notify_handle,
self._char_notify_handle,
self._char_write_handle,
)
await self._client.start_notify(
char_notify_handle or 0, self._notification_handler
)

await self._start_notify()

# query device info
await self._client.write_gatt_char(
Expand All @@ -216,6 +216,24 @@ async def _connect(self) -> None:
else:
LOGGER.debug("BMS %s already connected", self._ble_device.name)

async def _start_notify(self) -> None:
"""Start notification from BMS characteristic"""
assert self._client

await self._client.start_notify(
self._char_notify_handle or 0, self._notification_handler
)

# request cell info update
await self._client.write_gatt_char(
self._char_write_handle or 0, data=self._cmd(b"\x96")
)

async def _stop_notify(self) -> None:
"""Stop notification from BMS characteristic"""
assert self._client
await self._client.stop_notify(self._char_notify_handle or 0)

async def disconnect(self) -> None:
"""Disconnect the BMS and includes stoping notifications."""

Expand All @@ -228,6 +246,8 @@ async def disconnect(self) -> None:
LOGGER.warning("Disconnect failed!")

self._client = None
self._char_notify_handle = None
self._char_write_handle = None

def _crc(self, frame: bytes):
"""Calculate Jikong frame CRC."""
Expand All @@ -254,12 +274,9 @@ async def async_update(self) -> dict[str, int | float | bool]:
)
return {}

# query cell info
await self._client.write_gatt_char(
self._char_write_handle or 0, data=self._cmd(b"\x96")
)

await self._start_notify()
await asyncio.wait_for(self._wait_event(), timeout=BAT_TIMEOUT)
await self._stop_notify()

if self._data_final is None:
return {}
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ async def start_notify( # type: ignore
assert self._connected, "start_notify called, but client not connected."
self._notify_callback = callback

async def stop_notify( # type: ignore
self, char_specifier: Union[BleakGATTCharacteristic, int, str]
) -> None:
"""Mock stop_notify."""
LOGGER.debug("MockBleakClient stop_notify for %s", char_specifier)
assert self._connected, "stop_notify called, but client not connected."
self._notify_callback = None

async def write_gatt_char( # type: ignore
self,
char_specifier: Union[BleakGATTCharacteristic, int, str],
Expand Down
4 changes: 2 additions & 2 deletions tests/test_jikong_bms.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ async def write_gatt_char(
response: bool = None, # type: ignore[implicit-optional] # same as upstream
) -> None:
"""Issue write command to GATT."""
# await super().write_gatt_char(char_specifier, data, response)
assert self._notify_callback is not None

assert self._notify_callback, "write to characteristics but notification not enabled"
self._notify_callback(
"MockJikongBleakClient", bytearray(b"\x41\x54\x0d\x0a")
) # interleaved AT\r\n command
Expand Down

0 comments on commit a850bbe

Please sign in to comment.