Coverage for src/bluetooth_sig/gatt/characteristics/cookware_sensor_data.py: 98%
42 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-28 01:26 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-28 01:26 +0000
1"""Cookware Sensor Data characteristic (0x2C2C)."""
3from __future__ import annotations
5from enum import IntFlag
7import msgspec
9from ..constants import SIZE_UINT8
10from ..context import CharacteristicContext
11from ..descriptor_utils import get_descriptor_from_context
12from ..descriptors.cooking_sensor_info import CookingSensorInfoData, CookingSensorInfoDescriptor
13from .base import BaseCharacteristic
14from .cooking_common import validate_flags
15from .cooking_sensor_common import CookingSensorValue, encode_cooking_sensor_value, parse_cooking_sensor_value
16from .utils import DataParser
19class CookwareSensorStatus(IntFlag):
20 """Cookware Sensor Data status bits."""
22 NO_ERROR = 0
23 MEASURED_VALUE_OUT_OF_RANGE = 1 << 0
24 SENSOR_INTERNAL_ERROR = 1 << 1
27class CookwareSensorDataValue(msgspec.Struct, frozen=True, kw_only=True):
28 """Decoded Cookware Sensor Data payload.
30 The sensor data field's type is determined by the Cooking Sensor Info
31 descriptor UUID for the specific sensor instance.
32 """
34 sensor_status: CookwareSensorStatus
35 sensor_data: CookingSensorValue
38class CookwareSensorDataCharacteristic(BaseCharacteristic[CookwareSensorDataValue]):
39 """Cookware Sensor Data characteristic (0x2C2C).
41 org.bluetooth.characteristic.cookware_sensor_data
42 """
44 min_length = SIZE_UINT8
45 allow_variable_length = True
47 def _decode_value(
48 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
49 ) -> CookwareSensorDataValue:
50 sensor_status = CookwareSensorStatus(DataParser.parse_int8(data, 0, signed=False))
51 validate_flags(sensor_status, CookwareSensorStatus, "Sensor Status")
52 sensor_data = bytearray(data[SIZE_UINT8:])
53 sensor_info = _get_sensor_info(ctx)
54 if sensor_info is None:
55 raise ValueError("Cooking Sensor Info descriptor is required to decode Sensor Data")
56 return CookwareSensorDataValue(
57 sensor_status=sensor_status,
58 sensor_data=parse_cooking_sensor_value(sensor_info.sensor_uuid, sensor_data),
59 )
61 def _encode_value(self, data: CookwareSensorDataValue) -> bytearray:
62 validate_flags(data.sensor_status, CookwareSensorStatus, "Sensor Status")
63 result = bytearray()
64 result.extend(DataParser.encode_int8(int(data.sensor_status), signed=False))
65 result.extend(encode_cooking_sensor_value(data.sensor_data))
66 return result
69def _get_sensor_info(ctx: CharacteristicContext | None) -> CookingSensorInfoData | None:
70 """Return Cooking Sensor Info descriptor data from parse context, if present."""
71 descriptor_data = get_descriptor_from_context(ctx, CookingSensorInfoDescriptor)
72 if descriptor_data is None or not descriptor_data.parse_success:
73 return None
74 if isinstance(descriptor_data.value, CookingSensorInfoData):
75 return descriptor_data.value
76 raise ValueError("Cooking Sensor Info descriptor has unexpected value type")