Coverage for src/bluetooth_sig/gatt/characteristics/cooking_sensor_common.py: 100%
31 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"""Shared typed sensor-value formats for Cookware Service sensor payloads."""
3from __future__ import annotations
5from typing import ClassVar
7import msgspec
9from bluetooth_sig.types.uuid import BluetoothUUID
11from ..constants import SIZE_UINT16, SIZE_UINT32
12from .base import BaseCharacteristic
13from .cooking_common import COOKING_TEMPERATURE, HUMIDITY
14from .pressure import PressureCharacteristic
16_PRESSURE = PressureCharacteristic()
19class CookingSensorValue(msgspec.Struct, frozen=True, kw_only=True):
20 """Typed sensor value selected by a Cooking Sensor Info UUID."""
22 sensor_uuid: BluetoothUUID
23 value: float
26class CookingSensorDataFormat(msgspec.Struct, frozen=True, kw_only=True):
27 """Permitted Cookware Sensor Data format from Assigned Numbers."""
29 characteristic: BaseCharacteristic[float]
30 value_size: int
33class CookingSensorFormats:
34 """Permitted sensor data formats for CWS."""
36 formats: ClassVar[dict[BluetoothUUID, CookingSensorDataFormat]] = {
37 COOKING_TEMPERATURE.uuid: CookingSensorDataFormat(characteristic=COOKING_TEMPERATURE, value_size=SIZE_UINT16),
38 HUMIDITY.uuid: CookingSensorDataFormat(characteristic=HUMIDITY, value_size=SIZE_UINT16),
39 _PRESSURE.uuid: CookingSensorDataFormat(characteristic=_PRESSURE, value_size=SIZE_UINT32),
40 }
42 @classmethod
43 def get(cls, sensor_uuid: BluetoothUUID) -> CookingSensorDataFormat:
44 """Return the permitted format for a Cooking Sensor Info UUID."""
45 sensor_format = cls.formats.get(sensor_uuid)
46 if sensor_format is None:
47 raise ValueError("Cooking Sensor Info UUID is not permitted for Cookware Sensor Data")
48 return sensor_format
51def parse_cooking_sensor_value(sensor_uuid: BluetoothUUID, data: bytes | bytearray) -> CookingSensorValue:
52 """Parse a CWS sensor value using the format selected by the sensor UUID."""
53 sensor_format = CookingSensorFormats.get(sensor_uuid)
54 if len(data) != sensor_format.value_size:
55 raise ValueError(
56 f"Sensor Data for UUID {sensor_uuid.short_form} must be {sensor_format.value_size} octets, got {len(data)}"
57 )
58 return CookingSensorValue(
59 sensor_uuid=sensor_uuid,
60 value=sensor_format.characteristic.parse_value(bytearray(data)),
61 )
64def encode_cooking_sensor_value(sensor_value: CookingSensorValue) -> bytearray:
65 """Encode a CWS sensor value using the format selected by its UUID."""
66 sensor_format = CookingSensorFormats.get(sensor_value.sensor_uuid)
67 return sensor_format.characteristic.build_value(sensor_value.value)