@fww_123/uni-ble-scanner 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/factory.d.ts CHANGED
@@ -3,7 +3,7 @@ import { getPlatform, isApp } from './utils/platform';
3
3
  /**
4
4
  * 扫描器类型
5
5
  */
6
- export type ScannerType = 'auto' | 'uniapp' | 'native';
6
+ export type ScannerType = 'auto' | 'uniapp' | 'native' | 'web';
7
7
  /**
8
8
  * 创建扫描器的选项
9
9
  */
@@ -19,6 +19,11 @@ export interface CreateScannerOptions {
19
19
  * @returns 蓝牙扫描器实例
20
20
  */
21
21
  export declare function createScanner(options?: CreateScannerOptions): IBLEScanner;
22
+ /**
23
+ * 创建 Web 扫描器
24
+ * 使用 Web Bluetooth API,适用于浏览器环境
25
+ */
26
+ export declare function createWebScanner(platformConfig?: PlatformConfig): IBLEScanner;
22
27
  /**
23
28
  * 创建 UniApp 扫描器
24
29
  * 使用 UniApp 标准蓝牙 API,兼容所有平台
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAKhE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,aAAa;IACb,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,WAAW,CA2C7E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,CAEhF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,CAEhF;AAED;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAEjC;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAc9C;AAED;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAMhE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,aAAa;IACb,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,WAAW,CAqD7E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,CAE7E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,CAEhF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,WAAW,CAEhF;AAED;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AAEjC;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAc9C;AAED;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { UniAppScanner } from './platforms/uniapp-scanner';
10
10
  export { AndroidNativeScanner } from './platforms/android-native';
11
11
  export { IOSNativeScanner } from './platforms/ios-native';
12
12
  export { HarmonyNativeScanner } from './platforms/harmony-native';
13
+ export { WebScanner, isWebBluetoothSupported } from './platforms/web-scanner';
13
14
  import { createScanner } from './factory';
14
15
  export default createScanner;
15
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,oBAAoB,EACpB,KAAK,GACN,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,SAAS,GACV,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG3E,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,oBAAoB,EACpB,KAAK,GACN,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,SAAS,GACV,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG3E,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAG9E,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,eAAe,aAAa,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1721,6 +1721,174 @@ class HarmonyNativeScanner extends BaseScanner {
1721
1721
  }
1722
1722
  }
1723
1723
 
1724
+ /**
1725
+ * Web 浏览器蓝牙扫描器
1726
+ * 使用 Web Bluetooth API
1727
+ */
1728
+ class WebScanner extends BaseScanner {
1729
+ constructor() {
1730
+ super(...arguments);
1731
+ this.device = null;
1732
+ this.server = null;
1733
+ }
1734
+ getPlatform() {
1735
+ return Platform.UNKNOWN;
1736
+ }
1737
+ init() {
1738
+ return __awaiter(this, void 0, void 0, function* () {
1739
+ return new Promise((resolve, reject) => {
1740
+ // 检查浏览器是否支持 Web Bluetooth
1741
+ if (!navigator.bluetooth) {
1742
+ reject(createError(ErrorCodes.UNSUPPORTED_PLATFORM, '当前浏览器不支持 Web Bluetooth API,请使用 Chrome、Edge 或 Opera'));
1743
+ return;
1744
+ }
1745
+ this.initialized = true;
1746
+ resolve();
1747
+ });
1748
+ });
1749
+ }
1750
+ startScanInternal() {
1751
+ return __awaiter(this, void 0, void 0, function* () {
1752
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
1753
+ try {
1754
+ // Web Bluetooth 使用 requestDevice 而不是扫描
1755
+ // 这会弹出浏览器的选择设备对话框
1756
+ const options = {
1757
+ acceptAllDevices: true,
1758
+ };
1759
+ // 添加服务过滤
1760
+ if (this.options.services && this.options.services.length > 0) {
1761
+ options.filters = this.options.services.map(uuid => ({
1762
+ services: [uuid],
1763
+ }));
1764
+ }
1765
+ else {
1766
+ options.acceptAllDevices = true;
1767
+ }
1768
+ // 请求设备(浏览器会弹出选择框)
1769
+ this.device = yield navigator.bluetooth.requestDevice(options);
1770
+ // 监听设备广告(如果支持)
1771
+ if (this.device.watchAdvertisements) {
1772
+ this.device.addEventListener('advertisementreceived', (event) => {
1773
+ this.handleDeviceFound({
1774
+ deviceId: this.device.id,
1775
+ name: this.device.name,
1776
+ RSSI: event.rssi || -50,
1777
+ advertisData: event.manufacturerData,
1778
+ });
1779
+ });
1780
+ yield this.device.watchAdvertisements();
1781
+ }
1782
+ else {
1783
+ // 如果不支持广告监听,直接返回设备信息
1784
+ this.handleDeviceFound({
1785
+ deviceId: this.device.id,
1786
+ name: this.device.name,
1787
+ RSSI: -50, // 默认值
1788
+ });
1789
+ }
1790
+ resolve();
1791
+ }
1792
+ catch (error) {
1793
+ if (error.name === 'NotFoundError') {
1794
+ reject(Errors.scanFailed('用户取消了设备选择'));
1795
+ }
1796
+ else {
1797
+ reject(handlePlatformError(error, 'web'));
1798
+ }
1799
+ }
1800
+ }));
1801
+ });
1802
+ }
1803
+ stopScanInternal() {
1804
+ return __awaiter(this, void 0, void 0, function* () {
1805
+ // Web Bluetooth 没有明确的停止扫描方法
1806
+ // 设备选择后扫描就结束了
1807
+ if (this.device && this.device.unwatchAdvertisements) {
1808
+ yield this.device.unwatchAdvertisements();
1809
+ }
1810
+ return Promise.resolve();
1811
+ });
1812
+ }
1813
+ checkPermissions() {
1814
+ return __awaiter(this, void 0, void 0, function* () {
1815
+ // Web Bluetooth 权限由浏览器自动管理
1816
+ // 通过用户手势触发 requestDevice 时会自动请求权限
1817
+ return true;
1818
+ });
1819
+ }
1820
+ requestPermissions() {
1821
+ return __awaiter(this, void 0, void 0, function* () {
1822
+ // Web Bluetooth 不需要显式请求权限
1823
+ return true;
1824
+ });
1825
+ }
1826
+ onAdapterStateChange(callback) {
1827
+ this.adapterStateListeners.push(callback);
1828
+ // Web Bluetooth 没有适配器状态变化事件
1829
+ // 直接返回当前状态
1830
+ callback({
1831
+ available: !!navigator.bluetooth,
1832
+ discovering: this.state === 'scanning',
1833
+ });
1834
+ }
1835
+ destroy() {
1836
+ super.destroy();
1837
+ this.device = null;
1838
+ this.server = null;
1839
+ }
1840
+ /**
1841
+ * 连接到设备(Web Bluetooth 特有)
1842
+ */
1843
+ connect() {
1844
+ return __awaiter(this, void 0, void 0, function* () {
1845
+ var _a;
1846
+ if (!this.device) {
1847
+ throw Errors.notInitialized();
1848
+ }
1849
+ try {
1850
+ this.server = (yield ((_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.connect())) || null;
1851
+ return this.server;
1852
+ }
1853
+ catch (error) {
1854
+ throw handlePlatformError(error, 'web');
1855
+ }
1856
+ });
1857
+ }
1858
+ /**
1859
+ * 断开连接(Web Bluetooth 特有)
1860
+ */
1861
+ disconnect() {
1862
+ var _a;
1863
+ if (this.device && ((_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.connected)) {
1864
+ this.device.gatt.disconnect();
1865
+ }
1866
+ this.server = null;
1867
+ }
1868
+ /**
1869
+ * 获取已连接的服务(Web Bluetooth 特有)
1870
+ */
1871
+ getServices() {
1872
+ return __awaiter(this, void 0, void 0, function* () {
1873
+ if (!this.server) {
1874
+ throw Errors.notInitialized();
1875
+ }
1876
+ try {
1877
+ return yield this.server.getPrimaryServices();
1878
+ }
1879
+ catch (error) {
1880
+ throw handlePlatformError(error, 'web');
1881
+ }
1882
+ });
1883
+ }
1884
+ }
1885
+ /**
1886
+ * 检查浏览器是否支持 Web Bluetooth
1887
+ */
1888
+ function isWebBluetoothSupported() {
1889
+ return 'bluetooth' in navigator && typeof navigator.bluetooth.requestDevice === 'function';
1890
+ }
1891
+
1724
1892
  /**
1725
1893
  * 创建蓝牙扫描器
1726
1894
  * @param options 创建选项
@@ -1730,6 +1898,14 @@ function createScanner(options = {}) {
1730
1898
  const { type = 'auto', platformConfig = {} } = options;
1731
1899
  const platform = getPlatform();
1732
1900
  const app = isApp();
1901
+ // 检测是否在浏览器环境
1902
+ const isWeb = typeof window !== 'undefined' && !app;
1903
+ // 如果是 Web 环境或指定使用 Web 扫描器
1904
+ if (type === 'web' || (type === 'auto' && isWeb)) {
1905
+ const webScanner = new WebScanner();
1906
+ Object.assign(webScanner, { platformConfig });
1907
+ return webScanner;
1908
+ }
1733
1909
  // 根据类型和平台选择合适的扫描器
1734
1910
  if (type === 'uniapp' || (type === 'auto' && !app)) {
1735
1911
  // 使用 UniApp 标准 API
@@ -1788,4 +1964,4 @@ function isBluetoothSupported() {
1788
1964
 
1789
1965
  // 导出枚举
1790
1966
 
1791
- export { AndroidNativeScanner, BaseScanner, ErrorCodes, Errors, HarmonyNativeScanner, IOSNativeScanner, Platform, ScanState, UniAppScanner, arrayBufferToBase64, base64ToArrayBuffer, checkPermissions, createError, createNativeScanner, createScanner, createUniAppScanner, createScanner as default, formatUUID, getPlatform, getPlatformName, isApp, isBluetoothSupported, isPlatform, matchUUID, parseAdvertisementData, requestPermissions };
1967
+ export { AndroidNativeScanner, BaseScanner, ErrorCodes, Errors, HarmonyNativeScanner, IOSNativeScanner, Platform, ScanState, UniAppScanner, WebScanner, arrayBufferToBase64, base64ToArrayBuffer, checkPermissions, createError, createNativeScanner, createScanner, createUniAppScanner, createScanner as default, formatUUID, getPlatform, getPlatformName, isApp, isBluetoothSupported, isPlatform, isWebBluetoothSupported, matchUUID, parseAdvertisementData, requestPermissions };
package/dist/index.js CHANGED
@@ -1725,6 +1725,174 @@ class HarmonyNativeScanner extends BaseScanner {
1725
1725
  }
1726
1726
  }
1727
1727
 
1728
+ /**
1729
+ * Web 浏览器蓝牙扫描器
1730
+ * 使用 Web Bluetooth API
1731
+ */
1732
+ class WebScanner extends BaseScanner {
1733
+ constructor() {
1734
+ super(...arguments);
1735
+ this.device = null;
1736
+ this.server = null;
1737
+ }
1738
+ getPlatform() {
1739
+ return exports.Platform.UNKNOWN;
1740
+ }
1741
+ init() {
1742
+ return __awaiter(this, void 0, void 0, function* () {
1743
+ return new Promise((resolve, reject) => {
1744
+ // 检查浏览器是否支持 Web Bluetooth
1745
+ if (!navigator.bluetooth) {
1746
+ reject(createError(ErrorCodes.UNSUPPORTED_PLATFORM, '当前浏览器不支持 Web Bluetooth API,请使用 Chrome、Edge 或 Opera'));
1747
+ return;
1748
+ }
1749
+ this.initialized = true;
1750
+ resolve();
1751
+ });
1752
+ });
1753
+ }
1754
+ startScanInternal() {
1755
+ return __awaiter(this, void 0, void 0, function* () {
1756
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
1757
+ try {
1758
+ // Web Bluetooth 使用 requestDevice 而不是扫描
1759
+ // 这会弹出浏览器的选择设备对话框
1760
+ const options = {
1761
+ acceptAllDevices: true,
1762
+ };
1763
+ // 添加服务过滤
1764
+ if (this.options.services && this.options.services.length > 0) {
1765
+ options.filters = this.options.services.map(uuid => ({
1766
+ services: [uuid],
1767
+ }));
1768
+ }
1769
+ else {
1770
+ options.acceptAllDevices = true;
1771
+ }
1772
+ // 请求设备(浏览器会弹出选择框)
1773
+ this.device = yield navigator.bluetooth.requestDevice(options);
1774
+ // 监听设备广告(如果支持)
1775
+ if (this.device.watchAdvertisements) {
1776
+ this.device.addEventListener('advertisementreceived', (event) => {
1777
+ this.handleDeviceFound({
1778
+ deviceId: this.device.id,
1779
+ name: this.device.name,
1780
+ RSSI: event.rssi || -50,
1781
+ advertisData: event.manufacturerData,
1782
+ });
1783
+ });
1784
+ yield this.device.watchAdvertisements();
1785
+ }
1786
+ else {
1787
+ // 如果不支持广告监听,直接返回设备信息
1788
+ this.handleDeviceFound({
1789
+ deviceId: this.device.id,
1790
+ name: this.device.name,
1791
+ RSSI: -50, // 默认值
1792
+ });
1793
+ }
1794
+ resolve();
1795
+ }
1796
+ catch (error) {
1797
+ if (error.name === 'NotFoundError') {
1798
+ reject(Errors.scanFailed('用户取消了设备选择'));
1799
+ }
1800
+ else {
1801
+ reject(handlePlatformError(error, 'web'));
1802
+ }
1803
+ }
1804
+ }));
1805
+ });
1806
+ }
1807
+ stopScanInternal() {
1808
+ return __awaiter(this, void 0, void 0, function* () {
1809
+ // Web Bluetooth 没有明确的停止扫描方法
1810
+ // 设备选择后扫描就结束了
1811
+ if (this.device && this.device.unwatchAdvertisements) {
1812
+ yield this.device.unwatchAdvertisements();
1813
+ }
1814
+ return Promise.resolve();
1815
+ });
1816
+ }
1817
+ checkPermissions() {
1818
+ return __awaiter(this, void 0, void 0, function* () {
1819
+ // Web Bluetooth 权限由浏览器自动管理
1820
+ // 通过用户手势触发 requestDevice 时会自动请求权限
1821
+ return true;
1822
+ });
1823
+ }
1824
+ requestPermissions() {
1825
+ return __awaiter(this, void 0, void 0, function* () {
1826
+ // Web Bluetooth 不需要显式请求权限
1827
+ return true;
1828
+ });
1829
+ }
1830
+ onAdapterStateChange(callback) {
1831
+ this.adapterStateListeners.push(callback);
1832
+ // Web Bluetooth 没有适配器状态变化事件
1833
+ // 直接返回当前状态
1834
+ callback({
1835
+ available: !!navigator.bluetooth,
1836
+ discovering: this.state === 'scanning',
1837
+ });
1838
+ }
1839
+ destroy() {
1840
+ super.destroy();
1841
+ this.device = null;
1842
+ this.server = null;
1843
+ }
1844
+ /**
1845
+ * 连接到设备(Web Bluetooth 特有)
1846
+ */
1847
+ connect() {
1848
+ return __awaiter(this, void 0, void 0, function* () {
1849
+ var _a;
1850
+ if (!this.device) {
1851
+ throw Errors.notInitialized();
1852
+ }
1853
+ try {
1854
+ this.server = (yield ((_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.connect())) || null;
1855
+ return this.server;
1856
+ }
1857
+ catch (error) {
1858
+ throw handlePlatformError(error, 'web');
1859
+ }
1860
+ });
1861
+ }
1862
+ /**
1863
+ * 断开连接(Web Bluetooth 特有)
1864
+ */
1865
+ disconnect() {
1866
+ var _a;
1867
+ if (this.device && ((_a = this.device.gatt) === null || _a === void 0 ? void 0 : _a.connected)) {
1868
+ this.device.gatt.disconnect();
1869
+ }
1870
+ this.server = null;
1871
+ }
1872
+ /**
1873
+ * 获取已连接的服务(Web Bluetooth 特有)
1874
+ */
1875
+ getServices() {
1876
+ return __awaiter(this, void 0, void 0, function* () {
1877
+ if (!this.server) {
1878
+ throw Errors.notInitialized();
1879
+ }
1880
+ try {
1881
+ return yield this.server.getPrimaryServices();
1882
+ }
1883
+ catch (error) {
1884
+ throw handlePlatformError(error, 'web');
1885
+ }
1886
+ });
1887
+ }
1888
+ }
1889
+ /**
1890
+ * 检查浏览器是否支持 Web Bluetooth
1891
+ */
1892
+ function isWebBluetoothSupported() {
1893
+ return 'bluetooth' in navigator && typeof navigator.bluetooth.requestDevice === 'function';
1894
+ }
1895
+
1728
1896
  /**
1729
1897
  * 创建蓝牙扫描器
1730
1898
  * @param options 创建选项
@@ -1734,6 +1902,14 @@ function createScanner(options = {}) {
1734
1902
  const { type = 'auto', platformConfig = {} } = options;
1735
1903
  const platform = getPlatform();
1736
1904
  const app = isApp();
1905
+ // 检测是否在浏览器环境
1906
+ const isWeb = typeof window !== 'undefined' && !app;
1907
+ // 如果是 Web 环境或指定使用 Web 扫描器
1908
+ if (type === 'web' || (type === 'auto' && isWeb)) {
1909
+ const webScanner = new WebScanner();
1910
+ Object.assign(webScanner, { platformConfig });
1911
+ return webScanner;
1912
+ }
1737
1913
  // 根据类型和平台选择合适的扫描器
1738
1914
  if (type === 'uniapp' || (type === 'auto' && !app)) {
1739
1915
  // 使用 UniApp 标准 API
@@ -1799,6 +1975,7 @@ exports.Errors = Errors;
1799
1975
  exports.HarmonyNativeScanner = HarmonyNativeScanner;
1800
1976
  exports.IOSNativeScanner = IOSNativeScanner;
1801
1977
  exports.UniAppScanner = UniAppScanner;
1978
+ exports.WebScanner = WebScanner;
1802
1979
  exports.arrayBufferToBase64 = arrayBufferToBase64;
1803
1980
  exports.base64ToArrayBuffer = base64ToArrayBuffer;
1804
1981
  exports.checkPermissions = checkPermissions;
@@ -1813,6 +1990,7 @@ exports.getPlatformName = getPlatformName;
1813
1990
  exports.isApp = isApp;
1814
1991
  exports.isBluetoothSupported = isBluetoothSupported;
1815
1992
  exports.isPlatform = isPlatform;
1993
+ exports.isWebBluetoothSupported = isWebBluetoothSupported;
1816
1994
  exports.matchUUID = matchUUID;
1817
1995
  exports.parseAdvertisementData = parseAdvertisementData;
1818
1996
  exports.requestPermissions = requestPermissions;
@@ -0,0 +1,35 @@
1
+ import { BaseScanner } from '../core/base-scanner';
2
+ import { Platform, AdapterState } from '../types';
3
+ /**
4
+ * Web 浏览器蓝牙扫描器
5
+ * 使用 Web Bluetooth API
6
+ */
7
+ export declare class WebScanner extends BaseScanner {
8
+ private device;
9
+ private server;
10
+ getPlatform(): Platform;
11
+ init(): Promise<void>;
12
+ protected startScanInternal(): Promise<void>;
13
+ protected stopScanInternal(): Promise<void>;
14
+ checkPermissions(): Promise<boolean>;
15
+ requestPermissions(): Promise<boolean>;
16
+ onAdapterStateChange(callback: (state: AdapterState) => void): void;
17
+ destroy(): void;
18
+ /**
19
+ * 连接到设备(Web Bluetooth 特有)
20
+ */
21
+ connect(): Promise<BluetoothRemoteGATTServer | null>;
22
+ /**
23
+ * 断开连接(Web Bluetooth 特有)
24
+ */
25
+ disconnect(): void;
26
+ /**
27
+ * 获取已连接的服务(Web Bluetooth 特有)
28
+ */
29
+ getServices(): Promise<BluetoothRemoteGATTService[]>;
30
+ }
31
+ /**
32
+ * 检查浏览器是否支持 Web Bluetooth
33
+ */
34
+ export declare function isWebBluetoothSupported(): boolean;
35
+ //# sourceMappingURL=web-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-scanner.d.ts","sourceRoot":"","sources":["../../src/platforms/web-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EACL,QAAQ,EACR,YAAY,EAIb,MAAM,UAAU,CAAC;AAIlB;;;GAGG;AACH,qBAAa,UAAW,SAAQ,WAAW;IACzC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAA0C;IAExD,WAAW,IAAI,QAAQ;IAIjB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;cAgBX,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;cAqDlC,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3C,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAMpC,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;IAK5C,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,IAAI;IAWnE,OAAO,IAAI,IAAI;IAMf;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAa1D;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAW3D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fww_123/uni-ble-scanner",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "跨平台蓝牙低功耗扫描组件,支持安卓、鸿蒙、iOS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
package/src/factory.ts CHANGED
@@ -3,12 +3,13 @@ import { UniAppScanner } from './platforms/uniapp-scanner';
3
3
  import { AndroidNativeScanner } from './platforms/android-native';
4
4
  import { IOSNativeScanner } from './platforms/ios-native';
5
5
  import { HarmonyNativeScanner } from './platforms/harmony-native';
6
+ import { WebScanner } from './platforms/web-scanner';
6
7
  import { getPlatform, isApp } from './utils/platform';
7
8
 
8
9
  /**
9
10
  * 扫描器类型
10
11
  */
11
- export type ScannerType = 'auto' | 'uniapp' | 'native';
12
+ export type ScannerType = 'auto' | 'uniapp' | 'native' | 'web';
12
13
 
13
14
  /**
14
15
  * 创建扫描器的选项
@@ -30,6 +31,16 @@ export function createScanner(options: CreateScannerOptions = {}): IBLEScanner {
30
31
  const platform = getPlatform();
31
32
  const app = isApp();
32
33
 
34
+ // 检测是否在浏览器环境
35
+ const isWeb = typeof window !== 'undefined' && !app;
36
+
37
+ // 如果是 Web 环境或指定使用 Web 扫描器
38
+ if (type === 'web' || (type === 'auto' && isWeb)) {
39
+ const webScanner = new WebScanner();
40
+ Object.assign(webScanner, { platformConfig });
41
+ return webScanner;
42
+ }
43
+
33
44
  // 根据类型和平台选择合适的扫描器
34
45
  if (type === 'uniapp' || (type === 'auto' && !app)) {
35
46
  // 使用 UniApp 标准 API
@@ -45,17 +56,17 @@ export function createScanner(options: CreateScannerOptions = {}): IBLEScanner {
45
56
  const androidScanner = new AndroidNativeScanner();
46
57
  Object.assign(androidScanner, { platformConfig });
47
58
  return androidScanner;
48
-
59
+
49
60
  case Platform.IOS:
50
61
  const iosScanner = new IOSNativeScanner();
51
62
  Object.assign(iosScanner, { platformConfig });
52
63
  return iosScanner;
53
-
64
+
54
65
  case Platform.HARMONY:
55
66
  const harmonyScanner = new HarmonyNativeScanner();
56
67
  Object.assign(harmonyScanner, { platformConfig });
57
68
  return harmonyScanner;
58
-
69
+
59
70
  default:
60
71
  // 未知平台使用 UniApp API
61
72
  const defaultScanner = new UniAppScanner();
@@ -70,6 +81,14 @@ export function createScanner(options: CreateScannerOptions = {}): IBLEScanner {
70
81
  return defaultScanner;
71
82
  }
72
83
 
84
+ /**
85
+ * 创建 Web 扫描器
86
+ * 使用 Web Bluetooth API,适用于浏览器环境
87
+ */
88
+ export function createWebScanner(platformConfig?: PlatformConfig): IBLEScanner {
89
+ return createScanner({ type: 'web', platformConfig });
90
+ }
91
+
73
92
  /**
74
93
  * 创建 UniApp 扫描器
75
94
  * 使用 UniApp 标准蓝牙 API,兼容所有平台
package/src/index.ts CHANGED
@@ -48,6 +48,7 @@ export { UniAppScanner } from './platforms/uniapp-scanner';
48
48
  export { AndroidNativeScanner } from './platforms/android-native';
49
49
  export { IOSNativeScanner } from './platforms/ios-native';
50
50
  export { HarmonyNativeScanner } from './platforms/harmony-native';
51
+ export { WebScanner, isWebBluetoothSupported } from './platforms/web-scanner';
51
52
 
52
53
  // 默认导出
53
54
  import { createScanner } from './factory';
@@ -0,0 +1,177 @@
1
+ import { BaseScanner } from '../core/base-scanner';
2
+ import {
3
+ Platform,
4
+ AdapterState,
5
+ BLEError,
6
+ PlatformConfig,
7
+ BLEDevice,
8
+ } from '../types';
9
+ import { Errors, createError, ErrorCodes, handlePlatformError } from '../utils/errors';
10
+ import { normalizeDeviceData } from '../utils/data-parser';
11
+
12
+ /**
13
+ * Web 浏览器蓝牙扫描器
14
+ * 使用 Web Bluetooth API
15
+ */
16
+ export class WebScanner extends BaseScanner {
17
+ private device: BluetoothDevice | null = null;
18
+ private server: BluetoothRemoteGATTServer | null = null;
19
+
20
+ getPlatform(): Platform {
21
+ return Platform.UNKNOWN;
22
+ }
23
+
24
+ async init(): Promise<void> {
25
+ return new Promise((resolve, reject) => {
26
+ // 检查浏览器是否支持 Web Bluetooth
27
+ if (!navigator.bluetooth) {
28
+ reject(createError(
29
+ ErrorCodes.UNSUPPORTED_PLATFORM,
30
+ '当前浏览器不支持 Web Bluetooth API,请使用 Chrome、Edge 或 Opera'
31
+ ));
32
+ return;
33
+ }
34
+
35
+ this.initialized = true;
36
+ resolve();
37
+ });
38
+ }
39
+
40
+ protected async startScanInternal(): Promise<void> {
41
+ return new Promise(async (resolve, reject) => {
42
+ try {
43
+ // Web Bluetooth 使用 requestDevice 而不是扫描
44
+ // 这会弹出浏览器的选择设备对话框
45
+ const options: RequestDeviceOptions = {
46
+ acceptAllDevices: true,
47
+ };
48
+
49
+ // 添加服务过滤
50
+ if (this.options.services && this.options.services.length > 0) {
51
+ options.filters = this.options.services.map(uuid => ({
52
+ services: [uuid],
53
+ }));
54
+ } else {
55
+ options.acceptAllDevices = true;
56
+ }
57
+
58
+ // 请求设备(浏览器会弹出选择框)
59
+ this.device = await navigator.bluetooth.requestDevice(options);
60
+
61
+ // 监听设备广告(如果支持)
62
+ if (this.device.watchAdvertisements) {
63
+ this.device.addEventListener('advertisementreceived', (event: any) => {
64
+ this.handleDeviceFound({
65
+ deviceId: this.device!.id,
66
+ name: this.device!.name,
67
+ RSSI: event.rssi || -50,
68
+ advertisData: event.manufacturerData,
69
+ });
70
+ });
71
+
72
+ await this.device.watchAdvertisements();
73
+ } else {
74
+ // 如果不支持广告监听,直接返回设备信息
75
+ this.handleDeviceFound({
76
+ deviceId: this.device.id,
77
+ name: this.device.name,
78
+ RSSI: -50, // 默认值
79
+ });
80
+ }
81
+
82
+ resolve();
83
+ } catch (error: any) {
84
+ if (error.name === 'NotFoundError') {
85
+ reject(Errors.scanFailed('用户取消了设备选择'));
86
+ } else {
87
+ reject(handlePlatformError(error, 'web'));
88
+ }
89
+ }
90
+ });
91
+ }
92
+
93
+ protected async stopScanInternal(): Promise<void> {
94
+ // Web Bluetooth 没有明确的停止扫描方法
95
+ // 设备选择后扫描就结束了
96
+ if (this.device && this.device.unwatchAdvertisements) {
97
+ await this.device.unwatchAdvertisements();
98
+ }
99
+ return Promise.resolve();
100
+ }
101
+
102
+ async checkPermissions(): Promise<boolean> {
103
+ // Web Bluetooth 权限由浏览器自动管理
104
+ // 通过用户手势触发 requestDevice 时会自动请求权限
105
+ return true;
106
+ }
107
+
108
+ async requestPermissions(): Promise<boolean> {
109
+ // Web Bluetooth 不需要显式请求权限
110
+ return true;
111
+ }
112
+
113
+ onAdapterStateChange(callback: (state: AdapterState) => void): void {
114
+ this.adapterStateListeners.push(callback);
115
+
116
+ // Web Bluetooth 没有适配器状态变化事件
117
+ // 直接返回当前状态
118
+ callback({
119
+ available: !!navigator.bluetooth,
120
+ discovering: this.state === 'scanning',
121
+ });
122
+ }
123
+
124
+ destroy(): void {
125
+ super.destroy();
126
+ this.device = null;
127
+ this.server = null;
128
+ }
129
+
130
+ /**
131
+ * 连接到设备(Web Bluetooth 特有)
132
+ */
133
+ async connect(): Promise<BluetoothRemoteGATTServer | null> {
134
+ if (!this.device) {
135
+ throw Errors.notInitialized();
136
+ }
137
+
138
+ try {
139
+ this.server = await this.device.gatt?.connect() || null;
140
+ return this.server;
141
+ } catch (error) {
142
+ throw handlePlatformError(error, 'web');
143
+ }
144
+ }
145
+
146
+ /**
147
+ * 断开连接(Web Bluetooth 特有)
148
+ */
149
+ disconnect(): void {
150
+ if (this.device && this.device.gatt?.connected) {
151
+ this.device.gatt.disconnect();
152
+ }
153
+ this.server = null;
154
+ }
155
+
156
+ /**
157
+ * 获取已连接的服务(Web Bluetooth 特有)
158
+ */
159
+ async getServices(): Promise<BluetoothRemoteGATTService[]> {
160
+ if (!this.server) {
161
+ throw Errors.notInitialized();
162
+ }
163
+
164
+ try {
165
+ return await this.server.getPrimaryServices();
166
+ } catch (error) {
167
+ throw handlePlatformError(error, 'web');
168
+ }
169
+ }
170
+ }
171
+
172
+ /**
173
+ * 检查浏览器是否支持 Web Bluetooth
174
+ */
175
+ export function isWebBluetoothSupported(): boolean {
176
+ return 'bluetooth' in navigator && typeof navigator.bluetooth.requestDevice === 'function';
177
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Web Bluetooth API 类型声明
3
+ */
4
+
5
+ interface BluetoothDevice extends EventTarget {
6
+ readonly id: string;
7
+ readonly name: string | null;
8
+ readonly gatt: BluetoothRemoteGATTServer | null;
9
+ readonly uuids: string[];
10
+
11
+ watchAdvertisements(): Promise<void>;
12
+ unwatchAdvertisements(): Promise<void>;
13
+
14
+ addEventListener(type: 'advertisementreceived', listener: (event: BluetoothAdvertisingEvent) => void): void;
15
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject): void;
16
+ }
17
+
18
+ interface BluetoothRemoteGATTServer {
19
+ readonly device: BluetoothDevice;
20
+ readonly connected: boolean;
21
+
22
+ connect(): Promise<BluetoothRemoteGATTServer>;
23
+ disconnect(): void;
24
+ getPrimaryService(service: string | number): Promise<BluetoothRemoteGATTService>;
25
+ getPrimaryServices(service?: string | number): Promise<BluetoothRemoteGATTService[]>;
26
+ }
27
+
28
+ interface BluetoothRemoteGATTService extends EventTarget {
29
+ readonly device: BluetoothDevice;
30
+ readonly uuid: string;
31
+ readonly isPrimary: boolean;
32
+
33
+ getCharacteristic(characteristic: string | number): Promise<BluetoothRemoteGATTCharacteristic>;
34
+ getCharacteristics(characteristic?: string | number): Promise<BluetoothRemoteGATTCharacteristic[]>;
35
+ getIncludedService(service: string | number): Promise<BluetoothRemoteGATTService>;
36
+ getIncludedServices(service?: string | number): Promise<BluetoothRemoteGATTService[]>;
37
+ }
38
+
39
+ interface BluetoothRemoteGATTCharacteristic extends EventTarget {
40
+ readonly service: BluetoothRemoteGATTService;
41
+ readonly uuid: string;
42
+ readonly properties: BluetoothCharacteristicProperties;
43
+ readonly value: DataView | null;
44
+
45
+ readValue(): Promise<DataView>;
46
+ writeValue(value: BufferSource): Promise<void>;
47
+ startNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
48
+ stopNotifications(): Promise<BluetoothRemoteGATTCharacteristic>;
49
+ }
50
+
51
+ interface BluetoothCharacteristicProperties {
52
+ readonly broadcast: boolean;
53
+ readonly read: boolean;
54
+ readonly writeWithoutResponse: boolean;
55
+ readonly write: boolean;
56
+ readonly notify: boolean;
57
+ readonly indicate: boolean;
58
+ readonly authenticatedSignedWrites: boolean;
59
+ readonly reliableWrite: boolean;
60
+ readonly writableAuxiliaries: boolean;
61
+ }
62
+
63
+ interface BluetoothAdvertisingEvent extends Event {
64
+ readonly device: BluetoothDevice;
65
+ readonly rssi: number;
66
+ readonly txPower: number;
67
+ readonly manufacturerData: Map<number, DataView>;
68
+ readonly serviceData: Map<string, DataView>;
69
+ readonly uuids: string[];
70
+ readonly name: string | null;
71
+ readonly appearance: number | null;
72
+ }
73
+
74
+ interface BluetoothScanFilter {
75
+ services?: string[];
76
+ name?: string;
77
+ namePrefix?: string;
78
+ manufacturerData?: Map<number, BluetoothManufacturerDataFilter>;
79
+ }
80
+
81
+ interface BluetoothManufacturerDataFilter {
82
+ companyIdentifier: number;
83
+ dataPrefix?: DataView;
84
+ mask?: DataView;
85
+ }
86
+
87
+ interface RequestDeviceOptions {
88
+ filters?: BluetoothScanFilter[];
89
+ optionalServices?: string[];
90
+ acceptAllDevices?: boolean;
91
+ }
92
+
93
+ interface Bluetooth extends EventTarget {
94
+ requestDevice(options: RequestDeviceOptions): Promise<BluetoothDevice>;
95
+ getAvailability(): Promise<boolean>;
96
+
97
+ addEventListener(type: 'availabilitychanged', listener: (event: Event) => void): void;
98
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject): void;
99
+ }
100
+
101
+ interface Navigator {
102
+ bluetooth: Bluetooth;
103
+ }