Skip to content

Conversation

@bramd
Copy link
Contributor

@bramd bramd commented Oct 20, 2025

DotPad braille displays support both USB Serial and Bluetooth Low Energy (BLE) connections. NVDA previously only supported USB connections.

  • DotPad displays can now be automatically detected and connected via BLE
  • Users can manually select specific BLE devices from the braille settings dialog
  • BLE device scanning starts automatically when opening braille settings
  • Device list can be refreshed by switching to another display and back
  • When no BLE devices are found, a helpful message guides users to refresh
  • Add Bleak 1.1.0 dependency for BLE support
  • Implement asyncio event loop integration for async BLE operations
  • Create hwIo.ble module with Scanner and Ble classes
  • Extend bdDetect to support BLE device detection alongside USB/Bluetooth
  • Add BLE devices to getPossiblePorts() for manual selection in settings dialog
  • Update driverHasPossibleDevices() to include BLE devices
  • Implemented real-time device detection using extension points

DotPad Driver:

  • Registered BLE devices via driverRegistrar.addBleDevices() with name-based matching
  • Serialized BLE devices as "name@address" for unique identification
  • Overrode check() method to show driver when BLE adapter available
  • Added BLE connection support in _tryConnect() method

GUI Integration:

  • Start BLE scanner when braille settings dialog opens
  • Stop scanner on dialog close (unless background detection active)
  • Show "(No devices found - switch to another display and back to refresh)" when no ports available
  • Format BLE devices as "Bluetooth: DeviceName" in port dropdown

Link to issue number:

Fixes #18584

Summary of the issue:

DotPad braille displays support both USB Serial and Bluetooth Low Energy (BLE) connections. NVDA previously only supported USB connections.

Description of user facing changes:

  • DotPad displays can now be automatically detected and connected via BLE
  • Users can manually select specific BLE devices from the braille settings dialog
  • BLE device scanning starts automatically when opening braille settings
  • Device list can be refreshed by switching to another display and back
  • When no BLE devices are found, a helpful message guides users to refresh

Description of developer facing changes:

  • Add Bleak 1.1.0 dependency for cross-platform BLE support
  • Implement asyncio event loop integration for async BLE operations
  • Create hwIo.ble module with
    • scanner, a singleton to use as BLE scanner by NVDA core or other add-ons
    • Ble: a hwIo compatible class to communicate with BLE devices
  • Add an asyncioEventLoop module with an asyncio event loop and a utility method to schedule coroutines on that loop
  • Extend bdDetect to support BLE device detection alongside USB/Bluetooth
  • Add BLE devices to getPossiblePorts() for manual selection in settings dialog
  • Update driverHasPossibleDevices() to include BLE devices

Description of development approach:

Added Bleak for BLE communication. Since Bleak is an asyncio library, I had to implement an asyncio event loop as well. I chose to run this on it's separate thread and not integrate it in NVDA's WX main loop in any way.

bdDetect has been modified to scan for and match BLE devices with their drivers. Since with BLE in Windows the app is responsible for all the scanning and connecting, the scanner starts as soon as the braille display selection is opened. Otherwise, we never would have devices to connect to in the port selection list. If there are no ports, the ports list shows a message to help the user that they can switch to another display and back to refresh the list.

The DotPad driver has been modified to accept BLE devices to connect to. BLE devices are stored in the configuration as ble:name@address, since we need the address to connect if there is no scanner result. If there is a scanner result, we can match on name as well.

The onReceive method of the driver has been altered so it accepts both input one byte at a time (for serial) or as whole packets (BLE).

The BLE scanner implements a new Action (extension point) that fires when a BLE device is discovered. Therefore, connection after turning on the device should be very quickly established.

Testing strategy:

Wrote extensive unit tests and tested with actual hardware.

Things to test:

  1. Automatic detection on BLE and USB
  2. Turn DotPad on before NVDA starts
  3. Start NVDA, set display to auto and turn on Dot Pad
  4. Switching to USB when cable is plugged in and devices is already connected over BLE
  5. Manual selection of a specific BLE device, saving the configuration and restarting NVDA, the device should connect immediately after restart

Known issues with pull request:

Since BLE does not require any pairing, the bdDetect system now connects to any DotPad it discovers. Since DotPad is disabled for automatic detection by default and users can set a specific BLE device to connect to, I think this is acceptable. If not, we would have to implement a GUI for selecting devices and keep a list of selected devices that are allowed to be discovered automatically.

Code Review Checklist:

  • Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

@bramd bramd marked this pull request as ready for review October 20, 2025 16:00
@bramd bramd requested review from a team as code owners October 20, 2025 16:00
log.debug("Starting BLE scanner for background scan")
hwIo.ble.scanner.start()
# Give the scanner some time to get initial results
time.sleep(0.2)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we somehow wait non-blocking here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was assuming that a background scan was always running on the hwIo thread and that a blocking sleep wouldn't matter that much. In theory, Windows is already scanning and caching results, but depending on hardware and BLE stack implementation we might end up with 0 results without a wait. So I found it more acceptable to have a small delay in a background scan then having no results and only having a chance to find BLE devices in the next scanning round. Especially when having Bluetooth classic devices that can match in a background scan and having Windows take 10-20 seconds to get a timeout when trying to connect a Bluetooth serial port, I think a 0.2 sec delay is acceptable.

If my assumptions are invalid or you see a way to wait non-blocking and still get results in the same scanning round, I would like to hear your thoughts.

@param bluetooth: Whether the handler is expected to yield USB devices.
@param bluetooth: Whether the handler is expected to yield Bluetooth devices.
@type bluetooth: bool
@param ble: Whether the handler is expected to yield BLE devices.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have you considered reusing the bluetooth parameter? Can't you just handle BLE in case a driver is enabled that supports BLE?
I assume scanning for BLE can be a bit costly. It makes not that much sense to scan for these devices when I don't have a device that supports it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered it, but chose a separate ble parameter because:

  1. BLE can connect to devices without Windows pairing them first and there might be a wish to disable the feature for example in an environment with any BLE braille displays, where Bluetooth classic pairing would still be acceptable for automatic detection. Note that we do not expose that in the UI at the moment
  2. Windows' documentation is unclear about the cost of BLE scanning. It seems Windows is doing some kind of scanning and caching anyway, but this might also be dependent on the hardware and Bluetooth drivers/stack and is hard to test without clear documentation and not much Bluetooth adapters to test with

However, if there is no wish to disable BLE separately, I'd be fine with merging the ble/bluetooth parameters.

ports[portKey] = portName
except Exception:
# If BLE scanning fails, continue without BLE devices
pass
Copy link
Collaborator

Choose a reason for hiding this comment

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

Silently discarding a bare except feels like bad practice. Could you either make the exception more specific or log a debug warning or the like?

if isinstance(port, bdDetect.DeviceMatch):
yield port
elif isinstance(port, str):
# Check if this is a specific BLE device port (format: "ble:DeviceName@Address" or legacy "ble:DeviceName")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you split this out into a helper function?

try:
self._processPacket(packet[4:])
except Exception:
log.error("Error processing packet", exc_info=True)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will re-enter the loop, right? Can't this create an infinite loop we'll never break from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before processing the packet, it's removed from the buffer. So in most cases we would end up with an empty buffer and the loop would exit. If there somehow is a few bytes left in the buffer it will either be a valid packet and processed as such, or the loop will exit because there are not enough bytes. Where/how do you see the risk of the loop not being exited?

else:
# No ports available - show helpful message
# Translators: Message shown when no devices are available for a braille display
noDevicesMessage = _("(No devices found - switch to another display and back to refresh)")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could there be a better UX for this? Can't you just refocus or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you clarify this? Do you want the ports list to be enabled so people can read this message better or do you want that the ports list dynamically updates in realtime if devices are discovered?

@LeonarddeR LeonarddeR requested a review from Copilot October 20, 2025 19:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds Bluetooth Low Energy (BLE) support for DotPad braille displays, enabling automatic detection and manual selection of BLE-connected devices alongside existing USB support.

Key changes:

  • Introduced Bleak 1.1.0 dependency and asyncio event loop infrastructure for BLE communication
  • Extended bdDetect system to handle BLE device scanning and matching in parallel with USB/Bluetooth
  • Updated GUI to automatically start BLE scanning when braille settings dialog opens, with user guidance for device refresh

Reviewed Changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
user_docs/en/userGuide.md Added BLE connection documentation and usage instructions for DotPad
user_docs/en/changes.md Documented BLE support as a new feature in changelog
tests/unit/test_hwIo_ble.py Added comprehensive unit tests for Scanner, Ble I/O, and device lookup
tests/unit/test_bdDetect.py Extended tests to cover BLE device registration and matching
tests/unit/brailleDisplayDrivers/test_dotPad.py Added buffered receive tests for serial and BLE packet handling
source/hwIo/ble/_scanner.py Implemented BLE scanner wrapper around Bleak with extension point for device discovery
source/hwIo/ble/_io.py Created Ble I/O class for BLE communication with MTU-aware writes and notification handling
source/hwIo/ble/init.py Exposed scanner singleton and device lookup utility functions
source/gui/settingsDialogs.py Modified braille settings dialog to start/stop BLE scanner and display helpful messages
source/core.py Integrated asyncio event loop initialization and termination
source/brailleDisplayDrivers/dotPad/driver.py Enhanced driver to support BLE connections with buffered packet processing
source/brailleDisplayDrivers/dotPad/defs.py Added BLE service and characteristic UUIDs
source/braille.py Extended device detection and port enumeration to include BLE devices
source/bdDetect.py Added BLE scanning, real-time discovery notifications, and device matching
source/asyncioEventLoop.py Created asyncio event loop module for running async operations
pyproject.toml Added Bleak dependency and WinRT package exceptions

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

bramd and others added 5 commits October 20, 2025 23:55
DotPad braille displays support both USB Serial and Bluetooth Low Energy (BLE) connections. NVDA previously only supported USB connections.

- DotPad displays can now be automatically detected and connected via BLE
- Users can manually select specific BLE devices from the braille settings dialog
- BLE device scanning starts automatically when opening braille settings
- Device list can be refreshed by switching to another display and back
- When no BLE devices are found, a helpful message guides users to refresh
- Add Bleak 1.1.0 dependency for BLE support
- Implement asyncio event loop integration for async BLE operations
- Create hwIo.ble module with Scanner and Ble classes
- Extend bdDetect to support BLE device detection alongside USB/Bluetooth
- Add BLE devices to getPossiblePorts() for manual selection in settings dialog
- Update driverHasPossibleDevices() to include BLE devices
- Implemented real-time device detection using extension points

DotPad Driver:
- Registered BLE devices via driverRegistrar.addBleDevices() with name-based matching
- Serialized BLE devices as "name@address" for unique identification
- Overrode check() method to show driver when BLE adapter available
- Added BLE connection support in _tryConnect() method

GUI Integration:
- Start BLE scanner when braille settings dialog opens
- Stop scanner on dialog close (unless background detection active)
- Show "(No devices found - switch to another display and back to refresh)" when no ports available
- Format BLE devices as "Bluetooth: DeviceName" in port dropdown
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Leonard de Ruijter <[email protected]>
…routine, wait for the result and either return that result or any raised exceptions
@SaschaCowley SaschaCowley added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Oct 21, 2025
@bramd
Copy link
Contributor Author

bramd commented Oct 21, 2025

@SaschaCowley I see you fixed the Chrome system tests yesterday, but in this PR the startup/shutdown and installer tests are still failing. Is this expected or could it indicate a problem introduced in this PR?

@SaschaCowley
Copy link
Member

Hi @bramd, sometimes these tests do just fail. However the fact that the tests on Windows 2022 and 2025 failed in the same way is suspicious. I've triggered the tests to rerun to see if it was just a fluke

@bramd
Copy link
Contributor Author

bramd commented Oct 29, 2025

Hi @bramd, sometimes these tests do just fail. However the fact that the tests on Windows 2022 and 2025 failed in the same way is suspicious. I've triggered the tests to rerun to see if it was just a fluke

Unfortunately, they still seem to fail. I'm trying to get a VM going with NVDA source to test properly without a Bluetooth adapter, but have some issues getting the build environment set up for now. Pending the failing tests, I think this is not ready for merge. However, I would appreciate it if you can at least test this on your hardware, since there are not much users with Dot Pad hardware to try this PR.

@SaschaCowley
Copy link
Member

However, I would appreciate it if you can at least test this on your hardware, since there are not much users with Dot Pad hardware to try this PR.

I will try out the PR tomorrow and let you know how it goes.

Just so you're aware, this PR may take longer for us to get to as it is quite large, so I want to set aside a while to sit down with it and properly digest it. We definitely have not forgotten about it though :)

@SaschaCowley
Copy link
Member

Hi @bramd,

I have just tested this PR with our DotPad. I performed the current steps:

  1. Build and ran this PR from source.
  2. Switched on the DotPad.
  3. Opened the braille display selection dialog, selected DotPad, and selected bluetooth as the port.
  4. Pressed Ok.

This caused the DotPad to emit two long buzzes, pause for a few seconds, then emit two more long buzzes, and NVDA to report an error connecting.

IO - speech.speech.speak (14:28:11.551) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Log fragment start position marked, press again to copy to clipboard']
IO - inputCore.InputManager.executeGesture (14:28:12.765) - winInputHook (10844):
Input: kb(desktop):NVDA+control+a
DEBUG - gui.settingsDialogs.__new__ (14:28:12.775) - MainThread (8232):
Creating new settings dialog (multiInstanceAllowed:False). State of _instances {}
DEBUG - gui.contextHelp.bindHelpEvent (14:28:12.778) - MainThread (8232):
Did context help binding for BrailleDisplaySelectionDialog
DEBUG - gui.contextHelp.bindHelpEvent (14:28:12.783) - MainThread (8232):
Did context help binding for LabeledControlHelper.__init__.<locals>.WxCtrlWithEnableEvnt
DEBUG - gui.contextHelp.bindHelpEvent (14:28:12.787) - MainThread (8232):
Did context help binding for LabeledControlHelper.__init__.<locals>.WxCtrlWithEnableEvnt
DEBUG - gui.contextHelp.bindHelpEvent (14:28:12.794) - MainThread (8232):
Did context help binding for LabeledControlHelper.__init__.<locals>.WxCtrlWithEnableEvnt
DEBUGWARNING - hwPortUtils.listUsbDevices (14:28:12.796) - MainThread (8232):
Couldn't get DEVPKEY_Device_BusReportedDeviceDesc for {'hardwareID': 'USB\\VID_10AB&PID_9309&REV_0001', 'usbID': 'VID_10AB&PID_9309', 'devicePath': '\\\\?\\usb#vid_10ab&pid_9309#7&2f3a9896&0&1#{a5dcbf10-6530-11d2-901f-00c04fb951ed}'}: [WinError 1168] Element not found.
DEBUGWARNING - braille.getDisplayList (14:28:12.797) - MainThread (8232):
Braille display driver albatross reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.797) - MainThread (8232):
Braille display driver alva reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.798) - MainThread (8232):
Braille display driver baum reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.798) - MainThread (8232):
Braille display driver brailleNote reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.798) - MainThread (8232):
Braille display driver brailliantB reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.798) - MainThread (8232):
Braille display driver brltty reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.799) - MainThread (8232):
Braille display driver eurobraille reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.799) - MainThread (8232):
Braille display driver freedomScientific reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.799) - MainThread (8232):
Braille display driver handyTech reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.799) - MainThread (8232):
Braille display driver hidBrailleStandard reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.799) - MainThread (8232):
Braille display driver hims reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.800) - MainThread (8232):
Braille display driver nattiqbraille reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.800) - MainThread (8232):
Braille display driver nlseReaderZoomax reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.800) - MainThread (8232):
Braille display driver seikantk reports as unavailable, excluding
DEBUGWARNING - braille.getDisplayList (14:28:12.800) - MainThread (8232):
Braille display driver superBrl reports as unavailable, excluding
IO - speech.speech.speak (14:28:12.889) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Select Braille Display', 'dialog', CancellableSpeech (still valid)]
IO - speech.speech.speak (14:28:12.891) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Braille display:', 'combo box', 'Automatic', 'collapsed', 'Alt+', CharacterModeCommand(True), 'd', CharacterModeCommand(False), CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (14:28:14.744) - winInputHook (10844):
Input: kb(desktop):d
DEBUGWARNING - hwPortUtils.listUsbDevices (14:28:14.747) - MainThread (8232):
Couldn't get DEVPKEY_Device_BusReportedDeviceDesc for {'hardwareID': 'USB\\VID_10AB&PID_9309&REV_0001', 'usbID': 'VID_10AB&PID_9309', 'devicePath': '\\\\?\\usb#vid_10ab&pid_9309#7&2f3a9896&0&1#{a5dcbf10-6530-11d2-901f-00c04fb951ed}'}: [WinError 1168] Element not found.
IO - speech.speech.speak (14:28:14.769) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'DotPad Braille / Tactile Graphic display']
DEBUG - hwIo.ble._scanner.Scanner._onDeviceAdvertised (14:28:17.030) - Thread-2 (run_forever) (12744):
Discovered BLE device: 3E:F6:98:02:9B:9A
IO - inputCore.InputManager.executeGesture (14:28:17.161) - winInputHook (10844):
Input: kb(desktop):enter
DEBUG - hwIo.ble.findDeviceByAddress (14:28:17.164) - MainThread (8232):
Searching for BLE device with address 34:81:F4:45:CD:21
DEBUG - hwIo.ble.findDeviceByAddress (14:28:17.164) - MainThread (8232):
Found BLE device 34:81:F4:45:CD:21 in existing results
INFO - hwIo.ble._io.Ble.__init__ (14:28:17.164) - MainThread (8232):
Connecting to DotPad320A_CD21 (34:81:F4:45:CD:21)
DEBUGWARNING - brailleDisplayDrivers.dotPad.driver.BrailleDisplayDriver._tryConnect (14:28:19.170) - MainThread (8232):
Failed to connect
Traceback (most recent call last):
  File "asyncioEventLoop.py", line 83, in runCoroutineSync
    return future.result(timeout)
           ~~~~~~~~~~~~~^^^^^^^^^
  File "C:\Users\SaschaCowley\AppData\Local\Programs\Python\Python313\Lib\concurrent\futures\_base.py", line 458, in result
    raise TimeoutError()
TimeoutError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "brailleDisplayDrivers\dotPad\driver.py", line 350, in _tryConnect
    self._dev = hwIo.ble.Ble(
                ~~~~~~~~~~~~^
    	device=device,  # Can be BLEDevice or str
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<4 lines>...
    	onReceive=self._onReceive,
     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "hwIo\ble\_io.py", line 123, in __init__
    runCoroutineSync(self._initAndConnect(), timeout=CONNECT_TIMEOUT_SECONDS)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "asyncioEventLoop.py", line 87, in runCoroutineSync
    raise TimeoutError(f"Coroutine execution timed out after {timeout} seconds") from e
TimeoutError: Coroutine execution timed out after 2 seconds
ERROR - braille.BrailleHandler.setDisplayByName (14:28:19.172) - MainThread (8232):
Error initializing display driver 'dotPad'
Traceback (most recent call last):
  File "braille.py", line 2784, in setDisplayByName
    self._setDisplay(newDisplayClass, isFallback=isFallback, detected=detected)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "braille.py", line 2855, in _setDisplay
    newDisplay = self._switchDisplay(oldDisplay, newDisplayClass, **kwargs)
  File "braille.py", line 2825, in _switchDisplay
    extensionPoints.callWithSupportedKwargs(newDisplay.__init__, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "extensionPoints\util.py", line 230, in callWithSupportedKwargs
    return func(*boundArguments.args, **boundArguments.kwargs)
  File "brailleDisplayDrivers\dotPad\driver.py", line 325, in __init__
    raise RuntimeError("No DotPad device found")
RuntimeError: No DotPad device found
DEBUGWARNING - Python warning (14:28:19.189) - MainThread (8232):
D:\projects\nvda-pr\source\gui\message.py:129: DeprecationWarning: gui.message.messageBox is deprecated. Use gui.message.MessageDialog instead.
  warnings.warn(
DEBUG - gui.contextHelp.bindHelpEvent (14:28:19.191) - MainThread (8232):
Did context help binding for MessageDialog
DEBUG - gui.message.MessageDialog.ShowModal (14:28:19.201) - MainThread (8232):
Adding <gui.message.MessageDialog object at 0x00000172028DD130> to instances.
DEBUG - gui.message.MessageDialog.ShowModal (14:28:19.202) - MainThread (8232):
Showing <gui.message.MessageDialog object at 0x00000172028DD130> as modal
IO - speech.speech.speak (14:28:19.224) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Braille Display Error', 'dialog', 'Could not load the dotPad display.', CancellableSpeech (still valid)]
IO - speech.speech.speak (14:28:19.226) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'OK', 'button', CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (14:28:27.470) - winInputHook (10844):
Input: kb(desktop):enter
DEBUG - gui.message.MessageDialog._onButtonEvent (14:28:27.473) - MainThread (8232):
Got button event on id=5100
DEBUG - gui.message.MessageDialog._onCloseEvent (14:28:27.478) - MainThread (8232):
Queueing <gui.message.MessageDialog object at 0x00000172028DD130> for destruction
DEBUG - gui.message.MessageDialog._onCloseEvent (14:28:27.478) - MainThread (8232):
Removing <gui.message.MessageDialog object at 0x00000172028DD130> from instances.
IO - speech.speech.speak (14:28:27.500) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Select Braille Display', 'dialog', CancellableSpeech (still valid)]
IO - speech.speech.speak (14:28:27.504) - MainThread (8232):
Speaking [LangChangeCommand ('en_GB'), 'Braille display:', 'combo box', 'DotPad Braille / Tactile Graphic display', 'collapsed', 'Alt+', CharacterModeCommand(True), 'd', CharacterModeCommand(False), CancellableSpeech (still valid)]
IO - inputCore.InputManager.executeGesture (14:28:33.567) - winInputHook (10844):
Input: kb(desktop):NVDA+control+shift+f1

@seanbudd
Copy link
Member

seanbudd commented Nov 3, 2025

it seems like this is causing serious system test errors around NVDA exiting safely

@seanbudd seanbudd marked this pull request as draft November 3, 2025 02:23
@bramd
Copy link
Contributor Author

bramd commented Nov 4, 2025

Just so you're aware, this PR may take longer for us to get to as it is quite large, so I want to set aside a while to sit down with it and properly digest it. We definitely have not forgotten about it though :)

No problem, I'm also still having weird issues (not related to this PR) building on my test machine without a Bluetooth adapter and would like to do a good test run on such a machine. That might also give some more insight on the failing system tests. For me this work is partly ported from what was in the Dot Pad add-on and partly rewritten to better fit NVDA core or because I saw room for improvement since the last iteration. Given the complexity of introducing asyncio and the Bleak library and touching quite some points in bdDetect I really appreciate a thorough code review on this. I've quite some time to work on this the coming weeks, so I should be able to address comments quickly.

@bramd
Copy link
Contributor Author

bramd commented Nov 4, 2025

[...]
This caused the DotPad to emit two long buzzes, pause for a few seconds, then emit two more long buzzes, and NVDA to report an error connecting.

The first 2 buzzes are expected. The Dot Pad double buzzes every time a BLE connection is made or a connection is closed. So the second buzzes you see match the closing of the connection after the driver fails to initialize.

Could you please try the following:

  1. Enable hwIo debug logging in the advanced settings, logging categories. This will give you a raw dump of every BLE packet the driver sends/receives. Based on your log somehow the connection is not initialized correctly so I don't expect any packets to see now, but it helps you debugging
  2. There is a hardware limitation where the BLE interface may not work correctly if a USB cable is plugged in the data port (left USB port), so please ensure there is no cable plugged in that port
  3. Connect a charger to the power port (on the right) or press and hold panLeft+panRight to get battery state, it will buzz a few times to indicate the battery percentage, with 2 quick buzzes it's almost empty, 5 buzzes is fully charged, with an almost empty battery the BLE connection might drop immediately
  4. Since your unit is a quite early one, it might help to upgrade the firmware. Use a Chromium-based browser and go to https://support.dotincorp.com/update/firmware/connect
  5. You could increase the CONNECT_TIMEOUT constant in ble._io just to see if that makes any difference. I never had issues with the 2 seconds that it is now, but of course other hardware and Bluetooth drivers may give different results

If you can't get it working, I'd be glad to help you out here or by email if we want to keep the PR comments relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to connect Dot Pad via Bluetooth

4 participants