Coverage for src / bluetooth_sig / gatt / characteristics / pnp_id.py: 100%
28 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-18 11:17 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-18 11:17 +0000
1"""PnP ID characteristic implementation."""
3from __future__ import annotations
5from enum import IntEnum
7import msgspec
9from ...types.gatt_enums import CharacteristicRole
10from ..context import CharacteristicContext
11from .base import BaseCharacteristic
12from .utils import DataParser
15class VendorIdSource(IntEnum):
16 """Vendor ID Source enumeration.
18 Defines the namespace for the Vendor ID field.
19 """
21 RESERVED_0 = 0 # Reserved for Future Use
22 BLUETOOTH_SIG = 1 # Bluetooth SIG Assigned Company Identifier
23 USB_IF = 2 # USB Implementer's Forum assigned Vendor ID
26class PnpIdData(msgspec.Struct, frozen=True, kw_only=True):
27 """PnP ID data.
29 Attributes:
30 vendor_id_source: Vendor ID source namespace
31 vendor_id: Vendor ID from the specified namespace
32 product_id: Manufacturer managed identifier
33 product_version: Manufacturer managed version
34 """
36 vendor_id_source: VendorIdSource
37 vendor_id: int
38 product_id: int
39 product_version: int
42class PnpIdCharacteristic(BaseCharacteristic[PnpIdData]):
43 """PnP ID characteristic (0x2A50).
45 org.bluetooth.characteristic.pnp_id
47 Contains PnP ID information (7 bytes).
48 """
50 _manual_role = CharacteristicRole.INFO
51 expected_length = 7
53 def _decode_value(
54 self, data: bytearray, ctx: CharacteristicContext | None = None, *, validate: bool = True
55 ) -> PnpIdData:
56 """Parse PnP ID.
58 Args:
59 data: Raw bytearray (7 bytes).
60 ctx: Optional CharacteristicContext.
61 validate: Whether to validate ranges (default True)
63 Returns:
64 PnpIdData with vendor_id_source, vendor_id, product_id, product_version.
65 """
66 return PnpIdData(
67 vendor_id_source=VendorIdSource(data[0]),
68 vendor_id=DataParser.parse_int16(data, 1, signed=False),
69 product_id=DataParser.parse_int16(data, 3, signed=False),
70 product_version=DataParser.parse_int16(data, 5, signed=False),
71 )
73 def _encode_value(self, data: PnpIdData) -> bytearray:
74 """Encode PnP ID.
76 Args:
77 data: PnpIdData to encode
79 Returns:
80 Encoded bytes
81 """
82 result = bytearray()
83 result.append(int(data.vendor_id_source))
84 result.extend(DataParser.encode_int16(data.vendor_id, signed=False))
85 result.extend(DataParser.encode_int16(data.product_id, signed=False))
86 result.extend(DataParser.encode_int16(data.product_version, signed=False))
87 return result