examples/ev3/bluetooth: remove 3rd party dependency

Since Python 3.10, RFCOMM sockets are available in Python on Windows
so we no longer need 3rd party code.

Fixes: pybricks/support#902
This commit is contained in:
David Lechner
2023-01-06 14:17:29 -06:00
parent 8c673ab280
commit eea8ff0924
3 changed files with 52 additions and 69 deletions
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
from pybricks.messaging import BluetoothMailboxServer, TextMailbox
# This demo makes your PC talk to an EV3 over Bluetooth.
#
# This is identical to the EV3 server example in ../bluetooth_server
#
# The only difference is that it runs in Python3 on your computer, thanks to
# the Python3 implementation of the messaging module that is included here.
# As far as the EV3 is concerned, it thinks it just talks to an EV3 client.
#
# So, the EV3 client example needs no further modifications. The connection
# procedure is also the same as documented in the messaging module docs:
# https://docs.pybricks.com/en/latest/messaging.html
server = BluetoothMailboxServer()
mbox = TextMailbox("greeting", server)
# The server must be started before the client!
print("waiting for connection...")
server.wait_for_connection()
print("connected!")
# In this program, the server waits for the client to send the first message
# and then sends a reply.
mbox.wait()
print(mbox.read())
mbox.send("hello to you!")
+15 -57
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT
# Copyright (C) 2020 The Pybricks Authors
# Copyright (C) 2020,2023 The Pybricks Authors
"""
:class:`RFCOMMServer` can be used to communicate with other Bluetooth RFCOMM
@@ -10,29 +10,13 @@ remain a strict subset of that implementation when it comes to low-level
implementation details.
"""
from bluetooth import BluetoothSocket, RFCOMM
from socket import socket, AF_BLUETOOTH, BTPROTO_RFCOMM, SOCK_STREAM
from socketserver import ThreadingMixIn
BDADDR_ANY = ""
def str2ba(string, ba):
"""Convert string to Bluetooth address"""
for i, v in enumerate(string.split(":")):
ba.b[5 - i] = int(v, 16)
def ba2str(ba):
"""Convert Bluetooth address to string"""
string = []
for b in ba.b:
string.append("{:02X}".format(b))
string.reverse()
return ":".join(string).upper()
class RFCOMMServer:
"""Object that simplifies setting up an RFCOMM socket server.
"""
Object that simplifies setting up an RFCOMM socket server.
This is based on the ``socketserver.SocketServer`` class in the Python
standard library.
@@ -44,10 +28,10 @@ class RFCOMMServer:
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.socket = BluetoothSocket(RFCOMM)
self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
try:
self.socket.bind((server_address[0], server_address[1]))
self.socket.bind(server_address)
# self.server_address = self.socket.getsockname()
self.socket.listen(self.request_queue_size)
except Exception:
@@ -83,50 +67,21 @@ class RFCOMMServer:
self.socket.close()
class StreamRequestHandler:
"""Class that handles incoming requests.
This is based on ``socketserver.StreamRequestHandler`` from the Python
standard library.
"""
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
def setup(self):
self.wfile = self.request
self.rfile = self.request
def handle(self):
pass
def finish(self):
pass
class ThreadingRFCOMMServer(ThreadingMixIn, RFCOMMServer):
"""Version of :class:`RFCOMMServer` that handles connections in a new
thread.
"""
pass
Version of :class:`RFCOMMServer` that handles connections in a new thread.
"""
daemon_threads = True
class RFCOMMClient:
def __init__(self, client_address, RequestHandlerClass):
self.client_address = client_address
self.RequestHandlerClass = RequestHandlerClass
self.socket = BluetoothSocket(RFCOMM)
self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
def handle_request(self):
self.socket.connect((self.client_address[0], self.client_address[1]))
self.socket.connect(self.client_address)
try:
self.process_request(self.socket, self.client_address)
except Exception:
@@ -145,4 +100,7 @@ class RFCOMMClient:
class ThreadingRFCOMMClient(ThreadingMixIn, RFCOMMClient):
pass
"""
Version of :class:`RFCOMMClient` that handles connections in a new thread.
"""
daemon_threads = True
@@ -1,16 +1,13 @@
# SPDX-License-Identifier: MIT
# Copyright (C) 2020 The Pybricks Authors
# Copyright (C) 2020,2023 The Pybricks Authors
from _thread import allocate_lock
from errno import ECONNRESET
from struct import pack, unpack
from socket import BDADDR_ANY
from socketserver import StreamRequestHandler
from threading import Lock
from .bluetooth import (
BDADDR_ANY,
ThreadingRFCOMMServer,
ThreadingRFCOMMClient,
StreamRequestHandler,
)
from .bluetooth import ThreadingRFCOMMServer, ThreadingRFCOMMClient
def resolve(brick):
@@ -151,7 +148,7 @@ class MailboxHandler(StreamRequestHandler):
self.server._clients[self.client_address[0]] = self.request
while True:
try:
buf = self.rfile.recv(2)
buf = self.rfile.read(2)
if len(buf) == 0:
break
except OSError as ex:
@@ -160,7 +157,7 @@ class MailboxHandler(StreamRequestHandler):
break
raise
(size,) = unpack("<H", buf)
buf = self.rfile.recv(size)
buf = self.rfile.read(size)
msg_count, cmd_type, cmd, name_size = unpack("<HBBB", buf[0:5])
if cmd_type != SYSTEM_COMMAND_NO_REPLY:
raise ValueError("Bad message type")
@@ -180,7 +177,7 @@ class MailboxHandler(StreamRequestHandler):
class MailboxHandlerMixIn:
def __init__(self):
# protects against concurrent access of other attributes
self._lock = allocate_lock()
self._lock = Lock()
# map of mailbox name to raw data
self._mailboxes = {}
# map of device name/address to object with send() method
@@ -247,7 +244,7 @@ class MailboxHandlerMixIn:
def wait_for_mailbox_update(self, mbox):
"""Waits until ``mbox`` receives a value."""
lock = allocate_lock()
lock = Lock()
lock.acquire()
with self._lock:
self._updates[mbox] = lock