Add coverage for bluetooth advertising (#670)

This commit is contained in:
J. Nick Koston 2023-11-23 17:20:56 +01:00 committed by GitHub
parent 1cc6b3ed52
commit 33966938f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 14 deletions

View File

@ -563,19 +563,23 @@ class APIClient:
return resp[0] return resp[0]
async def subscribe_bluetooth_le_advertisements(
self, on_bluetooth_le_advertisement: Callable[[BluetoothLEAdvertisement], None]
) -> Callable[[], None]:
msg_types = (BluetoothLEAdvertisementResponse,)
def _on_bluetooth_le_advertising_response( def _on_bluetooth_le_advertising_response(
self,
on_bluetooth_le_advertisement: Callable[[BluetoothLEAdvertisement], None],
msg: BluetoothLEAdvertisementResponse, msg: BluetoothLEAdvertisementResponse,
) -> None: ) -> None:
on_bluetooth_le_advertisement(BluetoothLEAdvertisement.from_pb(msg)) # type: ignore[misc] on_bluetooth_le_advertisement(BluetoothLEAdvertisement.from_pb(msg)) # type: ignore[misc]
async def subscribe_bluetooth_le_advertisements(
self, on_bluetooth_le_advertisement: Callable[[BluetoothLEAdvertisement], None]
) -> Callable[[], None]:
msg_types = (BluetoothLEAdvertisementResponse,)
unsub_callback = self._get_connection().send_message_callback_response( unsub_callback = self._get_connection().send_message_callback_response(
SubscribeBluetoothLEAdvertisementsRequest(flags=0), SubscribeBluetoothLEAdvertisementsRequest(flags=0),
_on_bluetooth_le_advertising_response, partial(
self._on_bluetooth_le_advertising_response,
on_bluetooth_le_advertisement,
),
msg_types, msg_types,
) )
@ -588,21 +592,22 @@ class APIClient:
return unsub return unsub
def _on_ble_raw_advertisement_response(
self,
on_advertisements: Callable[[list[BluetoothLERawAdvertisement]], None],
msg: BluetoothLERawAdvertisementsResponse,
) -> None:
on_advertisements(msg.advertisements)
async def subscribe_bluetooth_le_raw_advertisements( async def subscribe_bluetooth_le_raw_advertisements(
self, on_advertisements: Callable[[list[BluetoothLERawAdvertisement]], None] self, on_advertisements: Callable[[list[BluetoothLERawAdvertisement]], None]
) -> Callable[[], None]: ) -> Callable[[], None]:
msg_types = (BluetoothLERawAdvertisementsResponse,) msg_types = (BluetoothLERawAdvertisementsResponse,)
def _on_ble_raw_advertisement_response(
data: BluetoothLERawAdvertisementsResponse,
) -> None:
on_advertisements(data.advertisements)
unsub_callback = self._get_connection().send_message_callback_response( unsub_callback = self._get_connection().send_message_callback_response(
SubscribeBluetoothLEAdvertisementsRequest( SubscribeBluetoothLEAdvertisementsRequest(
flags=BluetoothProxySubscriptionFlag.RAW_ADVERTISEMENTS flags=BluetoothProxySubscriptionFlag.RAW_ADVERTISEMENTS
), ),
_on_ble_raw_advertisement_response, partial(self._on_ble_raw_advertisement_response, on_advertisements),
msg_types, msg_types,
) )

View File

@ -24,6 +24,9 @@ from aioesphomeapi.api_pb2 import (
BluetoothGATTReadResponse, BluetoothGATTReadResponse,
BluetoothGATTService, BluetoothGATTService,
BluetoothGATTWriteResponse, BluetoothGATTWriteResponse,
BluetoothLEAdvertisementResponse,
BluetoothLERawAdvertisement,
BluetoothLERawAdvertisementsResponse,
ButtonCommandRequest, ButtonCommandRequest,
CameraImageRequest, CameraImageRequest,
CameraImageResponse, CameraImageResponse,
@ -63,6 +66,7 @@ from aioesphomeapi.model import (
) )
from aioesphomeapi.model import BluetoothGATTService as BluetoothGATTServiceModel from aioesphomeapi.model import BluetoothGATTService as BluetoothGATTServiceModel
from aioesphomeapi.model import ( from aioesphomeapi.model import (
BluetoothLEAdvertisement,
CameraState, CameraState,
ClimateFanMode, ClimateFanMode,
ClimateMode, ClimateMode,
@ -1212,6 +1216,88 @@ async def test_bluetooth_gatt_start_notify_fails(
) )
@pytest.mark.asyncio
async def test_subscribe_bluetooth_le_advertisements(
api_client: tuple[
APIClient, APIConnection, asyncio.Transport, APIPlaintextFrameHelper
],
) -> None:
"""Test subscribe_bluetooth_le_advertisements."""
client, connection, transport, protocol = api_client
advs = []
def on_bluetooth_le_advertisements(adv: BluetoothLEAdvertisement) -> None:
advs.append(adv)
unsub = await client.subscribe_bluetooth_le_advertisements(
on_bluetooth_le_advertisements
)
await asyncio.sleep(0)
response: message.Message = BluetoothLEAdvertisementResponse(
address=1234,
name=b"mydevice",
rssi=-50,
service_uuids=["1234"],
service_data={},
manufacturer_data={},
address_type=1,
)
protocol.data_received(generate_plaintext_packet(response))
assert advs == [
BluetoothLEAdvertisement(
address=1234,
name="mydevice",
rssi=-50,
service_uuids=["000034-0000-1000-8000-00805f9b34fb"],
manufacturer_data={},
service_data={},
address_type=1,
)
]
unsub()
@pytest.mark.asyncio
async def test_subscribe_bluetooth_le_raw_advertisements(
api_client: tuple[
APIClient, APIConnection, asyncio.Transport, APIPlaintextFrameHelper
],
) -> None:
"""Test subscribe_bluetooth_le_raw_advertisements."""
client, connection, transport, protocol = api_client
adv_groups = []
def on_raw_bluetooth_le_advertisements(
advs: list[BluetoothLERawAdvertisementsResponse],
) -> None:
adv_groups.append(advs)
unsub = await client.subscribe_bluetooth_le_raw_advertisements(
on_raw_bluetooth_le_advertisements
)
await asyncio.sleep(0)
response: message.Message = BluetoothLERawAdvertisementsResponse(
advertisements=[
BluetoothLERawAdvertisement(
address=1234,
rssi=-50,
address_type=1,
data=b"1234",
)
]
)
protocol.data_received(generate_plaintext_packet(response))
assert len(adv_groups) == 1
first_adv = adv_groups[0][0]
assert first_adv.address == 1234
assert first_adv.rssi == -50
assert first_adv.address_type == 1
assert first_adv.data == b"1234"
unsub()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_subscribe_logs(auth_client: APIClient) -> None: async def test_subscribe_logs(auth_client: APIClient) -> None:
send = patch_response_callback(auth_client) send = patch_response_callback(auth_client)