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

22 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-18 11:17 +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 ..context import CharacteristicContext 

9from .base import BaseCharacteristic 

10 

11 

12class UnknownCharacteristic(BaseCharacteristic[bytes]): 

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

14 

15 This class provides basic functionality for characteristics that are not 

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

17 attempting to parse it into structured types. 

18 """ 

19 

20 # NOTE: Exempt from registry validation — UnknownCharacteristic has no fixed UUID 

21 _is_base_class = True 

22 

23 _UNKNOWN_PREFIX = "Unknown: " 

24 

25 def __init__( 

26 self, 

27 info: CharacteristicInfo, 

28 ) -> None: 

29 """Initialize an unknown characteristic. 

30 

31 The name is normalised to ``"Unknown: <description>"`` format. 

32 If no name is provided, the UUID short form is used as the 

33 description. 

34 

35 Args: 

36 info: CharacteristicInfo object with UUID, name, unit, python_type 

37 

38 Raises: 

39 ValueError: If UUID is invalid 

40 

41 """ 

42 name = info.name.strip() if info.name else "" 

43 if not name: 

44 name = f"{self._UNKNOWN_PREFIX}{info.uuid.short_form}" 

45 elif not name.startswith(self._UNKNOWN_PREFIX): 

46 name = f"{self._UNKNOWN_PREFIX}{name}" 

47 

48 info = CharacteristicInfo( 

49 uuid=info.uuid, 

50 name=name, 

51 unit=info.unit or "", 

52 python_type=info.python_type, 

53 ) 

54 

55 super().__init__(info=info) 

56 

57 def _decode_value( 

58 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True 

59 ) -> bytes: # Context type varies 

60 """Return raw bytes for unknown characteristics. 

61 

62 Args: 

63 data: Raw bytes from the characteristic read 

64 ctx: Optional context (ignored) 

65 validate: Whether to validate ranges (default True) 

66 

67 Returns: 

68 Raw bytes as-is 

69 

70 """ 

71 return bytes(data) 

72 

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

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

75 

76 Args: 

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

78 

79 Returns: 

80 Encoded bytes 

81 

82 Raises: 

83 ValueError: If data is not bytes/bytearray 

84 

85 """ 

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

87 return bytearray(data) 

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