mirror of
https://github.com/markqvist/Sideband.git
synced 2025-07-20 05:29:50 -04:00
Added in-app BLE scanning and pairing
This commit is contained in:
parent
0d2f7b25a3
commit
73601ebe1e
2 changed files with 197 additions and 11 deletions
122
sbapp/main.py
122
sbapp/main.py
|
@ -277,6 +277,9 @@ else:
|
|||
from kivymd.utils.set_bars_colors import set_bars_colors
|
||||
android_api_version = autoclass('android.os.Build$VERSION').SDK_INT
|
||||
|
||||
from android.broadcast import BroadcastReceiver
|
||||
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')
|
||||
|
||||
else:
|
||||
from .sideband.core import SidebandCore
|
||||
import sbapp.plyer as plyer
|
||||
|
@ -408,6 +411,9 @@ class SidebandApp(MDApp):
|
|||
self.repository_url = None
|
||||
self.rnode_flasher_url = None
|
||||
|
||||
self.bt_adapter = None
|
||||
self.discovered_bt_devices = {}
|
||||
self.bt_bonded_devices = []
|
||||
|
||||
#################################################
|
||||
# Application Startup #
|
||||
|
@ -1008,6 +1014,65 @@ class SidebandApp(MDApp):
|
|||
|
||||
self.check_bluetooth_permissions()
|
||||
|
||||
def bluetooth_update_bonded_devices(self, sender=None):
|
||||
if self.bt_adapter == None: self.bt_adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
self.bt_bonded_devices = []
|
||||
for device in self.bt_adapter.getBondedDevices():
|
||||
device_addr = device.getAddress()
|
||||
self.bt_bonded_devices.append(device_addr)
|
||||
|
||||
RNS.log(f"Updated bonded devices: {self.bt_bonded_devices}", RNS.LOG_DEBUG)
|
||||
|
||||
def bluetooth_scan_action(self, sender=None):
|
||||
toast("Starting Bluetooth scan...")
|
||||
self.start_bluetooth_scan()
|
||||
|
||||
def start_bluetooth_scan(self):
|
||||
self.check_bluetooth_permissions()
|
||||
if not self.sideband.getpersistent("permissions.bluetooth"):
|
||||
self.request_bluetooth_permissions()
|
||||
else:
|
||||
RNS.log("Starting bluetooth scan", RNS.LOG_DEBUG)
|
||||
self.discovered_bt_devices = {}
|
||||
|
||||
BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice')
|
||||
self.bt_found_action = BluetoothDevice.ACTION_FOUND
|
||||
self.broadcast_receiver = BroadcastReceiver(self.on_broadcast, actions=[self.bt_found_action])
|
||||
self.broadcast_receiver.start()
|
||||
|
||||
self.bt_adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
self.bluetooth_update_bonded_devices()
|
||||
self.bt_adapter.startDiscovery()
|
||||
|
||||
def stop_bluetooth_scan(self):
|
||||
RNS.log("Stopping bluetooth scan", RNS.LOG_DEBUG)
|
||||
self.check_bluetooth_permissions()
|
||||
if not self.sideband.getpersistent("permissions.bluetooth"):
|
||||
self.request_bluetooth_permissions()
|
||||
else:
|
||||
self.bt_adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
self.bt_adapter.cancelDiscovery()
|
||||
|
||||
def on_broadcast(self, context, intent):
|
||||
BluetoothDevice = autoclass('android.bluetooth.BluetoothDevice')
|
||||
action = intent.getAction()
|
||||
extras = intent.getExtras()
|
||||
|
||||
if str(action) == "android.bluetooth.device.action.FOUND":
|
||||
if extras:
|
||||
try:
|
||||
device = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE", BluetoothDevice)
|
||||
dev_name = device.getName()
|
||||
dev_addr = device.getAddress()
|
||||
if dev_name.startswith("RNode "):
|
||||
dev_rssi = extras.getShort("android.bluetooth.device.extra.RSSI", -9999)
|
||||
discovered_device = {"name": dev_name, "address": dev_addr, "rssi": dev_rssi, "discovered": time.time()}
|
||||
self.discovered_bt_devices[dev_addr] = discovered_device
|
||||
RNS.log(f"Discovered RNode: {discovered_device}", RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while mapping discovered device: {e}", RNS.LOG_ERROR)
|
||||
|
||||
def on_new_intent(self, intent):
|
||||
intent_action = intent.getAction()
|
||||
action = None
|
||||
|
@ -4250,6 +4315,63 @@ class SidebandApp(MDApp):
|
|||
|
||||
self.sideband.save_configuration()
|
||||
|
||||
def hardware_rnode_scan_job(self):
|
||||
time.sleep(1.25)
|
||||
added_devices = []
|
||||
scan_timeout = time.time()+16
|
||||
while time.time() < scan_timeout:
|
||||
RNS.log("Scanning...", RNS.LOG_DEBUG)
|
||||
for device_addr in self.discovered_bt_devices:
|
||||
if device_addr not in added_devices and not device_addr in self.bt_bonded_devices:
|
||||
new_device = self.discovered_bt_devices[device_addr]
|
||||
added_devices.append(device_addr)
|
||||
RNS.log(f"Adding device: {new_device}")
|
||||
def add_factory(add_device):
|
||||
def add_job(dt):
|
||||
pair_addr = add_device["address"]
|
||||
btn_text = "Pair "+add_device["name"]
|
||||
def run_pair(sender): self.hardware_rnode_pair_device_action(pair_addr)
|
||||
# device_button = MDRectangleFlatButton(text=btn_text,font_size=dp(16))
|
||||
device_button = MDRectangleFlatButton(text=btn_text, font_size=dp(16), padding=[dp(0), dp(14), dp(0), dp(14)], size_hint=[1.0, None])
|
||||
device_button.bind(on_release=run_pair)
|
||||
self.hardware_rnode_screen.ids.rnode_scan_results.add_widget(device_button)
|
||||
return add_job
|
||||
|
||||
Clock.schedule_once(add_factory(new_device), 0.1)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
def job(dt):
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_scan_button.disabled = False
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_scan_button.text = "Pair New Device"
|
||||
Clock.schedule_once(job, 0.2)
|
||||
|
||||
if len(added_devices) == 0:
|
||||
def job(dt): toast("No unpaired RNodes discovered")
|
||||
Clock.schedule_once(job, 0.2)
|
||||
|
||||
def hardware_rnode_pair_device_action(self, pair_addr):
|
||||
RNS.log(f"Pair action for {pair_addr}", RNS.LOG_DEBUG)
|
||||
self.stop_bluetooth_scan()
|
||||
BluetoothSocket = autoclass('android.bluetooth.BluetoothSocket')
|
||||
if self.bt_adapter == None: self.bt_adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
addr_bytes = bytes.fromhex(pair_addr.replace(":", ""))
|
||||
remote_device = self.bt_adapter.getRemoteDevice(addr_bytes)
|
||||
RNS.log(f"Remote device: {remote_device}", RNS.LOG_DEBUG)
|
||||
remote_device.createBond()
|
||||
RNS.log("Create bond call returned", RNS.LOG_DEBUG)
|
||||
|
||||
def hardware_rnode_bt_scan_action(self, sender=None):
|
||||
self.discovered_bt_devices = {}
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_scan_button.disabled = True
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_scan_button.text = "Scanning..."
|
||||
rw = []
|
||||
for child in self.hardware_rnode_screen.ids.rnode_scan_results.children: rw.append(child)
|
||||
for w in rw: self.hardware_rnode_screen.ids.rnode_scan_results.remove_widget(w)
|
||||
|
||||
Clock.schedule_once(self.bluetooth_scan_action, 0.5)
|
||||
threading.Thread(target=self.hardware_rnode_scan_job, daemon=True).start()
|
||||
|
||||
def hardware_rnode_bt_on_action(self, sender=None):
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_pair_button.disabled = True
|
||||
self.hardware_rnode_screen.ids.hardware_rnode_bt_on_button.disabled = True
|
||||
|
|
|
@ -2400,7 +2400,7 @@ MDScreen:
|
|||
spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(0), dp(0), dp(0), dp(35)]
|
||||
padding: [dp(0), dp(0), dp(0), dp(48)]
|
||||
|
||||
MDRectangleFlatIconButton:
|
||||
id: rnode_mote_export
|
||||
|
@ -2423,7 +2423,7 @@ MDScreen:
|
|||
on_release: root.app.hardware_rnode_import(self)
|
||||
|
||||
MDLabel:
|
||||
text: "Radio Options"
|
||||
text: "Radio Options\\n"
|
||||
font_style: "H6"
|
||||
|
||||
# MDTextField:
|
||||
|
@ -2522,8 +2522,8 @@ MDScreen:
|
|||
MDBoxLayout:
|
||||
orientation: "horizontal"
|
||||
size_hint_y: None
|
||||
padding: [0,0,dp(24),dp(0)]
|
||||
height: dp(48)
|
||||
padding: [0,dp(14),dp(24),dp(48)]
|
||||
height: dp(86)
|
||||
|
||||
MDLabel:
|
||||
text: "Control RNode Display"
|
||||
|
@ -2534,6 +2534,18 @@ MDScreen:
|
|||
pos_hint: {"center_y": 0.3}
|
||||
active: False
|
||||
|
||||
MDLabel:
|
||||
text: "Bluetooth Settings\\n"
|
||||
font_style: "H6"
|
||||
|
||||
MDLabel:
|
||||
id: hardware_rnode_info
|
||||
markup: True
|
||||
text: "If you enable connection via Bluetooth, Sideband will attempt to connect to any available and paired RNodes over Bluetooth. If your RNode uses BLE (ESP32-S3 and nRF devices) instead of classic Bluetooth, enable the [i]Device requires BLE[/i] option as well."
|
||||
size_hint_y: None
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "horizontal"
|
||||
size_hint_y: None
|
||||
|
@ -2567,7 +2579,65 @@ MDScreen:
|
|||
MDLabel:
|
||||
id: hardware_rnode_info
|
||||
markup: True
|
||||
text: "If you enable connection via Bluetooth, Sideband will attempt to connect to any available and paired RNodes over Bluetooth.\\n\\nYou must first pair the RNode with your device for this to work. If your RNode does not have a physical pairing button, you can enable Bluetooth and put it into pairing mode by first connecting it via a USB cable, and using the buttons below. When plugging in the RNode over USB, you must grant Sideband permission to the USB device for this to work.\\n\\nYou can also change Bluetooth settings using the \\"rnodeconf\\" utility from a computer.\\n\\nBy default, Sideband will connect to the first available RNode that is paired. If you want to always use a specific RNode, you can enter its name in the Preferred RNode Device Name field below, for example \\"RNode A8EB\\".\\n"
|
||||
text: "You must first pair the RNode with your device for this to work. To put an RNode into pairing mode, hold down the multi-function user button for more than 5 seconds, and release it. The display will indicate pairing mode.You can then pair the device using the Bluetooth settings of your device, or by pressing the pairing button below.\\n"
|
||||
size_hint_y: None
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(0), dp(0), dp(0), dp(12)]
|
||||
|
||||
MDRectangleFlatIconButton:
|
||||
id: hardware_rnode_bt_scan_button
|
||||
icon: "bluetooth-connect"
|
||||
text: "Pair New Device"
|
||||
padding: [dp(0), dp(14), dp(0), dp(14)]
|
||||
icon_size: dp(24)
|
||||
font_size: dp(16)
|
||||
size_hint: [1.0, None]
|
||||
on_release: root.app.hardware_rnode_bt_scan_action(self)
|
||||
|
||||
MDBoxLayout:
|
||||
id: rnode_scan_results
|
||||
orientation: "vertical"
|
||||
spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(0), dp(0), dp(0), dp(12)]
|
||||
|
||||
MDLabel:
|
||||
id: hardware_rnode_info
|
||||
markup: True
|
||||
text: "By default, Sideband will connect to the first available RNode that is paired. If you want to always use a specific RNode, you can enter its name in the Preferred RNode Device Name field below, for example \\"RNode A8EB\\"."
|
||||
size_hint_y: None
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
# padding: [dp(0), dp(0), dp(0), dp(35)]
|
||||
|
||||
MDTextField:
|
||||
id: hardware_rnode_bt_device
|
||||
hint_text: "Preferred RNode Device Name"
|
||||
text: ""
|
||||
font_size: dp(24)
|
||||
|
||||
MDLabel:
|
||||
text: "\\n\\nDevice Bluetooth Control\\n"
|
||||
font_style: "H6"
|
||||
|
||||
MDLabel:
|
||||
id: hardware_rnode_info
|
||||
markup: True
|
||||
text: "\\n\\nIf your RNode does not have a physical pairing button, you can enable Bluetooth and put it into pairing mode by first connecting it via a USB cable, and using the buttons below. When plugging in the RNode over USB, you must grant Sideband permission to the USB device for this to work.\\n\\nYou can also change Bluetooth settings using the \\"rnodeconf\\" utility from a computer.\\n"
|
||||
size_hint_y: None
|
||||
text_size: self.width, None
|
||||
height: self.texture_size[1]
|
||||
|
@ -2610,12 +2680,6 @@ MDScreen:
|
|||
size_hint: [1.0, None]
|
||||
on_release: root.app.hardware_rnode_bt_pair_action(self)
|
||||
disabled: False
|
||||
|
||||
MDTextField:
|
||||
id: hardware_rnode_bt_device
|
||||
hint_text: "Preferred RNode Device Name"
|
||||
text: ""
|
||||
font_size: dp(24)
|
||||
"""
|
||||
|
||||
layout_hardware_serial_screen = """
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue