src.bluetooth_sig.gatt.characteristics.base¶
Base class for GATT characteristics.
This module implements the core characteristic parsing and encoding system for Bluetooth GATT characteristics, following official Bluetooth SIG specifications.
Architecture¶
The implementation uses a multi-stage pipeline for parsing and encoding:
- Parsing Pipeline (parse_value):
Length validation (pre-decode)
Raw integer extraction (little-endian per Bluetooth spec)
Special value detection (sentinel values like 0x8000)
Value decoding (via template or subclass override)
Range validation (post-decode)
Type validation
- Encoding Pipeline (build_value):
Type validation
Range validation
Value encoding (via template or subclass override)
Length validation (post-encode)
YAML Metadata Resolution¶
Characteristic metadata is automatically resolved from Bluetooth SIG YAML specifications:
UUID, name, value type from assigned numbers registry
Units, resolution, and scaling factors (M × 10^d + b formula)
Special sentinel values (e.g., 0x8000 = “value is not known”)
Validation ranges and length constraints
Manual overrides (_manual_unit, _special_values, etc.) should only be used for: - Fixing incomplete or incorrect SIG specifications - Custom characteristics not in official registry - Performance optimizations
Template Composition¶
Characteristics use templates for reusable parsing logic via composition:
- class TemperatureCharacteristic(BaseCharacteristic):
_template = Sint16Template(resolution=0.01, unit=”°C”) # No need to override decode_value() - template handles it
Subclasses only override decode_value() for custom logic that templates cannot handle. Templates take priority over YAML-derived extractors.
Validation Sources (Priority Order)¶
Descriptor Valid Range - Device-reported constraints (highest priority)
Class-level Attributes - Characteristic spec defaults (min_value, max_value)
YAML-derived Ranges - Bluetooth SIG specification ranges (fallback)
Special Values¶
Sentinel values (like 0x8000 for “unknown”) bypass range and type validation since they represent non-numeric states. The gss_special_values property handles both unsigned (0x8000) and signed (-32768) interpretations for compatibility with different parsing contexts.
Byte Order¶
All multi-byte values use little-endian encoding per Bluetooth Core Specification.
Attributes¶
Name | Description |
|---|---|
Classes¶
Name | Description |
|---|---|
Base class for all GATT characteristics. |
|
Metaclass to automatically handle template flags for characteristics. |
|
Resolves SIG characteristic information from YAML and registry. |
|
Configuration for characteristic validation constraints. |
Module Contents¶
- class src.bluetooth_sig.gatt.characteristics.base.BaseCharacteristic(info: src.bluetooth_sig.types.CharacteristicInfo | None = None, validation: ValidationConfig | None = None, properties: list[src.bluetooth_sig.types.gatt_enums.GattProperty] | None = None)¶
-
Base class for all GATT characteristics.
Generic over T, the return type of _decode_value().
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
- add_descriptor(descriptor: src.bluetooth_sig.gatt.descriptors.BaseDescriptor) None¶
Add a descriptor to this characteristic.
- Parameters:
descriptor – The descriptor instance to add
- build_value(data: T | src.bluetooth_sig.types.SpecialValueResult, validate: bool = True) bytearray¶
Encode value or special value to characteristic bytes.
- Parameters:
data – Value to encode (type T) or special value to encode
validate – Enable validation (type, range, length checks) Note: Special values bypass validation
- Returns:
Encoded bytes ready for BLE write
- Raises:
CharacteristicEncodeError – If encoding or validation fails
Examples
# Normal value data = char.build_value(37.5) # Returns: bytearray([0xAA, 0x0E])
# Special value (for testing/simulation) from bluetooth_sig.types import SpecialValueResult, SpecialValueType special = SpecialValueResult(
raw_value=0x8000, meaning=”value is not known”, value_type=SpecialValueType.NOT_KNOWN
) data = char.build_value(special) # Returns: bytearray([0x00, 0x80])
# With validation disabled (for debugging) data = char.build_value(200.0, validate=False) # Allows out-of-range
# Error handling try:
data = char.build_value(value)
- except CharacteristicEncodeError as e:
print(f”Encode failed: {e}”)
- can_notify() bool¶
Check if this characteristic supports notifications.
- Returns:
True if the characteristic has a CCCD descriptor, False otherwise
- encode_special(value_type: src.bluetooth_sig.types.SpecialValueType) bytearray¶
Encode a special value type to bytes (reverse lookup).
Raises ValueError if no raw value of that type is defined for this characteristic.
- encode_special_by_meaning(meaning: str) bytearray¶
Encode a special value by a partial meaning string match.
Raises ValueError if no matching special value is found.
- enhance_error_message_with_descriptors(base_message: str, ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None) str¶
Enhance error message with descriptor information for better debugging.
- Parameters:
base_message – Original error message
ctx – Characteristic context containing descriptors
- Returns:
Enhanced error message with descriptor context
- classmethod get_allows_sig_override() bool¶
Check if this characteristic class allows overriding SIG characteristics.
Custom characteristics that need to override official Bluetooth SIG characteristics must set _allows_sig_override = True as a class attribute.
- Returns:
True if SIG override is allowed, False otherwise.
- get_cccd() src.bluetooth_sig.gatt.descriptors.BaseDescriptor | None¶
Get the Client Characteristic Configuration Descriptor (CCCD).
- Returns:
CCCD descriptor instance if present, None otherwise
- classmethod get_class_uuid() src.bluetooth_sig.types.uuid.BluetoothUUID | None¶
Get the characteristic UUID for this class without creating an instance.
This is the public API for registry and other modules to resolve UUIDs.
- Returns:
BluetoothUUID if the class has a resolvable UUID, None otherwise.
- classmethod get_configured_info() src.bluetooth_sig.types.CharacteristicInfo | None¶
Get the class-level configured CharacteristicInfo.
This provides public access to the _configured_info attribute that is set by __init_subclass__ for custom characteristics.
- Returns:
CharacteristicInfo if configured, None otherwise
- get_context_characteristic(ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None, characteristic_name: src.bluetooth_sig.types.gatt_enums.CharacteristicName | str | type[BaseCharacteristic[Any]]) Any¶
Find a characteristic in a context by name or class.
Note
Returns
Anybecause the characteristic type is determined at runtime bycharacteristic_name. For type-safe access, use direct characteristic class instantiation instead of this lookup method.- Parameters:
ctx – Context containing other characteristics.
characteristic_name – Enum, string name, or characteristic class.
- Returns:
Parsed characteristic value if found, None otherwise.
- get_descriptor(uuid: str | src.bluetooth_sig.types.uuid.BluetoothUUID) src.bluetooth_sig.gatt.descriptors.BaseDescriptor | None¶
Get a descriptor by UUID.
- Parameters:
uuid – Descriptor UUID (string or BluetoothUUID)
- Returns:
Descriptor instance if found, None otherwise
- get_descriptor_from_context(ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None, descriptor_class: type[src.bluetooth_sig.gatt.descriptors.BaseDescriptor]) src.bluetooth_sig.types.registry.descriptor_types.DescriptorData | None¶
Get a descriptor of the specified type from the context.
- Parameters:
ctx – Characteristic context containing descriptors
descriptor_class – The descriptor class to look for (e.g., ValidRangeDescriptor)
- Returns:
DescriptorData if found, None otherwise
- get_descriptors() dict[str, src.bluetooth_sig.gatt.descriptors.BaseDescriptor]¶
Get all descriptors for this characteristic.
- Returns:
Dict mapping descriptor UUID strings to descriptor instances
- get_presentation_format_from_context(ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None) src.bluetooth_sig.gatt.descriptors.characteristic_presentation_format.CharacteristicPresentationFormatData | None¶
Get presentation format from descriptor context if available.
- Parameters:
ctx – Characteristic context containing descriptors
- Returns:
CharacteristicPresentationFormatData if present, None otherwise
- get_special_value_meaning(raw_value: int) str | None¶
Get the human-readable meaning of a special value.
- Parameters:
raw_value – The raw integer value to look up.
- Returns:
The meaning string (e.g., “value is not known”), or None if not special.
- get_special_value_type(raw_value: int) src.bluetooth_sig.types.SpecialValueType | None¶
Get the category of a special value.
- Parameters:
raw_value – The raw integer value to classify.
- Returns:
The SpecialValueType category, or None if not a special value.
- get_user_description_from_context(ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None) str | None¶
Get user description from descriptor context if available.
- Parameters:
ctx – Characteristic context containing descriptors
- Returns:
User description string if present, None otherwise
- get_valid_range_from_context(ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None) tuple[int | float, int | float] | None¶
Get valid range from descriptor context if available.
- Parameters:
ctx – Characteristic context containing descriptors
- Returns:
Tuple of (min, max) values if Valid Range descriptor present, None otherwise
- is_special_value(raw_value: int) bool¶
Check if a raw value is a special sentinel value.
Checks both manual overrides (_special_values class variable) and GSS-derived special values, with manual taking precedence.
- Parameters:
raw_value – The raw integer value to check.
- Returns:
True if this is a special sentinel value, False otherwise.
- classmethod matches_uuid(uuid: str | src.bluetooth_sig.types.uuid.BluetoothUUID) bool¶
Check if this characteristic matches the given UUID.
- parse_value(data: bytes | bytearray, ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None, validate: bool = True) T¶
Parse characteristic data.
Returns: Parsed value of type T :raises SpecialValueDetected: Special sentinel (0x8000=”unknown”, 0x7FFFFFFF=”NaN”) :raises CharacteristicParseError: Parse/validation failure
- validate_value_against_descriptor_range(value: int | float, ctx: src.bluetooth_sig.gatt.context.CharacteristicContext | None = None) bool¶
Validate a value against descriptor-defined valid range.
- Parameters:
value – Value to validate
ctx – Characteristic context containing descriptors
- Returns:
True if value is within valid range or no range defined, False otherwise
- property display_name: str¶
Get the display name for this characteristic.
Uses explicit _characteristic_name if set, otherwise falls back to class name.
- property gss_special_values: dict[int, str]¶
Get special values from GSS specification.
Extracts all special value definitions (e.g., 0x8000=”value is not known”) from the GSS YAML specification for this characteristic.
GSS stores values as unsigned hex (e.g., 0x8000). For signed types, this method also includes the signed interpretation so lookups work with both parsed signed values and raw unsigned values.
- Returns:
Dictionary mapping raw integer values to their human-readable meanings. Includes both unsigned and signed interpretations for applicable values.
- property info: src.bluetooth_sig.types.CharacteristicInfo¶
Characteristic information.
- properties: list[src.bluetooth_sig.types.gatt_enums.GattProperty] = None¶
- property size: int | None¶
Get the size in bytes for this characteristic from YAML specifications.
Returns the field size from YAML automation if available, otherwise None. This is useful for determining the expected data length for parsing and encoding.
- property spec: src.bluetooth_sig.types.registry.CharacteristicSpec | None¶
Get the full GSS specification with description and detailed metadata.
- property unit: str¶
Get the unit of measurement from _info.
Returns empty string for characteristics without units (e.g., bitfields).
- property uuid: src.bluetooth_sig.types.uuid.BluetoothUUID¶
Get the characteristic UUID from _info.
- value_type: src.bluetooth_sig.types.gatt_enums.ValueType¶
- property value_type_resolved: src.bluetooth_sig.types.gatt_enums.ValueType¶
Get the value type from _info.
- class src.bluetooth_sig.gatt.characteristics.base.CharacteristicMeta¶
Bases:
abc.ABCMetaMetaclass to automatically handle template flags for characteristics.
- class src.bluetooth_sig.gatt.characteristics.base.SIGCharacteristicResolver¶
Resolves SIG characteristic information from YAML and registry.
This class handles all SIG characteristic resolution logic, separating concerns from the BaseCharacteristic constructor. Uses shared utilities from the resolver module to avoid code duplication.
- static resolve_for_class(char_class: type[BaseCharacteristic[Any]]) src.bluetooth_sig.types.CharacteristicInfo¶
Resolve CharacteristicInfo for a SIG characteristic class.
- Parameters:
char_class – The characteristic class to resolve info for
- Returns:
CharacteristicInfo with resolved UUID, name, value_type, unit
- Raises:
UUIDResolutionError – If no UUID can be resolved for the class
- static resolve_from_registry(char_class: type[BaseCharacteristic[Any]]) src.bluetooth_sig.types.CharacteristicInfo | None¶
Fallback to registry resolution using shared search strategy.
- static resolve_yaml_spec_for_class(char_class: type[BaseCharacteristic[Any]]) src.bluetooth_sig.types.registry.CharacteristicSpec | None¶
Resolve YAML spec for a characteristic class using shared name variant logic.
- camel_case_to_display_name¶
- class src.bluetooth_sig.gatt.characteristics.base.ValidationConfig¶
Bases:
msgspec.StructConfiguration for characteristic validation constraints.
Groups validation parameters into a single, optional configuration object to simplify BaseCharacteristic constructor signatures.
- src.bluetooth_sig.gatt.characteristics.base.T¶