mirror of
https://github.com/google/bumble.git
synced 2026-05-09 04:08:02 +00:00
add ASHA advertising factory method
This commit is contained in:
@@ -940,6 +940,12 @@ class Device(CompositeEventEmitter):
|
|||||||
if self.advertising:
|
if self.advertising:
|
||||||
await self.stop_advertising()
|
await self.stop_advertising()
|
||||||
|
|
||||||
|
# add each Service's advertising data
|
||||||
|
for attribute in self.gatt_server.attributes:
|
||||||
|
if isinstance(attribute, Service):
|
||||||
|
self.advertising_data += attribute.get_service_advertising_data()
|
||||||
|
|
||||||
|
|
||||||
# Set/update the advertising data if the advertising type allows it
|
# Set/update the advertising data if the advertising type allows it
|
||||||
if advertising_type.has_data:
|
if advertising_type.has_data:
|
||||||
await self.send_command(HCI_LE_Set_Advertising_Data_Command(
|
await self.send_command(HCI_LE_Set_Advertising_Data_Command(
|
||||||
|
|||||||
@@ -210,6 +210,15 @@ class Service(Attribute):
|
|||||||
self.characteristics = characteristics[:]
|
self.characteristics = characteristics[:]
|
||||||
self.primary = primary
|
self.primary = primary
|
||||||
|
|
||||||
|
|
||||||
|
def get_service_advertising_data(self):
|
||||||
|
"""
|
||||||
|
Get Service specific advertising data
|
||||||
|
Defined by each Service, default value is empty
|
||||||
|
"""
|
||||||
|
return b''
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}'
|
return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}'
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
import struct
|
import struct
|
||||||
from ..device import (
|
import logging
|
||||||
Device, AdvertisingType
|
|
||||||
)
|
|
||||||
from ..core import AdvertisingData
|
from ..core import AdvertisingData
|
||||||
from ..gatt import (
|
from ..gatt import (
|
||||||
GATT_ASHA_SERVICE,
|
GATT_ASHA_SERVICE,
|
||||||
@@ -34,31 +32,49 @@ from ..gatt import (
|
|||||||
PackedCharacteristicAdapter
|
PackedCharacteristicAdapter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Logging
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class AshaService(TemplateService):
|
class AshaService(TemplateService):
|
||||||
UUID = GATT_ASHA_SERVICE
|
UUID = GATT_ASHA_SERVICE
|
||||||
|
OPCODE_START = 1
|
||||||
|
OPCODE_STOP = 2
|
||||||
|
OPCODE_STATUS = 3
|
||||||
|
PROTOCOL_VERSION = 0x01
|
||||||
|
RESERVED_FOR_FUTURE_USE = [00, 00]
|
||||||
|
FEATURE_MAP = [0x01] # [LE CoC audio output streaming supported]
|
||||||
|
SUPPORTED_CODEDC_ID = [0x02, 0x01] # Codec IDs [G.722 at 16 kHz]
|
||||||
|
|
||||||
def __init__(self,device:Device):
|
def __init__(self, capability: int, hisyncid: []):
|
||||||
self.device=device
|
self.hisyncid = hisyncid
|
||||||
|
self.capability = capability # Device Capabilities [Left, Monaural]
|
||||||
|
self.render_delay = [00, 00]
|
||||||
|
self.reserved_for_future_use = [00, 00]
|
||||||
|
|
||||||
|
# Four least significant bytes of the HiSyncId.
|
||||||
|
self.truncated_hisyncid = self.hisyncid[:4]
|
||||||
|
|
||||||
# Handler for volume control
|
# Handler for volume control
|
||||||
def on_volume_write(connection, value):
|
def on_volume_write(connection, value):
|
||||||
print('--- VOLUME Write:', value[0])
|
logger.info(f'--- VOLUME Write:{value[0]}')
|
||||||
|
|
||||||
# Handler for audio control commands
|
# Handler for audio control commands
|
||||||
def on_audio_control_point_write(connection, value):
|
def on_audio_control_point_write(connection, value):
|
||||||
print('--- AUDIO CONTROL POINT Write:', value.hex())
|
logger.info(f'--- AUDIO CONTROL POINT Write:{value.hex()}')
|
||||||
opcode = value[0]
|
opcode = value[0]
|
||||||
if opcode == 1:
|
if opcode == AshaService.OPCODE_START:
|
||||||
# Start
|
# Start
|
||||||
audio_type = ('Unknown', 'Ringtone', 'Phone Call', 'Media')[value[2]]
|
audio_type = ('Unknown', 'Ringtone', 'Phone Call', 'Media')[value[2]]
|
||||||
print(
|
logger.info(
|
||||||
f'### START: codec={value[1]}, audio_type={audio_type}, volume={value[3]}, otherstate={value[4]}')
|
f'### START: codec={value[1]}, audio_type={audio_type}, volume={value[3]}, otherstate={value[4]}')
|
||||||
elif opcode == 2:
|
elif opcode == AshaService.OPCODE_STOP:
|
||||||
print('### STOP')
|
logger.info('### STOP')
|
||||||
elif opcode == 3:
|
elif opcode == AshaService.OPCODE_STATUS:
|
||||||
print(f'### STATUS: connected={value[1]}')
|
logger.info(f'### STATUS: connected={value[1]}')
|
||||||
|
|
||||||
# TODO Respond with a status
|
# TODO Respond with a status
|
||||||
# asyncio.create_task(device.notify_subscribers(audio_status_characteristic, force=True))
|
# asyncio.create_task(device.notify_subscribers(audio_status_characteristic, force=True))
|
||||||
@@ -68,14 +84,14 @@ class AshaService(TemplateService):
|
|||||||
Characteristic.READ,
|
Characteristic.READ,
|
||||||
Characteristic.READABLE,
|
Characteristic.READABLE,
|
||||||
bytes([
|
bytes([
|
||||||
0x01, # Version
|
AshaService.PROTOCOL_VERSION, # Version
|
||||||
0x00, # Device Capabilities [Left, Monaural]
|
self.capability,
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, # HiSyncId
|
]) +
|
||||||
0x01, # Feature Map [LE CoC audio output streaming supported]
|
bytes(self.hisyncid) +
|
||||||
0x00, 0x00, # Render Delay
|
bytes(AshaService.FEATURE_MAP)+
|
||||||
0x00, 0x00, # RFU
|
bytes(self.render_delay)+
|
||||||
0x02, 0x00 # Codec IDs [G.722 at 16 kHz]
|
bytes(AshaService.RESERVED_FOR_FUTURE_USE)+
|
||||||
])
|
bytes(AshaService.SUPPORTED_CODEDC_ID)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.audio_control_point_characteristic = Characteristic(
|
self.audio_control_point_characteristic = Characteristic(
|
||||||
@@ -98,7 +114,7 @@ class AshaService(TemplateService):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# TODO add real psm value
|
# TODO add real psm value
|
||||||
self.psm=0x0080
|
self.psm = 0x0080
|
||||||
# self.psm = device.register_l2cap_channel_server(0, on_coc, 8)
|
# self.psm = device.register_l2cap_channel_server(0, on_coc, 8)
|
||||||
self.le_psm_out_characteristic = Characteristic(
|
self.le_psm_out_characteristic = Characteristic(
|
||||||
GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
|
GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
|
||||||
@@ -115,24 +131,15 @@ class AshaService(TemplateService):
|
|||||||
|
|
||||||
super().__init__(characteristics)
|
super().__init__(characteristics)
|
||||||
|
|
||||||
async def start_advertising(self, capability: int,
|
|
||||||
truncated_hisyncid: []):
|
def get_service_advertising_data(self):
|
||||||
assert self.device
|
return bytes(
|
||||||
self.device.advertising_data = bytes(
|
|
||||||
AdvertisingData([
|
AdvertisingData([
|
||||||
(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.device.name, 'utf-8')),
|
|
||||||
(AdvertisingData.FLAGS, bytes([0x06])),
|
|
||||||
(AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, bytes(GATT_ASHA_SERVICE)),
|
(AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, bytes(GATT_ASHA_SERVICE)),
|
||||||
(AdvertisingData.SERVICE_DATA_16_BIT_UUID, bytes(GATT_ASHA_SERVICE) + bytes([
|
(AdvertisingData.SERVICE_DATA_16_BIT_UUID, bytes(GATT_ASHA_SERVICE) + bytes([
|
||||||
0x01, # Protocol Version
|
AshaService.PROTOCOL_VERSION,
|
||||||
capability, # Capability
|
self.capability,
|
||||||
]) + bytes(truncated_hisyncid))
|
]) + bytes(self.truncated_hisyncid))
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO enable more advertising_type and own_address_type
|
|
||||||
advertising_type = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE
|
|
||||||
|
|
||||||
await self.device.start_advertising(
|
|
||||||
advertising_type=advertising_type
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user