src.bluetooth_sig.device.peripheral

Peripheral manager protocol for BLE GATT server adapters.

Defines an async abstract base class that peripheral adapter implementations (bless, bluez_peripheral, etc.) must inherit from to create BLE GATT servers that broadcast services and characteristics.

This is the server-side counterpart to ClientManagerProtocol. Where clients connect TO devices and READ/PARSE data, peripherals ARE devices that ENCODE and BROADCAST data for others to read.

Adapters must provide async implementations of all abstract methods below.

TODO: PeripheralDevice exists in peripheral_device.py with core functionality.

Remaining gaps to address (see ROADMAP.md Workstream F): - Subscription management (on_subscribe/on_unsubscribe, subscribed_clients tracking) - Client event callbacks (on_client_connected/on_client_disconnected) - Read/write request handling (typed on_read_request/on_write_request) - Descriptor hosting (CCCD, User Description, Presentation Format)

Classes

Name

Description

PeripheralManagerProtocol

Abstract base class for BLE peripheral/GATT server implementations.

Module Contents

class src.bluetooth_sig.device.peripheral.PeripheralManagerProtocol(name: str)

Bases: abc.ABC

Abstract base class for BLE peripheral/GATT server implementations.

This protocol defines the interface for creating BLE peripherals that broadcast services and characteristics. Implementations wrap backend libraries like bless, bluez_peripheral, etc.

Uses a fluent builder pattern for advertisement configuration - call configuration methods before start() to customise advertising.

The workflow is: 1. Create peripheral manager with a device name 2. Configure advertising (optional): with_manufacturer_data(), with_tx_power(), etc. 3. Add services and characteristics (using CharacteristicDefinition) 4. Start advertising 5. Update characteristic values as needed 6. Stop when done

Example::
>>> from bluetooth_sig.gatt.characteristics import BatteryLevelCharacteristic
>>> from bluetooth_sig.gatt.services import BatteryService
>>> from bluetooth_sig.types.company import ManufacturerData
>>>
>>> # Create peripheral with fluent configuration
>>> peripheral = SomePeripheralManager("My Sensor")
>>> peripheral.with_tx_power(-10).with_connectable(True)
>>>
>>> # Define a service with battery level
>>> char = BatteryLevelCharacteristic()
>>> char_def = CharacteristicDefinition.from_characteristic(char, 85)
>>>
>>> service = ServiceDefinition(
...     uuid=BatteryService.get_class_uuid(),
...     characteristics=[char_def],
... )
>>>
>>> await peripheral.add_service(service)
>>> await peripheral.start()
>>>
>>> # Later, update the battery level
>>> await peripheral.update_characteristic("2A19", char.build_value(75))
async add_service(service: bluetooth_sig.types.peripheral_types.ServiceDefinition) None

Add a GATT service to the peripheral.

Services must be added before calling start(). Each service contains one or more characteristics that clients can interact with.

Parameters:

service – The service definition to add

Raises:

RuntimeError – If called after start()

get_characteristic_definition(char_uuid: str | bluetooth_sig.types.uuid.BluetoothUUID) bluetooth_sig.types.peripheral_types.CharacteristicDefinition | None

Get the characteristic definition by UUID.

Parameters:

char_uuid – UUID of the characteristic.

Returns:

CharacteristicDefinition if found, None otherwise.

abstractmethod get_characteristic_value(char_uuid: str | bluetooth_sig.types.uuid.BluetoothUUID) bytearray
Async:

Get the current value of a characteristic.

Parameters:

char_uuid – UUID of the characteristic

Returns:

The current encoded value

Raises:

KeyError – If characteristic UUID not found

set_read_callback(char_uuid: str | bluetooth_sig.types.uuid.BluetoothUUID, callback: collections.abc.Callable[[], bytearray]) None

Set a callback for dynamic read value generation.

When a client reads the characteristic, this callback will be invoked to generate the current value instead of returning the stored value.

Parameters:
  • char_uuid – UUID of the characteristic

  • callback – Function that returns the encoded value to serve

Raises:

KeyError – If characteristic UUID not found

set_write_callback(char_uuid: str | bluetooth_sig.types.uuid.BluetoothUUID, callback: collections.abc.Callable[[bytearray], None]) None

Set a callback for handling client writes.

When a client writes to the characteristic, this callback will be invoked with the written data.

Parameters:
  • char_uuid – UUID of the characteristic

  • callback – Function called with the written data

Raises:

KeyError – If characteristic UUID not found

abstractmethod start() None
Async:

Start advertising and accepting connections.

Backend implementations must: 1. Create the platform-specific GATT server 2. Register all services and characteristics from self._services 3. Configure advertisement data from self._manufacturer_data, etc. 4. Begin advertising

Raises:

RuntimeError – If no services have been added

abstractmethod stop() None
Async:

Stop advertising and disconnect all clients.

abstractmethod update_characteristic(char_uuid: str | bluetooth_sig.types.uuid.BluetoothUUID, value: bytearray, *, notify: bool = True) None
Async:

Update a characteristic’s value.

This sets the new value that will be returned when clients read the characteristic. If notify=True and the characteristic supports notifications, subscribed clients will be notified of the change.

Parameters:
  • char_uuid – UUID of the characteristic to update

  • value – New encoded value (use characteristic.build_value() to encode)

  • notify – If True, notify subscribed clients of the change

Raises:
with_connectable(connectable: bool) Self

Set whether the peripheral accepts connections.

Parameters:

connectable – True to accept connections (default), False for broadcast only.

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

with_discoverable(discoverable: bool) Self

Set whether the peripheral is discoverable.

Parameters:

discoverable – True to be discoverable (default), False otherwise.

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

with_manufacturer_data(manufacturer_data: bluetooth_sig.types.company.ManufacturerData) Self

Set manufacturer-specific advertising data.

Parameters:

manufacturer_data – ManufacturerData instance from the types module.

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

Example::
>>> from bluetooth_sig.types.company import ManufacturerData
>>> mfr = ManufacturerData.from_id_and_payload(0x004C, b"\x02\x15...")
>>> peripheral.with_manufacturer_data(mfr)
with_manufacturer_id(company_id: int | bluetooth_sig.types.company.CompanyIdentifier, payload: bytes) Self

Set manufacturer data from company ID and payload.

Parameters:
  • company_id – Bluetooth SIG company identifier (e.g., 0x004C for Apple) or CompanyIdentifier instance.

  • payload – Manufacturer-specific payload bytes.

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

Example::
>>> peripheral.with_manufacturer_id(0x004C, b"\x02\x15...")
with_service_data(service_uuid: bluetooth_sig.types.uuid.BluetoothUUID, data: bytes) Self

Add service data to advertisement.

Parameters:
  • service_uuid – BluetoothUUID of the service.

  • data – Service-specific data bytes.

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

Example::
>>> from bluetooth_sig.gatt.services import BatteryService
>>> peripheral.with_service_data(
...     BatteryService.get_class_uuid(),
...     b"\x50",  # 80% battery
... )
with_tx_power(power_dbm: int) Self

Set TX power level for advertising.

Parameters:

power_dbm – Transmission power in dBm (-127 to +127).

Returns:

Self for method chaining.

Raises:

RuntimeError – If called after start().

property connected_clients: int
Abstractmethod:

Get the number of currently connected clients.

Returns:

Number of connected BLE centrals

Raises:

NotImplementedError – If backend doesn’t track connections

property is_advertising: bool
Abstractmethod:

Check if the peripheral is currently advertising.

Returns:

True if advertising, False otherwise

property is_connectable_config: bool

Get the connectable configuration.

Returns:

True if peripheral is configured to accept connections.

property is_discoverable_config: bool

Get the discoverable configuration.

Returns:

True if peripheral is configured to be discoverable.

property manufacturer_data: bluetooth_sig.types.company.ManufacturerData | None

Get the configured manufacturer data.

Returns:

ManufacturerData if configured, None otherwise.

property name: str

Get the advertised device name.

Returns:

The device name as it appears to BLE scanners

property service_data: dict[bluetooth_sig.types.uuid.BluetoothUUID, bytes]

Get the configured service data.

Returns:

Dictionary mapping service UUIDs to data bytes.

property services: list[bluetooth_sig.types.peripheral_types.ServiceDefinition]

Get the list of registered services.

Returns:

List of ServiceDefinition objects added to this peripheral.

supports_advertising: ClassVar[bool] = True
property tx_power: int | None

Get the configured TX power level.

Returns:

TX power in dBm if configured, None otherwise.