From a29d294a7a062c0770250a9738bf12992328d581 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Mon, 21 Oct 2024 14:24:14 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20BluetoothReque?= =?UTF-8?q?stOptions=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Bluetooth/BluetoothFilter.cs | 37 ++++++++++++++++ .../BluetoothManufacturerDataFilter.cs | 31 +++++++++++++ .../Bluetooth/BluetoothRequestOptions.cs | 43 +++++++++++++++++++ .../Bluetooth/BluetoothServiceDataFilter.cs | 31 +++++++++++++ .../Bluetooth/DefaultBluetoothService.cs | 4 +- .../Services/Bluetooth/IBluetoothService.cs | 4 +- 6 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs create mode 100644 src/BootstrapBlazor/Services/Bluetooth/BluetoothManufacturerDataFilter.cs create mode 100644 src/BootstrapBlazor/Services/Bluetooth/BluetoothRequestOptions.cs create mode 100644 src/BootstrapBlazor/Services/Bluetooth/BluetoothServiceDataFilter.cs diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs new file mode 100644 index 00000000000..a7212964643 --- /dev/null +++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs @@ -0,0 +1,37 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// BluetoothFilter 类 +/// +public class BluetoothFilter +{ + /// + /// An array of values indicating the Bluetooth GATT (Generic Attribute Profile) services that a Bluetooth device must support. Each value can be a valid name from the GATT assigned services list, such as 'battery_service' or 'blood_pressure'. You can also pass a full service UUID such as '0000180F-0000-1000-8000-00805f9b34fb' or the short 16-bit (0x180F) or 32-bit alias. Note that these are the same values that can be passed to BluetoothUUID.getService(). + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? Services { get; set; } + + /// + /// A string containing the precise name of the device to match against. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Name { get; set; } + + /// + /// A string containing the name prefix to match against. All devices that have a name starting with this string will be matched. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? NamePrefix { get; set; } + + /// + /// An array of objects matching against manufacturer data in the Bluetooth Low Energy (BLE) advertising packets. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? ManufacturerData { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothManufacturerDataFilter.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothManufacturerDataFilter.cs new file mode 100644 index 00000000000..39f0eb5d4a9 --- /dev/null +++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothManufacturerDataFilter.cs @@ -0,0 +1,31 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// BluetoothManufacturerDataFilter 配置类 +/// +public class BluetoothManufacturerDataFilter +{ + /// + /// A mandatory number identifying the manufacturer of the device. Company identifiers are listed in the Bluetooth specification Assigned numbers, Section 7. For example, to match against devices manufactured by "Digianswer A/S", with assigned hex number 0x000C, you would specify 12. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? CompanyIdentifier { get; set; } + + /// + /// The data prefix. A buffer containing values to match against the values at the start of the advertising manufacturer data. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? DataPrefix { get; set; } + + /// + /// This allows you to match against bytes within the manufacturer data, by masking some bytes of the service data dataPrefix. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Mask { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothRequestOptions.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothRequestOptions.cs new file mode 100644 index 00000000000..015d90e77ee --- /dev/null +++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothRequestOptions.cs @@ -0,0 +1,43 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// BluetoothRequestOptions 参数类 +/// +public class BluetoothRequestOptions +{ + /// + /// An array of filter objects indicating the properties of devices that will be matched. To match a filter object, a device must match all the values of the filter: all its specified services, name, namePrefix, and so on + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? Filters { get; set; } + + /// + /// An array of filter objects indicating the characteristics of devices that will be excluded from matching. The properties of the array elements are the same as for . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? ExclusionFilters { get; set; } + + /// + /// An array of optional service identifiers. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? OptionalServices { get; set; } + + /// + /// An optional array of integer manufacturer codes. This takes the same values as companyIdentifier. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? OptionalManufacturerData { get; set; } + + /// + /// A boolean value indicating that the requesting script can accept all Bluetooth devices. The default is false. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool AcceptAllDevices { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothServiceDataFilter.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothServiceDataFilter.cs new file mode 100644 index 00000000000..4179124e55e --- /dev/null +++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothServiceDataFilter.cs @@ -0,0 +1,31 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// BluetoothServiceDataFilter 配置类 +/// +public class BluetoothServiceDataFilter +{ + /// + /// The GATT service name, the service UUID, or the UUID 16-bit or 32-bit form. This takes the same values as the elements of the services array. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Service { get; set; } + + /// + /// The data prefix. A buffer containing values to match against the values at the start of the advertising service data. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? DataPrefix { get; set; } + + /// + /// This allows you to match against bytes within the manufacturer data, by masking some bytes of the service data dataPrefix. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Mask { get; set; } +} diff --git a/src/BootstrapBlazor/Services/Bluetooth/DefaultBluetoothService.cs b/src/BootstrapBlazor/Services/Bluetooth/DefaultBluetoothService.cs index abe3fb8b6eb..7ae91f8bccb 100644 --- a/src/BootstrapBlazor/Services/Bluetooth/DefaultBluetoothService.cs +++ b/src/BootstrapBlazor/Services/Bluetooth/DefaultBluetoothService.cs @@ -70,7 +70,7 @@ public async Task GetAvailability(CancellationToken token = default) /// /// /// - public async Task RequestDevice(string[] optionalServices, CancellationToken token = default) + public async Task RequestDevice(BluetoothRequestOptions? options = null, CancellationToken token = default) { _module ??= await LoadModule(); @@ -78,7 +78,7 @@ public async Task GetAvailability(CancellationToken token = default) if (IsSupport) { ErrorMessage = null; - var parameters = await _module.InvokeAsync("requestDevice", token, _deviceId, optionalServices, _interop, nameof(OnError)); + var parameters = await _module.InvokeAsync("requestDevice", token, _deviceId, options, _interop, nameof(OnError)); if (parameters != null) { device = new BluetoothDevice(_module, _deviceId, parameters); diff --git a/src/BootstrapBlazor/Services/Bluetooth/IBluetoothService.cs b/src/BootstrapBlazor/Services/Bluetooth/IBluetoothService.cs index f7ceadd248f..ed4c8a76773 100644 --- a/src/BootstrapBlazor/Services/Bluetooth/IBluetoothService.cs +++ b/src/BootstrapBlazor/Services/Bluetooth/IBluetoothService.cs @@ -33,8 +33,8 @@ public interface IBluetoothService /// /// 请求蓝牙配对方法 /// - /// 请求服务列表 请参考 https://github.com/WebBluetoothCG/registries/blob/master/gatt_assigned_services.txt + /// 实例 /// /// - Task RequestDevice(string[] optionalServices, CancellationToken token = default); + Task RequestDevice(BluetoothRequestOptions? options = null, CancellationToken token = default); } From a4c5fbd58e0f89de864fedc70dd9cf75f2a015f0 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Mon, 21 Oct 2024 14:24:22 +0800 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/wwwroot/modules/bt.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor/wwwroot/modules/bt.js b/src/BootstrapBlazor/wwwroot/modules/bt.js index aed1d74a616..394be95b8ed 100644 --- a/src/BootstrapBlazor/wwwroot/modules/bt.js +++ b/src/BootstrapBlazor/wwwroot/modules/bt.js @@ -12,7 +12,7 @@ export async function getAvailability() { return ret; } -export async function requestDevice(id, optionalServices, invoke, method) { +export async function requestDevice(id, options, invoke, method) { let ret = await getAvailability(); if (ret === false) { return null; @@ -22,9 +22,8 @@ export async function requestDevice(id, optionalServices, invoke, method) { const bt = { device: null }; Data.set(id, bt); try { - const ret = await navigator.bluetooth.requestDevice({ - acceptAllDevices: true, - optionalServices: optionalServices + const ret = await navigator.bluetooth.requestDevice(options ?? { + acceptAllDevices: true }); bt.device = ret; device = [ret.name, ret.id]; From 5d197c0cc9ad2300a22a240b6022925698debea6 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Mon, 21 Oct 2024 14:24:34 +0800 Subject: [PATCH 3/7] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/Bluetooth.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor.cs index 447ac818d14..beaf34bd93f 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor.cs @@ -25,7 +25,7 @@ public partial class Bluetooth private async Task RequestDevice() { - _blueDevice = await BluetoothService.RequestDevice(["battery_service"]); + _blueDevice = await BluetoothService.RequestDevice(); if (BluetoothService.IsSupport == false) { await ToastService.Error(Localizer["NotSupportBluetoothTitle"], Localizer["NotSupportBluetoothContent"]); From 641f7b46913732cca96a33ab48f079b0b7907716 Mon Sep 17 00:00:00 2001 From: Argo-AsicoTech Date: Mon, 21 Oct 2024 14:26:33 +0800 Subject: [PATCH 4/7] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor b/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor index 6189781f746..035104b5ffe 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Bluetooth.razor @@ -46,9 +46,9 @@ private IBluetoothService? BluetoothService { get; set; } private IBluetoothService? BluetoothService { get; set; }

2. 列出蓝牙设备

-

调用 BluetoothService 实例方法 RequestDevice 即可,通过 IsSupport 进行浏览器是否支持蓝牙

+

调用 BluetoothService 实例方法 RequestDevice 即可,通过 IsSupport 进行浏览器是否支持蓝牙。可以通过 BluetoothRequestOptions 过滤参数过滤蓝牙设备

-
_serialPort = await BluetoothService.RequestDevice(["battery_service"]);
+
_serialPort = await BluetoothService.RequestDevice();
 if (BluetoothService.IsSupport == false)
 {
     await ToastService.Error(Localizer["NotSupportBluetoothTitle"], Localizer["NotSupportBluetoothContent"]);

From dd4c186fd91faf3d0084e619996cf9f0b12e0d38 Mon Sep 17 00:00:00 2001
From: Argo-AsicoTech 
Date: Mon, 21 Oct 2024 15:32:39 +0800
Subject: [PATCH 5/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?=
 =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../UnitTest/Services/BluetoothServiceTest.cs | 53 ++++++++++++++++++-
 1 file changed, 51 insertions(+), 2 deletions(-)

diff --git a/test/UnitTest/Services/BluetoothServiceTest.cs b/test/UnitTest/Services/BluetoothServiceTest.cs
index a44551dc1d0..9239b9d8c66 100644
--- a/test/UnitTest/Services/BluetoothServiceTest.cs
+++ b/test/UnitTest/Services/BluetoothServiceTest.cs
@@ -17,7 +17,7 @@ public async Task RequestDevice_Ok()
         Context.JSInterop.Setup("disconnect", matcher => matcher.Arguments.Count == 3 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_bt_") ?? false)).SetResult(true);
 
         var bluetoothService = Context.Services.GetRequiredService();
-        var device = await bluetoothService.RequestDevice(["battery_service"]);
+        var device = await bluetoothService.RequestDevice();
         Assert.NotNull(device);
         Assert.Equal("test", device.Name);
         Assert.Equal("id_1234", device.Id);
@@ -50,7 +50,7 @@ public async Task ReadValue_null()
         Context.JSInterop.Setup("requestDevice", matcher => matcher.Arguments.Count == 4 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_bt_") ?? false)).SetResult(["test", "id_1234"]);
         Context.JSInterop.Setup("readValue", matcher => matcher.Arguments.Count == 5 && (matcher.Arguments[0]?.ToString()?.StartsWith("bb_bt_") ?? false)).SetResult(null);
         var bluetoothService = Context.Services.GetRequiredService();
-        var device = await bluetoothService.RequestDevice(["battery_service"]);
+        var device = await bluetoothService.RequestDevice();
         Assert.NotNull(device);
         var v = await device.GetBatteryValue();
         Assert.Equal(0x0, v);
@@ -73,4 +73,53 @@ public async Task GetAvailability_Ok()
         mi.Invoke(bluetoothService, ["test"]);
         Assert.Equal("test", bluetoothService.ErrorMessage);
     }
+
+    [Fact]
+    public void Filter_Ok()
+    {
+        var filter = new BluetoothRequestOptions()
+        {
+            Filters =
+            [
+                 new() {
+                      ManufacturerData =
+                      [
+                          new()
+                          {
+                               CompanyIdentifier = 0x004C,
+                               DataPrefix = "Apple",
+                               Mask = "test"
+                          }
+                      ],
+                      Name = "test-Name",
+                      NamePrefix = "test-NamePrefix",
+                      Services = ["test-service"]
+                 }
+            ],
+            AcceptAllDevices = false,
+            ExclusionFilters = [],
+            OptionalManufacturerData = ["test-manufacturer-data"],
+            OptionalServices = ["test-optional-service"]
+        };
+
+        Assert.NotNull(filter.Filters);
+
+        var data = filter.Filters[0].ManufacturerData;
+        Assert.NotNull(data);
+        Assert.Equal(0x004C, data[0].CompanyIdentifier);
+        Assert.Equal("Apple", data[0].DataPrefix);
+        Assert.Equal("test", data[0].Mask);
+
+        Assert.Equal("test-Name", filter.Filters[0].Name);
+        Assert.Equal("test-NamePrefix", filter.Filters[0].NamePrefix);
+
+        var services = filter.Filters[0].Services;
+        Assert.NotNull(services);
+        Assert.Equal("test-service", services[0]);
+
+        Assert.False(filter.AcceptAllDevices);
+        Assert.False(filter.AcceptAllDevices);
+        Assert.Equal(["test-manufacturer-data"], filter.OptionalManufacturerData);
+        Assert.Equal(["test-optional-service"], filter.OptionalServices);
+    }
 }

From 4ae837dd66da2793b9eaa1267996e4b67a36e357 Mon Sep 17 00:00:00 2001
From: Argo-AsicoTech 
Date: Mon, 21 Oct 2024 15:33:58 +0800
Subject: [PATCH 6/7] chore: bump version 8.10.4

---
 src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj
index 53074d70cd1..a10da1e8964 100644
--- a/src/BootstrapBlazor/BootstrapBlazor.csproj
+++ b/src/BootstrapBlazor/BootstrapBlazor.csproj
@@ -1,7 +1,7 @@
 
 
   
-    8.10.4-beta03
+    8.10.4
   
 
   

From a5891613c517ee338a3c58fc6bd8b42245ff82aa Mon Sep 17 00:00:00 2001
From: Argo-AsicoTech 
Date: Mon, 21 Oct 2024 15:44:13 +0800
Subject: [PATCH 7/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?=
 =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Services/Bluetooth/BluetoothFilter.cs        |  6 ++++++
 test/UnitTest/Services/BluetoothServiceTest.cs   | 16 ++++++++++++++--
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs
index a7212964643..7c46c59c14e 100644
--- a/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs
+++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs
@@ -34,4 +34,10 @@ public class BluetoothFilter
     /// 
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     public List? ManufacturerData { get; set; }
+
+    /// 
+    /// An array of objects matching against service data in the Bluetooth Low Energy (BLE) advertising packets.
+    /// 
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public List? ServiceData { get; set; }
 }
diff --git a/test/UnitTest/Services/BluetoothServiceTest.cs b/test/UnitTest/Services/BluetoothServiceTest.cs
index 9239b9d8c66..cdf811988cb 100644
--- a/test/UnitTest/Services/BluetoothServiceTest.cs
+++ b/test/UnitTest/Services/BluetoothServiceTest.cs
@@ -93,7 +93,13 @@ public void Filter_Ok()
                       ],
                       Name = "test-Name",
                       NamePrefix = "test-NamePrefix",
-                      Services = ["test-service"]
+                      Services = ["test-service"],
+                      ServiceData = [new BluetoothServiceDataFilter()
+                      {
+                           DataPrefix = "test-data-prefix",
+                           Service = "test-data-service",
+                           Mask = "test-data-mask"
+                      }]
                  }
             ],
             AcceptAllDevices = false,
@@ -117,8 +123,14 @@ public void Filter_Ok()
         Assert.NotNull(services);
         Assert.Equal("test-service", services[0]);
 
+        var serviceData = filter.Filters[0].ServiceData;
+        Assert.NotNull(serviceData);
+        Assert.Equal("test-data-prefix", serviceData[0].DataPrefix);
+        Assert.Equal("test-data-service", serviceData[0].Service);
+        Assert.Equal("test-data-mask", serviceData[0].Mask);
+
         Assert.False(filter.AcceptAllDevices);
-        Assert.False(filter.AcceptAllDevices);
+        Assert.Empty(filter.ExclusionFilters);
         Assert.Equal(["test-manufacturer-data"], filter.OptionalManufacturerData);
         Assert.Equal(["test-optional-service"], filter.OptionalServices);
     }