Coverage for src / bluetooth_sig / gatt / characteristics / device_time_parameters.py: 98%
46 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"""Device Time Parameters characteristic (0x2B8F).
3Per DTS v1.0 Table 3.4, the DT Parameters characteristic is 2-12 octets
4depending on which optional features are supported:
6 Field Condition Type Octets Unit
7 E2E_CRC C.1 uint16 0 or 2 None
8 RTC_Resolution M uint16 2 1/65,536 Second
9 Max_RTC_Drift_Limit C.2 uint16 0 or 2 Seconds
10 Max_Days_Until_Sync_Loss C.2 uint16 0 or 2 Days
11 Non_Logged_Time_Adjustment_Limit C.3 uint16 0 or 2 Seconds
12 Displayed_Formats C.4 uint16 0 or 2 N/A
14(C.1=E2E-CRC feature; C.2=RTC Drift Tracking feature;
15 C.3=Time Change Logging feature; C.4=Displayed Formats feature)
17References:
18 Bluetooth SIG Device Time Service v1.0, Table 3.4
19"""
21from __future__ import annotations
23import msgspec
25from ..context import CharacteristicContext
26from .base import BaseCharacteristic
27from .utils import DataParser
29_MIN_LENGTH = 2
32class DeviceTimeParametersData(msgspec.Struct, frozen=True, kw_only=True):
33 """Parsed data from Device Time Parameters characteristic.
35 Attributes:
36 rtc_resolution: RTC resolution in 1/65,536-second units (0=unknown).
37 max_rtc_drift_limit: Max drift before sync loss, in seconds (optional).
38 max_days_until_sync_loss: Max days without sync before loss (optional).
39 non_logged_time_adjustment_limit: Non-logged adjustment limit in
40 seconds; below this value Base_Time changes are not logged (optional).
41 displayed_formats: Device displayed date/time format encoding (optional).
42 """
44 rtc_resolution: int
45 max_rtc_drift_limit: int | None = None
46 max_days_until_sync_loss: int | None = None
47 non_logged_time_adjustment_limit: int | None = None
48 displayed_formats: int | None = None
51class DeviceTimeParametersCharacteristic(BaseCharacteristic[DeviceTimeParametersData]):
52 """Device Time Parameters characteristic (0x2B8F).
54 org.bluetooth.characteristic.device_time_parameters
56 Reveals the Server's capabilities and behavioural thresholds for the
57 Device Time Service. The characteristic is 2-12 octets in length
58 depending on which optional features are supported.
59 """
61 min_length = _MIN_LENGTH
62 allow_variable_length = True
64 def _decode_value(
65 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
66 ) -> DeviceTimeParametersData:
67 rtc_resolution = DataParser.parse_int16(data, 0, signed=False)
69 offset = 2
70 max_rtc_drift_limit: int | None = None
71 max_days_until_sync_loss: int | None = None
72 non_logged_time_adjustment_limit: int | None = None
73 displayed_formats: int | None = None
75 if len(data) >= offset + 2:
76 max_rtc_drift_limit = DataParser.parse_int16(data, offset, signed=False)
77 offset += 2
79 if len(data) >= offset + 2:
80 max_days_until_sync_loss = DataParser.parse_int16(data, offset, signed=False)
81 offset += 2
83 if len(data) >= offset + 2:
84 non_logged_time_adjustment_limit = DataParser.parse_int16(data, offset, signed=False)
85 offset += 2
87 if len(data) >= offset + 2:
88 displayed_formats = DataParser.parse_int16(data, offset, signed=False)
90 return DeviceTimeParametersData(
91 rtc_resolution=rtc_resolution,
92 max_rtc_drift_limit=max_rtc_drift_limit,
93 max_days_until_sync_loss=max_days_until_sync_loss,
94 non_logged_time_adjustment_limit=non_logged_time_adjustment_limit,
95 displayed_formats=displayed_formats,
96 )
98 def _encode_value(self, data: DeviceTimeParametersData) -> bytearray:
99 result = bytearray()
100 result.extend(DataParser.encode_int16(data.rtc_resolution, signed=False))
101 if data.max_rtc_drift_limit is not None:
102 result.extend(DataParser.encode_int16(data.max_rtc_drift_limit, signed=False))
103 if data.max_days_until_sync_loss is not None:
104 result.extend(DataParser.encode_int16(data.max_days_until_sync_loss, signed=False))
105 if data.non_logged_time_adjustment_limit is not None:
106 result.extend(DataParser.encode_int16(data.non_logged_time_adjustment_limit, signed=False))
107 if data.displayed_formats is not None:
108 result.extend(DataParser.encode_int16(data.displayed_formats, signed=False))
109 return result