Coverage for src / bluetooth_sig / gatt / characteristics / general_activity_summary_data.py: 100%
60 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"""General Activity Summary Data characteristic (0x2B3D).
3Reports summary statistics for a general activity period.
5References:
6 Bluetooth SIG Physical Activity Monitor Service 1.0
7"""
9from __future__ import annotations
11from enum import IntFlag
13import msgspec
15from ..context import CharacteristicContext
16from .base import BaseCharacteristic
17from .utils import DataParser
20class GeneralActivitySummaryFlags(IntFlag):
21 """General Activity Summary Data flags (32-bit, 4 octets)."""
23 NORMAL_WALKING_EE_PRESENT = 0x00000001
24 INTENSITY_EE_PRESENT = 0x00000002
25 TOTAL_EE_PRESENT = 0x00000004
26 FAT_BURNED_PRESENT = 0x00000008
27 MIN_METABOLIC_EQUIVALENT_PRESENT = 0x00000010
28 MAX_METABOLIC_EQUIVALENT_PRESENT = 0x00000020
29 AVG_METABOLIC_EQUIVALENT_PRESENT = 0x00000040
30 DISTANCE_PRESENT = 0x00000080
31 MIN_SPEED_PRESENT = 0x00000100
32 MAX_SPEED_PRESENT = 0x00000200
33 AVG_SPEED_PRESENT = 0x00000400
34 DURATION_NORMAL_WALKING_PRESENT = 0x00000800
35 DURATION_INTENSITY_WALKING_PRESENT = 0x00001000
36 MIN_MOTION_CADENCE_PRESENT = 0x00002000
37 MAX_MOTION_CADENCE_PRESENT = 0x00004000
38 AVG_MOTION_CADENCE_PRESENT = 0x00008000
39 FLOORS_PRESENT = 0x00010000
40 POSITIVE_ELEVATION_GAIN_PRESENT = 0x00020000
41 NEGATIVE_ELEVATION_GAIN_PRESENT = 0x00040000
42 ACTIVITY_COUNT_PRESENT = 0x00080000
43 MIN_ACTIVITY_LEVEL_PRESENT = 0x00100000
44 MAX_ACTIVITY_LEVEL_PRESENT = 0x00200000
45 AVG_ACTIVITY_LEVEL_PRESENT = 0x00400000
46 AVG_ACTIVITY_TYPE_PRESENT = 0x00800000
47 WORN_DURATION_PRESENT = 0x01000000
50class GeneralActivitySummaryData(msgspec.Struct, frozen=True, kw_only=True):
51 """Parsed data from General Activity Summary Data characteristic.
53 Attributes:
54 header: Segmentation header byte.
55 flags: Presence flags for optional fields (32-bit).
56 session_id: Session identifier (uint16).
57 sub_session_id: Sub-session identifier (uint16).
58 relative_timestamp: Relative timestamp in seconds (uint32).
59 sequence_number: Sequence number (uint32).
61 """
63 header: int
64 flags: GeneralActivitySummaryFlags
65 session_id: int
66 sub_session_id: int
67 relative_timestamp: int
68 sequence_number: int
71class GeneralActivitySummaryDataCharacteristic(BaseCharacteristic[GeneralActivitySummaryData]):
72 """General Activity Summary Data characteristic (0x2B3D).
74 org.bluetooth.characteristic.general_activity_summary_data
76 Reports summary statistics for a general activity period.
77 """
79 min_length = 17 # header(1) + flags(4) + session(2) + subsession(2) + timestamp(4) + sequence(4)
80 allow_variable_length = True
82 def _decode_value(
83 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
84 ) -> GeneralActivitySummaryData:
85 """Parse General Activity Summary Data.
87 Format: Header (uint8) + Flags (uint32) + SessionID (uint16)
88 + SubSessionID (uint16) + RelativeTimestamp (uint32)
89 + SequenceNumber (uint32) + [optional fields].
90 """
91 header = DataParser.parse_int8(data, 0, signed=False)
92 flags_raw = DataParser.parse_int32(data, 1, signed=False)
93 flags = GeneralActivitySummaryFlags(flags_raw)
94 session_id = DataParser.parse_int16(data, 5, signed=False)
95 sub_session_id = DataParser.parse_int16(data, 7, signed=False)
96 relative_timestamp = DataParser.parse_int32(data, 9, signed=False)
97 sequence_number = DataParser.parse_int32(data, 13, signed=False)
99 return GeneralActivitySummaryData(
100 header=header,
101 flags=flags,
102 session_id=session_id,
103 sub_session_id=sub_session_id,
104 relative_timestamp=relative_timestamp,
105 sequence_number=sequence_number,
106 )
108 def _encode_value(self, data: GeneralActivitySummaryData) -> bytearray:
109 """Encode General Activity Summary data."""
110 result = bytearray()
111 result.extend(DataParser.encode_int8(data.header, signed=False))
112 result.extend(DataParser.encode_int32(int(data.flags), signed=False))
113 result.extend(DataParser.encode_int16(data.session_id, signed=False))
114 result.extend(DataParser.encode_int16(data.sub_session_id, signed=False))
115 result.extend(DataParser.encode_int32(data.relative_timestamp, signed=False))
116 result.extend(DataParser.encode_int32(data.sequence_number, signed=False))
117 return result