Skip to content

GATT Layer API Reference

The GATT layer provides the fundamental building blocks for Bluetooth characteristic parsing.

Overview

The GATT layer consists of:

  • Characteristic parsers - 70+ implementations for standard characteristics
  • Service definitions - Organize characteristics into services
  • Validation logic - Ensure data integrity
  • Exception types - Clear error reporting

Base Classes

BaseCharacteristic

Bases: ABC

Base class for all GATT characteristics.

Automatically resolves UUID, unit, and value_type from Bluetooth SIG YAML specifications. Supports manual overrides via _manual_unit and _manual_value_type attributes.

Note: This class intentionally has >20 public methods as it provides the complete characteristic API including parsing, validation, UUID resolution, registry interaction, and metadata access. The methods are well-organized by functionality.

Validation Attributes (optional class-level declarations): min_value: Minimum allowed value for parsed data max_value: Maximum allowed value for parsed data expected_length: Exact expected data length in bytes min_length: Minimum required data length in bytes max_length: Maximum allowed data length in bytes allow_variable_length: Whether variable length data is acceptable expected_type: Expected Python type for parsed values

Example usage in subclasses

class ExampleCharacteristic(BaseCharacteristic): '''Example showing validation attributes usage.'''

# Declare validation constraints as class attributes
expected_length = 2
min_value = 0
max_value = 65535  # UINT16_MAX
expected_type = int

def decode_value(self, data: bytearray) -> int:
    # Just parse - validation happens automatically in parse_value
    return DataParser.parse_int16(data, 0, signed=False)

Before: BatteryLevelCharacteristic with hardcoded validation

class BatteryLevelCharacteristic(BaseCharacteristic):

def decode_value(self, data: bytearray) -> int:

if not data:

raise ValueError("Battery level data must be at least 1 byte")

level = data[0]

if not 0 <= level <= PERCENTAGE_MAX:

raise ValueError(f"Battery level must be 0-100, got {level}")

return level

After: BatteryLevelCharacteristic with declarative validation

class BatteryLevelCharacteristic(BaseCharacteristic):

expected_length = 1

min_value = 0

max_value = 100 # PERCENTAGE_MAX

expected_type = int

def decode_value(self, data: bytearray) -> int:

return data[0] # Validation happens automatically

Initialize characteristic with structured configuration.

Parameters:

Name Type Description Default
info CharacteristicInfo | None

Complete characteristic information (optional for SIG characteristics)

None
validation ValidationConfig | None

Validation constraints configuration (optional)

None
Source code in src/bluetooth_sig/gatt/characteristics/base.py
def __init__(
    self,
    info: CharacteristicInfo | None = None,
    validation: ValidationConfig | None = None,
) -> None:
    """Initialize characteristic with structured configuration.

    Args:
        info: Complete characteristic information (optional for SIG characteristics)
        validation: Validation constraints configuration (optional)

    """
    # Store provided info or None (will be resolved in __post_init__)
    self._provided_info = info

    # Instance variables (will be set in __post_init__)
    self._info: CharacteristicInfo

    # Manual overrides with proper types (using explicit class attributes)
    self._manual_unit: str | None = self.__class__._manual_unit
    self._manual_value_type: ValueType | str | None = self.__class__._manual_value_type
    self.value_type: ValueType = ValueType.UNKNOWN

    # Set validation attributes from ValidationConfig or class defaults
    if validation:
        self.min_value = validation.min_value
        self.max_value = validation.max_value
        self.expected_length = validation.expected_length
        self.min_length = validation.min_length
        self.max_length = validation.max_length
        self.allow_variable_length = validation.allow_variable_length
        self.expected_type = validation.expected_type
    else:
        # Fall back to class attributes for Progressive API Level 2
        self.min_value = self.__class__.min_value
        self.max_value = self.__class__.max_value
        self.expected_length = self.__class__.expected_length
        self.min_length = self.__class__.min_length
        self.max_length = self.__class__.max_length
        self.allow_variable_length = self.__class__.allow_variable_length
        self.expected_type = self.__class__.expected_type

    # Dependency caches (resolved once per instance)
    self._resolved_required_dependencies: list[str] | None = None
    self._resolved_optional_dependencies: list[str] | None = None

    # Descriptor support
    self._descriptors: dict[str, BaseDescriptor] = {}

    # Call post-init to resolve characteristic info
    self.__post_init__()

decode_value

decode_value(data: bytearray, ctx: CharacteristicContext | None = None) -> Any

Parse the characteristic's raw value.

If _template is set, uses the template's decode_value method. Otherwise, subclasses must override this method.

Parameters:

Name Type Description Default
data bytearray

Raw bytes from the characteristic read

required
ctx CharacteristicContext | None

Optional context information for parsing

None

Returns:

Type Description
Any

Parsed value in the appropriate type

Raises:

Type Description
NotImplementedError

If no template is set and subclass doesn't override

Source code in src/bluetooth_sig/gatt/characteristics/base.py
def decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> Any:  # noqa: ANN401  # Context and return types vary by characteristic
    """Parse the characteristic's raw value.

    If _template is set, uses the template's decode_value method.
    Otherwise, subclasses must override this method.

    Args:
        data: Raw bytes from the characteristic read
        ctx: Optional context information for parsing

    Returns:
        Parsed value in the appropriate type

    Raises:
        NotImplementedError: If no template is set and subclass doesn't override

    """
    if self._template is not None:
        return self._template.decode_value(data, offset=0, ctx=ctx)
    raise NotImplementedError(f"{self.__class__.__name__} must either set _template or override decode_value()")

encode_value

encode_value(data: Any) -> bytearray

Encode the characteristic's value to raw bytes.

If _template is set , uses the template's encode_value method. Otherwise, subclasses must override this method.

Parameters:

Name Type Description Default
data Any

Dataclass instance or value to encode

required

Returns:

Type Description
bytearray

Encoded bytes for characteristic write

Raises:

Type Description
ValueError

If data is invalid for encoding

NotImplementedError

If no template is set and subclass doesn't override

Source code in src/bluetooth_sig/gatt/characteristics/base.py
def encode_value(self, data: Any) -> bytearray:  # noqa: ANN401  # Encodes various value types (int, float, dataclass, etc.)
    """Encode the characteristic's value to raw bytes.

    If _template is set , uses the template's encode_value method.
    Otherwise, subclasses must override this method.

    Args:
        data: Dataclass instance or value to encode

    Returns:
        Encoded bytes for characteristic write

    Raises:
        ValueError: If data is invalid for encoding
        NotImplementedError: If no template is set and subclass doesn't override

    """
    if self._template is not None:
        return self._template.encode_value(data)
    raise NotImplementedError(f"{self.__class__.__name__} must either set _template or override encode_value()")

All characteristic implementations inherit from BaseCharacteristic.

BaseService

Base class for all GATT services.

Automatically resolves UUID, name, and summary from Bluetooth SIG specifications. Follows the same pattern as BaseCharacteristic for consistency.

Initialize service with structured configuration.

Parameters:

Name Type Description Default
info ServiceInfo | None

Complete service information (optional for SIG services)

None
validation ServiceValidationConfig | None

Validation constraints configuration (optional)

None
Source code in src/bluetooth_sig/gatt/services/base.py
def __init__(
    self,
    info: ServiceInfo | None = None,
    validation: ServiceValidationConfig | None = None,
) -> None:
    """Initialize service with structured configuration.

    Args:
        info: Complete service information (optional for SIG services)
        validation: Validation constraints configuration (optional)

    """
    # Store provided info or None (will be resolved in __post_init__)
    self._provided_info = info

    self.characteristics: dict[BluetoothUUID, BaseCharacteristic] = {}

    # Set validation attributes from ServiceValidationConfig
    if validation:
        self.strict_validation = validation.strict_validation
        self.require_all_optional = validation.require_all_optional
    else:
        self.strict_validation = False
        self.require_all_optional = False

    # Call post-init to resolve service info
    self.__post_init__()

info property

Return the resolved service information for this instance.

The info property provides all metadata about the service, including UUID, name, and description.

name property

name: str

Get the service name from _info.

summary property

summary: str

Get the service summary from _info.

supported_characteristics property

supported_characteristics: set[BaseCharacteristic]

Get the set of characteristic UUIDs supported by this service.

uuid property

uuid: BluetoothUUID

Get the service UUID from _info.

__post_init__

__post_init__() -> None

Initialize service with resolved information.

Source code in src/bluetooth_sig/gatt/services/base.py
def __post_init__(self) -> None:
    """Initialize service with resolved information."""
    # Use provided info if available, otherwise resolve from SIG specs
    if self._provided_info:
        self._info = self._provided_info
    else:
        # Resolve service information using proper resolver
        self._info = SIGServiceResolver.resolve_for_class(type(self))

get_characteristic

get_characteristic(uuid: BluetoothUUID) -> GattCharacteristic | None

Get a characteristic by UUID.

Source code in src/bluetooth_sig/gatt/services/base.py
def get_characteristic(self, uuid: BluetoothUUID) -> GattCharacteristic | None:
    """Get a characteristic by UUID."""
    if isinstance(uuid, str):
        uuid = BluetoothUUID(uuid)
    return self.characteristics.get(uuid)

get_characteristic_status

get_characteristic_status(characteristic_name: CharacteristicName) -> ServiceCharacteristicInfo | None

Get detailed status of a specific characteristic.

Parameters:

Name Type Description Default
characteristic_name CharacteristicName

CharacteristicName enum

required

Returns:

Type Description
ServiceCharacteristicInfo | None

CharacteristicInfo if characteristic is expected by this service, None otherwise

Source code in src/bluetooth_sig/gatt/services/base.py
def get_characteristic_status(self, characteristic_name: CharacteristicName) -> ServiceCharacteristicInfo | None:
    """Get detailed status of a specific characteristic.

    Args:
        characteristic_name: CharacteristicName enum

    Returns:
        CharacteristicInfo if characteristic is expected by this service, None otherwise

    """
    expected_chars = self.get_expected_characteristics()

    char_enum = characteristic_name

    # Only return status for characteristics that are expected by this service
    if char_enum not in expected_chars:
        return None

    char_info = uuid_registry.get_characteristic_info(char_enum.value)
    if not char_info:
        return None

    is_required, is_conditional, condition_desc = self._get_characteristic_metadata(char_enum)

    char_class = None
    if char_enum in expected_chars:
        char_spec = expected_chars[char_enum]
        if hasattr(char_spec, "char_class"):
            char_class = char_spec.char_class  # New format
        else:
            char_class = cast(type[BaseCharacteristic], char_spec)  # Legacy format: value is the class directly

    status = self._get_characteristic_status(char_info)

    return ServiceCharacteristicInfo(
        name=characteristic_name.value,
        uuid=char_info.uuid,
        status=status,
        is_required=is_required,
        is_conditional=is_conditional,
        condition_description=condition_desc,
        char_class=char_class,
    )

get_characteristics_schema classmethod

get_characteristics_schema() -> type | None

Get the TypedDict schema for this service's characteristics.

Override this method to provide strong typing for characteristics. If not implemented, falls back to get_expected_characteristics().

Returns:

Type Description
type | None

TypedDict class defining the service's characteristics, or None

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_characteristics_schema(cls) -> type | None:
    """Get the TypedDict schema for this service's characteristics.

    Override this method to provide strong typing for characteristics.
    If not implemented, falls back to get_expected_characteristics().

    Returns:
        TypedDict class defining the service's characteristics, or None

    """
    return None

get_class_uuid classmethod

get_class_uuid() -> BluetoothUUID

Get the UUID for this service class without instantiation.

Returns:

Type Description
BluetoothUUID

BluetoothUUID for this service class

Raises:

Type Description
UUIDResolutionError

If UUID cannot be resolved

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_class_uuid(cls) -> BluetoothUUID:
    """Get the UUID for this service class without instantiation.

    Returns:
        BluetoothUUID for this service class

    Raises:
        UUIDResolutionError: If UUID cannot be resolved

    """
    info = SIGServiceResolver.resolve_for_class(cls)
    return info.uuid

get_conditional_characteristics classmethod

get_conditional_characteristics() -> ServiceCharacteristicCollection

Get characteristics that are required only under certain conditions.

Returns:

Type Description
ServiceCharacteristicCollection

ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

Override in subclasses to specify conditional characteristics.

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_conditional_characteristics(cls) -> ServiceCharacteristicCollection:
    """Get characteristics that are required only under certain conditions.

    Returns:
        ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

    Override in subclasses to specify conditional characteristics.

    """
    return {}

get_expected_characteristic_uuids

get_expected_characteristic_uuids() -> set[BluetoothUUID]

Get the set of expected characteristic UUIDs for this service.

Source code in src/bluetooth_sig/gatt/services/base.py
def get_expected_characteristic_uuids(self) -> set[BluetoothUUID]:
    """Get the set of expected characteristic UUIDs for this service."""
    expected_uuids: set[BluetoothUUID] = set()
    for char_name, _char_spec in self.get_expected_characteristics().items():
        # char_name is expected to be a CharacteristicName enum; handle accordingly
        try:
            lookup_name = char_name.value
        except AttributeError:
            lookup_name = str(char_name)
        char_info = uuid_registry.get_characteristic_info(lookup_name)
        if char_info:
            expected_uuids.add(char_info.uuid)
    return expected_uuids

get_expected_characteristics classmethod

get_expected_characteristics() -> ServiceCharacteristicCollection

Get the expected characteristics for this service from the service_characteristics dict.

Looks for a 'service_characteristics' class attribute containing a dictionary of CharacteristicName -> required flag, and automatically builds CharacteristicSpec objects.

Returns:

Type Description
ServiceCharacteristicCollection

ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_expected_characteristics(cls) -> ServiceCharacteristicCollection:
    """Get the expected characteristics for this service from the service_characteristics dict.

    Looks for a 'service_characteristics' class attribute containing a dictionary of
        CharacteristicName -> required flag, and automatically builds CharacteristicSpec objects.

    Returns:
        ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

    """
    # Build expected mapping keyed by CharacteristicName enum for type safety.
    expected: dict[CharacteristicName, CharacteristicSpec[BaseCharacteristic]] = {}

    # Check if the service defines a service_characteristics dictionary
    svc_chars = getattr(cls, "service_characteristics", None)
    if svc_chars:
        for char_name, is_required in svc_chars.items():
            char_class = CharacteristicRegistry.get_characteristic_class(char_name)
            if char_class:
                expected[char_name] = CharacteristicSpec(char_class=char_class, required=is_required)

    # Return an enum-keyed dict for strong typing. Callers must perform
    # explicit conversions from strings to `CharacteristicName` where needed.
    return expected

get_missing_characteristics

get_missing_characteristics() -> dict[CharacteristicName, ServiceCharacteristicInfo]

Get detailed information about missing characteristics.

Returns:

Type Description
dict[CharacteristicName, ServiceCharacteristicInfo]

Dict mapping characteristic name to ServiceCharacteristicInfo

Source code in src/bluetooth_sig/gatt/services/base.py
def get_missing_characteristics(
    self,
) -> dict[CharacteristicName, ServiceCharacteristicInfo]:
    """Get detailed information about missing characteristics.

    Returns:
        Dict mapping characteristic name to ServiceCharacteristicInfo

    """
    missing: dict[CharacteristicName, ServiceCharacteristicInfo] = {}
    expected_chars = self.get_expected_characteristics()
    required_chars = self.get_required_characteristics()
    conditional_chars = self.get_conditional_characteristics()

    for char_name, _char_spec in expected_chars.items():
        char_info = uuid_registry.get_characteristic_info(char_name.value)
        if not char_info:
            continue

        uuid_obj = char_info.uuid
        if uuid_obj not in self.characteristics:
            is_required = char_name in required_chars
            is_conditional = char_name in conditional_chars
            condition_desc = ""
            if is_conditional:
                conditional_spec = conditional_chars.get(char_name)
                if conditional_spec:
                    condition_desc = conditional_spec.condition

            # Handle both new CharacteristicSpec format and legacy direct class format
            char_class = None
            if hasattr(_char_spec, "char_class"):
                char_class = _char_spec.char_class  # New format
            else:
                char_class = cast(
                    type[BaseCharacteristic], _char_spec
                )  # Legacy format: value is the class directly

            missing[char_name] = ServiceCharacteristicInfo(
                name=char_name.value,
                uuid=char_info.uuid,
                status=CharacteristicStatus.MISSING,
                is_required=is_required,
                is_conditional=is_conditional,
                condition_description=condition_desc,
                char_class=char_class,
            )

    return missing

get_optional_characteristics classmethod

get_optional_characteristics() -> ServiceCharacteristicCollection

Get the optional characteristics for this service by name and class.

Returns:

Type Description
ServiceCharacteristicCollection

ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_optional_characteristics(cls) -> ServiceCharacteristicCollection:
    """Get the optional characteristics for this service by name and class.

    Returns:
        ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

    """
    expected = cls.get_expected_characteristics()
    required = cls.get_required_characteristics()
    return {name: char_spec for name, char_spec in expected.items() if name not in required}

get_required_characteristic_keys classmethod

get_required_characteristic_keys() -> set[CharacteristicName]

Get the set of required characteristic keys from the schema.

Override this method when using strongly-typed characteristics. If not implemented, falls back to get_required_characteristics().keys().

Returns:

Type Description
set[CharacteristicName]

Set of required characteristic field names

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_required_characteristic_keys(cls) -> set[CharacteristicName]:
    """Get the set of required characteristic keys from the schema.

    Override this method when using strongly-typed characteristics.
    If not implemented, falls back to get_required_characteristics().keys().

    Returns:
        Set of required characteristic field names

    """
    return set(cls.get_required_characteristics().keys())

get_required_characteristic_uuids

get_required_characteristic_uuids() -> set[BluetoothUUID]

Get the set of required characteristic UUIDs for this service.

Source code in src/bluetooth_sig/gatt/services/base.py
def get_required_characteristic_uuids(self) -> set[BluetoothUUID]:
    """Get the set of required characteristic UUIDs for this service."""
    required_uuids: set[BluetoothUUID] = set()
    for char_name, _char_spec in self.get_required_characteristics().items():
        try:
            lookup_name = char_name.value
        except AttributeError:
            lookup_name = str(char_name)
        char_info = uuid_registry.get_characteristic_info(lookup_name)
        if char_info:
            required_uuids.add(char_info.uuid)
    return required_uuids

get_required_characteristics classmethod

get_required_characteristics() -> ServiceCharacteristicCollection

Get the required characteristics for this service from the characteristics dict.

Automatically filters the characteristics dictionary for required=True entries.

Returns:

Type Description
ServiceCharacteristicCollection

ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def get_required_characteristics(cls) -> ServiceCharacteristicCollection:
    """Get the required characteristics for this service from the characteristics dict.

    Automatically filters the characteristics dictionary for required=True entries.

    Returns:
        ServiceCharacteristicCollection mapping characteristic name to CharacteristicSpec

    """
    expected = cls.get_expected_characteristics()
    return {name: spec for name, spec in expected.items() if spec.required}

get_service_completeness_report

get_service_completeness_report() -> ServiceCompletenessReport

Get a comprehensive report about service completeness.

Returns:

Type Description
ServiceCompletenessReport

ServiceCompletenessReport with detailed service status information

Source code in src/bluetooth_sig/gatt/services/base.py
def get_service_completeness_report(self) -> ServiceCompletenessReport:
    """Get a comprehensive report about service completeness.

    Returns:
        ServiceCompletenessReport with detailed service status information

    """
    validation = self.validate_service(strict=True)
    missing = self.get_missing_characteristics()

    present_chars: list[str] = []
    for uuid, char in self.characteristics.items():
        try:
            char_name = char.name if hasattr(char, "name") else f"UUID:{str(uuid)}"
            present_chars.append(char_name)
        except (AttributeError, ValueError, TypeError):
            present_chars.append(f"Invalid:{str(uuid)}")

    missing_details = {
        name.value: ServiceCharacteristicInfo(
            name=info.name,
            uuid=info.uuid,
            status=info.status,
            is_required=info.is_required,
            is_conditional=info.is_conditional,
            condition_description=info.condition_description,
        )
        for name, info in missing.items()
    }

    return ServiceCompletenessReport(
        service_name=self.name,
        service_uuid=self.uuid,
        health_status=validation.status,
        is_healthy=validation.is_healthy,
        characteristics_present=len(self.characteristics),
        characteristics_expected=len(self.get_expected_characteristics()),
        characteristics_required=len(self.get_required_characteristics()),
        present_characteristics=list(self.characteristics.values()),
        missing_required=validation.missing_required,
        missing_optional=validation.missing_optional,
        invalid_characteristics=validation.invalid_characteristics,
        warnings=validation.warnings,
        errors=validation.errors,
        missing_details=missing_details,
    )

has_minimum_functionality

has_minimum_functionality() -> bool

Check if service has minimum required functionality.

Returns:

Type Description
bool

True if service has all required characteristics and is usable

Source code in src/bluetooth_sig/gatt/services/base.py
def has_minimum_functionality(self) -> bool:
    """Check if service has minimum required functionality.

    Returns:
        True if service has all required characteristics and is usable

    """
    validation = self.validate_service()
    return (not validation.missing_required) and (validation.status != ServiceHealthStatus.INCOMPLETE)

matches_uuid classmethod

matches_uuid(uuid: str | BluetoothUUID) -> bool

Check if this service matches the given UUID.

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def matches_uuid(cls, uuid: str | BluetoothUUID) -> bool:
    """Check if this service matches the given UUID."""
    try:
        service_uuid = cls.get_class_uuid()
        if isinstance(uuid, BluetoothUUID):
            input_uuid = uuid
        else:
            input_uuid = BluetoothUUID(uuid)
        return service_uuid == input_uuid
    except (ValueError, UUIDResolutionError):
        return False

process_characteristics

process_characteristics(characteristics: ServiceDiscoveryData) -> None

Process the characteristics for this service (default implementation).

Parameters:

Name Type Description Default
characteristics ServiceDiscoveryData

Dict mapping UUID to characteristic info

required
Source code in src/bluetooth_sig/gatt/services/base.py
def process_characteristics(self, characteristics: ServiceDiscoveryData) -> None:
    """Process the characteristics for this service (default implementation).

    Args:
        characteristics: Dict mapping UUID to characteristic info

    """
    for uuid, _ in characteristics.items():
        char = CharacteristicRegistry.create_characteristic(uuid=uuid)
        if char:
            self.characteristics[uuid] = char

validate_bluetooth_sig_compliance classmethod

validate_bluetooth_sig_compliance() -> list[str]

Validate compliance with Bluetooth SIG service specification.

Returns:

Type Description
list[str]

List of compliance issues found

Override in subclasses to provide service-specific validation.

Source code in src/bluetooth_sig/gatt/services/base.py
@classmethod
def validate_bluetooth_sig_compliance(cls) -> list[str]:
    """Validate compliance with Bluetooth SIG service specification.

    Returns:
        List of compliance issues found

    Override in subclasses to provide service-specific validation.

    """
    issues: list[str] = []

    # Check if service has at least one required characteristic
    required = cls.get_required_characteristics()
    if not required:
        issues.append("Service has no required characteristics defined")

    # Check if all expected characteristics are valid
    expected = cls.get_expected_characteristics()
    for char_name, _char_spec in expected.items():
        char_info = uuid_registry.get_characteristic_info(char_name.value)
        if not char_info:
            issues.append(f"Characteristic '{char_name.value}' not found in UUID registry")

    return issues

validate_service

validate_service(strict: bool = False) -> ServiceValidationResult

Validate the completeness and health of this service.

Parameters:

Name Type Description Default
strict bool

If True, missing optional characteristics are treated as warnings

False

Returns:

Type Description
ServiceValidationResult

ServiceValidationResult with detailed status information

Source code in src/bluetooth_sig/gatt/services/base.py
def validate_service(self, strict: bool = False) -> ServiceValidationResult:  # pylint: disable=too-many-branches
    """Validate the completeness and health of this service.

    Args:
        strict: If True, missing optional characteristics are treated as warnings

    Returns:
        ServiceValidationResult with detailed status information

    """
    result = ServiceValidationResult(status=ServiceHealthStatus.COMPLETE)

    # Validate required, optional, and conditional characteristics
    self._validate_characteristic_group(self.get_required_characteristics(), result, True, strict)
    self._validate_characteristic_group(self.get_optional_characteristics(), result, False, strict)
    self._validate_conditional_characteristics(self.get_conditional_characteristics(), result)

    # Validate existing characteristics
    for uuid, characteristic in self.characteristics.items():
        try:
            _ = characteristic.uuid
        except (AttributeError, ValueError, TypeError) as e:
            result.invalid_characteristics.append(characteristic)
            result.errors.append(f"Invalid characteristic {uuid}: {e}")

    # Determine overall health status
    self._determine_health_status(result, len(self.get_required_characteristics()), strict)

    return result

All service definitions inherit from BaseGattService.

Registries

CharacteristicRegistry

Encapsulates all GATT characteristic registry operations.

Use CharacteristicRegistry to register custom characteristics.

GattServiceRegistry

Registry for all supported GATT services.

Use GattServiceRegistry to register custom services.

Common Characteristic Examples

Battery Level

from bluetooth_sig.gatt.characteristics import BatteryLevelCharacteristic

char = BatteryLevelCharacteristic()
value = char.decode_value(bytearray([85]))
print(f"Battery: {value}%")  # Battery: 85%

Temperature

from bluetooth_sig.gatt.characteristics import TemperatureCharacteristic

char = TemperatureCharacteristic()
value = char.decode_value(bytearray([0x64, 0x09]))
print(f"Temperature: {value}°C")  # Temperature: 24.36°C

Humidity

from bluetooth_sig.gatt.characteristics import HumidityCharacteristic

char = HumidityCharacteristic()
value = char.decode_value(bytearray([0x3A, 0x13]))
print(f"Humidity: {value}%")  # Humidity: 49.42%

Exceptions

InsufficientDataError

Raised when data is too short for the characteristic.

from bluetooth_sig.gatt.exceptions import InsufficientDataError

try:
    char.decode_value(bytearray([]))  # Empty
except InsufficientDataError as e:
    print(f"Error: {e}")

ValueRangeError

Raised when value is outside valid range.

from bluetooth_sig.gatt.exceptions import ValueRangeError

try:
    char.decode_value(bytearray([150]))  # > 100%
except ValueRangeError as e:
    print(f"Error: {e}")

See Also