1# Copyright 2024 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# ----------------------------------------------------------------------------- 16# Imports 17# ----------------------------------------------------------------------------- 18from __future__ import annotations 19import dataclasses 20import struct 21from typing import List, Type 22from typing_extensions import Self 23 24from bumble import utils 25 26 27# ----------------------------------------------------------------------------- 28# Classes 29# ----------------------------------------------------------------------------- 30@dataclasses.dataclass 31class Metadata: 32 '''Bluetooth Assigned Numbers, Section 6.12.6 - Metadata LTV structures. 33 34 As Metadata fields may extend, and Spec doesn't forbid duplication, we don't parse 35 Metadata into a key-value style dataclass here. Rather, we encourage users to parse 36 again outside the lib. 37 ''' 38 39 class Tag(utils.OpenIntEnum): 40 # fmt: off 41 PREFERRED_AUDIO_CONTEXTS = 0x01 42 STREAMING_AUDIO_CONTEXTS = 0x02 43 PROGRAM_INFO = 0x03 44 LANGUAGE = 0x04 45 CCID_LIST = 0x05 46 PARENTAL_RATING = 0x06 47 PROGRAM_INFO_URI = 0x07 48 AUDIO_ACTIVE_STATE = 0x08 49 BROADCAST_AUDIO_IMMEDIATE_RENDERING_FLAG = 0x09 50 ASSISTED_LISTENING_STREAM = 0x0A 51 BROADCAST_NAME = 0x0B 52 EXTENDED_METADATA = 0xFE 53 VENDOR_SPECIFIC = 0xFF 54 55 @dataclasses.dataclass 56 class Entry: 57 tag: Metadata.Tag 58 data: bytes 59 60 @classmethod 61 def from_bytes(cls: Type[Self], data: bytes) -> Self: 62 return cls(tag=Metadata.Tag(data[0]), data=data[1:]) 63 64 def __bytes__(self) -> bytes: 65 return bytes([len(self.data) + 1, self.tag]) + self.data 66 67 entries: List[Entry] = dataclasses.field(default_factory=list) 68 69 @classmethod 70 def from_bytes(cls: Type[Self], data: bytes) -> Self: 71 entries = [] 72 offset = 0 73 length = len(data) 74 while offset < length: 75 entry_length = data[offset] 76 offset += 1 77 entries.append(cls.Entry.from_bytes(data[offset : offset + entry_length])) 78 offset += entry_length 79 80 return cls(entries) 81 82 def __bytes__(self) -> bytes: 83 return b''.join([bytes(entry) for entry in self.entries]) 84