forked from auracaster/bumble_mirror
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 34161e5e14 | |||
| 216ce2abd0 | |||
| 431445e6a2 | |||
| d7cc546248 | |||
| 29fd19f40d |
@@ -29,11 +29,11 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install ".[test,development,documentation]"
|
python -m pip install ".[build,test,development,documentation]"
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
pytest
|
pytest
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
inv build
|
inv build
|
||||||
inv mkdocs
|
inv build.mkdocs
|
||||||
|
|||||||
+11
-3
@@ -547,7 +547,9 @@ class Client:
|
|||||||
for subscriber_set in subscriber_sets:
|
for subscriber_set in subscriber_sets:
|
||||||
if subscriber is not None:
|
if subscriber is not None:
|
||||||
subscriber_set.add(subscriber)
|
subscriber_set.add(subscriber)
|
||||||
subscriber_set.add(lambda value: characteristic.emit('update', self.connection, value))
|
# Add the characteristic as a subscriber, which will result in the characteristic
|
||||||
|
# emitting an 'update' event when a notification or indication is received
|
||||||
|
subscriber_set.add(characteristic)
|
||||||
|
|
||||||
await self.write_value(cccd, struct.pack('<H', bits), with_response=True)
|
await self.write_value(cccd, struct.pack('<H', bits), with_response=True)
|
||||||
|
|
||||||
@@ -741,7 +743,10 @@ class Client:
|
|||||||
if not subscribers:
|
if not subscribers:
|
||||||
logger.warning('!!! received notification with no subscriber')
|
logger.warning('!!! received notification with no subscriber')
|
||||||
for subscriber in subscribers:
|
for subscriber in subscribers:
|
||||||
subscriber(notification.attribute_value)
|
if callable(subscriber):
|
||||||
|
subscriber(notification.attribute_value)
|
||||||
|
else:
|
||||||
|
subscriber.emit('update', notification.attribute_value)
|
||||||
|
|
||||||
def on_att_handle_value_indication(self, indication):
|
def on_att_handle_value_indication(self, indication):
|
||||||
# Call all subscribers
|
# Call all subscribers
|
||||||
@@ -749,7 +754,10 @@ class Client:
|
|||||||
if not subscribers:
|
if not subscribers:
|
||||||
logger.warning('!!! received indication with no subscriber')
|
logger.warning('!!! received indication with no subscriber')
|
||||||
for subscriber in subscribers:
|
for subscriber in subscribers:
|
||||||
subscriber(indication.attribute_value)
|
if callable(subscriber):
|
||||||
|
subscriber(indication.attribute_value)
|
||||||
|
else:
|
||||||
|
subscriber.emit('update', indication.attribute_value)
|
||||||
|
|
||||||
# Confirm that we received the indication
|
# Confirm that we received the indication
|
||||||
self.send_confirmation(ATT_Handle_Value_Confirmation())
|
self.send_confirmation(ATT_Handle_Value_Confirmation())
|
||||||
|
|||||||
+1
-1
@@ -20,11 +20,11 @@ import logging
|
|||||||
import struct
|
import struct
|
||||||
|
|
||||||
from colors import color
|
from colors import color
|
||||||
|
from pyee import EventEmitter
|
||||||
|
|
||||||
from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
|
from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
|
||||||
from .hci import (HCI_LE_Connection_Update_Command, HCI_Object, key_with_value,
|
from .hci import (HCI_LE_Connection_Update_Command, HCI_Object, key_with_value,
|
||||||
name_or_number)
|
name_or_number)
|
||||||
from .utils import EventEmitter
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logging
|
# Logging
|
||||||
|
|||||||
+3
-2
@@ -17,9 +17,10 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from colors import color
|
|
||||||
|
|
||||||
from .utils import EventEmitter
|
from colors import color
|
||||||
|
from pyee import EventEmitter
|
||||||
|
|
||||||
from .core import InvalidStateError, ProtocolError, ConnectionError
|
from .core import InvalidStateError, ProtocolError, ConnectionError
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ The Console app is an interactive text user interface that offers a number of fu
|
|||||||
|
|
||||||
* scanning
|
* scanning
|
||||||
* advertising
|
* advertising
|
||||||
* connecting to devices
|
* connecting to and disconnecting from devices
|
||||||
* changing connection parameters
|
* changing connection parameters
|
||||||
|
* enabling encryption
|
||||||
* discovering GATT services and characteristics
|
* discovering GATT services and characteristics
|
||||||
* read & write GATT characteristics
|
* reading and writing GATT characteristics
|
||||||
|
* subscribing to and unsubscribing from GATT characteristics
|
||||||
|
|
||||||
The console user interface has 3 main panes:
|
The console user interface has 3 main panes:
|
||||||
|
|
||||||
|
|||||||
@@ -57,12 +57,13 @@ console_scripts =
|
|||||||
bumble-link-relay = bumble.apps.link_relay.link_relay:main
|
bumble-link-relay = bumble.apps.link_relay.link_relay:main
|
||||||
|
|
||||||
[options.extras_require]
|
[options.extras_require]
|
||||||
|
build =
|
||||||
|
build >= 0.7
|
||||||
test =
|
test =
|
||||||
pytest >= 6.2
|
pytest >= 6.2
|
||||||
pytest-asyncio >= 0.17
|
pytest-asyncio >= 0.17
|
||||||
development =
|
development =
|
||||||
invoke >= 1.4
|
invoke >= 1.4
|
||||||
build >= 0.7
|
|
||||||
nox >= 2022
|
nox >= 2022
|
||||||
documentation =
|
documentation =
|
||||||
mkdocs >= 1.2.3
|
mkdocs >= 1.2.3
|
||||||
|
|||||||
@@ -23,35 +23,52 @@ ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
|
|||||||
|
|
||||||
ns = Collection()
|
ns = Collection()
|
||||||
|
|
||||||
|
# Building
|
||||||
build_tasks = Collection()
|
build_tasks = Collection()
|
||||||
ns.add_collection(build_tasks, name='build')
|
ns.add_collection(build_tasks, name="build")
|
||||||
|
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def build(ctx):
|
def build(ctx, install=False):
|
||||||
ctx.run('python -m build')
|
if install:
|
||||||
|
ctx.run('python -m pip install .[build]')
|
||||||
|
|
||||||
build_tasks.add_task(build, default=True, name='build')
|
ctx.run("python -m build")
|
||||||
|
|
||||||
|
build_tasks.add_task(build, default=True)
|
||||||
|
|
||||||
|
@task
|
||||||
|
def release_build(ctx):
|
||||||
|
build(ctx, install=True)
|
||||||
|
|
||||||
|
build_tasks.add_task(release_build, name="release")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def mkdocs(ctx):
|
||||||
|
ctx.run("mkdocs build -f docs/mkdocs/mkdocs.yml")
|
||||||
|
|
||||||
|
build_tasks.add_task(mkdocs, name="mkdocs")
|
||||||
|
|
||||||
|
# Testing
|
||||||
test_tasks = Collection()
|
test_tasks = Collection()
|
||||||
ns.add_collection(test_tasks, name='test')
|
ns.add_collection(test_tasks, name="test")
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def test(ctx, filter=None, junit=False):
|
def test(ctx, filter=None, junit=False, install=False):
|
||||||
|
# Install the package before running the tests
|
||||||
|
if install:
|
||||||
|
ctx.run("python -m pip install .[test]")
|
||||||
|
|
||||||
args = ""
|
args = ""
|
||||||
if junit:
|
if junit:
|
||||||
args += "--junit-xml test-results.xml"
|
args += "--junit-xml test-results.xml"
|
||||||
if filter is not None:
|
if filter is not None:
|
||||||
args += " -k '{}'".format(filter)
|
args += " -k '{}'".format(filter)
|
||||||
ctx.run('python -m pytest {} {}'
|
ctx.run("python -m pytest {} {}".format(os.path.join(ROOT_DIR, "tests"), args))
|
||||||
.format(os.path.join(ROOT_DIR, "tests"), args))
|
|
||||||
|
|
||||||
test_tasks.add_task(test, name='test', default=True)
|
|
||||||
|
|
||||||
|
test_tasks.add_task(test, default=True)
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def mkdocs(ctx):
|
def release_test(ctx):
|
||||||
ctx.run('mkdocs build -f docs/mkdocs/mkdocs.yml')
|
test(ctx, install=True)
|
||||||
|
|
||||||
|
test_tasks.add_task(release_test, name="release")
|
||||||
ns.add_task(mkdocs)
|
|
||||||
|
|||||||
+42
-25
@@ -419,10 +419,12 @@ async def test_subscribe_notify():
|
|||||||
assert(len(c) == 1)
|
assert(len(c) == 1)
|
||||||
c3 = c[0]
|
c3 = c[0]
|
||||||
|
|
||||||
|
c1._called = False
|
||||||
c1._last_update = None
|
c1._last_update = None
|
||||||
|
|
||||||
def on_c1_update(connection, value):
|
def on_c1_update(value):
|
||||||
c1._last_update = (connection, value)
|
c1._called = True
|
||||||
|
c1._last_update = value
|
||||||
|
|
||||||
c1.on('update', on_c1_update)
|
c1.on('update', on_c1_update)
|
||||||
await peer.subscribe(c1)
|
await peer.subscribe(c1)
|
||||||
@@ -434,58 +436,73 @@ async def test_subscribe_notify():
|
|||||||
assert(not characteristic1._last_subscription[2])
|
assert(not characteristic1._last_subscription[2])
|
||||||
await server.indicate_subscribers(characteristic1)
|
await server.indicate_subscribers(characteristic1)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c1._last_update is None)
|
assert(not c1._called)
|
||||||
await server.notify_subscribers(characteristic1)
|
await server.notify_subscribers(characteristic1)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c1._last_update is not None)
|
assert(c1._called)
|
||||||
assert(c1._last_update[1] == characteristic1.value)
|
assert(c1._last_update == characteristic1.value)
|
||||||
|
|
||||||
assert(peer.gatt_client.notification_subscribers[c1.handle])
|
c1._called = False
|
||||||
await peer.unsubscribe(c1)
|
await peer.unsubscribe(c1)
|
||||||
assert(c1.handle not in peer.gatt_client.notification_subscribers)
|
await server.notify_subscribers(characteristic1)
|
||||||
|
assert(not c1._called)
|
||||||
|
|
||||||
|
c2._called = False
|
||||||
c2._last_update = None
|
c2._last_update = None
|
||||||
|
|
||||||
def on_c2_update(value):
|
def on_c2_update(value):
|
||||||
c2._last_update = (connection, value)
|
c2._called = True
|
||||||
|
c2._last_update = value
|
||||||
|
|
||||||
await peer.subscribe(c2, on_c2_update)
|
await peer.subscribe(c2, on_c2_update)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
await server.notify_subscriber(characteristic2._last_subscription[0], characteristic2)
|
await server.notify_subscriber(characteristic2._last_subscription[0], characteristic2)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c2._last_update is None)
|
assert(not c2._called)
|
||||||
await server.indicate_subscriber(characteristic2._last_subscription[0], characteristic2)
|
await server.indicate_subscriber(characteristic2._last_subscription[0], characteristic2)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c2._last_update is not None)
|
assert(c2._called)
|
||||||
assert(c2._last_update[1] == characteristic2.value)
|
assert(c2._last_update == characteristic2.value)
|
||||||
|
|
||||||
assert(on_c2_update in peer.gatt_client.indication_subscribers[c2.handle])
|
c2._called = False
|
||||||
await peer.unsubscribe(c2, on_c2_update)
|
await peer.unsubscribe(c2, on_c2_update)
|
||||||
assert(on_c2_update not in peer.gatt_client.indication_subscribers[c2.handle])
|
await server.indicate_subscriber(characteristic2._last_subscription[0], characteristic2)
|
||||||
|
await async_barrier()
|
||||||
|
assert(not c2._called)
|
||||||
|
|
||||||
c3._last_update = None
|
def on_c3_update(value):
|
||||||
|
c3._called = True
|
||||||
|
c3._last_update = value
|
||||||
|
|
||||||
def on_c3_update(connection, value):
|
def on_c3_update_2(value):
|
||||||
c3._last_update = (connection, value)
|
c3._called_2 = True
|
||||||
|
c3._last_update_2 = value
|
||||||
|
|
||||||
c3.on('update', on_c3_update)
|
c3.on('update', on_c3_update)
|
||||||
await peer.subscribe(c3)
|
await peer.subscribe(c3, on_c3_update_2)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
await server.notify_subscriber(characteristic3._last_subscription[0], characteristic3)
|
await server.notify_subscriber(characteristic3._last_subscription[0], characteristic3)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c3._last_update is not None)
|
assert(c3._called)
|
||||||
assert(c3._last_update[1] == characteristic3.value)
|
assert(c3._last_update == characteristic3.value)
|
||||||
|
assert(c3._called_2)
|
||||||
|
assert(c3._last_update_2 == characteristic3.value)
|
||||||
characteristic3.value = bytes([1, 2, 3])
|
characteristic3.value = bytes([1, 2, 3])
|
||||||
await server.indicate_subscriber(characteristic3._last_subscription[0], characteristic3)
|
await server.indicate_subscriber(characteristic3._last_subscription[0], characteristic3)
|
||||||
await async_barrier()
|
await async_barrier()
|
||||||
assert(c3._last_update is not None)
|
assert(c3._called)
|
||||||
assert(c3._last_update[1] == characteristic3.value)
|
assert(c3._last_update == characteristic3.value)
|
||||||
|
assert(c3._called_2)
|
||||||
|
assert(c3._last_update_2 == characteristic3.value)
|
||||||
|
|
||||||
assert(peer.gatt_client.notification_subscribers[c3.handle])
|
c3._called = False
|
||||||
assert(peer.gatt_client.indication_subscribers[c3.handle])
|
c3._called_2 = False
|
||||||
await peer.unsubscribe(c3)
|
await peer.unsubscribe(c3)
|
||||||
assert(c3.handle not in peer.gatt_client.notification_subscribers)
|
await server.notify_subscriber(characteristic3._last_subscription[0], characteristic3)
|
||||||
assert(c3.handle not in peer.gatt_client.indication_subscribers)
|
await server.indicate_subscriber(characteristic3._last_subscription[0], characteristic3)
|
||||||
|
await async_barrier()
|
||||||
|
assert(not c3._called)
|
||||||
|
assert(not c3._called_2)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user