Coverage for src / bluetooth_sig / gatt / characteristics / unknown.py: 72%

18 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 20:14 +0000

1"""Unknown characteristic implementation for non-SIG characteristics.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from ...types import CharacteristicInfo 

8from ...types.gatt_enums import GattProperty 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11 

12 

13class UnknownCharacteristic(BaseCharacteristic[bytes]): 

14 """Generic characteristic implementation for unknown/non-SIG characteristics. 

15 

16 This class provides basic functionality for characteristics that are not 

17 defined in the Bluetooth SIG specification. It stores raw data without 

18 attempting to parse it into structured types. 

19 """ 

20 

21 # TODO handle better 

22 _is_base_class = True # Exclude from registry validation tests (requires info parameter) 

23 

24 def __init__( 

25 self, 

26 info: CharacteristicInfo, 

27 properties: list[GattProperty] | None = None, 

28 ) -> None: 

29 """Initialize an unknown characteristic. 

30 

31 Args: 

32 info: CharacteristicInfo object with UUID, name, unit, value_type 

33 properties: Runtime BLE properties discovered from device (optional) 

34 

35 Raises: 

36 ValueError: If UUID is invalid 

37 

38 """ 

39 # If no name provided, generate one from UUID 

40 if not info.name: 

41 info = CharacteristicInfo( 

42 uuid=info.uuid, 

43 name=f"Unknown Characteristic ({info.uuid})", 

44 unit=info.unit or "", 

45 value_type=info.value_type, 

46 ) 

47 

48 super().__init__(info=info, properties=properties) 

49 

50 def _decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> bytes: # Context type varies 

51 """Return raw bytes for unknown characteristics. 

52 

53 Args: 

54 data: Raw bytes from the characteristic read 

55 ctx: Optional context (ignored) 

56 

57 Returns: 

58 Raw bytes as-is 

59 

60 """ 

61 return bytes(data) 

62 

63 def _encode_value(self, data: Any) -> bytearray: # noqa: ANN401 # Accepts bytes-like objects 

64 """Encode data to bytes for unknown characteristics. 

65 

66 Args: 

67 data: Data to encode (must be bytes or bytearray) 

68 

69 Returns: 

70 Encoded bytes 

71 

72 Raises: 

73 ValueError: If data is not bytes/bytearray 

74 

75 """ 

76 if isinstance(data, (bytes, bytearray)): 

77 return bytearray(data) 

78 raise ValueError(f"Unknown characteristics require bytes data, got {type(data)}")