forked from auracaster/bumble_mirror
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5275ade29 | |||
| e7b39c4188 | |||
| 0594eaef09 | |||
| 05200284d2 | |||
| d21da78aa3 |
+1
-1
@@ -27,7 +27,6 @@ import re
|
||||
from collections import OrderedDict
|
||||
|
||||
import click
|
||||
import colors
|
||||
|
||||
from prompt_toolkit import Application
|
||||
from prompt_toolkit.history import FileHistory
|
||||
@@ -53,6 +52,7 @@ from prompt_toolkit.layout import (
|
||||
|
||||
from bumble import __version__
|
||||
import bumble.core
|
||||
from bumble import colors
|
||||
from bumble.core import UUID, AdvertisingData, BT_LE_TRANSPORT
|
||||
from bumble.device import ConnectionParametersPreferences, Device, Connection, Peer
|
||||
from bumble.utils import AsyncRunner
|
||||
|
||||
@@ -19,9 +19,9 @@ import asyncio
|
||||
import os
|
||||
import logging
|
||||
import click
|
||||
from colors import color
|
||||
from bumble.company_ids import COMPANY_IDENTIFIERS
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.core import name_or_number
|
||||
from bumble.hci import (
|
||||
map_null_terminated_utf8_string,
|
||||
|
||||
+1
-1
@@ -19,9 +19,9 @@ import asyncio
|
||||
import os
|
||||
import logging
|
||||
import click
|
||||
from colors import color
|
||||
|
||||
import bumble.core
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device, Peer
|
||||
from bumble.gatt import show_services
|
||||
from bumble.transport import open_transport_or_link
|
||||
|
||||
+1
-1
@@ -20,8 +20,8 @@ import os
|
||||
import struct
|
||||
import logging
|
||||
import click
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device, Peer
|
||||
from bumble.core import AdvertisingData
|
||||
from bumble.gatt import Service, Characteristic, CharacteristicValue
|
||||
|
||||
@@ -19,8 +19,8 @@ import asyncio
|
||||
import logging
|
||||
import os
|
||||
import click
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.device import Device
|
||||
from bumble.utils import FlowControlAsyncPipe
|
||||
|
||||
@@ -23,9 +23,10 @@ import argparse
|
||||
import uuid
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
from colors import color
|
||||
import websockets
|
||||
|
||||
from bumble.colors import color
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
+1
-1
@@ -19,9 +19,9 @@ import asyncio
|
||||
import os
|
||||
import logging
|
||||
import click
|
||||
from colors import color
|
||||
from prompt_toolkit.shortcuts import PromptSession
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device, Peer
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.smp import PairingDelegate, PairingConfig
|
||||
|
||||
+1
-1
@@ -19,8 +19,8 @@ import asyncio
|
||||
import os
|
||||
import logging
|
||||
import click
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.keys import JsonKeyStore
|
||||
|
||||
+1
-1
@@ -17,8 +17,8 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
import struct
|
||||
import click
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble import hci
|
||||
from bumble.transport.common import PacketReader
|
||||
from bumble.helpers import PacketTracer
|
||||
|
||||
+1
-1
@@ -30,8 +30,8 @@ import os
|
||||
import logging
|
||||
import click
|
||||
import usb1
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.transport.usb import load_libusb
|
||||
|
||||
|
||||
|
||||
+45
-30
@@ -18,7 +18,6 @@
|
||||
import struct
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
import construct
|
||||
|
||||
from .company_ids import COMPANY_IDENTIFIERS
|
||||
from .sdp import (
|
||||
@@ -258,17 +257,6 @@ class SbcMediaCodecInformation(
|
||||
A2DP spec - 4.3.2 Codec Specific Information Elements
|
||||
'''
|
||||
|
||||
BIT_FIELDS = construct.Bitwise(
|
||||
construct.Sequence(
|
||||
construct.BitsInteger(4),
|
||||
construct.BitsInteger(4),
|
||||
construct.BitsInteger(4),
|
||||
construct.BitsInteger(2),
|
||||
construct.BitsInteger(2),
|
||||
construct.BitsInteger(8),
|
||||
construct.BitsInteger(8),
|
||||
)
|
||||
)
|
||||
SAMPLING_FREQUENCY_BITS = {16000: 1 << 3, 32000: 1 << 2, 44100: 1 << 1, 48000: 1}
|
||||
CHANNEL_MODE_BITS = {
|
||||
SBC_MONO_CHANNEL_MODE: 1 << 3,
|
||||
@@ -284,9 +272,22 @@ class SbcMediaCodecInformation(
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_bytes(data):
|
||||
def from_bytes(data: bytes) -> 'SbcMediaCodecInformation':
|
||||
sampling_frequency = (data[0] >> 4) & 0x0F
|
||||
channel_mode = (data[0] >> 0) & 0x0F
|
||||
block_length = (data[1] >> 4) & 0x0F
|
||||
subbands = (data[1] >> 2) & 0x03
|
||||
allocation_method = (data[1] >> 0) & 0x03
|
||||
minimum_bitpool_value = (data[2] >> 0) & 0xFF
|
||||
maximum_bitpool_value = (data[3] >> 0) & 0xFF
|
||||
return SbcMediaCodecInformation(
|
||||
*SbcMediaCodecInformation.BIT_FIELDS.parse(data)
|
||||
sampling_frequency,
|
||||
channel_mode,
|
||||
block_length,
|
||||
subbands,
|
||||
allocation_method,
|
||||
minimum_bitpool_value,
|
||||
maximum_bitpool_value,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -335,8 +336,17 @@ class SbcMediaCodecInformation(
|
||||
maximum_bitpool_value=maximum_bitpool_value,
|
||||
)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.BIT_FIELDS.build(self)
|
||||
def __bytes__(self) -> bytes:
|
||||
return bytes(
|
||||
[
|
||||
(self.sampling_frequency << 4) | self.channel_mode,
|
||||
(self.block_length << 4)
|
||||
| (self.subbands << 2)
|
||||
| self.allocation_method,
|
||||
self.minimum_bitpool_value,
|
||||
self.maximum_bitpool_value,
|
||||
]
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
channel_modes = ['MONO', 'DUAL_CHANNEL', 'STEREO', 'JOINT_STEREO']
|
||||
@@ -367,16 +377,6 @@ class AacMediaCodecInformation(
|
||||
A2DP spec - 4.5.2 Codec Specific Information Elements
|
||||
'''
|
||||
|
||||
BIT_FIELDS = construct.Bitwise(
|
||||
construct.Sequence(
|
||||
construct.BitsInteger(8),
|
||||
construct.BitsInteger(12),
|
||||
construct.BitsInteger(2),
|
||||
construct.BitsInteger(2),
|
||||
construct.BitsInteger(1),
|
||||
construct.BitsInteger(23),
|
||||
)
|
||||
)
|
||||
OBJECT_TYPE_BITS = {
|
||||
MPEG_2_AAC_LC_OBJECT_TYPE: 1 << 7,
|
||||
MPEG_4_AAC_LC_OBJECT_TYPE: 1 << 6,
|
||||
@@ -400,9 +400,15 @@ class AacMediaCodecInformation(
|
||||
CHANNELS_BITS = {1: 1 << 1, 2: 1}
|
||||
|
||||
@staticmethod
|
||||
def from_bytes(data):
|
||||
def from_bytes(data: bytes) -> 'AacMediaCodecInformation':
|
||||
object_type = data[0]
|
||||
sampling_frequency = (data[1] << 4) | ((data[2] >> 4) & 0x0F)
|
||||
channels = (data[2] >> 2) & 0x03
|
||||
rfa = 0
|
||||
vbr = (data[3] >> 7) & 0x01
|
||||
bitrate = ((data[3] & 0x7F) << 16) | (data[4] << 8) | data[5]
|
||||
return AacMediaCodecInformation(
|
||||
*AacMediaCodecInformation.BIT_FIELDS.parse(data)
|
||||
object_type, sampling_frequency, channels, rfa, vbr, bitrate
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -430,8 +436,17 @@ class AacMediaCodecInformation(
|
||||
bitrate=bitrate,
|
||||
)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.BIT_FIELDS.build(self)
|
||||
def __bytes__(self) -> bytes:
|
||||
return bytes(
|
||||
[
|
||||
self.object_type & 0xFF,
|
||||
(self.sampling_frequency >> 4) & 0xFF,
|
||||
(((self.sampling_frequency & 0x0F) << 4) | (self.channels << 2)) & 0xFF,
|
||||
((self.vbr << 7) | ((self.bitrate >> 16) & 0x7F)) & 0xFF,
|
||||
((self.bitrate >> 8) & 0xFF) & 0xFF,
|
||||
self.bitrate & 0xFF,
|
||||
]
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
object_types = [
|
||||
|
||||
+1
-1
@@ -24,12 +24,12 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
from __future__ import annotations
|
||||
import struct
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
from typing import Dict, Type
|
||||
|
||||
from bumble.core import UUID, name_or_number
|
||||
from bumble.hci import HCI_Object, key_with_value
|
||||
from bumble.colors import color
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
+1
-1
@@ -20,7 +20,6 @@ import asyncio
|
||||
import struct
|
||||
import time
|
||||
import logging
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
from typing import Dict, Type
|
||||
|
||||
@@ -40,6 +39,7 @@ from .a2dp import (
|
||||
VendorSpecificMediaCodecInformation,
|
||||
)
|
||||
from . import sdp
|
||||
from .colors import color
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from functools import partial
|
||||
from typing import List, Optional, Union
|
||||
|
||||
|
||||
# ANSI color names. There is also a "default"
|
||||
COLORS = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
|
||||
|
||||
# ANSI style names
|
||||
STYLES = (
|
||||
'none',
|
||||
'bold',
|
||||
'faint',
|
||||
'italic',
|
||||
'underline',
|
||||
'blink',
|
||||
'blink2',
|
||||
'negative',
|
||||
'concealed',
|
||||
'crossed',
|
||||
)
|
||||
|
||||
|
||||
ColorSpec = Union[str, int]
|
||||
|
||||
|
||||
def _join(*values: ColorSpec) -> str:
|
||||
return ';'.join(str(v) for v in values)
|
||||
|
||||
|
||||
def _color_code(spec: ColorSpec, base: int) -> str:
|
||||
if isinstance(spec, str):
|
||||
spec = spec.strip().lower()
|
||||
|
||||
if spec == 'default':
|
||||
return _join(base + 9)
|
||||
elif spec in COLORS:
|
||||
return _join(base + COLORS.index(spec))
|
||||
elif isinstance(spec, int) and 0 <= spec <= 255:
|
||||
return _join(base + 8, 5, spec)
|
||||
else:
|
||||
raise ValueError('Invalid color spec "%s"' % spec)
|
||||
|
||||
|
||||
def color(
|
||||
s: str,
|
||||
fg: Optional[ColorSpec] = None,
|
||||
bg: Optional[ColorSpec] = None,
|
||||
style: Optional[str] = None,
|
||||
) -> str:
|
||||
codes: List[ColorSpec] = []
|
||||
|
||||
if fg:
|
||||
codes.append(_color_code(fg, 30))
|
||||
if bg:
|
||||
codes.append(_color_code(bg, 40))
|
||||
if style:
|
||||
for style_part in style.split('+'):
|
||||
if style_part in STYLES:
|
||||
codes.append(STYLES.index(style_part))
|
||||
else:
|
||||
raise ValueError('Invalid style "%s"' % style_part)
|
||||
|
||||
if codes:
|
||||
return '\x1b[{0}m{1}\x1b[0m'.format(_join(*codes), s)
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
# Foreground color shortcuts
|
||||
black = partial(color, fg='black')
|
||||
red = partial(color, fg='red')
|
||||
green = partial(color, fg='green')
|
||||
yellow = partial(color, fg='yellow')
|
||||
blue = partial(color, fg='blue')
|
||||
magenta = partial(color, fg='magenta')
|
||||
cyan = partial(color, fg='cyan')
|
||||
white = partial(color, fg='white')
|
||||
|
||||
# Style shortcuts
|
||||
bold = partial(color, style='bold')
|
||||
none = partial(color, style='none')
|
||||
faint = partial(color, style='faint')
|
||||
italic = partial(color, style='italic')
|
||||
underline = partial(color, style='underline')
|
||||
blink = partial(color, style='blink')
|
||||
blink2 = partial(color, style='blink2')
|
||||
negative = partial(color, style='negative')
|
||||
concealed = partial(color, style='concealed')
|
||||
crossed = partial(color, style='crossed')
|
||||
@@ -20,7 +20,7 @@ import asyncio
|
||||
import itertools
|
||||
import random
|
||||
import struct
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.core import BT_CENTRAL_ROLE, BT_PERIPHERAL_ROLE
|
||||
|
||||
from bumble.hci import (
|
||||
|
||||
+1
-2
@@ -25,8 +25,7 @@ from contextlib import asynccontextmanager, AsyncExitStack
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU
|
||||
from .gatt import Characteristic, Descriptor, Service
|
||||
from .hci import (
|
||||
|
||||
+1
-1
@@ -29,8 +29,8 @@ import functools
|
||||
import logging
|
||||
import struct
|
||||
from typing import Optional, Sequence
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .core import UUID, get_dict_key_by_value
|
||||
from .att import Attribute
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ import asyncio
|
||||
import logging
|
||||
import struct
|
||||
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
|
||||
from .colors import color
|
||||
from .hci import HCI_Constant
|
||||
from .att import (
|
||||
ATT_ATTRIBUTE_NOT_FOUND_ERROR,
|
||||
|
||||
@@ -29,8 +29,8 @@ from collections import defaultdict
|
||||
import struct
|
||||
from typing import List, Tuple, Optional
|
||||
from pyee import EventEmitter
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .core import UUID
|
||||
from .att import (
|
||||
ATT_ATTRIBUTE_NOT_FOUND_ERROR,
|
||||
|
||||
+9
-1
@@ -20,9 +20,9 @@ import struct
|
||||
import collections
|
||||
import logging
|
||||
import functools
|
||||
from colors import color
|
||||
from typing import Dict, Type, Union
|
||||
|
||||
from .colors import color
|
||||
from .core import (
|
||||
BT_BR_EDR_TRANSPORT,
|
||||
AdvertisingData,
|
||||
@@ -1846,6 +1846,8 @@ class HCI_Packet:
|
||||
Abstract Base class for HCI packets
|
||||
'''
|
||||
|
||||
hci_packet_type: int
|
||||
|
||||
@staticmethod
|
||||
def from_bytes(packet):
|
||||
packet_type = packet[0]
|
||||
@@ -1864,6 +1866,9 @@ class HCI_Packet:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
@@ -1875,6 +1880,9 @@ class HCI_CustomPacket(HCI_Packet):
|
||||
self.hci_packet_type = payload[0]
|
||||
self.payload = payload
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self.payload
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class HCI_Command(HCI_Packet):
|
||||
|
||||
+1
-1
@@ -16,8 +16,8 @@
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
import logging
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .att import ATT_CID, ATT_PDU
|
||||
from .smp import SMP_CID, SMP_Command
|
||||
from .core import name_or_number
|
||||
|
||||
+2
-1
@@ -18,7 +18,8 @@
|
||||
import logging
|
||||
import asyncio
|
||||
import collections
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
+9
-2
@@ -20,9 +20,9 @@ import collections
|
||||
import logging
|
||||
import struct
|
||||
|
||||
from colors import color
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.l2cap import L2CAP_PDU
|
||||
from bumble.snoop import Snooper
|
||||
|
||||
from .hci import (
|
||||
HCI_ACL_DATA_PACKET,
|
||||
@@ -134,6 +134,7 @@ class Host(AbortableEventEmitter):
|
||||
self.long_term_key_provider = None
|
||||
self.link_key_provider = None
|
||||
self.pairing_io_capability_provider = None # Classic only
|
||||
self.snooper = None
|
||||
|
||||
# Connect to the source and sink if specified
|
||||
if controller_source:
|
||||
@@ -274,6 +275,9 @@ class Host(AbortableEventEmitter):
|
||||
self.hci_sink = sink
|
||||
|
||||
def send_hci_packet(self, packet):
|
||||
if self.snooper:
|
||||
self.snooper.snoop(packet, Snooper.Direction.HOST_TO_CONTROLLER)
|
||||
|
||||
self.hci_sink.on_packet(packet.to_bytes())
|
||||
|
||||
async def send_command(self, command, check_result=False):
|
||||
@@ -420,6 +424,9 @@ class Host(AbortableEventEmitter):
|
||||
def on_hci_packet(self, packet):
|
||||
logger.debug(f'{color("### CONTROLLER -> HOST", "green")}: {packet}')
|
||||
|
||||
if self.snooper:
|
||||
self.snooper.snoop(packet, Snooper.Direction.CONTROLLER_TO_HOST)
|
||||
|
||||
# If the packet is a command, invoke the handler for this packet
|
||||
if packet.hci_packet_type == HCI_COMMAND_PACKET:
|
||||
self.on_hci_command_packet(packet)
|
||||
|
||||
+1
-1
@@ -25,8 +25,8 @@ import logging
|
||||
import os
|
||||
import json
|
||||
from typing import Optional
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .hci import Address
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -21,10 +21,10 @@ import logging
|
||||
import struct
|
||||
|
||||
from collections import deque
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
from typing import Dict, Type
|
||||
|
||||
from .colors import color
|
||||
from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
|
||||
from .hci import (
|
||||
HCI_LE_Connection_Update_Command,
|
||||
|
||||
+3
-3
@@ -19,9 +19,7 @@ import logging
|
||||
import asyncio
|
||||
from functools import partial
|
||||
|
||||
from colors import color
|
||||
import websockets
|
||||
|
||||
from bumble.colors import color
|
||||
from bumble.hci import (
|
||||
Address,
|
||||
HCI_SUCCESS,
|
||||
@@ -220,6 +218,8 @@ class RemoteLink:
|
||||
)
|
||||
|
||||
async def run_connection(self):
|
||||
import websockets # lazy import
|
||||
|
||||
# Connect to the relay
|
||||
logger.debug(f'connecting to {self.uri}')
|
||||
# pylint: disable-next=no-member
|
||||
|
||||
+1
-1
@@ -18,10 +18,10 @@
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
|
||||
from . import core
|
||||
from .colors import color
|
||||
from .core import BT_BR_EDR_TRANSPORT, InvalidStateError, ProtocolError
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
+2
-3
@@ -18,11 +18,10 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import struct
|
||||
from colors import color
|
||||
import colors
|
||||
from typing import Dict, List, Type
|
||||
|
||||
from . import core
|
||||
from .colors import color
|
||||
from .core import InvalidStateError
|
||||
from .hci import HCI_Object, name_or_number, key_with_value
|
||||
|
||||
@@ -506,7 +505,7 @@ class ServiceAttribute:
|
||||
def to_string(self, with_colors=False):
|
||||
if with_colors:
|
||||
return (
|
||||
f'Attribute(id={colors.color(self.id_name(self.id),"magenta")},'
|
||||
f'Attribute(id={color(self.id_name(self.id),"magenta")},'
|
||||
f'value={self.value})'
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -29,8 +29,8 @@ import secrets
|
||||
from typing import Dict, Optional, Type
|
||||
|
||||
from pyee import EventEmitter
|
||||
from colors import color
|
||||
|
||||
from .colors import color
|
||||
from .hci import Address, HCI_LE_Enable_Encryption_Command, HCI_Object, key_with_value
|
||||
from .core import (
|
||||
BT_BR_EDR_TRANSPORT,
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# Copyright 2021-2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
from enum import IntEnum
|
||||
import struct
|
||||
import datetime
|
||||
from typing import BinaryIO
|
||||
|
||||
from bumble.hci import HCI_Packet, HCI_COMMAND_PACKET, HCI_EVENT_PACKET
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Classes
|
||||
# -----------------------------------------------------------------------------
|
||||
class Snooper:
|
||||
"""
|
||||
Base class for snooper implementations.
|
||||
|
||||
A snooper is an object that will be provided with HCI packets as they are
|
||||
exchanged between a host and a controller.
|
||||
"""
|
||||
|
||||
class Direction(IntEnum):
|
||||
HOST_TO_CONTROLLER = 0
|
||||
CONTROLLER_TO_HOST = 1
|
||||
|
||||
class DataLinkType(IntEnum):
|
||||
H1 = 1001
|
||||
H4 = 1002
|
||||
HCI_BSCP = 1003
|
||||
H5 = 1004
|
||||
|
||||
def snoop(self, hci_packet: HCI_Packet, direction: Direction) -> None:
|
||||
"""Snoop on an HCI packet."""
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
class BtSnooper(Snooper):
|
||||
"""
|
||||
Snooper that saves HCI packets using the BTSnoop format, based on RFC 1761.
|
||||
"""
|
||||
|
||||
IDENTIFICATION_PATTERN = b'btsnoop\0'
|
||||
TIMESTAMP_ANCHOR = datetime.datetime(2000, 1, 1)
|
||||
TIMESTAMP_DELTA = 0x00E03AB44A676000
|
||||
ONE_MS = datetime.timedelta(microseconds=1)
|
||||
|
||||
def __init__(self, output: BinaryIO):
|
||||
self.output = output
|
||||
|
||||
# Write the header
|
||||
self.output.write(
|
||||
self.IDENTIFICATION_PATTERN + struct.pack('>LL', 1, self.DataLinkType.H4)
|
||||
)
|
||||
|
||||
def snoop(self, hci_packet: HCI_Packet, direction: Snooper.Direction) -> None:
|
||||
flags = int(direction)
|
||||
if hci_packet.hci_packet_type in (HCI_EVENT_PACKET, HCI_COMMAND_PACKET):
|
||||
flags |= 0x10
|
||||
|
||||
# Compute the current timestamp
|
||||
timestamp = (
|
||||
int((datetime.datetime.utcnow() - self.TIMESTAMP_ANCHOR) / self.ONE_MS)
|
||||
+ self.TIMESTAMP_DELTA
|
||||
)
|
||||
|
||||
# Emit the record
|
||||
packet_data = bytes(hci_packet)
|
||||
self.output.write(
|
||||
struct.pack(
|
||||
'>IIIIQ',
|
||||
len(packet_data), # Original Length
|
||||
len(packet_data), # Included Length
|
||||
flags, # Packet Flags
|
||||
0, # Cumulative Drops
|
||||
timestamp, # Timestamp
|
||||
)
|
||||
+ packet_data
|
||||
)
|
||||
@@ -18,7 +18,6 @@
|
||||
import logging
|
||||
|
||||
from .common import Transport, AsyncPipeSink
|
||||
from ..link import RemoteLink
|
||||
from ..controller import Controller
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -110,6 +109,8 @@ async def open_transport(name: str) -> Transport:
|
||||
# -----------------------------------------------------------------------------
|
||||
async def open_transport_or_link(name):
|
||||
if name.startswith('link-relay:'):
|
||||
from ..link import RemoteLink # lazy import
|
||||
|
||||
link = RemoteLink(name[11:])
|
||||
await link.wait_until_connected()
|
||||
controller = Controller('remote', link=link)
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
import struct
|
||||
import asyncio
|
||||
import logging
|
||||
from colors import color
|
||||
|
||||
from .. import hci
|
||||
from ..colors import color
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -22,10 +22,10 @@ import time
|
||||
|
||||
import usb.core
|
||||
import usb.util
|
||||
from colors import color
|
||||
|
||||
from .common import Transport, ParserSource
|
||||
from .. import hci
|
||||
from ..colors import color
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -23,10 +23,10 @@ import ctypes
|
||||
import platform
|
||||
|
||||
import usb1
|
||||
from colors import color
|
||||
|
||||
from .common import Transport, ParserSource
|
||||
from .. import hci
|
||||
from ..colors import color
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
+1
-1
@@ -22,9 +22,9 @@ import collections
|
||||
import sys
|
||||
from typing import Awaitable, TypeVar
|
||||
from functools import wraps
|
||||
from colors import color
|
||||
from pyee import EventEmitter
|
||||
|
||||
from .colors import color
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Logging
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport
|
||||
from bumble.profiles.battery_service import BatteryServiceProxy
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device, Peer
|
||||
from bumble.profiles.device_information_service import DeviceInformationServiceProxy
|
||||
from bumble.transport import open_transport
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport
|
||||
from bumble.profiles.heart_rate_service import HeartRateServiceProxy
|
||||
|
||||
@@ -22,7 +22,7 @@ import logging
|
||||
import struct
|
||||
import json
|
||||
import websockets
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.core import AdvertisingData
|
||||
from bumble.device import Device, Connection, Peer
|
||||
|
||||
@@ -20,7 +20,7 @@ import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.core import (
|
||||
|
||||
@@ -20,7 +20,7 @@ import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.core import BT_BR_EDR_TRANSPORT
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
|
||||
@@ -19,7 +19,7 @@ import logging
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.device import Device
|
||||
from bumble.controller import Controller
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# Copyright 2021-2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Imports
|
||||
# -----------------------------------------------------------------------------
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
from bumble.snoop import BtSnooper
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
async def main():
|
||||
if len(sys.argv) != 3:
|
||||
print('Usage: run_device_with_snooper.py <transport-spec> <snoop-file>')
|
||||
print('example: run_device_with_snooper.py usb:0 btsnoop.log')
|
||||
return
|
||||
|
||||
print('<<< connecting to HCI...')
|
||||
async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink):
|
||||
print('<<< connected')
|
||||
|
||||
device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink)
|
||||
|
||||
with open(sys.argv[2], "wb") as snoop_file:
|
||||
device.host.snooper = BtSnooper(snoop_file)
|
||||
await device.power_on()
|
||||
await device.start_scanning()
|
||||
|
||||
await hci_source.wait_for_termination()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
|
||||
asyncio.run(main())
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.core import ProtocolError
|
||||
from bumble.device import Device, Peer
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import asyncio
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.core import ProtocolError
|
||||
from bumble.controller import Controller
|
||||
|
||||
@@ -20,7 +20,7 @@ import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
import bumble.core
|
||||
from bumble.device import Device
|
||||
|
||||
@@ -20,7 +20,7 @@ import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
import bumble.core
|
||||
from bumble.device import Device
|
||||
|
||||
@@ -19,7 +19,7 @@ import asyncio
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from colors import color
|
||||
from bumble.colors import color
|
||||
|
||||
from bumble.device import Device
|
||||
from bumble.transport import open_transport_or_link
|
||||
|
||||
@@ -30,10 +30,8 @@ package_dir =
|
||||
bumble.apps = apps
|
||||
include-package-data = True
|
||||
install_requires =
|
||||
ansicolors >= 1.1
|
||||
appdirs >= 1.4
|
||||
click >= 7.1.2; platform_system!='Emscripten'
|
||||
construct >= 2.10
|
||||
cryptography == 35; platform_system!='Emscripten'
|
||||
grpcio >= 1.46; platform_system!='Emscripten'
|
||||
libusb1 >= 2.0.1; platform_system!='Emscripten'
|
||||
|
||||
Reference in New Issue
Block a user