mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
add support for data type classes
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -104,5 +104,8 @@
|
|||||||
"python.testing.unittestEnabled": false,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
"python-envs.defaultEnvManager": "ms-python.python:system",
|
"python-envs.defaultEnvManager": "ms-python.python:system",
|
||||||
"python-envs.pythonProjects": []
|
"python-envs.pythonProjects": [],
|
||||||
|
"nrf-connect.applications": [
|
||||||
|
"${workspaceFolder}/extras/zephyr/hci_usb"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import bumble.device
|
|||||||
import bumble.logging
|
import bumble.logging
|
||||||
import bumble.transport
|
import bumble.transport
|
||||||
import bumble.utils
|
import bumble.utils
|
||||||
from bumble import company_ids, core, gatt, hci
|
from bumble import company_ids, core, data_types, gatt, hci
|
||||||
from bumble.audio import io as audio_io
|
from bumble.audio import io as audio_io
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.profiles import bap, bass, le_audio, pbp
|
from bumble.profiles import bap, bass, le_audio, pbp
|
||||||
@@ -859,20 +859,12 @@ async def run_transmit(
|
|||||||
)
|
)
|
||||||
broadcast_audio_announcement = bap.BroadcastAudioAnnouncement(broadcast_id)
|
broadcast_audio_announcement = bap.BroadcastAudioAnnouncement(broadcast_id)
|
||||||
|
|
||||||
advertising_manufacturer_data = (
|
advertising_data_types: list[core.DataType] = [
|
||||||
b''
|
data_types.BroadcastName(broadcast_name)
|
||||||
if manufacturer_data is None
|
|
||||||
else bytes(
|
|
||||||
core.AdvertisingData(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
core.AdvertisingData.MANUFACTURER_SPECIFIC_DATA,
|
|
||||||
struct.pack('<H', manufacturer_data[0])
|
|
||||||
+ manufacturer_data[1],
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
)
|
if manufacturer_data is not None:
|
||||||
)
|
advertising_data_types.append(
|
||||||
|
data_types.ManufacturerSpecificData(*manufacturer_data)
|
||||||
)
|
)
|
||||||
|
|
||||||
advertising_set = await device.create_advertising_set(
|
advertising_set = await device.create_advertising_set(
|
||||||
@@ -885,12 +877,7 @@ async def run_transmit(
|
|||||||
),
|
),
|
||||||
advertising_data=(
|
advertising_data=(
|
||||||
broadcast_audio_announcement.get_advertising_data()
|
broadcast_audio_announcement.get_advertising_data()
|
||||||
+ bytes(
|
+ bytes(core.AdvertisingData(advertising_data_types))
|
||||||
core.AdvertisingData(
|
|
||||||
[(core.AdvertisingData.BROADCAST_NAME, broadcast_name.encode())]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
+ advertising_manufacturer_data
|
|
||||||
),
|
),
|
||||||
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
|
||||||
periodic_advertising_interval_min=80,
|
periodic_advertising_interval_min=80,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import click
|
|||||||
|
|
||||||
import bumble
|
import bumble
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
from bumble import utils
|
from bumble import data_types, utils
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import AdvertisingParameters, CisLink, Device, DeviceConfiguration
|
from bumble.device import AdvertisingParameters, CisLink, Device, DeviceConfiguration
|
||||||
@@ -330,22 +330,13 @@ class Speaker:
|
|||||||
advertising_data = bytes(
|
advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName(device_config.name),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(
|
||||||
bytes(device_config.name, 'utf-8'),
|
AdvertisingData.Flags.LE_GENERAL_DISCOVERABLE_MODE
|
||||||
|
| AdvertisingData.Flags.BR_EDR_NOT_SUPPORTED
|
||||||
),
|
),
|
||||||
(
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
AdvertisingData.FLAGS,
|
[pacs.PublishedAudioCapabilitiesService.UUID]
|
||||||
bytes(
|
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
|
||||||
| AdvertisingData.BR_EDR_NOT_SUPPORTED_FLAG
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(pacs.PublishedAudioCapabilitiesService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
41
apps/pair.py
41
apps/pair.py
@@ -23,6 +23,7 @@ import struct
|
|||||||
import click
|
import click
|
||||||
from prompt_toolkit.shortcuts import PromptSession
|
from prompt_toolkit.shortcuts import PromptSession
|
||||||
|
|
||||||
|
from bumble import data_types
|
||||||
from bumble.a2dp import make_audio_sink_service_sdp_records
|
from bumble.a2dp import make_audio_sink_service_sdp_records
|
||||||
from bumble.att import (
|
from bumble.att import (
|
||||||
ATT_INSUFFICIENT_AUTHENTICATION_ERROR,
|
ATT_INSUFFICIENT_AUTHENTICATION_ERROR,
|
||||||
@@ -34,6 +35,7 @@ from bumble.core import (
|
|||||||
UUID,
|
UUID,
|
||||||
AdvertisingData,
|
AdvertisingData,
|
||||||
Appearance,
|
Appearance,
|
||||||
|
DataType,
|
||||||
PhysicalTransport,
|
PhysicalTransport,
|
||||||
ProtocolError,
|
ProtocolError,
|
||||||
)
|
)
|
||||||
@@ -506,33 +508,21 @@ async def pair(
|
|||||||
if mode == 'dual':
|
if mode == 'dual':
|
||||||
flags |= AdvertisingData.Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
|
flags |= AdvertisingData.Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
|
||||||
|
|
||||||
ad_structs = [
|
advertising_data_types: list[DataType] = [
|
||||||
(
|
data_types.Flags(flags),
|
||||||
AdvertisingData.FLAGS,
|
data_types.CompleteLocalName('Bumble'),
|
||||||
bytes([flags]),
|
|
||||||
),
|
|
||||||
(AdvertisingData.COMPLETE_LOCAL_NAME, 'Bumble'.encode()),
|
|
||||||
]
|
]
|
||||||
if service_uuids_16:
|
if service_uuids_16:
|
||||||
ad_structs.append(
|
advertising_data_types.append(
|
||||||
(
|
data_types.IncompleteListOf16BitServiceUUIDs(service_uuids_16)
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
b"".join(bytes(uuid) for uuid in service_uuids_16),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if service_uuids_32:
|
if service_uuids_32:
|
||||||
ad_structs.append(
|
advertising_data_types.append(
|
||||||
(
|
data_types.IncompleteListOf32BitServiceUUIDs(service_uuids_32)
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
b"".join(bytes(uuid) for uuid in service_uuids_32),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if service_uuids_128:
|
if service_uuids_128:
|
||||||
ad_structs.append(
|
advertising_data_types.append(
|
||||||
(
|
data_types.IncompleteListOf128BitServiceUUIDs(service_uuids_128)
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
b"".join(bytes(uuid) for uuid in service_uuids_128),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if advertise_appearance:
|
if advertise_appearance:
|
||||||
@@ -559,13 +549,10 @@ async def pair(
|
|||||||
advertise_appearance_int = int(
|
advertise_appearance_int = int(
|
||||||
Appearance(category_enum, subcategory_enum)
|
Appearance(category_enum, subcategory_enum)
|
||||||
)
|
)
|
||||||
ad_structs.append(
|
advertising_data_types.append(
|
||||||
(
|
data_types.Appearance(category_enum, subcategory_enum)
|
||||||
AdvertisingData.APPEARANCE,
|
|
||||||
struct.pack('<H', advertise_appearance_int),
|
|
||||||
)
|
)
|
||||||
)
|
device.advertising_data = bytes(AdvertisingData(advertising_data_types))
|
||||||
device.advertising_data = bytes(AdvertisingData(ad_structs))
|
|
||||||
await device.start_advertising(
|
await device.start_advertising(
|
||||||
auto_restart=True,
|
auto_restart=True,
|
||||||
own_address_type=(
|
own_address_type=(
|
||||||
|
|||||||
12
apps/scan.py
12
apps/scan.py
@@ -20,6 +20,7 @@ import asyncio
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.device import Advertisement, Device
|
from bumble.device import Advertisement, Device
|
||||||
from bumble.hci import HCI_LE_1M_PHY, HCI_LE_CODED_PHY, Address, HCI_Constant
|
from bumble.hci import HCI_LE_1M_PHY, HCI_LE_CODED_PHY, Address, HCI_Constant
|
||||||
@@ -94,13 +95,22 @@ class AdvertisementPrinter:
|
|||||||
else:
|
else:
|
||||||
phy_info = ''
|
phy_info = ''
|
||||||
|
|
||||||
|
details = separator.join(
|
||||||
|
[
|
||||||
|
data_type.to_string(use_label=True)
|
||||||
|
for data_type in data_types.data_types_from_advertising_data(
|
||||||
|
advertisement.data
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f'>>> {color(address, address_color)} '
|
f'>>> {color(address, address_color)} '
|
||||||
f'[{color(address_type_string, type_color)}]{address_qualifier}'
|
f'[{color(address_type_string, type_color)}]{address_qualifier}'
|
||||||
f'{resolution_qualifier}:{separator}'
|
f'{resolution_qualifier}:{separator}'
|
||||||
f'{phy_info}'
|
f'{phy_info}'
|
||||||
f'RSSI:{advertisement.rssi:4} {rssi_bar}{separator}'
|
f'RSSI:{advertisement.rssi:4} {rssi_bar}{separator}'
|
||||||
f'{advertisement.data.to_string(separator)}\n'
|
f'{details}\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_advertisement(self, advertisement):
|
def on_advertisement(self, advertisement):
|
||||||
|
|||||||
764
bumble/core.py
764
bumble/core.py
@@ -17,9 +17,21 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import struct
|
import struct
|
||||||
from typing import Literal, Optional, Union, cast, overload
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
ClassVar,
|
||||||
|
Iterable,
|
||||||
|
Literal,
|
||||||
|
Optional,
|
||||||
|
Type,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
@@ -331,6 +343,9 @@ class UUID:
|
|||||||
result += f' ({self.name})'
|
result += f' ({self.name})'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.to_hex_str()
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Common UUID constants
|
# Common UUID constants
|
||||||
@@ -447,26 +462,25 @@ BT_HDP_SINK_SERVICE = UUID.from_16_bits(0x1402,
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# DeviceClass
|
# ClassOfDevice
|
||||||
|
# See Bluetooth - Assigned Numbers - 2.8 Class of Device
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class DeviceClass:
|
@dataclasses.dataclass
|
||||||
|
class ClassOfDevice:
|
||||||
# fmt: off
|
# fmt: off
|
||||||
# pylint: disable=line-too-long
|
class MajorServiceClasses(enum.IntFlag):
|
||||||
|
LIMITED_DISCOVERABLE_MODE = (1 << 0)
|
||||||
|
LE_AUDIO = (1 << 1)
|
||||||
|
POSITIONING = (1 << 3)
|
||||||
|
NETWORKING = (1 << 4)
|
||||||
|
RENDERING = (1 << 5)
|
||||||
|
CAPTURING = (1 << 6)
|
||||||
|
OBJECT_TRANSFER = (1 << 7)
|
||||||
|
AUDIO = (1 << 8)
|
||||||
|
TELEPHONY = (1 << 9)
|
||||||
|
INFORMATION = (1 << 10)
|
||||||
|
|
||||||
# Major Service Classes (flags combined with OR)
|
MAJOR_SERVICE_CLASS_LABELS: ClassVar[list[str]] = [
|
||||||
LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0)
|
|
||||||
LE_AUDIO_SERVICE_CLASS = (1 << 1)
|
|
||||||
RESERVED = (1 << 2)
|
|
||||||
POSITIONING_SERVICE_CLASS = (1 << 3)
|
|
||||||
NETWORKING_SERVICE_CLASS = (1 << 4)
|
|
||||||
RENDERING_SERVICE_CLASS = (1 << 5)
|
|
||||||
CAPTURING_SERVICE_CLASS = (1 << 6)
|
|
||||||
OBJECT_TRANSFER_SERVICE_CLASS = (1 << 7)
|
|
||||||
AUDIO_SERVICE_CLASS = (1 << 8)
|
|
||||||
TELEPHONY_SERVICE_CLASS = (1 << 9)
|
|
||||||
INFORMATION_SERVICE_CLASS = (1 << 10)
|
|
||||||
|
|
||||||
SERVICE_CLASS_LABELS = [
|
|
||||||
'Limited Discoverable Mode',
|
'Limited Discoverable Mode',
|
||||||
'LE audio',
|
'LE audio',
|
||||||
'(reserved)',
|
'(reserved)',
|
||||||
@@ -477,220 +491,440 @@ class DeviceClass:
|
|||||||
'Object Transfer',
|
'Object Transfer',
|
||||||
'Audio',
|
'Audio',
|
||||||
'Telephony',
|
'Telephony',
|
||||||
'Information'
|
'Information',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Major Device Classes
|
class MajorDeviceClass(utils.OpenIntEnum):
|
||||||
MISCELLANEOUS_MAJOR_DEVICE_CLASS = 0x00
|
MISCELLANEOUS = 0x00
|
||||||
COMPUTER_MAJOR_DEVICE_CLASS = 0x01
|
COMPUTER = 0x01
|
||||||
PHONE_MAJOR_DEVICE_CLASS = 0x02
|
PHONE = 0x02
|
||||||
LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = 0x03
|
LAN_NETWORK_ACCESS_POINT = 0x03
|
||||||
AUDIO_VIDEO_MAJOR_DEVICE_CLASS = 0x04
|
AUDIO_VIDEO = 0x04
|
||||||
PERIPHERAL_MAJOR_DEVICE_CLASS = 0x05
|
PERIPHERAL = 0x05
|
||||||
IMAGING_MAJOR_DEVICE_CLASS = 0x06
|
IMAGING = 0x06
|
||||||
WEARABLE_MAJOR_DEVICE_CLASS = 0x07
|
WEARABLE = 0x07
|
||||||
TOY_MAJOR_DEVICE_CLASS = 0x08
|
TOY = 0x08
|
||||||
HEALTH_MAJOR_DEVICE_CLASS = 0x09
|
HEALTH = 0x09
|
||||||
UNCATEGORIZED_MAJOR_DEVICE_CLASS = 0x1F
|
UNCATEGORIZED = 0x1F
|
||||||
|
|
||||||
MAJOR_DEVICE_CLASS_NAMES = {
|
MAJOR_DEVICE_CLASS_LABELS: ClassVar[dict[MajorDeviceClass, str]] = {
|
||||||
MISCELLANEOUS_MAJOR_DEVICE_CLASS: 'Miscellaneous',
|
MajorDeviceClass.MISCELLANEOUS: 'Miscellaneous',
|
||||||
COMPUTER_MAJOR_DEVICE_CLASS: 'Computer',
|
MajorDeviceClass.COMPUTER: 'Computer',
|
||||||
PHONE_MAJOR_DEVICE_CLASS: 'Phone',
|
MajorDeviceClass.PHONE: 'Phone',
|
||||||
LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS: 'LAN/Network Access Point',
|
MajorDeviceClass.LAN_NETWORK_ACCESS_POINT: 'LAN/Network Access Point',
|
||||||
AUDIO_VIDEO_MAJOR_DEVICE_CLASS: 'Audio/Video',
|
MajorDeviceClass.AUDIO_VIDEO: 'Audio/Video',
|
||||||
PERIPHERAL_MAJOR_DEVICE_CLASS: 'Peripheral',
|
MajorDeviceClass.PERIPHERAL: 'Peripheral',
|
||||||
IMAGING_MAJOR_DEVICE_CLASS: 'Imaging',
|
MajorDeviceClass.IMAGING: 'Imaging',
|
||||||
WEARABLE_MAJOR_DEVICE_CLASS: 'Wearable',
|
MajorDeviceClass.WEARABLE: 'Wearable',
|
||||||
TOY_MAJOR_DEVICE_CLASS: 'Toy',
|
MajorDeviceClass.TOY: 'Toy',
|
||||||
HEALTH_MAJOR_DEVICE_CLASS: 'Health',
|
MajorDeviceClass.HEALTH: 'Health',
|
||||||
UNCATEGORIZED_MAJOR_DEVICE_CLASS: 'Uncategorized'
|
MajorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class ComputerMinorDeviceClass(utils.OpenIntEnum):
|
||||||
COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS = 0x01
|
UNCATEGORIZED = 0x00
|
||||||
COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = 0x02
|
DESKTOP_WORKSTATION = 0x01
|
||||||
COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS = 0x03
|
SERVER_CLASS_COMPUTER = 0x02
|
||||||
COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS = 0x04
|
LAPTOP_COMPUTER = 0x03
|
||||||
COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS = 0x05
|
HANDHELD_PC_PDA = 0x04
|
||||||
COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS = 0x06
|
PALM_SIZE_PC_PDA = 0x05
|
||||||
COMPUTER_TABLET_MINOR_DEVICE_CLASS = 0x07
|
WEARABLE_COMPUTER = 0x06
|
||||||
|
TABLET = 0x07
|
||||||
|
|
||||||
COMPUTER_MINOR_DEVICE_CLASS_NAMES = {
|
COMPUTER_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[ComputerMinorDeviceClass, str]] = {
|
||||||
COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
ComputerMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS: 'Desktop workstation',
|
ComputerMinorDeviceClass.DESKTOP_WORKSTATION: 'Desktop workstation',
|
||||||
COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS: 'Server-class computer',
|
ComputerMinorDeviceClass.SERVER_CLASS_COMPUTER: 'Server-class computer',
|
||||||
COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS: 'Laptop',
|
ComputerMinorDeviceClass.LAPTOP_COMPUTER: 'Laptop',
|
||||||
COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS: 'Handheld PC/PDA',
|
ComputerMinorDeviceClass.HANDHELD_PC_PDA: 'Handheld PC/PDA',
|
||||||
COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS: 'Palm-size PC/PDA',
|
ComputerMinorDeviceClass.PALM_SIZE_PC_PDA: 'Palm-size PC/PDA',
|
||||||
COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS: 'Wearable computer',
|
ComputerMinorDeviceClass.WEARABLE_COMPUTER: 'Wearable computer',
|
||||||
COMPUTER_TABLET_MINOR_DEVICE_CLASS: 'Tablet'
|
ComputerMinorDeviceClass.TABLET: 'Tablet',
|
||||||
}
|
}
|
||||||
|
|
||||||
PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class PhoneMinorDeviceClass(utils.OpenIntEnum):
|
||||||
PHONE_CELLULAR_MINOR_DEVICE_CLASS = 0x01
|
UNCATEGORIZED = 0x00
|
||||||
PHONE_CORDLESS_MINOR_DEVICE_CLASS = 0x02
|
CELLULAR = 0x01
|
||||||
PHONE_SMARTPHONE_MINOR_DEVICE_CLASS = 0x03
|
CORDLESS = 0x02
|
||||||
PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = 0x04
|
SMARTPHONE = 0x03
|
||||||
PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS = 0x05
|
WIRED_MODEM_OR_VOICE_GATEWAY = 0x04
|
||||||
|
COMMON_ISDN = 0x05
|
||||||
|
|
||||||
PHONE_MINOR_DEVICE_CLASS_NAMES = {
|
PHONE_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[PhoneMinorDeviceClass, str]] = {
|
||||||
PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
PhoneMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
PHONE_CELLULAR_MINOR_DEVICE_CLASS: 'Cellular',
|
PhoneMinorDeviceClass.CELLULAR: 'Cellular',
|
||||||
PHONE_CORDLESS_MINOR_DEVICE_CLASS: 'Cordless',
|
PhoneMinorDeviceClass.CORDLESS: 'Cordless',
|
||||||
PHONE_SMARTPHONE_MINOR_DEVICE_CLASS: 'Smartphone',
|
PhoneMinorDeviceClass.SMARTPHONE: 'Smartphone',
|
||||||
PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS: 'Wired modem or voice gateway',
|
PhoneMinorDeviceClass.WIRED_MODEM_OR_VOICE_GATEWAY: 'Wired modem or voice gateway',
|
||||||
PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS: 'Common ISDN access'
|
PhoneMinorDeviceClass.COMMON_ISDN: 'Common ISDN access',
|
||||||
}
|
}
|
||||||
|
|
||||||
AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class LanNetworkMinorDeviceClass(utils.OpenIntEnum):
|
||||||
AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS = 0x01
|
FULLY_AVAILABLE = 0x00
|
||||||
AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS = 0x02
|
_1_TO_17_PERCENT_UTILIZED = 0x01
|
||||||
|
_17_TO_33_PERCENT_UTILIZED = 0x02
|
||||||
|
_33_TO_50_PERCENT_UTILIZED = 0x03
|
||||||
|
_50_TO_67_PERCENT_UTILIZED = 0x04
|
||||||
|
_67_TO_83_PERCENT_UTILIZED = 0x05
|
||||||
|
_83_TO_99_PERCENT_UTILIZED = 0x06
|
||||||
|
_NO_SERVICE_AVAILABLE = 0x07
|
||||||
|
|
||||||
|
LAN_NETWORK_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[LanNetworkMinorDeviceClass, str]] = {
|
||||||
|
LanNetworkMinorDeviceClass.FULLY_AVAILABLE: 'Fully availbable',
|
||||||
|
LanNetworkMinorDeviceClass._1_TO_17_PERCENT_UTILIZED: '1% to 17% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._17_TO_33_PERCENT_UTILIZED: '17% to 33% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._33_TO_50_PERCENT_UTILIZED: '33% to 50% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._50_TO_67_PERCENT_UTILIZED: '50% to 67% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._67_TO_83_PERCENT_UTILIZED: '67% to 83% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._83_TO_99_PERCENT_UTILIZED: '83% to 99% utilized',
|
||||||
|
LanNetworkMinorDeviceClass._NO_SERVICE_AVAILABLE: 'No service available',
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioVideoMinorDeviceClass(utils.OpenIntEnum):
|
||||||
|
UNCATEGORIZED = 0x00
|
||||||
|
WEARABLE_HEADSET_DEVICE = 0x01
|
||||||
|
HANDS_FREE_DEVICE = 0x02
|
||||||
# (RESERVED) = 0x03
|
# (RESERVED) = 0x03
|
||||||
AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS = 0x04
|
MICROPHONE = 0x04
|
||||||
AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x05
|
LOUDSPEAKER = 0x05
|
||||||
AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS = 0x06
|
HEADPHONES = 0x06
|
||||||
AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS = 0x07
|
PORTABLE_AUDIO = 0x07
|
||||||
AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS = 0x08
|
CAR_AUDIO = 0x08
|
||||||
AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS = 0x09
|
SET_TOP_BOX = 0x09
|
||||||
AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS = 0x0A
|
HIFI_AUDIO_DEVICE = 0x0A
|
||||||
AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS = 0x0B
|
VCR = 0x0B
|
||||||
AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS = 0x0C
|
VIDEO_CAMERA = 0x0C
|
||||||
AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS = 0x0D
|
CAMCORDER = 0x0D
|
||||||
AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS = 0x0E
|
VIDEO_MONITOR = 0x0E
|
||||||
AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = 0x0F
|
VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x0F
|
||||||
AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS = 0x10
|
VIDEO_CONFERENCING = 0x10
|
||||||
# (RESERVED) = 0x11
|
# (RESERVED) = 0x11
|
||||||
AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS = 0x12
|
GAMING_OR_TOY = 0x12
|
||||||
|
|
||||||
AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES = {
|
AUDIO_VIDEO_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[AudioVideoMinorDeviceClass, str]] = {
|
||||||
AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
AudioVideoMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS: 'Wearable Headset Device',
|
AudioVideoMinorDeviceClass.WEARABLE_HEADSET_DEVICE: 'Wearable Headset Device',
|
||||||
AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS: 'Hands-free Device',
|
AudioVideoMinorDeviceClass.HANDS_FREE_DEVICE: 'Hands-free Device',
|
||||||
AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS: 'Microphone',
|
AudioVideoMinorDeviceClass.MICROPHONE: 'Microphone',
|
||||||
AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Loudspeaker',
|
AudioVideoMinorDeviceClass.LOUDSPEAKER: 'Loudspeaker',
|
||||||
AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS: 'Headphones',
|
AudioVideoMinorDeviceClass.HEADPHONES: 'Headphones',
|
||||||
AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS: 'Portable Audio',
|
AudioVideoMinorDeviceClass.PORTABLE_AUDIO: 'Portable Audio',
|
||||||
AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS: 'Car audio',
|
AudioVideoMinorDeviceClass.CAR_AUDIO: 'Car audio',
|
||||||
AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS: 'Set-top box',
|
AudioVideoMinorDeviceClass.SET_TOP_BOX: 'Set-top box',
|
||||||
AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS: 'HiFi Audio Device',
|
AudioVideoMinorDeviceClass.HIFI_AUDIO_DEVICE: 'HiFi Audio Device',
|
||||||
AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS: 'VCR',
|
AudioVideoMinorDeviceClass.VCR: 'VCR',
|
||||||
AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS: 'Video Camera',
|
AudioVideoMinorDeviceClass.VIDEO_CAMERA: 'Video Camera',
|
||||||
AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS: 'Camcorder',
|
AudioVideoMinorDeviceClass.CAMCORDER: 'Camcorder',
|
||||||
AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS: 'Video Monitor',
|
AudioVideoMinorDeviceClass.VIDEO_MONITOR: 'Video Monitor',
|
||||||
AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS: 'Video Display and Loudspeaker',
|
AudioVideoMinorDeviceClass.VIDEO_DISPLAY_AND_LOUDSPEAKER: 'Video Display and Loudspeaker',
|
||||||
AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS: 'Video Conferencing',
|
AudioVideoMinorDeviceClass.VIDEO_CONFERENCING: 'Video Conferencing',
|
||||||
AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS: 'Gaming/Toy'
|
AudioVideoMinorDeviceClass.GAMING_OR_TOY: 'Gaming/Toy',
|
||||||
}
|
}
|
||||||
|
|
||||||
PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class PeripheralMinorDeviceClass(utils.OpenIntEnum):
|
||||||
PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS = 0x10
|
UNCATEGORIZED = 0x00
|
||||||
PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x20
|
KEYBOARD = 0x10
|
||||||
PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = 0x30
|
POINTING_DEVICE = 0x20
|
||||||
PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS = 0x01
|
COMBO_KEYBOARD_POINTING_DEVICE = 0x30
|
||||||
PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS = 0x02
|
JOYSTICK = 0x01
|
||||||
PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS = 0x03
|
GAMEPAD = 0x02
|
||||||
PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS = 0x04
|
REMOTE_CONTROL = 0x03
|
||||||
PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS = 0x05
|
SENSING_DEVICE = 0x04
|
||||||
PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS = 0x06
|
DIGITIZER_TABLET = 0x05
|
||||||
PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS = 0x07
|
CARD_READER = 0x06
|
||||||
PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS = 0x08
|
DIGITAL_PEN = 0x07
|
||||||
PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = 0x09
|
HANDHELD_SCANNER = 0x08
|
||||||
|
HANDHELD_GESTURAL_INPUT_DEVICE = 0x09
|
||||||
|
|
||||||
PERIPHERAL_MINOR_DEVICE_CLASS_NAMES = {
|
PERIPHERAL_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[PeripheralMinorDeviceClass, str]] = {
|
||||||
PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
PeripheralMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS: 'Keyboard',
|
PeripheralMinorDeviceClass.KEYBOARD: 'Keyboard',
|
||||||
PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Pointing device',
|
PeripheralMinorDeviceClass.POINTING_DEVICE: 'Pointing device',
|
||||||
PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS: 'Combo keyboard/pointing device',
|
PeripheralMinorDeviceClass.COMBO_KEYBOARD_POINTING_DEVICE: 'Combo keyboard/pointing device',
|
||||||
PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS: 'Joystick',
|
PeripheralMinorDeviceClass.JOYSTICK: 'Joystick',
|
||||||
PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS: 'Gamepad',
|
PeripheralMinorDeviceClass.GAMEPAD: 'Gamepad',
|
||||||
PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS: 'Remote control',
|
PeripheralMinorDeviceClass.REMOTE_CONTROL: 'Remote control',
|
||||||
PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS: 'Sensing device',
|
PeripheralMinorDeviceClass.SENSING_DEVICE: 'Sensing device',
|
||||||
PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS: 'Digitizer tablet',
|
PeripheralMinorDeviceClass.DIGITIZER_TABLET: 'Digitizer tablet',
|
||||||
PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS: 'Card Reader',
|
PeripheralMinorDeviceClass.CARD_READER: 'Card Reader',
|
||||||
PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS: 'Digital Pen',
|
PeripheralMinorDeviceClass.DIGITAL_PEN: 'Digital Pen',
|
||||||
PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS: 'Handheld scanner',
|
PeripheralMinorDeviceClass.HANDHELD_SCANNER: 'Handheld scanner',
|
||||||
PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS: 'Handheld gestural input device'
|
PeripheralMinorDeviceClass.HANDHELD_GESTURAL_INPUT_DEVICE: 'Handheld gestural input device',
|
||||||
}
|
}
|
||||||
|
|
||||||
WEARABLE_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class WearableMinorDeviceClass(utils.OpenIntEnum):
|
||||||
WEARABLE_WRISTWATCH_MINOR_DEVICE_CLASS = 0x01
|
UNCATEGORIZED = 0x00
|
||||||
WEARABLE_PAGER_MINOR_DEVICE_CLASS = 0x02
|
WRISTWATCH = 0x01
|
||||||
WEARABLE_JACKET_MINOR_DEVICE_CLASS = 0x03
|
PAGER = 0x02
|
||||||
WEARABLE_HELMET_MINOR_DEVICE_CLASS = 0x04
|
JACKET = 0x03
|
||||||
WEARABLE_GLASSES_MINOR_DEVICE_CLASS = 0x05
|
HELMET = 0x04
|
||||||
|
GLASSES = 0x05
|
||||||
|
|
||||||
WEARABLE_MINOR_DEVICE_CLASS_NAMES = {
|
WEARABLE_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[WearableMinorDeviceClass, str]] = {
|
||||||
WEARABLE_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
WearableMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
WEARABLE_WRISTWATCH_MINOR_DEVICE_CLASS: 'Wristwatch',
|
WearableMinorDeviceClass.WRISTWATCH: 'Wristwatch',
|
||||||
WEARABLE_PAGER_MINOR_DEVICE_CLASS: 'Pager',
|
WearableMinorDeviceClass.PAGER: 'Pager',
|
||||||
WEARABLE_JACKET_MINOR_DEVICE_CLASS: 'Jacket',
|
WearableMinorDeviceClass.JACKET: 'Jacket',
|
||||||
WEARABLE_HELMET_MINOR_DEVICE_CLASS: 'Helmet',
|
WearableMinorDeviceClass.HELMET: 'Helmet',
|
||||||
WEARABLE_GLASSES_MINOR_DEVICE_CLASS: 'Glasses',
|
WearableMinorDeviceClass.GLASSES: 'Glasses',
|
||||||
}
|
}
|
||||||
|
|
||||||
TOY_UNCATEGORIZED_MINOR_DEVICE_CLASS = 0x00
|
class ToyMinorDeviceClass(utils.OpenIntEnum):
|
||||||
TOY_ROBOT_MINOR_DEVICE_CLASS = 0x01
|
UNCATEGORIZED = 0x00
|
||||||
TOY_VEHICLE_MINOR_DEVICE_CLASS = 0x02
|
ROBOT = 0x01
|
||||||
TOY_DOLL_ACTION_FIGURE_MINOR_DEVICE_CLASS = 0x03
|
VEHICLE = 0x02
|
||||||
TOY_CONTROLLER_MINOR_DEVICE_CLASS = 0x04
|
DOLL_ACTION_FIGURE = 0x03
|
||||||
TOY_GAME_MINOR_DEVICE_CLASS = 0x05
|
CONTROLLER = 0x04
|
||||||
|
GAME = 0x05
|
||||||
|
|
||||||
TOY_MINOR_DEVICE_CLASS_NAMES = {
|
TOY_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[ToyMinorDeviceClass, str]] = {
|
||||||
TOY_UNCATEGORIZED_MINOR_DEVICE_CLASS: 'Uncategorized',
|
ToyMinorDeviceClass.UNCATEGORIZED: 'Uncategorized',
|
||||||
TOY_ROBOT_MINOR_DEVICE_CLASS: 'Robot',
|
ToyMinorDeviceClass.ROBOT: 'Robot',
|
||||||
TOY_VEHICLE_MINOR_DEVICE_CLASS: 'Vehicle',
|
ToyMinorDeviceClass.VEHICLE: 'Vehicle',
|
||||||
TOY_DOLL_ACTION_FIGURE_MINOR_DEVICE_CLASS: 'Doll/Action figure',
|
ToyMinorDeviceClass.DOLL_ACTION_FIGURE: 'Doll/Action figure',
|
||||||
TOY_CONTROLLER_MINOR_DEVICE_CLASS: 'Controller',
|
ToyMinorDeviceClass.CONTROLLER: 'Controller',
|
||||||
TOY_GAME_MINOR_DEVICE_CLASS: 'Game',
|
ToyMinorDeviceClass.GAME: 'Game',
|
||||||
}
|
}
|
||||||
|
|
||||||
HEALTH_UNDEFINED_MINOR_DEVICE_CLASS = 0x00
|
class HealthMinorDeviceClass(utils.OpenIntEnum):
|
||||||
HEALTH_BLOOD_PRESSURE_MONITOR_MINOR_DEVICE_CLASS = 0x01
|
UNDEFINED = 0x00
|
||||||
HEALTH_THERMOMETER_MINOR_DEVICE_CLASS = 0x02
|
BLOOD_PRESSURE_MONITOR = 0x01
|
||||||
HEALTH_WEIGHING_SCALE_MINOR_DEVICE_CLASS = 0x03
|
THERMOMETER = 0x02
|
||||||
HEALTH_GLUCOSE_METER_MINOR_DEVICE_CLASS = 0x04
|
WEIGHING_SCALE = 0x03
|
||||||
HEALTH_PULSE_OXIMETER_MINOR_DEVICE_CLASS = 0x05
|
GLUCOSE_METER = 0x04
|
||||||
HEALTH_HEART_PULSE_RATE_MONITOR_MINOR_DEVICE_CLASS = 0x06
|
PULSE_OXIMETER = 0x05
|
||||||
HEALTH_HEALTH_DATA_DISPLAY_MINOR_DEVICE_CLASS = 0x07
|
HEART_PULSE_RATE_MONITOR = 0x06
|
||||||
HEALTH_STEP_COUNTER_MINOR_DEVICE_CLASS = 0x08
|
HEALTH_DATA_DISPLAY = 0x07
|
||||||
HEALTH_BODY_COMPOSITION_ANALYZER_MINOR_DEVICE_CLASS = 0x09
|
STEP_COUNTER = 0x08
|
||||||
HEALTH_PEAK_FLOW_MONITOR_MINOR_DEVICE_CLASS = 0x0A
|
BODY_COMPOSITION_ANALYZER = 0x09
|
||||||
HEALTH_MEDICATION_MONITOR_MINOR_DEVICE_CLASS = 0x0B
|
PEAK_FLOW_MONITOR = 0x0A
|
||||||
HEALTH_KNEE_PROSTHESIS_MINOR_DEVICE_CLASS = 0x0C
|
MEDICATION_MONITOR = 0x0B
|
||||||
HEALTH_ANKLE_PROSTHESIS_MINOR_DEVICE_CLASS = 0x0D
|
KNEE_PROSTHESIS = 0x0C
|
||||||
HEALTH_GENERIC_HEALTH_MANAGER_MINOR_DEVICE_CLASS = 0x0E
|
ANKLE_PROSTHESIS = 0x0D
|
||||||
HEALTH_PERSONAL_MOBILITY_DEVICE_MINOR_DEVICE_CLASS = 0x0F
|
GENERIC_HEALTH_MANAGER = 0x0E
|
||||||
|
PERSONAL_MOBILITY_DEVICE = 0x0F
|
||||||
|
|
||||||
HEALTH_MINOR_DEVICE_CLASS_NAMES = {
|
HEALTH_MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[HealthMinorDeviceClass, str]] = {
|
||||||
HEALTH_UNDEFINED_MINOR_DEVICE_CLASS: 'Undefined',
|
HealthMinorDeviceClass.UNDEFINED: 'Undefined',
|
||||||
HEALTH_BLOOD_PRESSURE_MONITOR_MINOR_DEVICE_CLASS: 'Blood Pressure Monitor',
|
HealthMinorDeviceClass.BLOOD_PRESSURE_MONITOR: 'Blood Pressure Monitor',
|
||||||
HEALTH_THERMOMETER_MINOR_DEVICE_CLASS: 'Thermometer',
|
HealthMinorDeviceClass.THERMOMETER: 'Thermometer',
|
||||||
HEALTH_WEIGHING_SCALE_MINOR_DEVICE_CLASS: 'Weighing Scale',
|
HealthMinorDeviceClass.WEIGHING_SCALE: 'Weighing Scale',
|
||||||
HEALTH_GLUCOSE_METER_MINOR_DEVICE_CLASS: 'Glucose Meter',
|
HealthMinorDeviceClass.GLUCOSE_METER: 'Glucose Meter',
|
||||||
HEALTH_PULSE_OXIMETER_MINOR_DEVICE_CLASS: 'Pulse Oximeter',
|
HealthMinorDeviceClass.PULSE_OXIMETER: 'Pulse Oximeter',
|
||||||
HEALTH_HEART_PULSE_RATE_MONITOR_MINOR_DEVICE_CLASS: 'Heart/Pulse Rate Monitor',
|
HealthMinorDeviceClass.HEART_PULSE_RATE_MONITOR: 'Heart/Pulse Rate Monitor',
|
||||||
HEALTH_HEALTH_DATA_DISPLAY_MINOR_DEVICE_CLASS: 'Health Data Display',
|
HealthMinorDeviceClass.HEALTH_DATA_DISPLAY: 'Health Data Display',
|
||||||
HEALTH_STEP_COUNTER_MINOR_DEVICE_CLASS: 'Step Counter',
|
HealthMinorDeviceClass.STEP_COUNTER: 'Step Counter',
|
||||||
HEALTH_BODY_COMPOSITION_ANALYZER_MINOR_DEVICE_CLASS: 'Body Composition Analyzer',
|
HealthMinorDeviceClass.BODY_COMPOSITION_ANALYZER: 'Body Composition Analyzer',
|
||||||
HEALTH_PEAK_FLOW_MONITOR_MINOR_DEVICE_CLASS: 'Peak Flow Monitor',
|
HealthMinorDeviceClass.PEAK_FLOW_MONITOR: 'Peak Flow Monitor',
|
||||||
HEALTH_MEDICATION_MONITOR_MINOR_DEVICE_CLASS: 'Medication Monitor',
|
HealthMinorDeviceClass.MEDICATION_MONITOR: 'Medication Monitor',
|
||||||
HEALTH_KNEE_PROSTHESIS_MINOR_DEVICE_CLASS: 'Knee Prosthesis',
|
HealthMinorDeviceClass.KNEE_PROSTHESIS: 'Knee Prosthesis',
|
||||||
HEALTH_ANKLE_PROSTHESIS_MINOR_DEVICE_CLASS: 'Ankle Prosthesis',
|
HealthMinorDeviceClass.ANKLE_PROSTHESIS: 'Ankle Prosthesis',
|
||||||
HEALTH_GENERIC_HEALTH_MANAGER_MINOR_DEVICE_CLASS: 'Generic Health Manager',
|
HealthMinorDeviceClass.GENERIC_HEALTH_MANAGER: 'Generic Health Manager',
|
||||||
HEALTH_PERSONAL_MOBILITY_DEVICE_MINOR_DEVICE_CLASS: 'Personal Mobility Device',
|
HealthMinorDeviceClass.PERSONAL_MOBILITY_DEVICE: 'Personal Mobility Device',
|
||||||
}
|
}
|
||||||
|
|
||||||
MINOR_DEVICE_CLASS_NAMES = {
|
MINOR_DEVICE_CLASS_LABELS: ClassVar[dict[MajorDeviceClass, dict[Any, str]]] = {
|
||||||
COMPUTER_MAJOR_DEVICE_CLASS: COMPUTER_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.COMPUTER: COMPUTER_MINOR_DEVICE_CLASS_LABELS,
|
||||||
PHONE_MAJOR_DEVICE_CLASS: PHONE_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.PHONE: PHONE_MINOR_DEVICE_CLASS_LABELS,
|
||||||
AUDIO_VIDEO_MAJOR_DEVICE_CLASS: AUDIO_VIDEO_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.LAN_NETWORK_ACCESS_POINT: LAN_NETWORK_MINOR_DEVICE_CLASS_LABELS,
|
||||||
PERIPHERAL_MAJOR_DEVICE_CLASS: PERIPHERAL_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.AUDIO_VIDEO: AUDIO_VIDEO_MINOR_DEVICE_CLASS_LABELS,
|
||||||
WEARABLE_MAJOR_DEVICE_CLASS: WEARABLE_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.PERIPHERAL: PERIPHERAL_MINOR_DEVICE_CLASS_LABELS,
|
||||||
TOY_MAJOR_DEVICE_CLASS: TOY_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.WEARABLE: WEARABLE_MINOR_DEVICE_CLASS_LABELS,
|
||||||
HEALTH_MAJOR_DEVICE_CLASS: HEALTH_MINOR_DEVICE_CLASS_NAMES,
|
MajorDeviceClass.TOY: TOY_MINOR_DEVICE_CLASS_LABELS,
|
||||||
|
MajorDeviceClass.HEALTH: HEALTH_MINOR_DEVICE_CLASS_LABELS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_MINOR_DEVICE_CLASSES: ClassVar[dict[MajorDeviceClass, Type]] = {
|
||||||
|
MajorDeviceClass.COMPUTER: ComputerMinorDeviceClass,
|
||||||
|
MajorDeviceClass.PHONE: PhoneMinorDeviceClass,
|
||||||
|
MajorDeviceClass.LAN_NETWORK_ACCESS_POINT: LanNetworkMinorDeviceClass,
|
||||||
|
MajorDeviceClass.AUDIO_VIDEO: AudioVideoMinorDeviceClass,
|
||||||
|
MajorDeviceClass.PERIPHERAL: PeripheralMinorDeviceClass,
|
||||||
|
MajorDeviceClass.WEARABLE: WearableMinorDeviceClass,
|
||||||
|
MajorDeviceClass.TOY: ToyMinorDeviceClass,
|
||||||
|
MajorDeviceClass.HEALTH: HealthMinorDeviceClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
major_service_classes: MajorServiceClasses
|
||||||
|
major_device_class: MajorDeviceClass
|
||||||
|
minor_device_class: Union[
|
||||||
|
ComputerMinorDeviceClass,
|
||||||
|
PhoneMinorDeviceClass,
|
||||||
|
LanNetworkMinorDeviceClass,
|
||||||
|
AudioVideoMinorDeviceClass,
|
||||||
|
PeripheralMinorDeviceClass,
|
||||||
|
WearableMinorDeviceClass,
|
||||||
|
ToyMinorDeviceClass,
|
||||||
|
HealthMinorDeviceClass,
|
||||||
|
int,
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_int(cls, class_of_device: int) -> Self:
|
||||||
|
major_service_classes = cls.MajorServiceClasses(class_of_device >> 13 & 0x7FF)
|
||||||
|
major_device_class = cls.MajorDeviceClass(class_of_device >> 8 & 0x1F)
|
||||||
|
minor_device_class_int = class_of_device >> 2 & 0x3F
|
||||||
|
if minor_device_class_object := cls._MINOR_DEVICE_CLASSES.get(
|
||||||
|
major_device_class
|
||||||
|
):
|
||||||
|
minor_device_class = minor_device_class_object(minor_device_class_int)
|
||||||
|
else:
|
||||||
|
minor_device_class = minor_device_class_int
|
||||||
|
return cls(major_service_classes, major_device_class, minor_device_class)
|
||||||
|
|
||||||
|
def __int__(self) -> int:
|
||||||
|
return (
|
||||||
|
self.major_service_classes << 13
|
||||||
|
| self.major_device_class << 8
|
||||||
|
| self.minor_device_class << 2
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
minor_device_class_name = (
|
||||||
|
self.minor_device_class.name
|
||||||
|
if hasattr(self.minor_device_class, 'name')
|
||||||
|
else hex(self.minor_device_class)
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
f"ClassOfDevice({self.major_service_classes.name},"
|
||||||
|
f"{self.major_device_class.name}/{minor_device_class_name})"
|
||||||
|
)
|
||||||
|
|
||||||
|
def major_service_classes_labels(self) -> str:
|
||||||
|
return "|".join(
|
||||||
|
bit_flags_to_strings(
|
||||||
|
self.major_service_classes, self.MAJOR_SERVICE_CLASS_LABELS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def major_device_class_label(self) -> str:
|
||||||
|
return name_or_number(
|
||||||
|
cast(dict[int, str], self.MAJOR_DEVICE_CLASS_LABELS),
|
||||||
|
self.major_device_class,
|
||||||
|
)
|
||||||
|
|
||||||
|
def minor_device_class_label(self) -> str:
|
||||||
|
class_names = self.MINOR_DEVICE_CLASS_LABELS.get(self.major_device_class)
|
||||||
|
if class_names is None:
|
||||||
|
return f'#{self.minor_device_class:02X}'
|
||||||
|
return name_or_number(class_names, self.minor_device_class)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# DeviceClass
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
class DeviceClass:
|
||||||
|
"""Legacy only. Use ClassOfDevice instead"""
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
# pylint: disable=line-too-long
|
||||||
|
|
||||||
|
# Major Service Classes (flags combined with OR)
|
||||||
|
LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.LIMITED_DISCOVERABLE_MODE
|
||||||
|
LE_AUDIO_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.LE_AUDIO
|
||||||
|
POSITIONING_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.POSITIONING
|
||||||
|
NETWORKING_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.NETWORKING
|
||||||
|
RENDERING_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.RENDERING
|
||||||
|
CAPTURING_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.CAPTURING
|
||||||
|
OBJECT_TRANSFER_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.OBJECT_TRANSFER
|
||||||
|
AUDIO_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.AUDIO
|
||||||
|
TELEPHONY_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.TELEPHONY
|
||||||
|
INFORMATION_SERVICE_CLASS = ClassOfDevice.MajorServiceClasses.INFORMATION
|
||||||
|
|
||||||
|
# Major Device Classes
|
||||||
|
MISCELLANEOUS_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.MISCELLANEOUS
|
||||||
|
COMPUTER_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.COMPUTER
|
||||||
|
PHONE_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.PHONE
|
||||||
|
LAN_NETWORK_ACCESS_POINT_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.LAN_NETWORK_ACCESS_POINT
|
||||||
|
AUDIO_VIDEO_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.AUDIO_VIDEO
|
||||||
|
PERIPHERAL_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.PERIPHERAL
|
||||||
|
IMAGING_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.IMAGING
|
||||||
|
WEARABLE_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.WEARABLE
|
||||||
|
TOY_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.TOY
|
||||||
|
HEALTH_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.HEALTH
|
||||||
|
UNCATEGORIZED_MAJOR_DEVICE_CLASS = ClassOfDevice.MajorDeviceClass.UNCATEGORIZED
|
||||||
|
|
||||||
|
COMPUTER_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.UNCATEGORIZED
|
||||||
|
COMPUTER_DESKTOP_WORKSTATION_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.DESKTOP_WORKSTATION
|
||||||
|
COMPUTER_SERVER_CLASS_COMPUTER_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.SERVER_CLASS_COMPUTER
|
||||||
|
COMPUTER_LAPTOP_COMPUTER_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.LAPTOP_COMPUTER
|
||||||
|
COMPUTER_HANDHELD_PC_PDA_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.HANDHELD_PC_PDA
|
||||||
|
COMPUTER_PALM_SIZE_PC_PDA_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.PALM_SIZE_PC_PDA
|
||||||
|
COMPUTER_WEARABLE_COMPUTER_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.WEARABLE_COMPUTER
|
||||||
|
COMPUTER_TABLET_MINOR_DEVICE_CLASS = ClassOfDevice.ComputerMinorDeviceClass.TABLET
|
||||||
|
|
||||||
|
PHONE_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.UNCATEGORIZED
|
||||||
|
PHONE_CELLULAR_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.CELLULAR
|
||||||
|
PHONE_CORDLESS_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.CORDLESS
|
||||||
|
PHONE_SMARTPHONE_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.SMARTPHONE
|
||||||
|
PHONE_WIRED_MODEM_OR_VOICE_GATEWAY_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.WIRED_MODEM_OR_VOICE_GATEWAY
|
||||||
|
PHONE_COMMON_ISDN_MINOR_DEVICE_CLASS = ClassOfDevice.PhoneMinorDeviceClass.COMMON_ISDN
|
||||||
|
|
||||||
|
AUDIO_VIDEO_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.UNCATEGORIZED
|
||||||
|
AUDIO_VIDEO_WEARABLE_HEADSET_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.WEARABLE_HEADSET_DEVICE
|
||||||
|
AUDIO_VIDEO_HANDS_FREE_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.HANDS_FREE_DEVICE
|
||||||
|
AUDIO_VIDEO_MICROPHONE_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.MICROPHONE
|
||||||
|
AUDIO_VIDEO_LOUDSPEAKER_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.LOUDSPEAKER
|
||||||
|
AUDIO_VIDEO_HEADPHONES_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.HEADPHONES
|
||||||
|
AUDIO_VIDEO_PORTABLE_AUDIO_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.PORTABLE_AUDIO
|
||||||
|
AUDIO_VIDEO_CAR_AUDIO_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.CAR_AUDIO
|
||||||
|
AUDIO_VIDEO_SET_TOP_BOX_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.SET_TOP_BOX
|
||||||
|
AUDIO_VIDEO_HIFI_AUDIO_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.HIFI_AUDIO_DEVICE
|
||||||
|
AUDIO_VIDEO_VCR_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.VCR
|
||||||
|
AUDIO_VIDEO_VIDEO_CAMERA_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.VIDEO_CAMERA
|
||||||
|
AUDIO_VIDEO_CAMCORDER_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.CAMCORDER
|
||||||
|
AUDIO_VIDEO_VIDEO_MONITOR_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.VIDEO_MONITOR
|
||||||
|
AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.VIDEO_DISPLAY_AND_LOUDSPEAKER
|
||||||
|
AUDIO_VIDEO_VIDEO_CONFERENCING_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.VIDEO_CONFERENCING
|
||||||
|
AUDIO_VIDEO_GAMING_OR_TOY_MINOR_DEVICE_CLASS = ClassOfDevice.AudioVideoMinorDeviceClass.GAMING_OR_TOY
|
||||||
|
|
||||||
|
PERIPHERAL_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.UNCATEGORIZED
|
||||||
|
PERIPHERAL_KEYBOARD_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.KEYBOARD
|
||||||
|
PERIPHERAL_POINTING_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.POINTING_DEVICE
|
||||||
|
PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.COMBO_KEYBOARD_POINTING_DEVICE
|
||||||
|
PERIPHERAL_JOYSTICK_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.JOYSTICK
|
||||||
|
PERIPHERAL_GAMEPAD_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.GAMEPAD
|
||||||
|
PERIPHERAL_REMOTE_CONTROL_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.REMOTE_CONTROL
|
||||||
|
PERIPHERAL_SENSING_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.SENSING_DEVICE
|
||||||
|
PERIPHERAL_DIGITIZER_TABLET_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.DIGITIZER_TABLET
|
||||||
|
PERIPHERAL_CARD_READER_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.CARD_READER
|
||||||
|
PERIPHERAL_DIGITAL_PEN_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.DIGITAL_PEN
|
||||||
|
PERIPHERAL_HANDHELD_SCANNER_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.HANDHELD_SCANNER
|
||||||
|
PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.PeripheralMinorDeviceClass.HANDHELD_GESTURAL_INPUT_DEVICE
|
||||||
|
|
||||||
|
WEARABLE_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.UNCATEGORIZED
|
||||||
|
WEARABLE_WRISTWATCH_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.WRISTWATCH
|
||||||
|
WEARABLE_PAGER_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.PAGER
|
||||||
|
WEARABLE_JACKET_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.JACKET
|
||||||
|
WEARABLE_HELMET_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.HELMET
|
||||||
|
WEARABLE_GLASSES_MINOR_DEVICE_CLASS = ClassOfDevice.WearableMinorDeviceClass.GLASSES
|
||||||
|
|
||||||
|
TOY_UNCATEGORIZED_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.UNCATEGORIZED
|
||||||
|
TOY_ROBOT_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.ROBOT
|
||||||
|
TOY_VEHICLE_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.VEHICLE
|
||||||
|
TOY_DOLL_ACTION_FIGURE_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.DOLL_ACTION_FIGURE
|
||||||
|
TOY_CONTROLLER_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.CONTROLLER
|
||||||
|
TOY_GAME_MINOR_DEVICE_CLASS = ClassOfDevice.ToyMinorDeviceClass.GAME
|
||||||
|
|
||||||
|
HEALTH_UNDEFINED_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.UNDEFINED
|
||||||
|
HEALTH_BLOOD_PRESSURE_MONITOR_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.BLOOD_PRESSURE_MONITOR
|
||||||
|
HEALTH_THERMOMETER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.THERMOMETER
|
||||||
|
HEALTH_WEIGHING_SCALE_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.WEIGHING_SCALE
|
||||||
|
HEALTH_GLUCOSE_METER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.GLUCOSE_METER
|
||||||
|
HEALTH_PULSE_OXIMETER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.PULSE_OXIMETER
|
||||||
|
HEALTH_HEART_PULSE_RATE_MONITOR_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.HEART_PULSE_RATE_MONITOR
|
||||||
|
HEALTH_HEALTH_DATA_DISPLAY_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.HEALTH_DATA_DISPLAY
|
||||||
|
HEALTH_STEP_COUNTER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.STEP_COUNTER
|
||||||
|
HEALTH_BODY_COMPOSITION_ANALYZER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.BODY_COMPOSITION_ANALYZER
|
||||||
|
HEALTH_PEAK_FLOW_MONITOR_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.PEAK_FLOW_MONITOR
|
||||||
|
HEALTH_MEDICATION_MONITOR_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.MEDICATION_MONITOR
|
||||||
|
HEALTH_KNEE_PROSTHESIS_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.KNEE_PROSTHESIS
|
||||||
|
HEALTH_ANKLE_PROSTHESIS_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.ANKLE_PROSTHESIS
|
||||||
|
HEALTH_GENERIC_HEALTH_MANAGER_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.GENERIC_HEALTH_MANAGER
|
||||||
|
HEALTH_PERSONAL_MOBILITY_DEVICE_MINOR_DEVICE_CLASS = ClassOfDevice.HealthMinorDeviceClass.PERSONAL_MOBILITY_DEVICE
|
||||||
|
|
||||||
# fmt: on
|
# fmt: on
|
||||||
# pylint: enable=line-too-long
|
# pylint: enable=line-too-long
|
||||||
|
|
||||||
@@ -711,16 +945,16 @@ class DeviceClass:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def service_class_labels(service_class_flags):
|
def service_class_labels(service_class_flags):
|
||||||
return bit_flags_to_strings(
|
return bit_flags_to_strings(
|
||||||
service_class_flags, DeviceClass.SERVICE_CLASS_LABELS
|
service_class_flags, ClassOfDevice.MAJOR_SERVICE_CLASS_LABELS
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def major_device_class_name(device_class):
|
def major_device_class_name(device_class):
|
||||||
return name_or_number(DeviceClass.MAJOR_DEVICE_CLASS_NAMES, device_class)
|
return name_or_number(ClassOfDevice.MAJOR_DEVICE_CLASS_LABELS, device_class)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def minor_device_class_name(major_device_class, minor_device_class):
|
def minor_device_class_name(major_device_class, minor_device_class):
|
||||||
class_names = DeviceClass.MINOR_DEVICE_CLASS_NAMES.get(major_device_class)
|
class_names = ClassOfDevice.MINOR_DEVICE_CLASS_LABELS.get(major_device_class)
|
||||||
if class_names is None:
|
if class_names is None:
|
||||||
return f'#{minor_device_class:02X}'
|
return f'#{minor_device_class:02X}'
|
||||||
return name_or_number(class_names, minor_device_class)
|
return name_or_number(class_names, minor_device_class)
|
||||||
@@ -1255,6 +1489,10 @@ class Appearance:
|
|||||||
category = cls.Category(appearance >> 6)
|
category = cls.Category(appearance >> 6)
|
||||||
return cls(category, appearance & 0x3F)
|
return cls(category, appearance & 0x3F)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bytes(cls, data: bytes):
|
||||||
|
return cls.from_int(int.from_bytes(data, byteorder="little"))
|
||||||
|
|
||||||
def __init__(self, category: Category, subcategory: int) -> None:
|
def __init__(self, category: Category, subcategory: int) -> None:
|
||||||
self.category = category
|
self.category = category
|
||||||
if subcategory_class := self.SUBCATEGORY_CLASSES.get(category):
|
if subcategory_class := self.SUBCATEGORY_CLASSES.get(category):
|
||||||
@@ -1265,6 +1503,9 @@ class Appearance:
|
|||||||
def __int__(self) -> int:
|
def __int__(self) -> int:
|
||||||
return self.category << 6 | self.subcategory
|
return self.category << 6 | self.subcategory
|
||||||
|
|
||||||
|
def __bytes__(self) -> bytes:
|
||||||
|
return int(self).to_bytes(2, byteorder="little")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
'Appearance('
|
'Appearance('
|
||||||
@@ -1276,6 +1517,61 @@ class Appearance:
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.category.name}/{self.subcategory.name}'
|
return f'{self.category.name}/{self.subcategory.name}'
|
||||||
|
|
||||||
|
def __eq__(self, value: Any) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(value, Appearance)
|
||||||
|
and self.category == value.category
|
||||||
|
and self.subcategory == value.subcategory
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Classes representing "Data Types" defined in
|
||||||
|
# "Supplement to the Bluetooth Core Specification", Part A
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# TODO: use ABC, figure out multiple base classes with metaclasses
|
||||||
|
class DataType:
|
||||||
|
# Human-reable label/name for the type
|
||||||
|
label = ""
|
||||||
|
|
||||||
|
# Advertising Data type ID for this data type.
|
||||||
|
ad_type: AdvertisingData.Type = 0 # type: ignore
|
||||||
|
|
||||||
|
def value_string(self) -> str:
|
||||||
|
"""Human-reable string representation of the value."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def to_string(self, use_label: bool = False) -> str:
|
||||||
|
if use_label:
|
||||||
|
return f"[{self.label}]: {self.value_string()}"
|
||||||
|
|
||||||
|
return f"{self.__class__.__name__}({self.value_string()})"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_advertising_data(cls, advertising_data: AdvertisingData) -> Optional[Self]:
|
||||||
|
if (data := advertising_data.get(cls.ad_type, raw=True)) is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return cls.from_bytes(data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_from_advertising_data(cls, advertising_data: AdvertisingData) -> list[Self]:
|
||||||
|
return [
|
||||||
|
cls.from_bytes(data)
|
||||||
|
for data in advertising_data.get_all(cls.ad_type, raw=True)
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bytes(cls, data: bytes) -> Self:
|
||||||
|
"""Create an instance from a serialized form."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def __bytes__(self) -> bytes:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.to_string()
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Advertising Data
|
# Advertising Data
|
||||||
@@ -1419,15 +1715,25 @@ class AdvertisingData:
|
|||||||
BR_EDR_CONTROLLER_FLAG = Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
|
BR_EDR_CONTROLLER_FLAG = Flags.SIMULTANEOUS_LE_BR_EDR_CAPABLE
|
||||||
BR_EDR_HOST_FLAG = 0x10 # Deprecated
|
BR_EDR_HOST_FLAG = 0x10 # Deprecated
|
||||||
|
|
||||||
ad_structures: list[tuple[int, bytes]]
|
ad_structures: list[tuple[AdvertisingData.Type, bytes]]
|
||||||
|
|
||||||
# fmt: on
|
# fmt: on
|
||||||
# pylint: enable=line-too-long
|
# pylint: enable=line-too-long
|
||||||
|
|
||||||
def __init__(self, ad_structures: Optional[list[tuple[int, bytes]]] = None) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
ad_structures: Optional[Iterable[Union[tuple[int, bytes], DataType]]] = None,
|
||||||
|
) -> None:
|
||||||
if ad_structures is None:
|
if ad_structures is None:
|
||||||
ad_structures = []
|
ad_structures = []
|
||||||
self.ad_structures = ad_structures[:]
|
self.ad_structures = [
|
||||||
|
(
|
||||||
|
(element.ad_type, bytes(element))
|
||||||
|
if isinstance(element, DataType)
|
||||||
|
else (AdvertisingData.Type(element[0]), element[1])
|
||||||
|
)
|
||||||
|
for element in ad_structures
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, data: bytes) -> AdvertisingData:
|
def from_bytes(cls, data: bytes) -> AdvertisingData:
|
||||||
@@ -1444,11 +1750,10 @@ class AdvertisingData:
|
|||||||
'LE Limited Discoverable Mode',
|
'LE Limited Discoverable Mode',
|
||||||
'LE General Discoverable Mode',
|
'LE General Discoverable Mode',
|
||||||
'BR/EDR Not Supported',
|
'BR/EDR Not Supported',
|
||||||
'Simultaneous LE and BR/EDR (Controller)',
|
'Simultaneous LE and BR/EDR',
|
||||||
'Simultaneous LE and BR/EDR (Host)',
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
return ','.join(bit_flags_to_strings(flags, flag_names))
|
return ', '.join(bit_flags_to_strings(flags, flag_names))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> list[UUID]:
|
def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> list[UUID]:
|
||||||
@@ -1604,7 +1909,7 @@ class AdvertisingData:
|
|||||||
if length > 0:
|
if length > 0:
|
||||||
ad_type = data[offset]
|
ad_type = data[offset]
|
||||||
ad_data = data[offset + 1 : offset + length]
|
ad_data = data[offset + 1 : offset + length]
|
||||||
self.ad_structures.append((ad_type, ad_data))
|
self.ad_structures.append((AdvertisingData.Type(ad_type), ad_data))
|
||||||
offset += length
|
offset += length
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@@ -1623,6 +1928,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[list[UUID]]: ...
|
) -> list[list[UUID]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
@@ -1633,6 +1939,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[tuple[UUID, bytes]]: ...
|
) -> list[tuple[UUID, bytes]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
@@ -1644,6 +1951,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[str]: ...
|
) -> list[str]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
@@ -1655,26 +1963,31 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[int]: ...
|
) -> list[int]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
|
type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[tuple[int, int]]: ...
|
) -> list[tuple[int, int]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
|
type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[tuple[int, bytes]]: ...
|
) -> list[tuple[int, bytes]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.APPEARANCE,],
|
type_id: Literal[AdvertisingData.Type.APPEARANCE,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> list[Appearance]: ...
|
) -> list[Appearance]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(self, type_id: int, raw: Literal[True]) -> list[bytes]: ...
|
def get_all(self, type_id: int, raw: Literal[True]) -> list[bytes]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_all(
|
def get_all(
|
||||||
self, type_id: int, raw: bool = False
|
self, type_id: int, raw: bool = False
|
||||||
@@ -1682,7 +1995,7 @@ class AdvertisingData:
|
|||||||
|
|
||||||
def get_all(self, type_id: int, raw: bool = False) -> list[AdvertisingDataObject]: # type: ignore[misc]
|
def get_all(self, type_id: int, raw: bool = False) -> list[AdvertisingDataObject]: # type: ignore[misc]
|
||||||
'''
|
'''
|
||||||
Get Advertising Data Structure(s) with a given type
|
Get all advertising data elements as simple AdvertisingDataObject objects.
|
||||||
|
|
||||||
Returns a (possibly empty) list of matches.
|
Returns a (possibly empty) list of matches.
|
||||||
'''
|
'''
|
||||||
@@ -1708,6 +2021,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[list[UUID]]: ...
|
) -> Optional[list[UUID]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
@@ -1718,6 +2032,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[tuple[UUID, bytes]]: ...
|
) -> Optional[tuple[UUID, bytes]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
@@ -1729,6 +2044,7 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[Optional[str]]: ...
|
) -> Optional[Optional[str]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
@@ -1740,26 +2056,31 @@ class AdvertisingData:
|
|||||||
],
|
],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[int]: ...
|
) -> Optional[int]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
|
type_id: Literal[AdvertisingData.Type.PERIPHERAL_CONNECTION_INTERVAL_RANGE,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[tuple[int, int]]: ...
|
) -> Optional[tuple[int, int]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
|
type_id: Literal[AdvertisingData.Type.MANUFACTURER_SPECIFIC_DATA,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[tuple[int, bytes]]: ...
|
) -> Optional[tuple[int, bytes]]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
type_id: Literal[AdvertisingData.Type.APPEARANCE,],
|
type_id: Literal[AdvertisingData.Type.APPEARANCE,],
|
||||||
raw: Literal[False] = False,
|
raw: Literal[False] = False,
|
||||||
) -> Optional[Appearance]: ...
|
) -> Optional[Appearance]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(self, type_id: int, raw: Literal[True]) -> Optional[bytes]: ...
|
def get(self, type_id: int, raw: Literal[True]) -> Optional[bytes]: ...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get(
|
def get(
|
||||||
self, type_id: int, raw: bool = False
|
self, type_id: int, raw: bool = False
|
||||||
@@ -1767,7 +2088,7 @@ class AdvertisingData:
|
|||||||
|
|
||||||
def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
|
def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingDataObject]:
|
||||||
'''
|
'''
|
||||||
Get Advertising Data Structure(s) with a given type
|
Get advertising data as a simple AdvertisingDataObject object.
|
||||||
|
|
||||||
Returns the first entry, or None if no structure matches.
|
Returns the first entry, or None if no structure matches.
|
||||||
'''
|
'''
|
||||||
@@ -1822,7 +2143,22 @@ class ConnectionPHY:
|
|||||||
# LE Role
|
# LE Role
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class LeRole(enum.IntEnum):
|
class LeRole(enum.IntEnum):
|
||||||
|
# fmt: off
|
||||||
PERIPHERAL_ONLY = 0x00
|
PERIPHERAL_ONLY = 0x00
|
||||||
CENTRAL_ONLY = 0x01
|
CENTRAL_ONLY = 0x01
|
||||||
BOTH_PERIPHERAL_PREFERRED = 0x02
|
BOTH_PERIPHERAL_PREFERRED = 0x02
|
||||||
BOTH_CENTRAL_PREFERRED = 0x03
|
BOTH_CENTRAL_PREFERRED = 0x03
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Security Manager OOB Flag
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
class SecurityManagerOutOfBandFlag(enum.IntFlag):
|
||||||
|
"""
|
||||||
|
See Supplement to the Bluetooth Core Specification, Part A
|
||||||
|
1.7 SECURITY MANAGER OUT OF BAND (OOB)
|
||||||
|
"""
|
||||||
|
|
||||||
|
OOB_FLAGS_FIELD = 1 << 0
|
||||||
|
LE_SUPPORTED = 1 << 1
|
||||||
|
ADDRESS_TYPE = 1 << 3
|
||||||
|
|||||||
1018
bumble/data_types.py
Normal file
1018
bumble/data_types.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,7 +45,18 @@ from typing import (
|
|||||||
|
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from bumble import core, gatt_client, gatt_server, hci, l2cap, pairing, sdp, smp, utils
|
from bumble import (
|
||||||
|
core,
|
||||||
|
data_types,
|
||||||
|
gatt_client,
|
||||||
|
gatt_server,
|
||||||
|
hci,
|
||||||
|
l2cap,
|
||||||
|
pairing,
|
||||||
|
sdp,
|
||||||
|
smp,
|
||||||
|
utils,
|
||||||
|
)
|
||||||
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
from bumble.att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import (
|
from bumble.core import (
|
||||||
@@ -2049,9 +2060,7 @@ class DeviceConfiguration:
|
|||||||
connectable: bool = True
|
connectable: bool = True
|
||||||
discoverable: bool = True
|
discoverable: bool = True
|
||||||
advertising_data: bytes = bytes(
|
advertising_data: bytes = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData([data_types.CompleteLocalName(DEVICE_DEFAULT_NAME)])
|
||||||
[(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(DEVICE_DEFAULT_NAME, 'utf-8'))]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
irk: bytes = bytes(16) # This really must be changed for any level of security
|
irk: bytes = bytes(16) # This really must be changed for any level of security
|
||||||
keystore: Optional[str] = None
|
keystore: Optional[str] = None
|
||||||
@@ -2095,9 +2104,7 @@ class DeviceConfiguration:
|
|||||||
self.advertising_data = bytes.fromhex(advertising_data)
|
self.advertising_data = bytes.fromhex(advertising_data)
|
||||||
elif name is not None:
|
elif name is not None:
|
||||||
self.advertising_data = bytes(
|
self.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData([data_types.CompleteLocalName(self.name)])
|
||||||
[(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Load scan response data
|
# Load scan response data
|
||||||
@@ -3544,14 +3551,7 @@ class Device(utils.CompositeEventEmitter):
|
|||||||
# Synthesize an inquiry response if none is set already
|
# Synthesize an inquiry response if none is set already
|
||||||
if self.inquiry_response is None:
|
if self.inquiry_response is None:
|
||||||
self.inquiry_response = bytes(
|
self.inquiry_response = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData([data_types.CompleteLocalName(self.name)])
|
||||||
[
|
|
||||||
(
|
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
|
||||||
bytes(self.name, 'utf-8'),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the controller
|
# Update the controller
|
||||||
|
|||||||
@@ -2135,6 +2135,7 @@ class Address:
|
|||||||
if len(address) == 12 + 5:
|
if len(address) == 12 + 5:
|
||||||
# Form with ':' separators
|
# Form with ':' separators
|
||||||
address = address.replace(':', '')
|
address = address.replace(':', '')
|
||||||
|
|
||||||
self.address_bytes = bytes(reversed(bytes.fromhex(address)))
|
self.address_bytes = bytes(reversed(bytes.fromhex(address)))
|
||||||
|
|
||||||
if len(self.address_bytes) != 6:
|
if len(self.address_bytes) != 6:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
from typing import Any, Callable, Optional, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from bumble import gatt, gatt_client, l2cap, utils
|
from bumble import data_types, gatt, gatt_client, l2cap, utils
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Connection, Device
|
from bumble.device import Connection, Device
|
||||||
|
|
||||||
@@ -185,12 +185,11 @@ class AshaService(gatt.TemplateService):
|
|||||||
return bytes(
|
return bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.ServiceData16BitUUID(
|
||||||
AdvertisingData.SERVICE_DATA_16_BIT_UUID,
|
gatt.GATT_ASHA_SERVICE,
|
||||||
bytes(gatt.GATT_ASHA_SERVICE)
|
bytes([self.protocol_version, self.capability])
|
||||||
+ bytes([self.protocol_version, self.capability])
|
|
||||||
+ self.hisyncid[:4],
|
+ self.hisyncid[:4],
|
||||||
),
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from collections.abc import Sequence
|
|||||||
|
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from bumble import core, gatt, hci, utils
|
from bumble import core, data_types, gatt, hci, utils
|
||||||
from bumble.profiles import le_audio
|
from bumble.profiles import le_audio
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -257,11 +257,10 @@ class UnicastServerAdvertisingData:
|
|||||||
return bytes(
|
return bytes(
|
||||||
core.AdvertisingData(
|
core.AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.ServiceData16BitUUID(
|
||||||
core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
|
gatt.GATT_AUDIO_STREAM_CONTROL_SERVICE,
|
||||||
struct.pack(
|
struct.pack(
|
||||||
'<2sBIB',
|
'<BIB',
|
||||||
bytes(gatt.GATT_AUDIO_STREAM_CONTROL_SERVICE),
|
|
||||||
self.announcement_type,
|
self.announcement_type,
|
||||||
self.available_audio_contexts,
|
self.available_audio_contexts,
|
||||||
len(self.metadata),
|
len(self.metadata),
|
||||||
@@ -490,12 +489,8 @@ class BroadcastAudioAnnouncement:
|
|||||||
return bytes(
|
return bytes(
|
||||||
core.AdvertisingData(
|
core.AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.ServiceData16BitUUID(
|
||||||
core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
|
gatt.GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE, bytes(self)
|
||||||
(
|
|
||||||
bytes(gatt.GATT_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE)
|
|
||||||
+ bytes(self)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -607,12 +602,8 @@ class BasicAudioAnnouncement:
|
|||||||
return bytes(
|
return bytes(
|
||||||
core.AdvertisingData(
|
core.AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.ServiceData16BitUUID(
|
||||||
core.AdvertisingData.SERVICE_DATA_16_BIT_UUID,
|
gatt.GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE, bytes(self)
|
||||||
(
|
|
||||||
bytes(gatt.GATT_BASIC_AUDIO_ANNOUNCEMENT_SERVICE)
|
|
||||||
+ bytes(self)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.profiles.battery_service import BatteryService
|
from bumble.profiles.battery_service import BatteryService
|
||||||
@@ -47,15 +48,14 @@ async def main() -> None:
|
|||||||
device.advertising_data = bytes(
|
device.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble Battery'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
bytes('Bumble Battery', 'utf-8'),
|
[battery_service.uuid]
|
||||||
),
|
),
|
||||||
(
|
data_types.Appearance(
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
data_types.Appearance.Category.WEARABLE_AUDIO_DEVICE,
|
||||||
bytes(battery_service.uuid),
|
data_types.Appearance.WearableAudioDeviceSubcategory.EARBUD,
|
||||||
),
|
),
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.profiles.device_information_service import DeviceInformationService
|
from bumble.profiles.device_information_service import DeviceInformationService
|
||||||
@@ -53,11 +54,11 @@ async def main() -> None:
|
|||||||
device.advertising_data = bytes(
|
device.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble Device'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Appearance(
|
||||||
bytes('Bumble Device', 'utf-8'),
|
data_types.Appearance.Category.HEART_RATE_SENSOR,
|
||||||
|
data_types.Appearance.HeartRateSensorSubcategory.GENERIC_HEART_RATE_SENSOR,
|
||||||
),
|
),
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.profiles.device_information_service import DeviceInformationService
|
from bumble.profiles.device_information_service import DeviceInformationService
|
||||||
@@ -88,15 +89,14 @@ async def main() -> None:
|
|||||||
device.advertising_data = bytes(
|
device.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble Heart'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
bytes('Bumble Heart', 'utf-8'),
|
[heart_rate_service.uuid]
|
||||||
),
|
),
|
||||||
(
|
data_types.Appearance(
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
data_types.Appearance.Category.HEART_RATE_SENSOR,
|
||||||
bytes(heart_rate_service.uuid),
|
data_types.Appearance.HeartRateSensorSubcategory.GENERIC_HEART_RATE_SENSOR,
|
||||||
),
|
),
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import sys
|
|||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Connection, Device, Peer
|
from bumble.device import Connection, Device, Peer
|
||||||
@@ -341,16 +342,18 @@ async def keyboard_device(device, command):
|
|||||||
device.advertising_data = bytes(
|
device.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble Keyboard'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
bytes('Bumble Keyboard', 'utf-8'),
|
[GATT_HUMAN_INTERFACE_DEVICE_SERVICE]
|
||||||
),
|
),
|
||||||
(
|
data_types.Appearance(
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
data_types.Appearance.Category.HUMAN_INTERFACE_DEVICE,
|
||||||
bytes(GATT_HUMAN_INTERFACE_DEVICE_SERVICE),
|
data_types.Appearance.HumanInterfaceDeviceSubcategory.KEYBOARD,
|
||||||
|
),
|
||||||
|
data_types.Flags(
|
||||||
|
AdvertisingData.Flags.LE_LIMITED_DISCOVERABLE_MODE
|
||||||
|
| AdvertisingData.Flags.BR_EDR_NOT_SUPPORTED
|
||||||
),
|
),
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x03C1)),
|
|
||||||
(AdvertisingData.FLAGS, bytes([0x05])),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import AdvertisingType, Device
|
from bumble.device import AdvertisingType, Device
|
||||||
from bumble.hci import Address
|
from bumble.hci import Address
|
||||||
@@ -60,7 +61,10 @@ async def main() -> None:
|
|||||||
device.scan_response_data = bytes(
|
device.scan_response_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
|
data_types.Appearance(
|
||||||
|
data_types.Appearance.Category.HEART_RATE_SENSOR,
|
||||||
|
data_types.Appearance.HeartRateSensorSubcategory.GENERIC_HEART_RATE_SENSOR,
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from typing import Optional
|
|||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
from bumble import decoder, gatt
|
from bumble import data_types, decoder, gatt
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import AdvertisingParameters, Device
|
from bumble.device import AdvertisingParameters, Device
|
||||||
from bumble.profiles import asha
|
from bumble.profiles import asha
|
||||||
@@ -78,14 +78,10 @@ async def main() -> None:
|
|||||||
bytes(
|
bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName(device.name),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(AdvertisingData.Flags(0x06)),
|
||||||
bytes(device.name, 'utf-8'),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
),
|
[gatt.GATT_ASHA_SERVICE]
|
||||||
(AdvertisingData.FLAGS, bytes([0x06])),
|
|
||||||
(
|
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(gatt.GATT_ASHA_SERVICE),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import secrets
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.hci import Address
|
from bumble.hci import Address
|
||||||
@@ -66,23 +67,14 @@ async def main() -> None:
|
|||||||
bytes(
|
bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName(f'Bumble LE Audio-{i}'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(
|
||||||
bytes(f'Bumble LE Audio-{i}', 'utf-8'),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.FLAGS,
|
|
||||||
bytes(
|
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
||||||
| AdvertisingData.BR_EDR_HOST_FLAG
|
| AdvertisingData.BR_EDR_HOST_FLAG
|
||||||
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
||||||
]
|
|
||||||
),
|
),
|
||||||
),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
(
|
[CoordinatedSetIdentificationService.UUID]
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(CoordinatedSetIdentificationService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import asyncio
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.profiles.hap import (
|
from bumble.profiles.hap import (
|
||||||
@@ -71,23 +72,14 @@ async def main() -> None:
|
|||||||
advertising_data = bytes(
|
advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble HearingAccessService'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(
|
||||||
bytes('Bumble HearingAccessService', 'utf-8'),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.FLAGS,
|
|
||||||
bytes(
|
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
||||||
| AdvertisingData.BR_EDR_HOST_FLAG
|
| AdvertisingData.BR_EDR_HOST_FLAG
|
||||||
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
||||||
]
|
|
||||||
),
|
),
|
||||||
),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
(
|
[HearingAccessService.UUID]
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(HearingAccessService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from typing import Optional
|
|||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import (
|
from bumble.device import (
|
||||||
AdvertisingEventProperties,
|
AdvertisingEventProperties,
|
||||||
@@ -106,17 +107,10 @@ async def main() -> None:
|
|||||||
advertising_data = bytes(
|
advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble LE Audio'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG),
|
||||||
bytes('Bumble LE Audio', 'utf-8'),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
),
|
[PublishedAudioCapabilitiesService.UUID]
|
||||||
(
|
|
||||||
AdvertisingData.FLAGS,
|
|
||||||
bytes([AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG]),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(PublishedAudioCapabilitiesService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.hci import CodecID, CodingFormat, HCI_IsoDataPacket
|
from bumble.hci import CodecID, CodingFormat, HCI_IsoDataPacket
|
||||||
@@ -111,23 +112,14 @@ async def main() -> None:
|
|||||||
bytes(
|
bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble LE Audio'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(
|
||||||
bytes('Bumble LE Audio', 'utf-8'),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.FLAGS,
|
|
||||||
bytes(
|
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
||||||
| AdvertisingData.BR_EDR_HOST_FLAG
|
| AdvertisingData.BR_EDR_HOST_FLAG
|
||||||
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
||||||
]
|
|
||||||
),
|
),
|
||||||
),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
(
|
[PublishedAudioCapabilitiesService.UUID]
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(PublishedAudioCapabilitiesService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from typing import Optional
|
|||||||
import websockets
|
import websockets
|
||||||
|
|
||||||
import bumble.logging
|
import bumble.logging
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import AdvertisingEventProperties, AdvertisingParameters, Device
|
from bumble.device import AdvertisingEventProperties, AdvertisingParameters, Device
|
||||||
from bumble.hci import CodecID, CodingFormat, OwnAddressType
|
from bumble.hci import CodecID, CodingFormat, OwnAddressType
|
||||||
@@ -127,23 +128,14 @@ async def main() -> None:
|
|||||||
bytes(
|
bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.CompleteLocalName('Bumble LE Audio'),
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Flags(
|
||||||
bytes('Bumble LE Audio', 'utf-8'),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
AdvertisingData.FLAGS,
|
|
||||||
bytes(
|
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
||||||
| AdvertisingData.BR_EDR_HOST_FLAG
|
| AdvertisingData.BR_EDR_HOST_FLAG
|
||||||
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
| AdvertisingData.BR_EDR_CONTROLLER_FLAG
|
||||||
]
|
|
||||||
),
|
),
|
||||||
),
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
(
|
[PublishedAudioCapabilitiesService.UUID]
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(PublishedAudioCapabilitiesService.UUID),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,7 +16,13 @@
|
|||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
from bumble.core import UUID, AdvertisingData, Appearance, get_dict_key_by_value
|
from bumble.core import (
|
||||||
|
UUID,
|
||||||
|
AdvertisingData,
|
||||||
|
Appearance,
|
||||||
|
ClassOfDevice,
|
||||||
|
get_dict_key_by_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -93,6 +99,24 @@ def test_appearance() -> None:
|
|||||||
assert int(a) == 0x3333
|
assert int(a) == 0x3333
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def test_class_of_device() -> None:
|
||||||
|
c1 = ClassOfDevice(
|
||||||
|
ClassOfDevice.MajorServiceClasses.AUDIO
|
||||||
|
| ClassOfDevice.MajorServiceClasses.RENDERING,
|
||||||
|
ClassOfDevice.MajorDeviceClass.AUDIO_VIDEO,
|
||||||
|
ClassOfDevice.AudioVideoMinorDeviceClass.CAMCORDER,
|
||||||
|
)
|
||||||
|
assert str(c1) == "ClassOfDevice(RENDERING|AUDIO,AUDIO_VIDEO/CAMCORDER)"
|
||||||
|
|
||||||
|
c2 = ClassOfDevice(
|
||||||
|
ClassOfDevice.MajorServiceClasses.AUDIO,
|
||||||
|
ClassOfDevice.MajorDeviceClass.AUDIO_VIDEO,
|
||||||
|
0x123,
|
||||||
|
)
|
||||||
|
assert str(c2) == "ClassOfDevice(AUDIO,AUDIO_VIDEO/0x123)"
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_ad_data()
|
test_ad_data()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from bumble import data_types
|
||||||
from bumble.core import AdvertisingData
|
from bumble.core import AdvertisingData
|
||||||
from bumble.device import Device
|
from bumble.device import Device
|
||||||
from bumble.hci import HCI_Reset_Command
|
from bumble.hci import HCI_Reset_Command
|
||||||
@@ -65,24 +66,18 @@ class HeartRateMonitor:
|
|||||||
self.device.advertising_data = bytes(
|
self.device.advertising_data = bytes(
|
||||||
AdvertisingData(
|
AdvertisingData(
|
||||||
[
|
[
|
||||||
(
|
data_types.Flags(
|
||||||
AdvertisingData.FLAGS,
|
AdvertisingData.Flags.LE_GENERAL_DISCOVERABLE_MODE
|
||||||
bytes(
|
| AdvertisingData.Flags.BR_EDR_NOT_SUPPORTED
|
||||||
[
|
|
||||||
AdvertisingData.LE_GENERAL_DISCOVERABLE_MODE_FLAG
|
|
||||||
| AdvertisingData.BR_EDR_NOT_SUPPORTED_FLAG
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
|
data_types.CompleteLocalName('Bumble Heart'),
|
||||||
|
data_types.IncompleteListOf16BitServiceUUIDs(
|
||||||
|
[self.heart_rate_service.uuid]
|
||||||
),
|
),
|
||||||
(
|
data_types.Appearance(
|
||||||
AdvertisingData.COMPLETE_LOCAL_NAME,
|
data_types.Appearance.Category.HEART_RATE_SENSOR,
|
||||||
bytes('Bumble Heart', 'utf-8'),
|
data_types.Appearance.HeartRateSensorSubcategory.GENERIC_HEART_RATE_SENSOR,
|
||||||
),
|
),
|
||||||
(
|
|
||||||
AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS,
|
|
||||||
bytes(self.heart_rate_service.uuid),
|
|
||||||
),
|
|
||||||
(AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user