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"]);
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"]);
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
   
 
   
diff --git a/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.cs
new file mode 100644
index 00000000000..7c46c59c14e
--- /dev/null
+++ b/src/BootstrapBlazor/Services/Bluetooth/BluetoothFilter.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;
+
+/// 
+/// 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; }
+
+    /// 
+    /// 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/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);
 }
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];
diff --git a/test/UnitTest/Services/BluetoothServiceTest.cs b/test/UnitTest/Services/BluetoothServiceTest.cs
index a44551dc1d0..cdf811988cb 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,65 @@ 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"],
+                      ServiceData = [new BluetoothServiceDataFilter()
+                      {
+                           DataPrefix = "test-data-prefix",
+                           Service = "test-data-service",
+                           Mask = "test-data-mask"
+                      }]
+                 }
+            ],
+            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]);
+
+        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.Empty(filter.ExclusionFilters);
+        Assert.Equal(["test-manufacturer-data"], filter.OptionalManufacturerData);
+        Assert.Equal(["test-optional-service"], filter.OptionalServices);
+    }
 }