bumble 0.0.178__py3-none-any.whl → 0.0.180__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/a2dp.py +83 -68
- bumble/apps/bench.py +180 -24
- bumble/apps/controller_info.py +14 -0
- bumble/apps/pair.py +9 -2
- bumble/avdtp.py +3 -3
- bumble/crypto.py +82 -66
- bumble/device.py +247 -23
- bumble/gatt.py +117 -7
- bumble/gatt_client.py +56 -20
- bumble/hci.py +351 -78
- bumble/helpers.py +67 -42
- bumble/hid.py +8 -7
- bumble/l2cap.py +8 -0
- bumble/profiles/csip.py +147 -0
- bumble/rfcomm.py +2 -3
- bumble/sdp.py +4 -4
- bumble/smp.py +66 -43
- bumble/transport/common.py +1 -1
- bumble/transport/usb.py +58 -61
- bumble/utils.py +17 -1
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/METADATA +1 -1
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/RECORD +27 -26
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/WHEEL +1 -1
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/LICENSE +0 -0
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.178.dist-info → bumble-0.0.180.dist-info}/top_level.txt +0 -0
bumble/gatt_client.py
CHANGED
|
@@ -38,6 +38,7 @@ from typing import (
|
|
|
38
38
|
Any,
|
|
39
39
|
Iterable,
|
|
40
40
|
Type,
|
|
41
|
+
Set,
|
|
41
42
|
TYPE_CHECKING,
|
|
42
43
|
)
|
|
43
44
|
|
|
@@ -128,7 +129,7 @@ class ServiceProxy(AttributeProxy):
|
|
|
128
129
|
included_services: List[ServiceProxy]
|
|
129
130
|
|
|
130
131
|
@staticmethod
|
|
131
|
-
def from_client(service_class, client, service_uuid):
|
|
132
|
+
def from_client(service_class, client: Client, service_uuid: UUID):
|
|
132
133
|
# The service and its characteristics are considered to have already been
|
|
133
134
|
# discovered
|
|
134
135
|
services = client.get_services_by_uuid(service_uuid)
|
|
@@ -206,11 +207,11 @@ class CharacteristicProxy(AttributeProxy):
|
|
|
206
207
|
|
|
207
208
|
return await self.client.subscribe(self, subscriber, prefer_notify)
|
|
208
209
|
|
|
209
|
-
async def unsubscribe(self, subscriber=None):
|
|
210
|
+
async def unsubscribe(self, subscriber=None, force=False):
|
|
210
211
|
if subscriber in self.subscribers:
|
|
211
212
|
subscriber = self.subscribers.pop(subscriber)
|
|
212
213
|
|
|
213
|
-
return await self.client.unsubscribe(self, subscriber)
|
|
214
|
+
return await self.client.unsubscribe(self, subscriber, force)
|
|
214
215
|
|
|
215
216
|
def __str__(self) -> str:
|
|
216
217
|
return (
|
|
@@ -246,8 +247,12 @@ class ProfileServiceProxy:
|
|
|
246
247
|
class Client:
|
|
247
248
|
services: List[ServiceProxy]
|
|
248
249
|
cached_values: Dict[int, Tuple[datetime, bytes]]
|
|
249
|
-
notification_subscribers: Dict[
|
|
250
|
-
|
|
250
|
+
notification_subscribers: Dict[
|
|
251
|
+
int, Set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
|
|
252
|
+
]
|
|
253
|
+
indication_subscribers: Dict[
|
|
254
|
+
int, Set[Union[CharacteristicProxy, Callable[[bytes], Any]]]
|
|
255
|
+
]
|
|
251
256
|
pending_response: Optional[asyncio.futures.Future[ATT_PDU]]
|
|
252
257
|
pending_request: Optional[ATT_PDU]
|
|
253
258
|
|
|
@@ -257,10 +262,8 @@ class Client:
|
|
|
257
262
|
self.request_semaphore = asyncio.Semaphore(1)
|
|
258
263
|
self.pending_request = None
|
|
259
264
|
self.pending_response = None
|
|
260
|
-
self.notification_subscribers =
|
|
261
|
-
|
|
262
|
-
) # Notification subscribers, by attribute handle
|
|
263
|
-
self.indication_subscribers = {} # Indication subscribers, by attribute handle
|
|
265
|
+
self.notification_subscribers = {} # Subscriber set, by attribute handle
|
|
266
|
+
self.indication_subscribers = {} # Subscriber set, by attribute handle
|
|
264
267
|
self.services = []
|
|
265
268
|
self.cached_values = {}
|
|
266
269
|
|
|
@@ -682,8 +685,8 @@ class Client:
|
|
|
682
685
|
async def discover_descriptors(
|
|
683
686
|
self,
|
|
684
687
|
characteristic: Optional[CharacteristicProxy] = None,
|
|
685
|
-
start_handle=None,
|
|
686
|
-
end_handle=None,
|
|
688
|
+
start_handle: Optional[int] = None,
|
|
689
|
+
end_handle: Optional[int] = None,
|
|
687
690
|
) -> List[DescriptorProxy]:
|
|
688
691
|
'''
|
|
689
692
|
See Vol 3, Part G - 4.7.1 Discover All Characteristic Descriptors
|
|
@@ -789,7 +792,12 @@ class Client:
|
|
|
789
792
|
|
|
790
793
|
return attributes
|
|
791
794
|
|
|
792
|
-
async def subscribe(
|
|
795
|
+
async def subscribe(
|
|
796
|
+
self,
|
|
797
|
+
characteristic: CharacteristicProxy,
|
|
798
|
+
subscriber: Optional[Callable[[bytes], Any]] = None,
|
|
799
|
+
prefer_notify: bool = True,
|
|
800
|
+
) -> None:
|
|
793
801
|
# If we haven't already discovered the descriptors for this characteristic,
|
|
794
802
|
# do it now
|
|
795
803
|
if not characteristic.descriptors_discovered:
|
|
@@ -826,6 +834,7 @@ class Client:
|
|
|
826
834
|
subscriber_set = subscribers.setdefault(characteristic.handle, set())
|
|
827
835
|
if subscriber is not None:
|
|
828
836
|
subscriber_set.add(subscriber)
|
|
837
|
+
|
|
829
838
|
# Add the characteristic as a subscriber, which will result in the
|
|
830
839
|
# characteristic emitting an 'update' event when a notification or indication
|
|
831
840
|
# is received
|
|
@@ -833,7 +842,18 @@ class Client:
|
|
|
833
842
|
|
|
834
843
|
await self.write_value(cccd, struct.pack('<H', bits), with_response=True)
|
|
835
844
|
|
|
836
|
-
async def unsubscribe(
|
|
845
|
+
async def unsubscribe(
|
|
846
|
+
self,
|
|
847
|
+
characteristic: CharacteristicProxy,
|
|
848
|
+
subscriber: Optional[Callable[[bytes], Any]] = None,
|
|
849
|
+
force: bool = False,
|
|
850
|
+
) -> None:
|
|
851
|
+
'''
|
|
852
|
+
Unsubscribe from a characteristic.
|
|
853
|
+
|
|
854
|
+
If `force` is True, this will write zeros to the CCCD when there are no
|
|
855
|
+
subscribers left, even if there were already no registered subscribers.
|
|
856
|
+
'''
|
|
837
857
|
# If we haven't already discovered the descriptors for this characteristic,
|
|
838
858
|
# do it now
|
|
839
859
|
if not characteristic.descriptors_discovered:
|
|
@@ -847,31 +867,45 @@ class Client:
|
|
|
847
867
|
logger.warning('unsubscribing from characteristic with no CCCD descriptor')
|
|
848
868
|
return
|
|
849
869
|
|
|
870
|
+
# Check if the characteristic has subscribers
|
|
871
|
+
if not (
|
|
872
|
+
characteristic.handle in self.notification_subscribers
|
|
873
|
+
or characteristic.handle in self.indication_subscribers
|
|
874
|
+
):
|
|
875
|
+
if not force:
|
|
876
|
+
return
|
|
877
|
+
|
|
878
|
+
# Remove the subscriber(s)
|
|
850
879
|
if subscriber is not None:
|
|
851
880
|
# Remove matching subscriber from subscriber sets
|
|
852
881
|
for subscriber_set in (
|
|
853
882
|
self.notification_subscribers,
|
|
854
883
|
self.indication_subscribers,
|
|
855
884
|
):
|
|
856
|
-
|
|
857
|
-
|
|
885
|
+
if (
|
|
886
|
+
subscribers := subscriber_set.get(characteristic.handle)
|
|
887
|
+
) and subscriber in subscribers:
|
|
858
888
|
subscribers.remove(subscriber)
|
|
859
889
|
|
|
860
890
|
# Cleanup if we removed the last one
|
|
861
891
|
if not subscribers:
|
|
862
892
|
del subscriber_set[characteristic.handle]
|
|
863
893
|
else:
|
|
864
|
-
# Remove all subscribers for this attribute from the sets
|
|
894
|
+
# Remove all subscribers for this attribute from the sets
|
|
865
895
|
self.notification_subscribers.pop(characteristic.handle, None)
|
|
866
896
|
self.indication_subscribers.pop(characteristic.handle, None)
|
|
867
897
|
|
|
868
|
-
|
|
898
|
+
# Update the CCCD
|
|
899
|
+
if not (
|
|
900
|
+
characteristic.handle in self.notification_subscribers
|
|
901
|
+
or characteristic.handle in self.indication_subscribers
|
|
902
|
+
):
|
|
869
903
|
# No more subscribers left
|
|
870
904
|
await self.write_value(cccd, b'\x00\x00', with_response=True)
|
|
871
905
|
|
|
872
906
|
async def read_value(
|
|
873
907
|
self, attribute: Union[int, AttributeProxy], no_long_read: bool = False
|
|
874
|
-
) ->
|
|
908
|
+
) -> bytes:
|
|
875
909
|
'''
|
|
876
910
|
See Vol 3, Part G - 4.8.1 Read Characteristic Value
|
|
877
911
|
|
|
@@ -1067,7 +1101,7 @@ class Client:
|
|
|
1067
1101
|
def on_att_handle_value_notification(self, notification):
|
|
1068
1102
|
# Call all subscribers
|
|
1069
1103
|
subscribers = self.notification_subscribers.get(
|
|
1070
|
-
notification.attribute_handle,
|
|
1104
|
+
notification.attribute_handle, set()
|
|
1071
1105
|
)
|
|
1072
1106
|
if not subscribers:
|
|
1073
1107
|
logger.warning('!!! received notification with no subscriber')
|
|
@@ -1081,7 +1115,9 @@ class Client:
|
|
|
1081
1115
|
|
|
1082
1116
|
def on_att_handle_value_indication(self, indication):
|
|
1083
1117
|
# Call all subscribers
|
|
1084
|
-
subscribers = self.indication_subscribers.get(
|
|
1118
|
+
subscribers = self.indication_subscribers.get(
|
|
1119
|
+
indication.attribute_handle, set()
|
|
1120
|
+
)
|
|
1085
1121
|
if not subscribers:
|
|
1086
1122
|
logger.warning('!!! received indication with no subscriber')
|
|
1087
1123
|
|