Coverage for src / bluetooth_sig / gatt / characteristics / physical_activity_session_descriptor.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"""Physical Activity Session Descriptor characteristic (0x2B45).
3Describes properties of a completed or ongoing activity session.
5References:
6 Bluetooth SIG Physical Activity Monitor Service 1.0
7"""
9from __future__ import annotations
11from enum import IntEnum
13import msgspec
15from ..context import CharacteristicContext
16from .base import BaseCharacteristic
17from .utils import DataParser
20class PAMSessionStatus(IntEnum):
21 """Physical Activity session status."""
23 COMPLETE = 0x00
24 IN_PROGRESS = 0x01
25 PAUSED = 0x02
28class ActivityType(IntEnum):
29 """Physical Activity session activity type (PAMS v1.0 Table 3.7)."""
31 UNSPECIFIED = 0x00
32 OTHER = 0x01
33 SIT = 0x02
34 LIE = 0x03
35 STAND = 0x04
36 WALK = 0x05
37 SHUFFLE = 0x06
38 RUN = 0x07
39 CYCLE_INDOOR = 0x08
40 CYCLE_OUTDOOR = 0x09
41 CYCLE = 0x0A
42 AEROBIC_WORKOUT = 0x0B
43 ELLIPTICAL = 0x0C
44 SPORTS = 0x0D
45 SWIM = 0x0E
46 UNKNOWN = 0xFF
49class PhysicalActivitySessionDescriptorData(msgspec.Struct, frozen=True, kw_only=True):
50 """Parsed data from Physical Activity Session Descriptor characteristic.
52 Attributes:
53 session_id: Session identifier.
54 session_status: Status of the session.
55 activity_type: Type of activity for this session (raw uint8).
56 additional_data: Any additional descriptor bytes.
58 """
60 session_id: int
61 session_status: PAMSessionStatus
62 activity_type: ActivityType
63 additional_data: bytes = b""
66class PhysicalActivitySessionDescriptorCharacteristic(BaseCharacteristic[PhysicalActivitySessionDescriptorData]):
67 """Physical Activity Session Descriptor characteristic (0x2B45).
69 org.bluetooth.characteristic.physical_activity_session_descriptor
71 Describes properties of an activity session.
72 """
74 min_length = 4 # session_id(2) + status(1) + activity_type(1)
75 allow_variable_length = True
77 def _decode_value(
78 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
79 ) -> PhysicalActivitySessionDescriptorData:
80 """Parse Physical Activity Session Descriptor data.
82 Format: SessionID (uint16 LE) + SessionStatus (uint8) + ActivityType (uint8)
83 + AdditionalData (variable).
84 """
85 session_id = DataParser.parse_int16(data, 0, signed=False)
86 session_status = PAMSessionStatus(DataParser.parse_int8(data, 2, signed=False))
87 activity_type = ActivityType(DataParser.parse_int8(data, 3, signed=False))
88 additional_data = bytes(data[4:])
90 return PhysicalActivitySessionDescriptorData(
91 session_id=session_id,
92 session_status=session_status,
93 activity_type=activity_type,
94 additional_data=additional_data,
95 )
97 def _encode_value(self, data: PhysicalActivitySessionDescriptorData) -> bytearray:
98 """Encode Physical Activity Session Descriptor data."""
99 result = bytearray()
100 result.extend(DataParser.encode_int16(data.session_id, signed=False))
101 result.extend(DataParser.encode_int8(int(data.session_status), signed=False))
102 result.extend(DataParser.encode_int8(int(data.activity_type), signed=False))
103 result.extend(data.additional_data)
104 return result