forked from auracaster/bumble_mirror
Merge pull request #593 from zxzxwu/periodic
Support Periodic Advertising
This commit is contained in:
@@ -1543,6 +1543,41 @@ class Controller:
|
||||
}
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_advertising_set_random_address_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.52 LE Set Advertising Set Random Address
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_extended_advertising_parameters_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.53 LE Set Extended Advertising Parameters
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS, 0])
|
||||
|
||||
def on_hci_le_set_extended_advertising_data_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.54 LE Set Extended Advertising Data
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_extended_scan_response_data_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.55 LE Set Extended Scan Response Data
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_extended_advertising_enable_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.56 LE Set Extended Advertising Enable
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_read_maximum_advertising_data_length_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.57 LE Read Maximum Advertising Data
|
||||
@@ -1557,6 +1592,27 @@ class Controller:
|
||||
'''
|
||||
return struct.pack('<BB', HCI_SUCCESS, 0xF0)
|
||||
|
||||
def on_hci_le_set_periodic_advertising_parameters_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.61 LE Set Periodic Advertising Parameters
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_periodic_advertising_data_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.62 LE Set Periodic Advertising Data
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_set_periodic_advertising_enable_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.63 LE Set Periodic Advertising Enable
|
||||
Command
|
||||
'''
|
||||
return bytes([HCI_SUCCESS])
|
||||
|
||||
def on_hci_le_read_transmit_power_command(self, _command):
|
||||
'''
|
||||
See Bluetooth spec Vol 4, Part E - 7.8.74 LE Read Transmit Power Command
|
||||
|
||||
@@ -557,8 +557,15 @@ class AdvertisingParameters:
|
||||
# -----------------------------------------------------------------------------
|
||||
@dataclass
|
||||
class PeriodicAdvertisingParameters:
|
||||
# TODO implement this class
|
||||
pass
|
||||
periodic_advertising_interval_min: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
||||
periodic_advertising_interval_max: int = DEVICE_DEFAULT_ADVERTISING_INTERVAL
|
||||
periodic_advertising_properties: (
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties
|
||||
) = field(
|
||||
default_factory=lambda: hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command.Properties(
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -575,6 +582,7 @@ class AdvertisingSet(EventEmitter):
|
||||
periodic_advertising_data: bytes
|
||||
selected_tx_power: int = 0
|
||||
enabled: bool = False
|
||||
periodic_enabled: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
super().__init__()
|
||||
@@ -603,7 +611,7 @@ class AdvertisingSet(EventEmitter):
|
||||
int(advertising_parameters.primary_advertising_interval_min / 0.625)
|
||||
),
|
||||
primary_advertising_interval_max=(
|
||||
int(advertising_parameters.primary_advertising_interval_min / 0.625)
|
||||
int(advertising_parameters.primary_advertising_interval_max / 0.625)
|
||||
),
|
||||
primary_advertising_channel_map=int(
|
||||
advertising_parameters.primary_advertising_channel_map
|
||||
@@ -671,10 +679,26 @@ class AdvertisingSet(EventEmitter):
|
||||
async def set_periodic_advertising_parameters(
|
||||
self, advertising_parameters: PeriodicAdvertisingParameters
|
||||
) -> None:
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Parameters_Command(
|
||||
advertising_handle=self.advertising_handle,
|
||||
periodic_advertising_interval_min=advertising_parameters.periodic_advertising_interval_min,
|
||||
periodic_advertising_interval_max=advertising_parameters.periodic_advertising_interval_max,
|
||||
periodic_advertising_properties=advertising_parameters.periodic_advertising_properties,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
self.periodic_advertising_parameters = advertising_parameters
|
||||
|
||||
async def set_periodic_advertising_data(self, advertising_data: bytes) -> None:
|
||||
# TODO: send command
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Data_Command(
|
||||
advertising_handle=self.advertising_handle,
|
||||
operation=hci.HCI_LE_Set_Extended_Advertising_Data_Command.Operation.COMPLETE_DATA,
|
||||
advertising_data=advertising_data,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
self.periodic_advertising_data = advertising_data
|
||||
|
||||
async def set_random_address(self, random_address: hci.Address) -> None:
|
||||
@@ -712,17 +736,6 @@ class AdvertisingSet(EventEmitter):
|
||||
|
||||
self.emit('start')
|
||||
|
||||
async def start_periodic(self, include_adi: bool = False) -> None:
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Enable_Command(
|
||||
enable=1 | (2 if include_adi else 0),
|
||||
advertising_handles=self.advertising_handle,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
|
||||
self.emit('start_periodic')
|
||||
|
||||
async def stop(self) -> None:
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Extended_Advertising_Enable_Command(
|
||||
@@ -737,14 +750,31 @@ class AdvertisingSet(EventEmitter):
|
||||
|
||||
self.emit('stop')
|
||||
|
||||
async def stop_periodic(self) -> None:
|
||||
async def start_periodic(self, include_adi: bool = False) -> None:
|
||||
if self.periodic_enabled:
|
||||
return
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Enable_Command(
|
||||
enable=0,
|
||||
advertising_handles=self.advertising_handle,
|
||||
enable=1 | (2 if include_adi else 0),
|
||||
advertising_handle=self.advertising_handle,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
self.periodic_enabled = True
|
||||
|
||||
self.emit('start_periodic')
|
||||
|
||||
async def stop_periodic(self) -> None:
|
||||
if not self.periodic_enabled:
|
||||
return
|
||||
await self.device.send_command(
|
||||
hci.HCI_LE_Set_Periodic_Advertising_Enable_Command(
|
||||
enable=0,
|
||||
advertising_handle=self.advertising_handle,
|
||||
),
|
||||
check_result=True,
|
||||
)
|
||||
self.periodic_enabled = False
|
||||
|
||||
self.emit('stop_periodic')
|
||||
|
||||
@@ -2464,14 +2494,27 @@ class Device(CompositeEventEmitter):
|
||||
if advertising_parameters is None:
|
||||
advertising_parameters = AdvertisingParameters()
|
||||
|
||||
if periodic_advertising_data and periodic_advertising_parameters is None:
|
||||
periodic_advertising_parameters = PeriodicAdvertisingParameters()
|
||||
|
||||
if (
|
||||
not advertising_parameters.advertising_event_properties.is_legacy
|
||||
and advertising_data
|
||||
and scan_response_data
|
||||
):
|
||||
raise InvalidArgumentError(
|
||||
"Extended advertisements can't have both data and scan \
|
||||
response data"
|
||||
"Extended advertisements can't have both data and scan response data"
|
||||
)
|
||||
|
||||
if periodic_advertising_parameters and (
|
||||
advertising_parameters.advertising_event_properties.is_connectable
|
||||
or advertising_parameters.advertising_event_properties.is_scannable
|
||||
or advertising_parameters.advertising_event_properties.is_anonymous
|
||||
or advertising_parameters.advertising_event_properties.is_legacy
|
||||
):
|
||||
raise InvalidArgumentError(
|
||||
"Periodic advertising set cannot be connectable, scannable, anonymous,"
|
||||
"or legacy"
|
||||
)
|
||||
|
||||
# Allocate a new handle
|
||||
@@ -2526,12 +2569,14 @@ class Device(CompositeEventEmitter):
|
||||
await advertising_set.set_scan_response_data(scan_response_data)
|
||||
|
||||
if periodic_advertising_parameters:
|
||||
# TODO: call LE Set Periodic Advertising Parameters command
|
||||
raise NotImplementedError('periodic advertising not yet supported')
|
||||
await advertising_set.set_periodic_advertising_parameters(
|
||||
periodic_advertising_parameters
|
||||
)
|
||||
|
||||
if periodic_advertising_data:
|
||||
# TODO: call LE Set Periodic Advertising Data command
|
||||
raise NotImplementedError('periodic advertising not yet supported')
|
||||
await advertising_set.set_periodic_advertising_data(
|
||||
periodic_advertising_data
|
||||
)
|
||||
|
||||
except hci.HCI_Error as error:
|
||||
# Remove the advertising set so that it doesn't stay dangling in the
|
||||
|
||||
@@ -4331,6 +4331,61 @@ class HCI_LE_Clear_Advertising_Sets_Command(HCI_Command):
|
||||
'''
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command(
|
||||
[
|
||||
('advertising_handle', 1),
|
||||
('periodic_advertising_interval_min', 2),
|
||||
('periodic_advertising_interval_max', 2),
|
||||
('periodic_advertising_properties', 2),
|
||||
]
|
||||
)
|
||||
class HCI_LE_Set_Periodic_Advertising_Parameters_Command(HCI_Command):
|
||||
'''
|
||||
See Bluetooth spec @ 7.8.61 LE Set Periodic Advertising Parameters command
|
||||
'''
|
||||
|
||||
class Properties(enum.IntFlag):
|
||||
INCLUDE_TX_POWER = 1 << 6
|
||||
|
||||
advertising_handle: int
|
||||
periodic_advertising_interval_min: int
|
||||
periodic_advertising_interval_max: int
|
||||
periodic_advertising_properties: int
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command(
|
||||
[
|
||||
('advertising_handle', 1),
|
||||
(
|
||||
'operation',
|
||||
{
|
||||
'size': 1,
|
||||
'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.Operation(
|
||||
x
|
||||
).name,
|
||||
},
|
||||
),
|
||||
(
|
||||
'advertising_data',
|
||||
{
|
||||
'parser': HCI_Object.parse_length_prefixed_bytes,
|
||||
'serializer': HCI_Object.serialize_length_prefixed_bytes,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
class HCI_LE_Set_Periodic_Advertising_Data_Command(HCI_Command):
|
||||
'''
|
||||
See Bluetooth spec @ 7.8.62 LE Set Periodic Advertising Data command
|
||||
'''
|
||||
|
||||
advertising_handle: int
|
||||
operation: int
|
||||
advertising_data: bytes
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@HCI_Command.command([('enable', 1), ('advertising_handle', 1)])
|
||||
class HCI_LE_Set_Periodic_Advertising_Enable_Command(HCI_Command):
|
||||
|
||||
Reference in New Issue
Block a user