Coverage for src / bluetooth_sig / gatt / descriptors / server_characteristic_configuration.py: 79%
29 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 20:14 +0000
1"""Server Characteristic Configuration Descriptor implementation."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..characteristics.utils import DataParser
10from .base import BaseDescriptor
13class SCCDFlags(IntFlag):
14 """SCCD (Server Characteristic Configuration Descriptor) flags."""
16 BROADCASTS_ENABLED = 0x0001
19class SCCDData(msgspec.Struct, frozen=True, kw_only=True):
20 """SCCD (Server Characteristic Configuration Descriptor) data."""
22 broadcasts_enabled: bool
25class ServerCharacteristicConfigurationDescriptor(BaseDescriptor):
26 """Server Characteristic Configuration Descriptor (0x2903).
28 Controls server-side configuration for a characteristic.
29 Currently only supports broadcast enable/disable.
30 """
32 _writable = True # SCCD is always writable
34 def _has_structured_data(self) -> bool:
35 return True
37 def _get_data_format(self) -> str:
38 return "uint16"
40 def _parse_descriptor_value(self, data: bytes) -> SCCDData:
41 """Parse SCCD value into broadcast flags.
43 Args:
44 data: Raw bytes (should be 2 bytes for uint16)
46 Returns:
47 SCCDData with broadcast flags
49 Raises:
50 ValueError: If data is not exactly 2 bytes
51 """
52 if len(data) != 2:
53 raise ValueError(f"SCCD data must be exactly 2 bytes, got {len(data)}")
55 # Parse as little-endian uint16
56 value = DataParser.parse_int16(data, endian="little")
58 return SCCDData(
59 broadcasts_enabled=bool(value & SCCDFlags.BROADCASTS_ENABLED),
60 )
62 @staticmethod
63 def create_enable_broadcasts_value() -> bytes:
64 """Create value to enable broadcasts."""
65 return SCCDFlags.BROADCASTS_ENABLED.to_bytes(2, "little")
67 @staticmethod
68 def create_disable_broadcasts_value() -> bytes:
69 """Create value to disable broadcasts."""
70 return (0).to_bytes(2, "little")
72 def is_broadcasts_enabled(self, data: bytes) -> bool:
73 """Check if broadcasts are enabled."""
74 parsed = self._parse_descriptor_value(data)
75 return parsed.broadcasts_enabled