Coverage for src / bluetooth_sig / gatt / characteristics / sleep_activity_instantaneous_data.py: 100%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-03 16:41 +0000

1"""Sleep Activity Instantaneous Data characteristic (0x2B41).""" 

2 

3from __future__ import annotations 

4 

5from enum import IntFlag 

6 

7import msgspec 

8 

9from ..context import CharacteristicContext 

10from .base import BaseCharacteristic 

11from .utils import DataParser 

12 

13 

14class SleepActivityInstantaneousFlags(IntFlag): 

15 """Flags for Sleep Activity Instantaneous Data (Table 3.17).""" 

16 

17 VISIBLE_LIGHT_LEVEL_PRESENT = 0x0001 

18 UV_LIGHT_LEVEL_PRESENT = 0x0002 

19 IR_LIGHT_LEVEL_PRESENT = 0x0004 

20 SLEEP_STAGE_PRESENT = 0x0008 

21 SLEEPING_HEART_RATE_PRESENT = 0x0010 

22 DEVICE_WORN = 0x8000 

23 

24 

25class SleepStage(IntFlag): 

26 """Sleep stage bitfield (Table 3.18).""" 

27 

28 WAKE = 0x000001 

29 SLEEP = 0x000002 

30 REM = 0x000004 

31 NON_REM = 0x000008 

32 LIGHT_SLEEP = 0x000010 

33 DEEP_SLEEP = 0x000020 

34 N1 = 0x000040 

35 N2 = 0x000080 

36 N3 = 0x000100 

37 N4 = 0x000200 

38 ACTIVE_SLEEP = 0x000400 

39 QUIET_SLEEP = 0x000800 

40 INTERMEDIATE_SLEEP = 0x001000 

41 AROUSAL = 0x002000 

42 UNKNOWN = 0x800000 

43 

44 

45_ADDITIONAL_DATA_START_OFFSET = 5 

46 

47 

48class SleepActivityInstantaneousData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods 

49 """Parsed data from Sleep Activity Instantaneous Data. 

50 

51 Contains flags, sleep state, and any additional optional 

52 field data as raw bytes. 

53 """ 

54 

55 flags: SleepActivityInstantaneousFlags 

56 sleep_stage: SleepStage 

57 additional_data: bytes = b"" 

58 

59 

60class SleepActivityInstantaneousDataCharacteristic( 

61 BaseCharacteristic[SleepActivityInstantaneousData], 

62): 

63 """Sleep Activity Instantaneous Data characteristic (0x2B41). 

64 

65 org.bluetooth.characteristic.sleep_activity_instantaneous_data 

66 

67 Instantaneous sleep activity data from the Physical Activity 

68 Monitor service. Contains the current sleep state and optional 

69 additional fields indicated by flags. 

70 """ 

71 

72 min_length = 5 # flags (uint16) + sleep_stage (uint24) 

73 allow_variable_length = True 

74 

75 def _decode_value( 

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

77 ) -> SleepActivityInstantaneousData: 

78 """Parse Sleep Activity Instantaneous Data. 

79 

80 Format: flags (uint16) + sleep_stage (uint24) + optional fields. 

81 """ 

82 flags = SleepActivityInstantaneousFlags(DataParser.parse_int16(data, 0, signed=False)) 

83 sleep_stage = SleepStage(DataParser.parse_int24(data, 2, signed=False)) 

84 additional_data = ( 

85 bytes(data[_ADDITIONAL_DATA_START_OFFSET:]) if len(data) > _ADDITIONAL_DATA_START_OFFSET else b"" 

86 ) 

87 

88 return SleepActivityInstantaneousData( 

89 flags=flags, 

90 sleep_stage=sleep_stage, 

91 additional_data=additional_data, 

92 ) 

93 

94 def _encode_value(self, data: SleepActivityInstantaneousData) -> bytearray: 

95 """Encode Sleep Activity Instantaneous Data to bytes.""" 

96 result = bytearray() 

97 result += DataParser.encode_int16(int(data.flags), signed=False) 

98 result += DataParser.encode_int24(int(data.sleep_stage), signed=False) 

99 result += bytearray(data.additional_data) 

100 return result