src.bluetooth_sig.advertising.base

Base classes for advertising data interpreters.

Advertising data interpretation follows a two-layer architecture:

  1. PDU Parsing (AdvertisingPDUParser): Raw bytes → AD structures - Extracts manufacturer_data, service_data, flags, local_name, etc. - Framework-agnostic, works with raw BLE PDU bytes

  2. Payload Interpretation (PayloadInterpreter): AD structures → typed results - Interprets vendor-specific protocols (BTHome, Xiaomi, RuuviTag, etc.) - Returns strongly-typed sensor data (temperature, humidity, etc.) - State managed externally by caller; interpreter updates state directly - Errors are raised as exceptions (consistent with GATT characteristic parsing)

Error Handling:

Interpreters raise exceptions for error conditions instead of returning status codes. This is consistent with GATT characteristic parsing.

Exceptions:

EncryptionRequiredError: Payload encrypted, no bindkey available DecryptionFailedError: Decryption failed (wrong key or corrupt data) ReplayDetectedError: Counter not increasing (potential replay attack) DuplicatePacketError: Same packet_id as previous AdvertisingParseError: General parse failure UnsupportedVersionError: Unknown protocol version

Attributes

Name

Description

T

Classes

Name

Description

AdvertisingData

Complete advertising data from a BLE advertisement packet.

DataSource

Primary data source for interpreter routing.

InterpreterInfo

Interpreter metadata for routing and identification.

PayloadInterpreter

Base class for payload interpretation (service data + manufacturer data).

Module Contents

class src.bluetooth_sig.advertising.base.AdvertisingData

Bases: msgspec.Struct

Complete advertising data from a BLE advertisement packet.

Encapsulates all extracted AD structures from a BLE PDU. Interpreters access only the fields they need.

manufacturer_data

Company ID → ManufacturerData mapping. Each entry contains resolved company info and payload bytes.

service_data

Service UUID → payload bytes mapping.

local_name

Device local name (may contain protocol info).

rssi

Signal strength in dBm.

timestamp

Advertisement timestamp (Unix epoch seconds).

local_name: str | None = None
manufacturer_data: dict[int, bluetooth_sig.types.company.ManufacturerData]
rssi: int | None = None
service_data: dict[bluetooth_sig.types.uuid.BluetoothUUID, bytes]
timestamp: float | None = None
class src.bluetooth_sig.advertising.base.DataSource(*args, **kwds)

Bases: enum.Enum

Primary data source for interpreter routing.

LOCAL_NAME = 'local_name'
MANUFACTURER = 'manufacturer'
SERVICE = 'service'
class src.bluetooth_sig.advertising.base.InterpreterInfo

Bases: msgspec.Struct

Interpreter metadata for routing and identification.

company_id

Bluetooth SIG company ID for manufacturer data routing.

service_uuid

Service UUID for service data routing.

name

Human-readable interpreter name.

data_source

Primary data source for fast routing.

company_id: int | None = None
data_source: DataSource
name: str = ''
service_uuid: bluetooth_sig.types.uuid.BluetoothUUID | None = None
class src.bluetooth_sig.advertising.base.PayloadInterpreter(mac_address: str)

Bases: abc.ABC, Generic[T]

Base class for payload interpretation (service data + manufacturer data).

Interprets raw bytes from BLE advertisements into typed domain objects. State is managed externally by the caller - interpreter receives state and updates it directly. Errors are raised as exceptions.

Encryption Flow (following BTHome/Xiaomi patterns):
  1. Check if payload is encrypted (flag byte in payload header)

  2. If encrypted, check state.encryption.bindkey

  3. If no bindkey, raise EncryptionRequiredError

  4. Extract counter from payload, compare to state.encryption.encryption_counter

  5. If counter <= old counter, raise ReplayDetectedError

  6. Attempt decryption with AES-CCM

  7. If decryption fails, raise DecryptionFailedError

  8. Parse decrypted payload

  9. Update state.encryption.encryption_counter directly

  10. Return parsed data

Example:

class BTHomeInterpreter(PayloadInterpreter[BTHomeData]):
    _info = InterpreterInfo(
        service_uuid=BluetoothUUID("0000fcd2-0000-1000-8000-00805f9b34fb"),
        name="BTHome",
        data_source=DataSource.SERVICE,
    )

    @classmethod
    def supports(cls, advertising_data):
        return "0000fcd2-0000-1000-8000-00805f9b34fb" in advertising_data.service_data

    def interpret(self, advertising_data, state):
        # Parse BTHome service data
        # Update state.encryption.encryption_counter if encrypted
        # Raise exceptions on error
        # Return BTHomeData on success
        ...
abstractmethod interpret(advertising_data: AdvertisingData, state: bluetooth_sig.advertising.state.DeviceAdvertisingState) T

Interpret payload bytes and return typed result.

Updates state directly (state is mutable). Raises exceptions for errors.

Parameters:
  • advertising_data – Complete advertising data from BLE packet.

  • state – Current device advertising state (caller-managed, mutable).

Returns:

Parsed data of type T.

Raises:
classmethod supports(advertising_data: AdvertisingData) bool
Abstractmethod:

Check if this interpreter handles the advertisement.

Called by registry for fast routing. Should be a quick check based on company_id, service_uuid, or local_name pattern.

Parameters:

advertising_data – Complete advertising data from BLE packet.

Returns:

True if this interpreter can handle the advertisement.

property info: InterpreterInfo

Interpreter metadata.

property mac_address: str

Device MAC address.

src.bluetooth_sig.advertising.base.T