npm package: @fww_123/uni-ble-scanner
Description: 跨平台蓝牙低功耗扫描组件，支持安卓、鸿蒙、iOS
Latest version: 2.0.0

# @fww_123/uni-ble-scanner

跨平台蓝牙低功耗（BLE）扫描组件，一套代码支持 **Android**、**iOS**、**鸿蒙** 三大原生平台，以及 **UniApp**、**APICloud**、**H5 WebView** 等多种开发框架。

## 📖 目录

- [特性](#-特性)
- [安装](#-安装)
- [快速开始](#-快速开始)
- [架构说明](#-架构说明)
- [框架集成](#-框架集成)
- [API 文档](#-api-文档)
- [常见问题](#-常见问题)

## ✨ 特性

- 🚀 **跨框架**：支持 UniApp、APICloud、H5(WebView)、纯 H5
- 📱 **真原生**：直接调用 Android/iOS/鸿蒙原生蓝牙 API，非 JS 模拟
- 🔧 **统一接口**：所有框架使用相同的 JavaScript API
- � **自动降级**：原生插件不可用时自动切换到标准 API
- 🛡️ **TypeScript**：完整的类型定义和代码提示
- 📦 **开箱即用**：npm 安装，引入即用

## 📦 安装

```bash
npm install @fww_123/uni-ble-scanner
```

## 🚀 快速开始

### 最简单的使用方式（推荐）

适用于：**UniApp**、**APICloud**、**H5 + WebView**、**纯 H5**

```html
<!-- 1. 引入 Bridge 文件 -->
<script src="node_modules/@fww_123/uni-ble-scanner/native-plugins/bridge/universal-bridge.js"></script>

<script>
// 2. 初始化蓝牙
BLEScanner.init({}, function(ret, err) {
  if (err) {
    console.error('初始化失败:', err.message);
    return;
  }
  console.log('初始化成功:', ret.platform);
});

// 3. 开始扫描
BLEScanner.startScan({
  timeout: 10000,  // 扫描 10 秒
  services: []     // 可选：指定要扫描的服务 UUID
}, function(ret, err) {
  if (err) {
    console.error('扫描错误:', err.message);
    return;
  }
  
  // 处理扫描事件
  switch(ret.event) {
    case 'scanStart':
      console.log('扫描开始');
      break;
    case 'deviceFound':
      console.log('发现设备:', ret.device.name, ret.device.RSSI);
      break;
    case 'scanStop':
      console.log('扫描停止');
      break;
    case 'timeout':
      console.log('扫描超时');
      break;
  }
});
</script>
```

### TypeScript / ES Module 方式

```typescript
import { createScanner, createNativeScanner } from '@fww_123/uni-ble-scanner';

// 方式1：自动检测平台
const scanner = createScanner({ type: 'auto' });

// 方式2：强制使用原生
const scanner = createNativeScanner();

// 初始化
await scanner.init();

// 开始扫描
await scanner.startScan({
  timeout: 30000,
  allowDuplicates: false
}, {
  onDeviceFound: (device) => {
    console.log('发现设备:', device.name, device.RSSI);
  },
  onScanStart: () => console.log('扫描开始'),
  onScanStop: () => console.log('扫描停止')
});
```

## 🏗️ 架构说明

### 整体架构

```
┌─────────────────────────────────────────────────────────────┐
│                    你的 APP（任意框架）                       │
│         UniApp / APICloud / H5+原生壳 / 纯 H5               │
└───────────────────────────┬─────────────────────────────────┘
                            │
              ┌─────────────▼──────────────┐
              │   universal-bridge.js      │
              │   ├─ 自动检测运行环境       │
              │   ├─ 统一 API 封装          │
              │   └─ 自动降级处理           │
              └─────────────┬──────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
┌───────▼───────┐  ┌────────▼────────┐  ┌──────▼──────┐
│    Android    │  │       iOS       │  │   鸿蒙      │
│  Java 原生    │  │   OC 原生        │  │  ArkTS 原生 │
│               │  │                 │  │             │
│ BluetoothLe   │  │ CoreBluetooth   │  │ @ohos.      │
│   Scanner     │  │                 │  │ bluetooth   │
└───────────────┘  └─────────────────┘  └─────────────┘
```

### 工作原理

1. **环境检测**：`universal-bridge.js` 自动检测当前运行环境
2. **适配层**：根据环境选择最佳的实现方式
   - 有原生插件 → 使用原生插件（性能最好）
   - 无原生插件 → 使用框架标准 API
   - H5 环境 → 使用 Web Bluetooth API
3. **统一接口**：无论底层如何，对外提供一致的 API

### 各平台实现

| 平台 | 技术方案 | 原生 API | 最低版本 |
|------|---------|---------|---------|
| Android | UniApp 原生插件 / Native.js | `BluetoothLeScanner` | Android 5.0+ |
| iOS | UniApp 原生插件 / Native.js | `CoreBluetooth` | iOS 7.0+ |
| 鸿蒙 | 原生 ArkTS 模块 | `@ohos.bluetooth.ble` | HarmonyOS 2.0+ |
| Web | Web Bluetooth API | `navigator.bluetooth` | Chrome/Edge |

## 📱 框架集成

### UniApp

#### 方案 A：使用原生插件（推荐，性能最佳）

1. **复制原生插件到项目**
```bash
# Android
mkdir -p nativeplugins/ble-scanner/android
mkdir -p nativeplugins/ble-scanner/ios

# 复制插件文件
cp -r node_modules/@fww_123/uni-ble-scanner/native-plugins/android/ble-scanner/* nativeplugins/ble-scanner/android/
cp -r node_modules/@fww_123/uni-ble-scanner/native-plugins/ios/ble-scanner/* nativeplugins/ble-scanner/ios/
```

2. **配置 manifest.json**
```json
{
  "nativePlugins": {
    "ble-scanner": {
      "android": {
        "package": "com.blescanner",
        "class": "BLEScannerModule"
      },
      "ios": {
        "class": "BLEScannerModule"
      }
    }
  }
}
```

3. **在页面中引入 Bridge**
```html
<template>
  <view>
    <!-- 你的页面内容 -->
  </view>
</template>

<script>
// 方式1：直接引入 Bridge（推荐）
// 在 index.html 的 head 中添加：
// <script src="static/js/universal-bridge.js"><\/script>

// 方式2：使用 npm 包
import { createScanner } from '@fww_123/uni-ble-scanner';

export default {
  methods: {
    async initBLE() {
      // 使用全局 BLEScanner 对象
      BLEScanner.init({}, (ret, err) => {
        if (ret) console.log('成功');
      });
      
      // 或使用 npm 包
      const scanner = createScanner({ type: 'native' });
      await scanner.init();
    }
  }
}
</script>
```

#### 方案 B：使用标准 API（无需配置原生插件）

直接使用 `universal-bridge.js`，它会自动使用 UniApp 标准蓝牙 API。

```html
<script src="path/to/universal-bridge.js"></script>
<script>
  // 自动降级到 uni.openBluetoothAdapter
  BLEScanner.init({}, callback);
</script>
```

### APICloud

1. **导入模块**
   - 将 Android/iOS 模块编译后导入 APICloud 项目
   - 在 `config.xml` 中添加模块配置

2. **使用 Bridge**
```html
<!DOCTYPE html>
<html>
<head>
  <script src="path/to/universal-bridge.js"></script>
</head>
<body>
  <script>
    apiready = function() {
      BLEScanner.init({}, function(ret, err) {
        // 初始化完成
      });
    };
  </script>
</body>
</html>
```

### H5 + 原生 WebView

如果你的 APP 是原生开发，内嵌 H5 页面：

**Android 端（Java/Kotlin）：**
```java
// 在 WebView 中注入 Bridge
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new BLEScannerBridge(this), "BLEScannerNative");

// BLEScannerBridge.java
public class BLEScannerBridge {
    @JavascriptInterface
    public void init(String options, String callbackId) {
        // 调用原生蓝牙 API
    }
}
```

**iOS 端（Objective-C/Swift）：**
```objc
// 使用 WebViewJavascriptBridge
[bridge registerHandler:@"BLEScanner.init" handler:^(id data, WVJBResponseCallback responseCallback) {
    // 初始化蓝牙
    responseCallback(@{@"status": @YES});
}];

[bridge registerHandler:@"BLEScanner.startScan" handler:^(id data, WVJBResponseCallback responseCallback) {
    // 开始扫描
    // 扫描到设备后通过 callback 返回
}];
```

**H5 页面：**
```html
<script src="path/to/universal-bridge.js"></script>
<script>
  // universal-bridge.js 会自动检测 WebViewJavascriptBridge
  BLEScanner.init({}, function(ret, err) {
    console.log('初始化结果:', ret);
  });
</script>
```

### 纯 H5（Web Bluetooth）

```html
<script src="path/to/universal-bridge.js"></script>
<script>
  // 仅支持 Chrome、Edge、Opera 等浏览器
  // 必须在 HTTPS 环境下运行
  BLEScanner.init({}, function(ret, err) {
    if (err) {
      alert('浏览器不支持蓝牙或权限被拒绝');
      return;
    }
    
    BLEScanner.startScan({}, function(ret, err) {
      if (ret.event === 'deviceFound') {
        console.log(ret.device);
      }
    });
  });
</script>
```

## � API 文档

### 统一 API（Bridge 模式）

#### `BLEScanner.init(options, callback)`

初始化蓝牙适配器。

**参数：**
- `options` `{Object}` - 配置选项（可选）
  - `restoreIdentifier` `{string}` - iOS 后台恢复标识
- `callback` `{Function}` - 回调函数 `callback(ret, err)`

**回调结果：**
```javascript
// 成功
{
  status: true,
  platform: 'android' // 'android' | 'ios' | 'harmony' | 'web'
}

// 失败
{
  code: -1,           // 错误码
  message: '错误信息'  // 错误描述
}
```

**错误码说明：**
| 错误码 | 说明 |
|--------|------|
| -1 | 缺少权限 |
| -2 | 设备不支持蓝牙 |
| -3 | 蓝牙未开启 |
| -4 | 扫描已在进行中 |
| -5 | 启动扫描失败 |
| -10 | 用户取消 |
| -99 | 其他错误 |

#### `BLEScanner.startScan(options, callback)`

开始扫描 BLE 设备。

**参数：**
- `options` `{Object}` - 扫描选项
  - `timeout` `{number}` - 扫描超时时间（毫秒），默认 10000
  - `services` `{string[]}` - 要扫描的服务 UUID 数组
  - `allowDuplicates` `{boolean}` - 是否允许重复设备，默认 false
  - `rssiThreshold` `{number}` - RSSI 信号阈值（仅部分平台支持）
- `callback` `{Function}` - 回调函数 `callback(ret, err)`

**回调事件：**
```javascript
// 扫描开始
{ event: 'scanStart' }

// 发现设备
{
  event: 'deviceFound',
  device: {
    deviceId: 'xx:xx:xx:xx:xx:xx',  // 设备 MAC 地址或 UUID
    name: '设备名称',               // 蓝牙设备名称
    RSSI: -65,                     // 信号强度（dBm）
    advertisData: '...',           // 广播数据（十六进制字符串）
    serviceUuids: ['...']          // 服务 UUID 列表
  }
}

// 扫描停止
{ event: 'scanStop' }

// 扫描超时
{ event: 'timeout' }
```

#### `BLEScanner.stopScan(callback)`

停止扫描。

**参数：**
- `callback` `{Function}` - 回调函数 `callback(ret)`

**回调结果：**
```javascript
{ event: 'scanStop' }
```

#### `BLEScanner.checkPermission(callback)`

检查蓝牙权限。

**参数：**
- `callback` `{Function}` - 回调函数 `callback(ret)`

**回调结果：**
```javascript
{ granted: true }  // true: 有权限，false: 无权限
```

#### `BLEScanner.requestPermission(callback)`

请求蓝牙权限。

**参数：**
- `callback` `{Function}` - 回调函数 `callback(ret)`

**回调结果：**
```javascript
{
  requested: true,
  message: '提示信息'
}
```

#### `BLEScanner.getDevices()`

获取已发现的设备列表。

**返回值：**
```javascript
[
  {
    deviceId: 'xx:xx:xx:xx:xx:xx',
    name: '设备名称',
    RSSI: -65
  }
]
```

### TypeScript API

```typescript
import { 
  createScanner, 
  createNativeScanner,
  BLEDevice,
  ScanOptions,
  ScanCallbacks 
} from '@fww_123/uni-ble-scanner';

// 创建设备
const scanner = createScanner({
  type: 'auto' // 'auto' | 'uniapp' | 'native' | 'web'
});

// 类型定义
interface BLEDevice {
  deviceId: string;
  name?: string;
  RSSI: number;
  advertisData?: string;
  serviceUuids?: string[];
}

interface ScanOptions {
  timeout?: number;
  services?: string[];
  allowDuplicates?: boolean;
  rssiThreshold?: number;
}

interface ScanCallbacks {
  onDeviceFound?: (device: BLEDevice) => void;
  onScanStart?: () => void;
  onScanStop?: () => void;
  onTimeout?: () => void;
  onError?: (error: BLEError) => void;
}
```

## ❓ 常见问题

### Q1: 为什么扫描不到设备？

**可能原因：**
1. 没有蓝牙权限 → 调用 `BLEScanner.requestPermission()`
2. 蓝牙未开启 → 引导用户开启蓝牙
3. Android 6.0+ 需要位置权限 → 检查定位权限
4. iOS 需要在真机上测试 → 模拟器不支持蓝牙

### Q2: 原生插件和 Bridge 有什么区别？

| 特性 | 原生插件 | Bridge |
|------|---------|--------|
| 性能 | 最好 | 好 |
| 配置 | 需要配置 manifest | 引入即用 |
| 兼容性 | 需要编译 | 纯 JS，无需编译 |
| 适用场景 | 生产环境 | 开发/快速集成 |

### Q3: 如何在 UniApp 中判断使用原生还是标准 API？

```javascript
// universal-bridge.js 会自动判断
// 你也可以手动指定
const scanner = createScanner({ 
  type: 'native' // 强制使用原生
});

const scanner = createScanner({ 
  type: 'uniapp' // 强制使用标准 API
});
```

### Q4: 支持后台扫描吗？

- **Android**: 支持，需要后台定位权限
- **iOS**: 支持，需要在 `Info.plist` 中配置 `UIBackgroundModes`
- **鸿蒙**: 支持
- **Web**: 不支持

### Q5: 如何打包发布？

```bash
# 1. 安装依赖
npm install

# 2. 编译 TypeScript
npm run build

# 3. 发布到 npm
npm publish --access public
```

## � 目录结构

```
ble_test/
├── src/                          # TypeScript 源码
│   ├── core/
│   │   └── base-scanner.ts       # 扫描器基类
│   ├── platforms/
│   │   ├── android-native.ts     # Android 原生实现
│   │   ├── ios-native.ts         # iOS 原生实现
│   │   ├── harmony-native.ts     # 鸿蒙原生实现
│   │   ├── uniapp-scanner.ts     # UniApp 标准 API
│   │   └── web-scanner.ts        # Web Bluetooth
│   ├── utils/
│   │   ├── platform.ts           # 平台检测
│   │   ├── permissions.ts        # 权限管理
│   │   ├── errors.ts             # 错误处理
│   │   └── data-parser.ts        # 数据解析
│   ├── factory.ts                # 工厂函数
│   └── index.ts                  # 入口文件
├── native-plugins/               # 原生插件
│   ├── android/                  # Android 原生模块
│   │   └── ble-scanner/          # UniApp/APICloud 模块
│   ├── ios/                      # iOS 原生模块
│   │   └── ble-scanner/
│   ├── harmony/                  # 鸿蒙原生模块
│   │   └── entry/src/main/ets/components/ble/
│   └── bridge/
│       └── universal-bridge.js   # 跨框架 Bridge ⭐核心文件
├── examples/                     # 示例代码
│   ├── cross-framework-demo/     # 多框架集成示例
│   │   ├── uniapp/               # UniApp 示例
│   │   ├── apicloud/             # APICloud 示例
│   │   └── h5-webview/           # H5 WebView 示例
│   ├── native-app-demo/          # 原生 APP 示例
│   └── web-demo/                 # Web 示例
└── dist/                         # 编译输出
```

## ⚠️ 注意事项

### Android

- **权限配置**（`AndroidManifest.xml`）：
```xml
<!-- Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Android 11 及以下 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- 所有版本都需要 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

- **动态权限申请**（Android 6.0+）：
  - 需要动态申请位置和蓝牙权限

### iOS

- **Info.plist 配置**：
```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要使用蓝牙扫描设备</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要使用蓝牙扫描设备</string>
```

- **必须使用真机测试**，模拟器不支持蓝牙

### 鸿蒙

- **module.json5 配置**：
```json
{
  "requestPermissions": [
    { "name": "ohos.permission.ACCESS_BLUETOOTH" },
    { "name": "ohos.permission.LOCATION" }
  ]
}
```

### Web

- **浏览器支持**：仅 Chrome、Edge、Opera 支持 Web Bluetooth
- **HTTPS**：必须在 HTTPS 环境下使用
- **不支持后台扫描**

## � 支持与反馈

- 提交 Issue：[GitHub Issues](https://github.com/yourusername/uni-ble-scanner/issues)
- 邮件联系：your.email@example.com

## 📄 许可证

MIT License

---

**Made with ❤️ for cross-platform BLE scanning**