Skip to content

Commit

Permalink
Improve performance of converting raw BLE advertisements (#462)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Jul 9, 2023
1 parent b81fe76 commit 8ac62a3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 32 deletions.
64 changes: 41 additions & 23 deletions aioesphomeapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@
BluetoothGATTServices,
BluetoothLEAdvertisement,
BluetoothLERawAdvertisement,
BluetoothLERawAdvertisements,
BluetoothProxyFeature,
BluetoothProxySubscriptionFlag,
ButtonInfo,
Expand Down Expand Up @@ -183,6 +182,7 @@
UserServiceArgType,
VoiceAssistantCommand,
VoiceAssistantEventType,
make_ble_raw_advertisement_processor,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -409,7 +409,7 @@ async def subscribe_states(self, on_state: Callable[[EntityState], None]) -> Non
}
msg_types = (*response_types, CameraImageResponse)

def on_msg(msg: message.Message) -> None:
def _on_state_msg(msg: message.Message) -> None:
msg_type = type(msg)
cls = response_types.get(msg_type)
if cls:
Expand All @@ -432,7 +432,7 @@ def on_msg(msg: message.Message) -> None:

assert self._connection is not None
self._connection.send_message_callback_response(
SubscribeStatesRequest(), on_msg, msg_types
SubscribeStatesRequest(), _on_state_msg, msg_types
)

async def subscribe_logs(
Expand All @@ -457,13 +457,15 @@ async def subscribe_service_calls(
) -> None:
self._check_authenticated()

def on_msg(msg: HomeassistantServiceResponse) -> None:
def _on_home_assistant_service_response(
msg: HomeassistantServiceResponse,
) -> None:
on_service_call(HomeassistantServiceCall.from_pb(msg))

assert self._connection is not None
self._connection.send_message_callback_response(
SubscribeHomeassistantServicesRequest(),
on_msg,
_on_home_assistant_service_response,
(HomeassistantServiceResponse,),
)

Expand Down Expand Up @@ -501,19 +503,23 @@ async def subscribe_bluetooth_le_advertisements(
self._check_authenticated()
msg_types = (BluetoothLEAdvertisementResponse,)

def on_msg(msg: BluetoothLEAdvertisementResponse) -> None:
def _on_bluetooth_le_advertising_response(
msg: BluetoothLEAdvertisementResponse,
) -> None:
on_bluetooth_le_advertisement(BluetoothLEAdvertisement.from_pb(msg)) # type: ignore[misc]

assert self._connection is not None
self._connection.send_message_callback_response(
SubscribeBluetoothLEAdvertisementsRequest(flags=0),
on_msg,
_on_bluetooth_le_advertising_response,
msg_types,
)

def unsub() -> None:
if self._connection is not None:
self._connection.remove_message_callback(on_msg, msg_types)
self._connection.remove_message_callback(
_on_bluetooth_le_advertising_response, msg_types
)
self._connection.send_message(
UnsubscribeBluetoothLEAdvertisementsRequest()
)
Expand All @@ -526,10 +532,8 @@ async def subscribe_bluetooth_le_raw_advertisements(
self._check_authenticated()
msg_types = (BluetoothLERawAdvertisementsResponse,)

def on_msg(msg: BluetoothLERawAdvertisementsResponse) -> None:
on_advertisements(BluetoothLERawAdvertisements.from_pb(msg).advertisements) # type: ignore[misc]

assert self._connection is not None
on_msg = make_ble_raw_advertisement_processor(on_advertisements)
self._connection.send_message_callback_response(
SubscribeBluetoothLEAdvertisementsRequest(
flags=BluetoothProxySubscriptionFlag.RAW_ADVERTISEMENTS
Expand All @@ -553,18 +557,24 @@ async def subscribe_bluetooth_connections_free(
self._check_authenticated()
msg_types = (BluetoothConnectionsFreeResponse,)

def on_msg(msg: BluetoothConnectionsFreeResponse) -> None:
def _on_bluetooth_connections_free_response(
msg: BluetoothConnectionsFreeResponse,
) -> None:
resp = BluetoothConnectionsFree.from_pb(msg)
on_bluetooth_connections_free_update(resp.free, resp.limit)

assert self._connection is not None
self._connection.send_message_callback_response(
SubscribeBluetoothConnectionsFreeRequest(), on_msg, msg_types
SubscribeBluetoothConnectionsFreeRequest(),
_on_bluetooth_connections_free_response,
msg_types,
)

def unsub() -> None:
if self._connection is not None:
self._connection.remove_message_callback(on_msg, msg_types)
self._connection.remove_message_callback(
_on_bluetooth_connections_free_response, msg_types
)

return unsub

Expand All @@ -583,7 +593,9 @@ async def bluetooth_device_connect( # pylint: disable=too-many-locals

event = asyncio.Event()

def on_msg(msg: BluetoothDeviceConnectionResponse) -> None:
def _on_bluetooth_device_connection_response(
msg: BluetoothDeviceConnectionResponse,
) -> None:
resp = BluetoothDeviceConnection.from_pb(msg)
if address == resp.address:
on_bluetooth_connection_state(resp.connected, resp.mtu, resp.error)
Expand Down Expand Up @@ -614,13 +626,15 @@ def on_msg(msg: BluetoothDeviceConnectionResponse) -> None:
has_address_type=address_type is not None,
address_type=address_type or 0,
),
on_msg,
_on_bluetooth_device_connection_response,
msg_types,
)

def unsub() -> None:
if self._connection is not None:
self._connection.remove_message_callback(on_msg, msg_types)
self._connection.remove_message_callback(
_on_bluetooth_device_connection_response, msg_types
)

try:
try:
Expand Down Expand Up @@ -930,14 +944,16 @@ async def bluetooth_gatt_start_notify(
BluetoothGATTNotifyResponse,
)

def on_msg(msg: BluetoothGATTNotifyDataResponse) -> None:
def _on_bluetooth_gatt_notify_data_response(
msg: BluetoothGATTNotifyDataResponse,
) -> None:
notify = BluetoothGATTRead.from_pb(msg)
if address == notify.address and handle == notify.handle:
on_bluetooth_gatt_notify(handle, bytearray(notify.data))

assert self._connection is not None
remove_callback = self._connection.add_message_callback(
on_msg, (BluetoothGATTNotifyDataResponse,)
_on_bluetooth_gatt_notify_data_response, (BluetoothGATTNotifyDataResponse,)
)

async def stop_notify() -> None:
Expand All @@ -959,13 +975,15 @@ async def subscribe_home_assistant_states(
) -> None:
self._check_authenticated()

def on_msg(msg: SubscribeHomeAssistantStateResponse) -> None:
def _on_subscribe_home_assistant_state_response(
msg: SubscribeHomeAssistantStateResponse,
) -> None:
on_state_sub(msg.entity_id, msg.attribute)

assert self._connection is not None
self._connection.send_message_callback_response(
SubscribeHomeAssistantStatesRequest(),
on_msg,
_on_subscribe_home_assistant_state_response,
(SubscribeHomeAssistantStateResponse,),
)

Expand Down Expand Up @@ -1349,7 +1367,7 @@ def _started(fut: asyncio.Task[Optional[int]]) -> None:
_LOGGER.error("Server could not be started")
self._connection.send_message(VoiceAssistantResponse(error=True))

def on_msg(msg: VoiceAssistantRequest) -> None:
def _on_voice_assistant_request(msg: VoiceAssistantRequest) -> None:
command = VoiceAssistantCommand.from_pb(msg)
if command.start:
start_task = asyncio.create_task(
Expand All @@ -1368,7 +1386,7 @@ def on_msg(msg: VoiceAssistantRequest) -> None:
self._connection.send_message(SubscribeVoiceAssistantRequest(subscribe=True))

remove_callback = self._connection.add_message_callback(
on_msg, (VoiceAssistantRequest,)
_on_voice_assistant_request, (VoiceAssistantRequest,)
)

def unsub() -> None:
Expand Down
19 changes: 10 additions & 9 deletions aioesphomeapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,24 +935,25 @@ class BluetoothLERawAdvertisement:
data: bytes = field(default_factory=bytes)


@_dataclass_decorator
class BluetoothLERawAdvertisements:
advertisements: List[BluetoothLERawAdvertisement]
def make_ble_raw_advertisement_processor(
on_advertisements: Callable[[List[BluetoothLERawAdvertisement]], None]
) -> Callable[["BluetoothLERawAdvertisementsResponse"], None]:
"""Make a processor for BluetoothLERawAdvertisementResponse."""

@classmethod
def from_pb( # type: ignore[misc]
cls: "BluetoothLERawAdvertisements",
def _on_ble_raw_advertisement_response(
data: "BluetoothLERawAdvertisementsResponse",
) -> "BluetoothLERawAdvertisements":
return cls( # type: ignore[operator, no-any-return]
advertisements=[
) -> None:
on_advertisements(
[
BluetoothLERawAdvertisement( # type: ignore[call-arg]
adv.address, adv.rssi, adv.address_type, adv.data
)
for adv in data.advertisements
]
)

return _on_ble_raw_advertisement_response


@dataclass(frozen=True)
class BluetoothDeviceConnection(APIModelBase):
Expand Down

0 comments on commit 8ac62a3

Please sign in to comment.