bumble 0.0.147__py3-none-any.whl → 0.0.149__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/pairing.py ADDED
@@ -0,0 +1,184 @@
1
+ # Copyright 2021-2023 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # -----------------------------------------------------------------------------
16
+ # Imports
17
+ # -----------------------------------------------------------------------------
18
+ import enum
19
+ from typing import Optional, Tuple
20
+
21
+ from .hci import (
22
+ HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
23
+ HCI_DISPLAY_ONLY_IO_CAPABILITY,
24
+ HCI_DISPLAY_YES_NO_IO_CAPABILITY,
25
+ HCI_KEYBOARD_ONLY_IO_CAPABILITY,
26
+ )
27
+ from .smp import (
28
+ SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
29
+ SMP_KEYBOARD_ONLY_IO_CAPABILITY,
30
+ SMP_DISPLAY_ONLY_IO_CAPABILITY,
31
+ SMP_DISPLAY_YES_NO_IO_CAPABILITY,
32
+ SMP_KEYBOARD_DISPLAY_IO_CAPABILITY,
33
+ SMP_ENC_KEY_DISTRIBUTION_FLAG,
34
+ SMP_ID_KEY_DISTRIBUTION_FLAG,
35
+ SMP_SIGN_KEY_DISTRIBUTION_FLAG,
36
+ SMP_LINK_KEY_DISTRIBUTION_FLAG,
37
+ )
38
+
39
+
40
+ # -----------------------------------------------------------------------------
41
+ class PairingDelegate:
42
+ """Abstract base class for Pairing Delegates."""
43
+
44
+ # I/O Capabilities.
45
+ # These are defined abstractly, and can be mapped to specific Classic pairing
46
+ # and/or SMP constants.
47
+ class IoCapability(enum.IntEnum):
48
+ NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
49
+ KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY
50
+ DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY
51
+ DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY
52
+ DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
53
+
54
+ # Direct names for backward compatibility.
55
+ NO_OUTPUT_NO_INPUT = IoCapability.NO_OUTPUT_NO_INPUT
56
+ KEYBOARD_INPUT_ONLY = IoCapability.KEYBOARD_INPUT_ONLY
57
+ DISPLAY_OUTPUT_ONLY = IoCapability.DISPLAY_OUTPUT_ONLY
58
+ DISPLAY_OUTPUT_AND_YES_NO_INPUT = IoCapability.DISPLAY_OUTPUT_AND_YES_NO_INPUT
59
+ DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT
60
+
61
+ # Key Distribution [LE only]
62
+ class KeyDistribution(enum.IntFlag):
63
+ DISTRIBUTE_ENCRYPTION_KEY = SMP_ENC_KEY_DISTRIBUTION_FLAG
64
+ DISTRIBUTE_IDENTITY_KEY = SMP_ID_KEY_DISTRIBUTION_FLAG
65
+ DISTRIBUTE_SIGNING_KEY = SMP_SIGN_KEY_DISTRIBUTION_FLAG
66
+ DISTRIBUTE_LINK_KEY = SMP_LINK_KEY_DISTRIBUTION_FLAG
67
+
68
+ DEFAULT_KEY_DISTRIBUTION: int = (
69
+ SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG
70
+ )
71
+
72
+ # Default mapping from abstract to Classic I/O capabilities.
73
+ # Subclasses may override this if they prefer a different mapping.
74
+ CLASSIC_IO_CAPABILITIES_MAP = {
75
+ NO_OUTPUT_NO_INPUT: HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
76
+ KEYBOARD_INPUT_ONLY: HCI_KEYBOARD_ONLY_IO_CAPABILITY,
77
+ DISPLAY_OUTPUT_ONLY: HCI_DISPLAY_ONLY_IO_CAPABILITY,
78
+ DISPLAY_OUTPUT_AND_YES_NO_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY,
79
+ DISPLAY_OUTPUT_AND_KEYBOARD_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY,
80
+ }
81
+
82
+ io_capability: IoCapability
83
+ local_initiator_key_distribution: KeyDistribution
84
+ local_responder_key_distribution: KeyDistribution
85
+
86
+ def __init__(
87
+ self,
88
+ io_capability=NO_OUTPUT_NO_INPUT,
89
+ local_initiator_key_distribution=DEFAULT_KEY_DISTRIBUTION,
90
+ local_responder_key_distribution=DEFAULT_KEY_DISTRIBUTION,
91
+ ) -> None:
92
+ self.io_capability = io_capability
93
+ self.local_initiator_key_distribution = local_initiator_key_distribution
94
+ self.local_responder_key_distribution = local_responder_key_distribution
95
+
96
+ @property
97
+ def classic_io_capability(self) -> int:
98
+ """Map the abstract I/O capability to a Classic constant."""
99
+
100
+ # pylint: disable=line-too-long
101
+ return self.CLASSIC_IO_CAPABILITIES_MAP.get(
102
+ self.io_capability, HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
103
+ )
104
+
105
+ @property
106
+ def smp_io_capability(self) -> int:
107
+ """Map the abstract I/O capability to an SMP constant."""
108
+
109
+ # This is just a 1-1 direct mapping
110
+ return self.io_capability
111
+
112
+ async def accept(self) -> bool:
113
+ """Accept or reject a Pairing request."""
114
+ return True
115
+
116
+ async def confirm(self) -> bool:
117
+ """Respond yes or no to a Pairing confirmation question."""
118
+ return True
119
+
120
+ # pylint: disable-next=unused-argument
121
+ async def compare_numbers(self, number: int, digits: int) -> bool:
122
+ """Compare two numbers."""
123
+ return True
124
+
125
+ async def get_number(self) -> Optional[int]:
126
+ """
127
+ Return an optional number as an answer to a passkey request.
128
+ Returning `None` will result in a negative reply.
129
+ """
130
+ return 0
131
+
132
+ async def get_string(self, max_length) -> Optional[str]:
133
+ """
134
+ Return a string whose utf-8 encoding is up to max_length bytes.
135
+ """
136
+ return None
137
+
138
+ # pylint: disable-next=unused-argument
139
+ async def display_number(self, number: int, digits: int) -> None:
140
+ """Display a number."""
141
+
142
+ # [LE only]
143
+ async def key_distribution_response(
144
+ self, peer_initiator_key_distribution: int, peer_responder_key_distribution: int
145
+ ) -> Tuple[int, int]:
146
+ """
147
+ Return the key distribution response in an SMP protocol context.
148
+
149
+ NOTE: since it is only used by the SMP protocol, this method's input and output
150
+ are directly as integers, using the SMP constants, rather than the abstract
151
+ KeyDistribution enums.
152
+ """
153
+ return (
154
+ int(
155
+ peer_initiator_key_distribution & self.local_initiator_key_distribution
156
+ ),
157
+ int(
158
+ peer_responder_key_distribution & self.local_responder_key_distribution
159
+ ),
160
+ )
161
+
162
+
163
+ # -----------------------------------------------------------------------------
164
+ class PairingConfig:
165
+ """Configuration for the Pairing protocol."""
166
+
167
+ def __init__(
168
+ self,
169
+ sc: bool = True,
170
+ mitm: bool = True,
171
+ bonding: bool = True,
172
+ delegate: Optional[PairingDelegate] = None,
173
+ ) -> None:
174
+ self.sc = sc
175
+ self.mitm = mitm
176
+ self.bonding = bonding
177
+ self.delegate = delegate or PairingDelegate()
178
+
179
+ def __str__(self) -> str:
180
+ return (
181
+ f'PairingConfig(sc={self.sc}, '
182
+ f'mitm={self.mitm}, bonding={self.bonding}, '
183
+ f'delegate[{self.delegate.io_capability}])'
184
+ )
@@ -103,7 +103,7 @@ class AshaService(TemplateService):
103
103
 
104
104
  self.read_only_properties_characteristic = Characteristic(
105
105
  GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC,
106
- Characteristic.READ,
106
+ Characteristic.Properties.READ,
107
107
  Characteristic.READABLE,
108
108
  bytes(
109
109
  [
@@ -120,19 +120,20 @@ class AshaService(TemplateService):
120
120
 
121
121
  self.audio_control_point_characteristic = Characteristic(
122
122
  GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
123
- Characteristic.WRITE | Characteristic.WRITE_WITHOUT_RESPONSE,
123
+ Characteristic.Properties.WRITE
124
+ | Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
124
125
  Characteristic.WRITEABLE,
125
126
  CharacteristicValue(write=on_audio_control_point_write),
126
127
  )
127
128
  self.audio_status_characteristic = Characteristic(
128
129
  GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC,
129
- Characteristic.READ | Characteristic.NOTIFY,
130
+ Characteristic.Properties.READ | Characteristic.Properties.NOTIFY,
130
131
  Characteristic.READABLE,
131
132
  bytes([0]),
132
133
  )
133
134
  self.volume_characteristic = Characteristic(
134
135
  GATT_ASHA_VOLUME_CHARACTERISTIC,
135
- Characteristic.WRITE_WITHOUT_RESPONSE,
136
+ Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
136
137
  Characteristic.WRITEABLE,
137
138
  CharacteristicValue(write=on_volume_write),
138
139
  )
@@ -151,7 +152,7 @@ class AshaService(TemplateService):
151
152
  self.psm = self.device.register_l2cap_channel_server(self.psm, on_coc, 8)
152
153
  self.le_psm_out_characteristic = Characteristic(
153
154
  GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
154
- Characteristic.READ,
155
+ Characteristic.Properties.READ,
155
156
  Characteristic.READABLE,
156
157
  struct.pack('<H', self.psm),
157
158
  )
@@ -36,7 +36,7 @@ class BatteryService(TemplateService):
36
36
  self.battery_level_characteristic = PackedCharacteristicAdapter(
37
37
  Characteristic(
38
38
  GATT_BATTERY_LEVEL_CHARACTERISTIC,
39
- Characteristic.READ | Characteristic.NOTIFY,
39
+ Characteristic.Properties.READ | Characteristic.Properties.NOTIFY,
40
40
  Characteristic.READABLE,
41
41
  CharacteristicValue(read=read_battery_level),
42
42
  ),
@@ -63,7 +63,9 @@ class DeviceInformationService(TemplateService):
63
63
  # TODO: pnp_id
64
64
  ):
65
65
  characteristics = [
66
- Characteristic(uuid, Characteristic.READ, Characteristic.READABLE, field)
66
+ Characteristic(
67
+ uuid, Characteristic.Properties.READ, Characteristic.READABLE, field
68
+ )
67
69
  for (field, uuid) in (
68
70
  (manufacturer_name, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC),
69
71
  (model_number, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC),
@@ -79,7 +81,7 @@ class DeviceInformationService(TemplateService):
79
81
  characteristics.append(
80
82
  Characteristic(
81
83
  GATT_SYSTEM_ID_CHARACTERISTIC,
82
- Characteristic.READ,
84
+ Characteristic.Properties.READ,
83
85
  Characteristic.READABLE,
84
86
  self.pack_system_id(*system_id),
85
87
  )
@@ -89,7 +91,7 @@ class DeviceInformationService(TemplateService):
89
91
  characteristics.append(
90
92
  Characteristic(
91
93
  GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
92
- Characteristic.READ,
94
+ Characteristic.Properties.READ,
93
95
  Characteristic.READABLE,
94
96
  ieee_regulatory_certification_data_list,
95
97
  )
@@ -152,7 +152,7 @@ class HeartRateService(TemplateService):
152
152
  self.heart_rate_measurement_characteristic = DelegatedCharacteristicAdapter(
153
153
  Characteristic(
154
154
  GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
155
- Characteristic.NOTIFY,
155
+ Characteristic.Properties.NOTIFY,
156
156
  0,
157
157
  CharacteristicValue(read=read_heart_rate_measurement),
158
158
  ),
@@ -164,7 +164,7 @@ class HeartRateService(TemplateService):
164
164
  if body_sensor_location is not None:
165
165
  self.body_sensor_location_characteristic = Characteristic(
166
166
  GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
167
- Characteristic.READ,
167
+ Characteristic.Properties.READ,
168
168
  Characteristic.READABLE,
169
169
  bytes([int(body_sensor_location)]),
170
170
  )
@@ -182,7 +182,7 @@ class HeartRateService(TemplateService):
182
182
  self.heart_rate_control_point_characteristic = PackedCharacteristicAdapter(
183
183
  Characteristic(
184
184
  GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC,
185
- Characteristic.WRITE,
185
+ Characteristic.Properties.WRITE,
186
186
  Characteristic.WRITEABLE,
187
187
  CharacteristicValue(write=write_heart_rate_control_point_value),
188
188
  ),
bumble/rfcomm.py CHANGED
@@ -439,7 +439,7 @@ class DLC(EventEmitter):
439
439
 
440
440
  logger.debug(
441
441
  f'<<< Credits [{self.dlci}]: '
442
- f'received {credits}, total={self.tx_credits}'
442
+ f'received {received_credits}, total={self.tx_credits}'
443
443
  )
444
444
  data = data[1:]
445
445
 
bumble/smp.py CHANGED
@@ -31,7 +31,16 @@ from typing import Dict, Optional, Type
31
31
  from pyee import EventEmitter
32
32
 
33
33
  from .colors import color
34
- from .hci import Address, HCI_LE_Enable_Encryption_Command, HCI_Object, key_with_value
34
+ from .hci import (
35
+ HCI_DISPLAY_ONLY_IO_CAPABILITY,
36
+ HCI_DISPLAY_YES_NO_IO_CAPABILITY,
37
+ HCI_KEYBOARD_ONLY_IO_CAPABILITY,
38
+ HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
39
+ Address,
40
+ HCI_LE_Enable_Encryption_Command,
41
+ HCI_Object,
42
+ key_with_value,
43
+ )
35
44
  from .core import (
36
45
  BT_BR_EDR_TRANSPORT,
37
46
  BT_CENTRAL_ROLE,
@@ -476,7 +485,7 @@ class AddressResolver:
476
485
  address_bytes = bytes(address)
477
486
  hash_part = address_bytes[0:3]
478
487
  prand = address_bytes[3:6]
479
- for (irk, resolved_address) in self.resolving_keys:
488
+ for irk, resolved_address in self.resolving_keys:
480
489
  local_hash = crypto.ah(irk, prand)
481
490
  if local_hash == hash_part:
482
491
  # Match!
@@ -491,86 +500,6 @@ class AddressResolver:
491
500
  return None
492
501
 
493
502
 
494
- # -----------------------------------------------------------------------------
495
- class PairingDelegate:
496
- NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
497
- KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY
498
- DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY
499
- DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY
500
- DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
501
- DEFAULT_KEY_DISTRIBUTION: int = (
502
- SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG
503
- )
504
-
505
- def __init__(
506
- self,
507
- io_capability: int = NO_OUTPUT_NO_INPUT,
508
- local_initiator_key_distribution: int = DEFAULT_KEY_DISTRIBUTION,
509
- local_responder_key_distribution: int = DEFAULT_KEY_DISTRIBUTION,
510
- ) -> None:
511
- self.io_capability = io_capability
512
- self.local_initiator_key_distribution = local_initiator_key_distribution
513
- self.local_responder_key_distribution = local_responder_key_distribution
514
-
515
- async def accept(self) -> bool:
516
- return True
517
-
518
- async def confirm(self) -> bool:
519
- return True
520
-
521
- # pylint: disable-next=unused-argument
522
- async def compare_numbers(self, number: int, digits: int) -> bool:
523
- return True
524
-
525
- async def get_number(self) -> Optional[int]:
526
- '''
527
- Returns an optional number as an answer to a passkey request.
528
- Returning `None` will result in a negative reply.
529
- '''
530
- return 0
531
-
532
- async def get_string(self, max_length) -> Optional[str]:
533
- '''
534
- Returns a string whose utf-8 encoding is up to max_length bytes.
535
- '''
536
- return None
537
-
538
- # pylint: disable-next=unused-argument
539
- async def display_number(self, number: int, digits: int) -> None:
540
- pass
541
-
542
- async def key_distribution_response(
543
- self, peer_initiator_key_distribution, peer_responder_key_distribution
544
- ):
545
- return (
546
- (peer_initiator_key_distribution & self.local_initiator_key_distribution),
547
- (peer_responder_key_distribution & self.local_responder_key_distribution),
548
- )
549
-
550
-
551
- # -----------------------------------------------------------------------------
552
- class PairingConfig:
553
- def __init__(
554
- self,
555
- sc: bool = True,
556
- mitm: bool = True,
557
- bonding: bool = True,
558
- delegate: Optional[PairingDelegate] = None,
559
- ) -> None:
560
- self.sc = sc
561
- self.mitm = mitm
562
- self.bonding = bonding
563
- self.delegate = delegate or PairingDelegate()
564
-
565
- def __str__(self):
566
- io_capability_str = SMP_Command.io_capability_name(self.delegate.io_capability)
567
- return (
568
- f'PairingConfig(sc={self.sc}, '
569
- f'mitm={self.mitm}, bonding={self.bonding}, '
570
- f'delegate[{io_capability_str}])'
571
- )
572
-
573
-
574
503
  # -----------------------------------------------------------------------------
575
504
  class Session:
576
505
  # Pairing methods
@@ -645,7 +574,7 @@ class Session:
645
574
  },
646
575
  }
647
576
 
648
- def __init__(self, manager, connection, pairing_config):
577
+ def __init__(self, manager, connection, pairing_config, is_initiator):
649
578
  self.manager = manager
650
579
  self.connection = connection
651
580
  self.preq = None
@@ -684,7 +613,7 @@ class Session:
684
613
  self.ctkd_task = None
685
614
 
686
615
  # Decide if we're the initiator or the responder
687
- self.is_initiator = connection.role == BT_CENTRAL_ROLE
616
+ self.is_initiator = is_initiator
688
617
  self.is_responder = not self.is_initiator
689
618
 
690
619
  # Listen for connection events
@@ -1662,12 +1591,12 @@ class Manager(EventEmitter):
1662
1591
  Implements the Initiator and Responder roles of the Security Manager Protocol
1663
1592
  '''
1664
1593
 
1665
- def __init__(self, device):
1594
+ def __init__(self, device, pairing_config_factory):
1666
1595
  super().__init__()
1667
1596
  self.device = device
1668
1597
  self.sessions = {}
1669
1598
  self._ecc_key = None
1670
- self.pairing_config_factory = lambda connection: PairingConfig()
1599
+ self.pairing_config_factory = pairing_config_factory
1671
1600
 
1672
1601
  def send_command(self, connection, command):
1673
1602
  logger.debug(
@@ -1680,6 +1609,8 @@ class Manager(EventEmitter):
1680
1609
  def on_smp_pdu(self, connection, pdu):
1681
1610
  # Look for a session with this connection, and create one if none exists
1682
1611
  if not (session := self.sessions.get(connection.handle)):
1612
+ if connection.role == BT_CENTRAL_ROLE:
1613
+ logger.warning('Remote starts pairing as Peripheral!')
1683
1614
  pairing_config = self.pairing_config_factory(connection)
1684
1615
  if pairing_config is None:
1685
1616
  # Pairing disabled
@@ -1688,7 +1619,7 @@ class Manager(EventEmitter):
1688
1619
  SMP_Pairing_Failed_Command(reason=SMP_PAIRING_NOT_SUPPORTED_ERROR),
1689
1620
  )
1690
1621
  return
1691
- session = Session(self, connection, pairing_config)
1622
+ session = Session(self, connection, pairing_config, is_initiator=False)
1692
1623
  self.sessions[connection.handle] = session
1693
1624
 
1694
1625
  # Parse the L2CAP payload into an SMP Command object
@@ -1709,10 +1640,12 @@ class Manager(EventEmitter):
1709
1640
 
1710
1641
  async def pair(self, connection):
1711
1642
  # TODO: check if there's already a session for this connection
1643
+ if connection.role != BT_CENTRAL_ROLE:
1644
+ logger.warning('Start pairing as Peripheral!')
1712
1645
  pairing_config = self.pairing_config_factory(connection)
1713
1646
  if pairing_config is None:
1714
1647
  raise ValueError('pairing config must not be None when initiating')
1715
- session = Session(self, connection, pairing_config)
1648
+ session = Session(self, connection, pairing_config, is_initiator=True)
1716
1649
  self.sessions[connection.handle] = session
1717
1650
  return await session.pair()
1718
1651
 
@@ -200,3 +200,22 @@
200
200
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
201
  See the License for the specific language governing permissions and
202
202
  limitations under the License.
203
+
204
+
205
+ ---
206
+
207
+
208
+ Files: bumble/colors.py
209
+ Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com>
210
+
211
+ Permission to use, copy, modify, and distribute this software for any
212
+ purpose with or without fee is hereby granted, provided that the above
213
+ copyright notice and this permission notice appear in all copies.
214
+
215
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
216
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
217
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
218
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
219
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
220
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
221
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bumble
3
- Version: 0.0.147
3
+ Version: 0.0.149
4
4
  Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
5
5
  Home-page: https://github.com/google/bumble
6
6
  Author: Google
@@ -11,6 +11,8 @@ License-File: LICENSE
11
11
  Requires-Dist: appdirs (>=1.4)
12
12
  Requires-Dist: protobuf (>=3.12.4)
13
13
  Requires-Dist: pyee (>=8.2.2)
14
+ Requires-Dist: prettytable (>=3.6.0)
15
+ Requires-Dist: humanize (>=4.6.0)
14
16
  Requires-Dist: click (>=7.1.2) ; platform_system != "Emscripten"
15
17
  Requires-Dist: cryptography (==35) ; platform_system != "Emscripten"
16
18
  Requires-Dist: grpcio (>=1.46) ; platform_system != "Emscripten"
@@ -1,7 +1,7 @@
1
1
  bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
2
- bumble/_version.py,sha256=TVLmTwKIV9YYZjCd71sIOJynW28DBtMakDeltIPQS-k,164
2
+ bumble/_version.py,sha256=j0GOlQtZQxiYwB0F8H5T6P-quL9GhBu9Qr0FPc6jw70,164
3
3
  bumble/a2dp.py,sha256=MyCieGd2rA--nwpEdi1PsMXxfcki06ut6x7RXZUcO-w,21904
4
- bumble/att.py,sha256=f1oVzan4wefHliE0LSUb8fWJZkxV6Br0JJzEqEg8eBQ,30220
4
+ bumble/att.py,sha256=qXwC5gtC-4Bwjqul0sCm_bWA_BbK4NVf5xNlOCRE7LI,30231
5
5
  bumble/avdtp.py,sha256=LVX536sds4M5IJoN46pbX7wZIcJG5DzunZ55KPjquiQ,71471
6
6
  bumble/bridge.py,sha256=T6es5oS1dy8QgkxQ8iOD-YcZ0SWOv8jaqC7TGxqodk4,3003
7
7
  bumble/colors.py,sha256=9H-qzGgMr-YoWdIFpcGPaiRvTvkCzz7FPIkpwqKyKug,3033
@@ -10,35 +10,36 @@ bumble/controller.py,sha256=YwOVfcsPD8BvzXqfswY-wUECym2BX7_8zEBjIjUnhWw,44266
10
10
  bumble/core.py,sha256=KE_ymW8JLQ3X2R0xvwk0ClRD1hM4R97YuM7Az0xEbMI,48410
11
11
  bumble/crypto.py,sha256=JX3wg6kd-aFMxkxJz2FYFvyXHOukD-z0QcGH9jpEYvk,8679
12
12
  bumble/decoder.py,sha256=N9nMvuVhuwpnfw7EDVuNe9uYY6B6c3RY2dh8RhRPC1U,9608
13
- bumble/device.py,sha256=xrONyzrhA234DQjpyuQo458Hh_aPffiof711NeFRAsI,118922
14
- bumble/gap.py,sha256=VegMte5j-esK-4DAKFZKrYMHDP9j7CTBbAy_gP-2On8,2115
15
- bumble/gatt.py,sha256=nyuVlbEHm4NeT0jsAvxZq1AHBC45AJYiuteDndK9fqI,26294
16
- bumble/gatt_client.py,sha256=gqX3wVWhCy2ZaJYVUiEHyqnBj7nmQ_1iNKbGqDyH45U,35931
17
- bumble/gatt_server.py,sha256=CpPy3HIWiWVH-NjDRnVBfKloguW0SR7sHmi9xj1E0YY,34167
13
+ bumble/device.py,sha256=Ymyvq1fiHpmF_nkO-nnW2dlhxVvKqJ0tZTz-wnXpOnw,120201
14
+ bumble/gap.py,sha256=axlOZIv99357Ehq2vMokeioU85z81qdQvplGn0pF70Q,2137
15
+ bumble/gatt.py,sha256=s7FFeIuN0VRAWZ3qeobW7zjvluY66ytQIRnh6lszhP0,27074
16
+ bumble/gatt_client.py,sha256=mAkYXeYKdr2PyGYsA9ZmrYonSYi9mc0ZeFm-CAcM1FA,37848
17
+ bumble/gatt_server.py,sha256=W1bzSToxJipqFBK9rPoU4ylCzrJMo5wE9fWMOUgR3WQ,34759
18
18
  bumble/hci.py,sha256=NTLA7l5SI312Gp77yZNRsaduV95Az_UeUBnA4IH_NBE,209326
19
19
  bumble/helpers.py,sha256=AgtOigAqQzzTEMMwHQJ2EO7dOTp77qhnyfHQ2LCDd-k,8896
20
20
  bumble/hfp.py,sha256=GUOkQ2_earE9IiLXWa8uKugpEIyavEgm1tCSm8naGnI,3187
21
- bumble/host.py,sha256=W1eRBIl6MuIuBqztHQ1sYoIRBQpFfhnN6r5s47rXx-k,33903
22
- bumble/keys.py,sha256=g0yXRWdBff3SUaSko4mn-G1Ya-aXRkhW2TShuA7lIrY,9366
21
+ bumble/host.py,sha256=ZeKsZg0qwqb-DmIZu5ZY6eMOGby0kXbcQHKvQDPit9g,33678
22
+ bumble/keys.py,sha256=1KO2Jo1_h7z5gk_WUu1EU93VBRTOGMWMnswQTVBRpaw,9945
23
23
  bumble/l2cap.py,sha256=Md_1af1jEP0dXlXpVkaXINnnmElq423o5m9OJFBaiYk,72106
24
24
  bumble/link.py,sha256=dvb3fdmV8689A4rLhFVHHKMR3u4wQbUapFi-GeheK0M,20220
25
+ bumble/pairing.py,sha256=4XVgjWc50kOIdDwTpJVi7675saYhdhpRBG22djzRmm4,6798
25
26
  bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- bumble/rfcomm.py,sha256=COaFo1ACwqGXjqQGbG01AvHy_Hkn4JC4IsQVrpS_IN8,29874
27
+ bumble/rfcomm.py,sha256=r8Sl76HY-rQpctz7QYAKoEvg8VPcOtJngWdLjmRcrsA,29883
27
28
  bumble/sdp.py,sha256=h0DyXOYpQ5OLzwVZsE4b5YZq_jxxu7m_QSbFkaPy9mQ,42464
28
- bumble/smp.py,sha256=_nUtVBq-QXK7eZNr1We56POLscs7WKmPtPMHKnYk5MU,67056
29
+ bumble/smp.py,sha256=w9y8-1vGAMoIU3UsLYVuQPPtymhfRD8uhzO9hCzWtYQ,64702
29
30
  bumble/snoop.py,sha256=_QfF36eylBW6Snd-_KYOwKaGiM8i_Ed-B5XoFIPt3Dg,5631
30
31
  bumble/utils.py,sha256=x7AKW6QD35Dl1OkROLJ6TX4dNN6ajwFjW8w_BZGGcA4,10195
31
32
  bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
32
33
  bumble/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- bumble/apps/bench.py,sha256=n-EicQRTNLNqfonNyM8oJb_cPHd6B8LnpaDnaSd1Oz8,38498
34
- bumble/apps/console.py,sha256=1jDcs6Z-OAq8dIJOwJJ0FwPyzHLeMFc0ftZ_maBA0wM,37795
34
+ bumble/apps/bench.py,sha256=Nm22A1PjrJFgjfHCTyw3crlMJFHRwyVgADa2uKtJgbE,38542
35
+ bumble/apps/console.py,sha256=Zko0fw4ByYsdGDOrtrXLX0id001Rq45nVImdYHxNKsQ,46055
35
36
  bumble/apps/controller_info.py,sha256=h6tBnpVe02vvoF-cyrdvE4DqGcxSfE5jcEuvCKMlr_g,6507
36
37
  bumble/apps/controllers.py,sha256=R6XJ1XpyuXlyqSCmI7PromVIcoYTcYfpmO-TqTYXnUI,2326
37
38
  bumble/apps/gatt_dump.py,sha256=-dCvCgjuHAp0h1zxm-gmqB4lVlSdas1Kp4cpzzx4gGw,4245
38
- bumble/apps/gg_bridge.py,sha256=y98U8EGpknWjKKGgAK9e-6dRi4HmqnCZa418li0SImE,14586
39
+ bumble/apps/gg_bridge.py,sha256=G32XdCqYzt8Kqv9jZZDY29-ENjOxDMX_7CPolA8Jm1U,14555
39
40
  bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,4033
40
41
  bumble/apps/l2cap_bridge.py,sha256=9914J7UomNtdWyE3VPgvzfJvZ304OM9AW9jzO_zRwNY,12604
41
- bumble/apps/pair.py,sha256=2jTatK9eXlqjsQ9yOpFaehtTQ8InhSqYc_DLIN3z2to,14315
42
+ bumble/apps/pair.py,sha256=vorcWV98u0ZRVVhh-lF2-QOuTutaJ94GO_jCgXdIJZc,14776
42
43
  bumble/apps/scan.py,sha256=TBjeEWkyLDDdIYeq2HRA9oZ2xv5-_Ja18Mt_nG9WkvA,7467
43
44
  bumble/apps/show.py,sha256=qvCY0RcACws8iU5EKPQ3GcURJkykvVvBi8EMAu5GOus,4385
44
45
  bumble/apps/unbond.py,sha256=-PXf6Jk4v-CQiogxhol6CjJ4mMWCApLFkbNHDuclUNk,2096
@@ -47,10 +48,10 @@ bumble/apps/link_relay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
47
48
  bumble/apps/link_relay/link_relay.py,sha256=GOESYPzkMJFrz5VOI_BSmnmgz4Y8EdSLHMWgdA63aDg,10066
48
49
  bumble/apps/link_relay/logging.yml,sha256=t-P72RAHsTZOESw0M4I3CQPonymcjkd9SLE0eY_ZNiM,311
49
50
  bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,581
50
- bumble/profiles/asha_service.py,sha256=UOq1WVEIR2A6Kp0tOTqIZTjqwisau-4foTFCsBJEJ9w,7000
51
- bumble/profiles/battery_service.py,sha256=Qui8LmWn089zTgqMw3BKfdvYfP045WbQBcOMZLZNILA,2270
52
- bumble/profiles/device_information_service.py,sha256=xYyGkpsXVqJbXh2BDMNL7XEnGRQGAQAGsIYzIVGpfHk,5595
53
- bumble/profiles/heart_rate_service.py,sha256=AuuJbXC1vUsSe38CgvKMIM682myxklpsDj4ujAclbNg,8560
51
+ bumble/profiles/asha_service.py,sha256=C7-xwG1DgdPbt6IKLglIP1VYAg1X5s61YPAiOzHHgVw,7089
52
+ bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
53
+ bumble/profiles/device_information_service.py,sha256=H1Db4BAOnsC-rRtfpoAIsDETYT4F9yM_WgByn_3LfRQ,5658
54
+ bumble/profiles/heart_rate_service.py,sha256=Xv-KzgXlagOwu8NfiUGl1RJtR6hJC9P8WefynSIShiw,8593
54
55
  bumble/profiles/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
56
  bumble/transport/__init__.py,sha256=wlqIXIsTcZxISlPBYeJFPMj7IruOJVUHmlQX6AjZpkM,5647
56
57
  bumble/transport/android_emulator.py,sha256=aGbv4PKKBPUv7Ecx5UuxMz23gexUEY_TEGQ4RTXkPhc,4023
@@ -77,9 +78,9 @@ bumble/transport/usb.py,sha256=PWTrvdcyQ3OWXgygbuDMMZJleGFaxG6lSsz2gScLVwk,20901
77
78
  bumble/transport/vhci.py,sha256=MbsRB1I1hPo-EytP_riNyKLtOZXRI9VkB_U4CRi8-1Y,2116
78
79
  bumble/transport/ws_client.py,sha256=dtSMDWu2JQGUJYoXygAWuLgrIclGmT5onHJ8ggslY50,1684
79
80
  bumble/transport/ws_server.py,sha256=H0g6ZvHvR-O0m4ptXmZLInN7N-JpxI_M65ZTvMYUjxc,3330
80
- bumble-0.0.147.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
81
- bumble-0.0.147.dist-info/METADATA,sha256=W21dNY2h4g7_WZVlT54DknJ1iwA9O3qSNIIYPlEdEOg,4975
82
- bumble-0.0.147.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
83
- bumble-0.0.147.dist-info/entry_points.txt,sha256=hYqaVAX36lT2pak04gtRx5C6WQWdMFZqFwJnbE_7YRw,555
84
- bumble-0.0.147.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
85
- bumble-0.0.147.dist-info/RECORD,,
81
+ bumble-0.0.149.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
82
+ bumble-0.0.149.dist-info/METADATA,sha256=8QUOEPXZGGZeaCv8GN4pH-vPb9-R1SL6WhdGy4ZOh-o,5046
83
+ bumble-0.0.149.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
84
+ bumble-0.0.149.dist-info/entry_points.txt,sha256=hYqaVAX36lT2pak04gtRx5C6WQWdMFZqFwJnbE_7YRw,555
85
+ bumble-0.0.149.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
86
+ bumble-0.0.149.dist-info/RECORD,,