Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/windows pairing #640

Closed
wants to merge 28 commits into from

Conversation

jpeters-ml
Copy link

Code derived from #523.

Added Windows pairing to backends .NET and WinRT. Updated pairing example to allow Windows.

bojanpotocnik and others added 23 commits April 26, 2021 00:16
This agent currently does nothing, it just logs method calls to
check if it is properly registered.

Signed-off-by: Bojan Potočnik <[email protected]>
Also pairingAgent is moved to BleakClient class for easier access

Signed-off-by: Bojan Potočnik <[email protected]>
Also modify example to use this callback and retrieve passkey from the
user.

Signed-off-by: Bojan Potočnik <[email protected]>
Ensure that if no callback is provided, pairing works as before, meaning
that Just Works pairing requests are confirmed.

Signed-off-by: Bojan Potočnik <[email protected]>
Signed-off-by: Bojan Potočnik <[email protected]>
Only use the protection level if it's set to anything
Pairing code had to be modified to meet the pythonic winrt module
in order to complete the pairing process without installing
the dotnet libraries (pythonnet and the .net core sdk)
Builds on PR hbldh#523 to include pairing for Windowns with both
dotnet and winrt backends. Updated timeout in connect to use
the same timeout as provided to the connect function.
Added windows pairing to .NET and WinRT backends, following the process
started in PR hbldh#523.
@dlech
Copy link
Collaborator

dlech commented Oct 4, 2021

Thanks for this. I currently don't have any devices to test this with. Does anyone have any suggestions of something I could get (or wants to send me a spare)?

@jpeters-ml
Copy link
Author

jpeters-ml commented Oct 5, 2021

Thanks for this. I currently don't have any devices to test this with. Does anyone have any suggestions of something I could get (or wants to send me a spare)?

I think some suggested a device in another thread. Let me see what I can do.

Edit: Please send me an email, to the one listed on my account.

Copy link
Collaborator

@dlech dlech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a closer look at this now. It looks like a great start, but there is still work to do before merging.

Comment on lines +24 to +27
DBusObject = "o"
DBusString = "s"
DBusUInt16 = "q"
DBusUInt32 = "u"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aliases have lead to wrong usage. They are only meant for bindings of D-Bus methods, however they are used below where actual Python types should be used (e.g. str, int). So I think we should remove them to avoid the confusion.

io_capabilities (`Capability`): I/O capabilities of this device, used for determining pairing method.
"""

def __init__(self, io_capabilities: IOCapability = IOCapability.KEYBOARD_DISPLAY):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it makes sense to have a default here or if we should require users to specify this along with their callback function.

Copy link
Collaborator

@dlech dlech Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IOCapability could also be part of the proposed callback class. In that case, it should be an enum.IntFlags and have the same values as this so it can be used directly in the WinRT backend (these values are defined by Bluetooth spec and not specific to Windows, IIRC - should probably research more before doing exactly as I've suggested) and converted to the appropriate strings in the BlueZ backend.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented by using the `BlueZ DBUS Agent API <https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt>`_.

Args:
io_capabilities (`Capability`): I/O capabilities of this device, used for determining pairing method.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using type hints, we can leave out the type from the doc strings. If we want to keep them anyway, the ` needs to be removed.

# D-Bus message bus
self._bus: Optional[MessageBus] = None
# Path can be anything as long as it is unique
self._path = f"/org/bluez/agent{time.time() * 1000:.0f}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to include bleak in the path.

self._io_capabilities = io_capabilities

# Callback for every device (single agent handles all pairing requests)
self._callbacks: Dict[DBusObject, PairingCallback] = {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._callbacks: Dict[DBusObject, PairingCallback] = {}
self._callbacks: Dict[str, PairingCallback] = {}

print("\t\tValue: ", await client.read_gatt_char(char))


if platform.system() == "Darwin":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be better to leave out this check and just let the example throw whatever error the pair method actually throws on Mac.

print("Paired")

services = await client.get_services()
print(services)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unnecessary since we are enumerating the services below.

@@ -295,6 +298,11 @@ async def pair(self, protection_level: int = None, **kwargs) -> bool:
2: Encryption - Pair the device using encryption.
3: EncryptionAndAuthentication - Pair the device using
encryption and authentication. (This will not work in Bleak...)
callback (`PairingCallback`): callback to be called to provide or confirm pairing pin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can omit the type here.

Suggested change
callback (`PairingCallback`): callback to be called to provide or confirm pairing pin
callback: callback to be called to provide or confirm pairing pin

print(service)
for char in service.characteristics:
print("\t", char)
print("\t\tValue: ", await client.read_gatt_char(char))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print("\t\tValue: ", await client.read_gatt_char(char))
print("\t\tValue:", await client.read_gatt_char(char))


async def main(mac_addr: str):
# Remove this device if it is already paired (from previous runs)
if await BleakClient.remove_device(mac_addr):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this method only work on BlueZ? Maybe we should connect first, then unpair if paired, then pair again instead.

@dlech dlech added the Backend: BlueZ Issues and PRs relating to the BlueZ backend label Oct 11, 2021
@dlech dlech added the Backend: WinRT Issues or PRs relating to the WinRT backend label Oct 11, 2021
@hbldh
Copy link
Owner

hbldh commented Nov 26, 2021

There are still some conflicts to address. This is a lot of commits to rebase on develop, but try to resolve the last conflicts and we can see if this is mergable after that!

@dlech dlech linked an issue Dec 22, 2021 that may be closed by this pull request
@jpeters-ml
Copy link
Author

I would really like to get this merged in to the main code base, but I am very busy with work at the moment (and have been since the PR was reviewed). If anyone would like to assist, I do not mind granting access for them.

@tcamise-gpsw
Copy link
Contributor

I'm pretty interested in looking into this. If I'm understanding correctly, it is functionally complete and is just a matter of cleanup from PR comments and more testing?

@jpeters-ml
Copy link
Author

I'm pretty interested in looking into this. If I'm understanding correctly, it is functionally complete and is just a matter of cleanup from PR comments and more testing?

Yes, that's correct.

@tcamise-gpsw
Copy link
Contributor

I'm pretty interested in looking into this. If I'm understanding correctly, it is functionally complete and is just a matter of cleanup from PR comments and more testing?

Yes, that's correct.

OK cool well I'll at least try to get this working on Ubuntu and Windows and go from there.

@jpeters-ml
Copy link
Author

OK cool well I'll at least try to get this working on Ubuntu and Windows and go from there.

let me know if you need/want access to make changes to the branch.

@snowuyl
Copy link

snowuyl commented Aug 4, 2022

Is there any schedule for release this feature?

@jochenjagers
Copy link
Contributor

I want to bring this topic back to live. I already ported the WinRT part to bleak version 0.15.1 and using it successfully for a few weeks now in my environment.
What do you think about splitting this thing up in two parts, one for WinRT and one for BlueZ?
I can take care of the WinRT part but unfortunately have no capacity to do it also for BlueZ.

@dlech
Copy link
Collaborator

dlech commented Sep 22, 2022

Splitting it into smaller pieces is always a good thing.

I can't find it now, but someone recently mentioned implementing this as an async context manager, which I think is a good idea (it's been too long, I don't remember if it is done this way already or not). So it would be used like this:

async with BleakPairingAgent(callback):
    await client.pair()

Also, since #982 was just merged, I would like to rework this to follow the pattern used by BleakClient and BleakScanner there where we have a common top-level class with a platform specific backend (depedency-injection-like).

@dlech dlech mentioned this pull request Oct 31, 2022
6 tasks
@dlech
Copy link
Collaborator

dlech commented Oct 31, 2022

FYI, I've made some attempt to move this along in #1100. It still needs a bit of work and there are still some unresolved issues, so feedback from anyone who wants to use this feature would be appreciated.

@bojanpotocnik
Copy link
Contributor

After a too long time, I found some time (and need) to get back to this feature. However, I see that in the meantime bleak has evolved so much that it would be better to start from scratch than to update my initial implementation. Moreover, I see that this was again superseded with #1100, which puts even more thought into the implementation.

In my opinion, this PR could be closed - however major part of this PR is also done by @jpeters-ml, which should provide his opinion. I'll base my fork on the pairing-agent branch and test.

@jpeters-ml
Copy link
Author

Considering that these changes are being moved with #1100 I don't mind closing this as I haven't had the time to update.

@dlech
Copy link
Collaborator

dlech commented Nov 16, 2022

Closing in favor of #1100

@dlech dlech closed this Nov 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backend: BlueZ Issues and PRs relating to the BlueZ backend Backend: WinRT Issues or PRs relating to the WinRT backend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

programmatically accept pairing request in python without dialog interaction Unable to unpair with device
7 participants