Coverage for src/bluetooth_sig/gatt/characteristics/noise.py: 93%

30 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 00:10 +0000

1"""Noise (Sound Pressure Level) characteristic implementation. 

2 

3Per Bluetooth SIG specification (UUID 0x2BE4): 

4- Data type: uint8 (1 byte) 

5- Unit: decibel SPL with 1 dB resolution 

6- Range: 0-253 dB 

7- Special values: 0xFE (≥254 dB), 0xFF (unknown) 

8""" 

9 

10from __future__ import annotations 

11 

12from typing import cast 

13 

14from ..context import CharacteristicContext 

15from .base import BaseCharacteristic 

16from .templates import Uint8Template 

17 

18 

19class NoiseCharacteristic(BaseCharacteristic): 

20 """Noise characteristic (0x2BE4) - Sound pressure level measurement. 

21 

22 Represents sound pressure level in decibels (dB SPL) per SIG specification. 

23 Uses uint8 format with 1 dB resolution. 

24 

25 Valid range: 0-253 dB 

26 Special values: 

27 - 0xFE (254): Value is 254 dB or greater 

28 - 0xFF (255): Value is not known 

29 """ 

30 

31 UNKNOWN_VALUE = 0xFF # Value is not known 

32 MAX_OR_GREATER_VALUE = 0xFE # 254 dB or greater 

33 MAX_MEASURABLE_VALUE = 254 # Maximum measurable value in dB 

34 MAX_NORMAL_VALUE = 253 # Maximum normal range value 

35 

36 _characteristic_name = "Noise" 

37 _template = Uint8Template() 

38 _manual_unit: str | None = "dB SPL" 

39 

40 # Range constraints per SIG spec 

41 min_value: int | float | None = 0 

42 max_value: int | float | None = MAX_MEASURABLE_VALUE 

43 

44 def decode_value(self, data: bytearray, ctx: CharacteristicContext | None = None) -> int | None: 

45 """Decode noise level with special value handling.""" 

46 raw_value = cast(int, super().decode_value(data, ctx)) 

47 

48 if raw_value == self.MAX_OR_GREATER_VALUE: 

49 return self.MAX_MEASURABLE_VALUE # Return minimum of "254 or greater" range 

50 if raw_value == self.UNKNOWN_VALUE: 

51 return None # Represent unknown as None 

52 

53 return raw_value 

54 

55 def encode_value(self, data: int | None) -> bytearray: 

56 """Encode noise level with special value handling.""" 

57 if data is None: 

58 return super().encode_value(self.UNKNOWN_VALUE) 

59 

60 if data < 0: 

61 raise ValueError("Noise level cannot be negative") 

62 if data >= self.MAX_MEASURABLE_VALUE: 

63 return super().encode_value(self.MAX_OR_GREATER_VALUE) 

64 

65 return super().encode_value(data)