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
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-30 00:10 +0000
1"""Noise (Sound Pressure Level) characteristic implementation.
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"""
10from __future__ import annotations
12from typing import cast
14from ..context import CharacteristicContext
15from .base import BaseCharacteristic
16from .templates import Uint8Template
19class NoiseCharacteristic(BaseCharacteristic):
20 """Noise characteristic (0x2BE4) - Sound pressure level measurement.
22 Represents sound pressure level in decibels (dB SPL) per SIG specification.
23 Uses uint8 format with 1 dB resolution.
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 """
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
36 _characteristic_name = "Noise"
37 _template = Uint8Template()
38 _manual_unit: str | None = "dB SPL"
40 # Range constraints per SIG spec
41 min_value: int | float | None = 0
42 max_value: int | float | None = MAX_MEASURABLE_VALUE
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))
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
53 return raw_value
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)
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)
65 return super().encode_value(data)