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
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-03 16:41 +0000
1"""Sleep Activity Instantaneous Data characteristic (0x2B41)."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..context import CharacteristicContext
10from .base import BaseCharacteristic
11from .utils import DataParser
14class SleepActivityInstantaneousFlags(IntFlag):
15 """Flags for Sleep Activity Instantaneous Data (Table 3.17)."""
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
25class SleepStage(IntFlag):
26 """Sleep stage bitfield (Table 3.18)."""
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
45_ADDITIONAL_DATA_START_OFFSET = 5
48class SleepActivityInstantaneousData(msgspec.Struct, frozen=True, kw_only=True): # pylint: disable=too-few-public-methods
49 """Parsed data from Sleep Activity Instantaneous Data.
51 Contains flags, sleep state, and any additional optional
52 field data as raw bytes.
53 """
55 flags: SleepActivityInstantaneousFlags
56 sleep_stage: SleepStage
57 additional_data: bytes = b""
60class SleepActivityInstantaneousDataCharacteristic(
61 BaseCharacteristic[SleepActivityInstantaneousData],
62):
63 """Sleep Activity Instantaneous Data characteristic (0x2B41).
65 org.bluetooth.characteristic.sleep_activity_instantaneous_data
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 """
72 min_length = 5 # flags (uint16) + sleep_stage (uint24)
73 allow_variable_length = True
75 def _decode_value(
76 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
77 ) -> SleepActivityInstantaneousData:
78 """Parse Sleep Activity Instantaneous Data.
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 )
88 return SleepActivityInstantaneousData(
89 flags=flags,
90 sleep_stage=sleep_stage,
91 additional_data=additional_data,
92 )
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