Electron Web 蓝牙 API requestDevice() 错误

2024-04-22

我正在尝试开发一个与蓝牙低功耗设备通信的应用程序。我使用 Web Bluetooth API 建立了一个工作“网站”。一切正常,所以我使用 Electron 框架来构建一个应用程序。

这个问题是已知的 - 如果你开始navigator.bluetooth.requestDevice(),您会收到此错误消息:User cancelled the requestDevice() chooser..

这是由于 Chromium 中缺少设备选择器造成的。我找到了几个有关解决方法的主题,但没有示例。这是我的第一个电子项目。也许有人解决了这个问题并可以给我一个提示:-)


非常感谢您的支持。根据您的建议和一些研究,我开发了一个可行的解决方案,并愿意与您分享。

这两个链接对我帮助很大:

https://github.com/electron/electron/issues/11865 https://github.com/electron/electron/issues/11865

https://github.com/electron/electron/issues/10764 https://github.com/electron/electron/issues/10764

尤其是 MarshallOfSound 的这篇文章 - 描述得很清楚:

  • 主进程中的钩子事件
  • 使用设备列表向渲染器进程发送消息
  • 在渲染器进程中显示 UI
  • 将选定的设备发送到主进程
  • 呼叫回调

要获取有关主进程和渲染器进程、事件及其 API 的更多信息,请阅读以下内容:

https://www.electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes https://www.electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes

https://www.electronjs.org/docs/api/ipc-main https://www.electronjs.org/docs/api/ipc-main

https://www.electronjs.org/docs/api/web-contents#contentssendchannel-args https://www.electronjs.org/docs/api/web-contents#contentssendchannel-args

https://www.electronjs.org/docs/api/ipc-renderer https://www.electronjs.org/docs/api/ipc-renderer

https:// Electronjs.org/docs/api/web-contents#event-select-bluetooth-device https://electronjs.org/docs/api/web-contents#event-select-bluetooth-device(已由 Gerrit 发布)

https://www.electronjs.org/docs/api/structs/bluetooth-device https://www.electronjs.org/docs/api/structures/bluetooth-device

对于我的应用程序,我想要一个设备选择器,如 Chrome 中所示。我想要实现的顺序是:

  • 开始申请
  • 搜索设备
  • 弹出设备选择器
  • 选择设备
  • 设备选择器关闭
  • 查看应用程序中的数据

教程流程代码参考及代码片段:

电子应用程序:main.js(主进程)renderer.js(渲染进程)devicepicker GUI:devicepicker.js(渲染进程)devicepicker.html&layout.css(GUI)

1)使用 GUI(我使用了两个)和脚本创建 devicepicker

2)在你的 main.js 中创建一个select-bluetooth-device内的事件'ready'您的应用程序对象的事件(上面链接中的文档)当您开始时navigator.bluetooth.requestDevice()在 renderer.js 中,事件 get 被触发,并且设备列表位于主进程中。和console.log(deviceList)它在外壳中可见。要处理它,您需要将其发送到渲染器进程(您的应用程序窗口)。

3)为了实现这一目标,我们实施webContents.sendwebContents.on 事件中的 BrowserWindow 对象。现在主进程每次通过通道发现新设备时都会发送一个设备列表channelForBluetoothDeviceList

4)在 renderer.js 中创建startDevicePicker(). devicePicker()必须在与以下相同的函数中启动navigator.bluetooth.requestDevice(). startDevicePicker()实例化一个新的BrowserWindow()加载的对象devicepicker.html

5)要从主进程获取列表ipcRenderer.on()侦听器必须在中实现startDevicePicker()那个听channelForBluetoothDeviceList我们的主要流程的通道。现在我们可以在电子应用程序(渲染器过程)中获取列表。要将其发送到 devicepicker UI,我们需要将其从我们的电子应用程序(渲染器进程)转发到 devicepicker(也是渲染器进程)

6)为了实现这一目标,我们需要ipcRenderer.sendTo()发件人在devicePicker(),它将消息从渲染器进程转发到特定的其他渲染器进程。频道附加bluetoothDeviceDiscoverList我们需要BrowserWindow.id设备选择器的。由于我们刚刚实例化了它,因此我们可以使用 devicepicker 对象。我有一个只发送一次的设备,主进程比 devicepicker 的构建速度更快,并且我的列表从未发送到 devicepicker。所以我用了一个Promise()等待ipcRenderer.sendTo()直到设备选择器可供使用。

7)为了在我们的 devicepicker GUI 上接收设备列表,我们需要监听bluetoothDeviceDiscoverList with ipcRenderer.on()(devicepicker.js)。我现在将设备列表插入到<option>对于 devicepicker,您当然可以使用其他元素(devicepicker.html)。请注意:实现一个查询,将发送的列表与当前的列表进行比较。否则,您会获得多个设备,并且您的选择会变得很长。我还需要这样做,它还没有完成:-)

8)要选择一个设备navigator.bluetooth.requestDevice()(renderer.js) 得到解决,我们需要发回BluetoothDevice.deviceId我们选择的设备到主进程,我们使用 deviceId 作为回调参数回调“callback”(main.js)。

9)现在我们可以使用ipcRenderer.sendTo()发送所选的BluetoothDevice.deviceId到主进程(devicepicker.js)。

10)在我们的电子应用程序的主进程(main.js)中,我们监听通道channelForSelectingDevice with ipcMain.on()并回调收到的BluetoothDevice.deviceId。设备发现停止,navigator.bluetooth.requestDevice()得到解决,我们在应用程序(renderer.js)中从设备接收数据。要取消设备的发现,请使用ipcMain.on()在另一个频道channelForTerminationSignal只是主进程(main.js)的一个信号,例如单击(devicepicker.js)后并使用空字符串调用回调(如文档中所写)

我承认如果没有设备选择器,它可以更简单地完成。然后只需将设备列表从主进程(main.js)发送到您的应用程序(渲染器进程)即可。但这对我理解电子过程有很大帮​​助。我希望本教程对您有用:-)!

        main.js
        
    const { ipcMain, app, BrowserWindow } = require('electron')
    
    let win = null;
    
    var callbackForBluetoothEvent = null;
    
    // Create the browser window.
    function createWindow () {
      
      win = new BrowserWindow({
        webPreferences: {
          nodeIntegration: true //to activate require()
        }
      })
    
      win.maximize()
      win.show()
    
      //This sender sends the devicelist from the main process to all renderer processes
      win.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
        event.preventDefault(); //important, otherwise first available device will be selected
        console.log(deviceList); //if you want to see the devices in the shell
        let bluetoothDeviceList = deviceList;
        callbackForBluetoothEvent = callback; //to make it accessible outside createWindow()
    
        win.webContents.send('channelForBluetoothDeviceList', bluetoothDeviceList);
        });
    
        // This method will be called when Electron has finished
        // initialization and is ready to create browser windows.
        // Some APIs can only be used after this event occurs.
        app.on('ready', createWindow)
    
        //cancels Discovery
        ipcMain.on('channelForTerminationSignal', _ => {
            callbackForBluetoothEvent(''); //reference to callback of win.webContents.on('select-bluetooth-device'...)
            console.log("Discovery cancelled");
        });
    
        //resolves navigator.bluetooth.requestDevice() and stops device discovery
        ipcMain.on('channelForSelectingDevice', (event, DeviceId) => {
            callbackForBluetoothEvent(sentDeviceId); //reference to callback of win.webContents.on('select-bluetooth-device'...)
            console.log("Device selected, discovery finished");
        })
        renderer.js
    
    function discoverDevice() {
        navigator.bluetooth.requestDevice()
        startDevicepicker()
    }
    
    function startDevicepicker(){
        
        let devicepicker = null;
        let mainProcessDeviceList = null;
        
        devicepicker = new BrowserWindow({
            width: 350,
            height: 270,
            show: false, //needed to resolve promise devicepickerStarted()            
            webPreferences: {
                nodeIntegration: true
              }
        })
    
        devicepicker.loadFile('devicePicker.html');
    
        //electron application listens for the devicelist from main process
        ipcRenderer.on('channelForBluetoothDeviceList', (event, list) => {
            mainProcessDeviceList = list;
            devicepickerStarted.then(_=> {
                console.log("Promise resolved!");
                ipcRenderer.sendTo(devicepicker.webContents.id, 'bluetoothDeviceDiscoverList', mainProcessDeviceList);     
            })   
        })
    
        //Promise that ensures that devicepicker GUI gets the list if the device only sends once
        var devicepickerStarted = new Promise(
            function (resolve, reject) {
                console.log("Promise started");
                devicepicker.once('ready-to-show', () => {
                    devicepicker.show();
                    resolve();
                    console.log("Devicepicker is ready!")
                })
            }
        )
    
        //remove listeners after closing devicepicker
        devicepicker.on('closed', _ => {            
            devicepicker = null;
            ipcRenderer.removeAllListeners('channelForBluetoothDeviceList');
            ipcRenderer.removeAllListeners('currentWindowId');
            ipcRenderer.removeAllListeners('receivedDeviceList');            
        })
    }
    
        devicepicker.js
        
    //save received list here
    var myDeviceList = new Array();
    
    //Html elements
    const devicePickerSelection = document.getElementById("devicePickerSelection");
    const buttonSelect = document.getElementById("Select");
    const buttonCancel = document.getElementById("Cancel");
    
    //eventListeners for buttons
    buttonSelect.addEventListener('click', selectFromDevicePicker);
    buttonCancel.addEventListener('click', cancelDevicePicker);
    
    //listens for deviceList
    ipcRenderer.on('receivedDeviceList', (event, bluetoothDeviceDiscoverList) => {
        console.log("list arrived!")
        //code: add list to html element
        });
    
    function selectFromDevicePicker() {
        let selectedDevice = devicePickerSelection.value;
        let deviceId = //depends on where you save the BluetoothDevice.deviceId values
    
        //sends deviceId to main process for callback to resolve navigator.bluetooth.requestDevice()
        ipcRenderer.send('channelForSelectingDevice', deviceId);
    
        ipcRenderer.removeAllListeners('receivedDeviceList');    
        closeDevicePicker();
    }
    
    function cancelDevicePicker() {    
        ipcRenderer.send('channelForTerminationSignal');
        closeDevicePicker();
    }
    
    function closeDevicePicker() {    
        myDevicePicker.close();
    }}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Electron Web 蓝牙 API requestDevice() 错误 的相关文章

  • bluez 同时具有经典和低能耗设备

    Linux下的bluez是否可以同时连接多个经典和低能耗设备 bluez 网站提供这样的信息不是很有帮助 是的 我已经成功同时连接到 7 个低能耗设备 最大值因您使用的硬件而异 您还可以连接到多个经典设备 以下是我用于通过 L2CAP 连接
  • 电子关闭按钮不起作用

    我正在尝试使用 Electron 以前称为 Atom Shell 创建一个应用程序 该应用程序包装了 AngularJS 应用程序 并与在 Node js 中创建的端点交互以编辑和保存 HTML 内容 我能够毫无问题地创建应用程序 当我尝试
  • Electron打包后不支持ES6

    我正在使用各种ES6语法 http es6 features org 例如importETC React https reactjs org code JSX https reactjs org docs introducing jsx h
  • 在电子应用程序中从主程序到渲染器发出自定义事件

    所以我知道这是有效的 因为我尝试过 但它没有在任何地方记录 所以我问是否可以使用这种做法 而不用担心它将来会停止工作 众所周知 Electron 和 Nodejs 会破坏一个东西 版本到另一个 这就是我所说的练习类型 main js app
  • 我应该在 OBDII 的 BLE IOS 设备中使用什么 BLE 特性

    您好 我想知道我应该从这个 OBDII BLE 设备 加密狗中使用什么写入和通知特性 我想在 Flutter 中创建一个适用于 IOS 的程序 有不少 Device name VEEPEAK Device id 34E2B2AF 60F4
  • 使用bluez5读取蓝牙低能耗信标的RSSI

    我想在 2 个蓝牙 le USB 适配器上同时读取蓝牙低功耗信标的 RSSI 我使用的是带有 bluez 5 和 LogiLink BT0015 适配器 CSR 芯片 的 fedora 20 系统 当仅连接一个适配器时 我的代码就像一个魅力
  • 处理来自 Electron (或其他桌面平台)的 oauth2 重定向

    这主要是缺乏对 oauth2 的理解 并且可能不是特定于 Electron 的 但是我试图理解某人如何处理来自桌面平台 如 Electron 的 oauth2 重定向 url 假设应用程序中没有 Web 服务设置 桌面应用程序如何提示用户输
  • 如果设备关闭,尝试在 IOS 应用程序中检索之前配对的蓝牙设备将不会响应失败

    很抱歉标题很长 但我们在使用 iOS 版 corebluetooth 时遇到了一个非常有趣的问题 我们正在 CBCentralManager 中发出对retrievePeripherals 的调用 并且能够找到之前配对的设备 不管设备是打开
  • 从 iBeacon 接收 BLE 信号到 Bluno(arduino with BLE)

    我想从 iBeacon 到 Bluno 接收 rssi 信号和 UUID Arduino 板具有 BLE 对此有一些疑问 有没有从 BLE 到 BLE 接收 UUID 和 rssi 的解决方案 两个BLE设备可以互相通信吗 我想找一些网站来
  • BLE:使用 Android / iOS 读取长特征值

    对于正常的特征读取 仅读取 MTU 大小 20 字节 的数据 我的客户将提供更大尺寸 超过 100 字节 的特征 我看到 BLE 提供了 长读取 功能 可以读取直到达到特征的大小 https bluegiga zendesk com ent
  • 在 Android 中读取蓝牙 RSSI 以获取 BLE 邻近配置文件

    我目前正在为 Android 开发低功耗蓝牙接近配置文件 并且偶然发现了有关 RSSI 的问题 为了使邻近配置文件起作用 我必须每隔很短的时间就通过已连接的设备接收 RSSI 数据 正如我所做的一些研究 我了解到在设备发现过程中可以获得蓝牙
  • Electron Autoupdater 与私有 GitHub 存储库?

    我已经使用 PRIVATE GitHub Repository 作为发布电子应用程序的提供者实现了 Electron AutoUpdater 现在 我可以使用 GitHub 存储库发布它 但是每当 AutoUpdater 尝试从 GitHu
  • ElectronJS 捕获屏幕质量低

    我正在使用 ElectronJS 测试屏幕捕获 我可以捕获屏幕 但捕获的视频质量低于原始视频 操作系统 Linux Mint 20 电子版本 11 1 0 这是我的代码 我选择我的屏幕 然后使用以下命令在电子应用程序中显示捕获的屏幕vide
  • Android BLE - 如何分块读取大特征值(使用偏移量)?

    我正在使用 Android SDKandroid 蓝牙 and android 蓝牙 le APIs 我想实现一个应用程序 发挥核心作用 并连接到 BLE 外设以读取特征值和描述符 应用程序需要读取的特征值较大 因此需要分块连续读取 我对如
  • Android Lollipop BLE 扫描 - 获取没有重复的外设

    Android Lollipop 引入了一种扫描 BLE 外设的新方法 通过蓝牙扫描仪 http developer android com reference android bluetooth le BluetoothLeScanner
  • 蓝牙 LE 中步数计数的服务和特性是什么?

    低功耗蓝牙中步数的服务 特征值 UUID 是什么 蓝牙文档的相应页面没有列出任何步骤 如何计算步数 计步设备是否使用跑步速度和步频服务 谢谢 有些设备确实使用跑步速度和节奏服务 您应该能够通过将 总距离 除以 瞬时步长 两者都可以在RSC测
  • 蓝牙 BLE Android 以最大吞吐量写入外设

    我们公司开发了一个具有蓝牙 BLE 的硬件单元 并且我们在 Nexus 7 2013 中有一个服务应用程序 我们希望使用它向该单元发送固件文件 文件最大可达 500kb BT芯片是德州仪器CC2540 我浏览了大量的页面 并扫描了 Stac
  • 如何在React Material UI简单输入中启用文件上传?

    我正在创建一个简单的表单来使用带有 redux 表单和材料 ui 的 electro react boilerplate 来上传文件 问题是我不知道如何创建输入文件字段 因为材料用户界面不支持上传文件输入 关于如何实现这一目标有什么想法吗
  • 从 WP8 连接至 Polar H7 心率监测器

    有没有办法从 Windows Phone 8 连接到低功耗蓝牙设备 例如心率监测器 例如 Polar H7 我尝试过的一些背景 我看过这个 Windows 8 的示例 http code msdn microsoft com windows
  • 电子应用程序可以播放本地支持的任何视频吗?

    我知道浏览器通常会限制玩家mp4 and webm类型的媒体 但我想知道基于电子的应用程序是否可以运行 MKV 和 AVI 等格式的本地视频 我找不到明确的来源来告诉我什么是可用的 什么是不可用的 Electron 仍然仅限于网络技术 这意

随机推荐

  • UIButton 自定义边框中的 Tintcolor

    我为圆形自定义边框创建了 UIButton 的子类 void drawRect CGRect rect self layer setCornerRadius CORNER RADIUS self layer setMasksToBounds
  • jQuery UI 可排序表格和单元格在拖动 tr 时缩小

    在拖动时我面临两个问题 当我有一个隐藏的表时 它本身正在缩小td 拖动 tr 单元格 td 正在缩小 这是可排序的代码 tbody sortable items gt tr appendTo parent opacity 1 contain
  • PyMongo 在大量查询后引发 [errno 49] 无法分配请求的地址

    我有一个MongoDB收集超过 1 000 000 份文档 我正在执行初始 find my query 返回这些文档的子集 约 25 000 个文档 然后我将其放入list object 然后 我循环遍历每个对象 解析列表中返回的文档中的一
  • 我的 SQL 动态查询执行并将输出获取到存储过程中的变量中

    我在 My sql 存储过程中生成动态查询 我想将此查询的结果放入输出参数中 这个怎么做 CREATE PROCEDURE searchInvoice OUT numOfRecords INT BEGIN DECLARE query1 TE
  • 这个 UWSGI 错误是什么意思? “ SIGPIPE:写入关闭的管道/套接字/fd ”

    有几个与此问题相关的问题 但它们都没有真正帮助我理解发生了什么 完整错误 SIGPIPE writing to a closed pipe socket fd probably the client disconnected on requ
  • 在 Android 中将数据从 Activity 发送到 Fragment

    我有两节课 第一个是活动 第二个是我有一些的片段EditText 在活动中 我有一个带有异步任务和方法的子类doInBackground我得到一些结果 并将其保存到变量中 如何将此变量从子类 我的活动 发送到此片段 从 Activity 中
  • 从 parse.com 获取 objectId

    所以我正在构建一个使用 parse 作为后端的应用程序 我以前写过自己的 但我想我会节省一些时间并使用解析 我正在使用解析中的数据填充表视图 这很好 我想从解析数组构建的字典中获取 objectId 我的数组的输出如下
  • 漏洞! “语义分析”阶段的异常

    我有一个使用 Gradle 1 10 和 jdk1 8 的 1 1 7 spring boot 应用程序 我使用 Groovy Spock 进行测试它有两个依赖项 使用 Apache Maven 3 1 1 和 jdk 1 8 构建的 ja
  • 每个配置文件中的部分只能出现一次!为什么?

    我收到以下异常 每个配置文件中的部分只能出现一次 有关例外情况 请参阅帮助主题 我的配置文件看起来像这样
  • Sass - 安装错误[重复]

    这个问题在这里已经有答案了 我最近安装了 Ruby gem Sass 并且经常将它用于我正在开发的应用程序 今天我像平常一样去运行 sass watch scss css 并注意到我收到以下错误 bash sass command not
  • 故障转储分析:CxxUnhandledExceptionFilter

    我有一个崩溃小型转储需要分析 我的程序是多线程Qt5应用程序 我不是调试专家 但通常我可以轻松找到程序失败的地方 但这次我不能 我在 Visual Studio 2010 中打开转储文件 单击 仅使用本机调试 它向我显示问题所在 它是位置为
  • 有界上下文、子域和通用语言

    a 对于包含两个或多个子域的 BC 存在概念重叠的可能性 甚至更糟糕的是 相同的概念 由其中几个子域使用 可能会被每个子域以不同的方式解释 理解 无论如何 如果 BC 确实包含许多子域 它是否应该提供几种通用语言 每个子域一种 或者所有子域
  • Gson ClassCastException(LinkedTreeMap)

    Using Gson反序列化参数化类的对象Container
  • 将react-route-dom中的语法从“Switch”更改为“Routes”,但浏览器中仍然没有显示

    我正在创建一个 Google 克隆 这是我当前的代码 我读到 考虑到反应路由器的更新 我需要将语法从 Switch 更改为 Routes 我这样做了 但我的 这是搜索页面 没有显示在浏览器内部 import React from react
  • C#:Resharper 的替代品,C# 版本 [重复]

    这个问题在这里已经有答案了 这对我来说不太便宜 149 美元 我尝试过 但我很喜欢它 对我来说最酷的事情是重构为 LINQ 所以我想知道是否有一个更便宜的替代方案可以做到同样的事情 将我的循环重构为更小的 LINQ 循环 DevXpress
  • python文本框中文本和滚动条的自动滚动

    我有一个 tkinter 文本 和 滚动条 工作正常 在我的程序中 文本窗口中的行会自动不断添加 因此 当插入新的文本行且数据超出限制时 我希望文本和滚动条自动滚动到底部 以便始终显示最新的文本行 这个怎么做 另外 如何链接文本窗口和滚动条
  • 我们如何有效地处理 mnesia 记录的时间相关约束?

    我正在将记录写入mnesia 该记录应该保存在那里 仅在允许的时间 24 小时 内 24小时后 在用户修改其中的一部分之前 系统应该自动删除它们 例如 用户获得免费通话时间 用于语音通话 他们应该在给定时间内使用它们 如果他们不使用它 24
  • QtWebKit 无需安装 flash 播放器即可播放 HTML5 视频

    安装最新的 Flash 播放器并启用插件后 我的简单示例可以播放 YouTube 视频 操作系统 Windows 7 Qt 4 7 4 32 位和 64 位均可 但是 根据 http www youtube com html5 我的示例浏览
  • 三引号内可以有变量吗?如果是这样,怎么办?

    对于某些人来说 这可能是一个非常简单的问题 但它却难倒了我 你能在Python的三引号内使用变量吗 在下面的例子中 如何在文本中使用变量 wash clothes tuesdays clean dishes never mystring I
  • Electron Web 蓝牙 API requestDevice() 错误

    我正在尝试开发一个与蓝牙低功耗设备通信的应用程序 我使用 Web Bluetooth API 建立了一个工作 网站 一切正常 所以我使用 Electron 框架来构建一个应用程序 这个问题是已知的 如果你开始navigator blueto