“益康养老”HarmonyOS APP门禁设备:WiFi状态检查与密码下发功能详解
·
1. 业务背景与功能架构
1.1 养老场景中的WiFi配网需求
在益康养老物联网系统中,门禁设备作为重要的安防终端,需要通过WiFi网络接入华为云IoT平台,实现远程监控、数据上报和指令下发。WiFi状态检查与密码下发功能是设备配网的核心环节,直接关系到设备的在线率和系统稳定性。
核心痛点与解决方案:
- 痛点1:护理员多为非技术人员,设备配网流程复杂
- 痛点2:养老院WiFi环境复杂,多热点切换频繁
- 痛点3:设备离线后重新配网困难
- 解决方案:一站式自动化配网流程,智能WiFi检测与密码安全下发
1.2 整体架构设计
2. WiFi状态检查模块实现
2.1 权限配置与依赖声明
// features/device/src/main/module.json5
{
"module": {
"name": "device",
"type": "shared",
"description": "$string:device_desc",
"deviceTypes": ["phone", "tablet"],
"requestPermissions": [
{
"name": "ohos.permission.GET_WIFI_INFO",
"reason": "$string:wifi_info_reason",
"usedScene": {
"abilities": ["DeviceAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.SET_WIFI_INFO",
"reason": "$string:set_wifi_reason",
"usedScene": {
"abilities": ["DeviceAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": ["DeviceAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "$string:bluetooth_reason",
"usedScene": {
"abilities": ["DeviceAbility"],
"when": "always"
}
}
],
"definePermissions": [],
"abilities": [
{
"name": "DeviceAbility",
"srcEntry": "./ets/deviceability/DeviceAbility.ets",
"description": "$string:DeviceAbility_desc",
"icon": "$media:icon",
"label": "$string:DeviceAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
]
}
]
}
}
2.2 WiFi状态检查核心实现
// features/device/src/main/ets/wifi/WifiStatusChecker.ets
import { wifi } from '@ohos.wifi';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';
import { emitter } from '@ohos.events.emitter';
/**
* WiFi状态检查器
* 负责检查设备WiFi状态、扫描网络、评估信号质量
*/
export class WifiStatusChecker {
private static instance: WifiStatusChecker;
private isChecking: boolean = false;
private checkInterval: number = 3000; // 检查间隔3秒
private maxRetryCount: number = 3;
// WiFi信号强度等级
private static readonly SIGNAL_LEVELS = {
EXCELLENT: { min: -50, max: 0, level: 4, color: '#4CAF50' }, // 极好
GOOD: { min: -60, max: -51, level: 3, color: '#8BC34A' }, // 良好
FAIR: { min: -70, max: -61, level: 2, color: '#FFC107' }, // 一般
POOR: { min: -85, max: -71, level: 1, color: '#FF9800' }, // 差
UNUSABLE: { min: -100, max: -86, level: 0, color: '#F44336' } // 不可用
};
public static getInstance(): WifiStatusChecker {
if (!WifiStatusChecker.instance) {
WifiStatusChecker.instance = new WifiStatusChecker();
}
return WifiStatusChecker.instance;
}
/**
* 检查WiFi基础状态
*/
public async checkBasicStatus(): Promise<WifiBasicStatus> {
try {
Logger.info('WifiStatusChecker', '开始检查WiFi基础状态');
// 1. 检查WiFi开关状态
const isWifiActive = await wifi.isWifiActive();
if (!isWifiActive) {
return {
isEnabled: false,
isConnected: false,
message: 'WiFi未开启,请先打开WiFi',
level: 0,
recommendation: 'enable_wifi'
};
}
// 2. 检查连接状态
const linkedInfo = await wifi.getLinkedInfo();
if (!linkedInfo) {
return {
isEnabled: true,
isConnected: false,
message: 'WiFi已开启但未连接网络',
level: 0,
recommendation: 'connect_network'
};
}
// 3. 分析连接质量
const signalLevel = this.calculateSignalLevel(linkedInfo.rssi);
const ssid = linkedInfo.ssid ? linkedInfo.ssid.replace(/"/g, '') : '未知网络';
return {
isEnabled: true,
isConnected: true,
ssid,
bssid: linkedInfo.bssid,
rssi: linkedInfo.rssi,
frequency: linkedInfo.frequency,
level: signalLevel.level,
levelColor: signalLevel.color,
ipAddress: linkedInfo.ipAddress,
message: `已连接: ${ssid} (${signalLevel.level}/4)`,
recommendation: signalLevel.level >= 2 ? 'ready' : 'improve_signal'
};
} catch (error) {
const err: BusinessError = error as BusinessError;
Logger.error('WifiStatusChecker', `WiFi状态检查失败: ${err.code}, ${err.message}`);
return {
isEnabled: false,
isConnected: false,
message: `WiFi状态检查失败: ${err.message}`,
level: 0,
recommendation: 'check_permission'
};
}
}
/**
* 持续监测WiFi状态(用于配网过程)
*/
public startContinuousMonitoring(callback: (status: WifiMonitoringStatus) => void): void {
if (this.isChecking) {
Logger.warn('WifiStatusChecker', 'WiFi监测已在进行中');
return;
}
this.isChecking = true;
let checkCount = 0;
const monitoringInterval = setInterval(async () => {
if (!this.isChecking) {
clearInterval(monitoringInterval);
return;
}
checkCount++;
try {
const basicStatus = await this.checkBasicStatus();
const scanResults = await this.scanNetworksQuick();
const monitoringStatus: WifiMonitoringStatus = {
...basicStatus,
scanResults,
checkCount,
timestamp: Date.now(),
stability: this.calculateStability(checkCount, basicStatus)
};
// 触发事件
emitter.emit({
eventId: EventConstants.WIFI_STATUS_MONITORING
}, { status: monitoringStatus });
// 执行回调
callback(monitoringStatus);
// 达到最大检查次数后停止
if (checkCount >= this.maxRetryCount) {
this.stopContinuousMonitoring();
}
} catch (error) {
Logger.error('WifiStatusChecker', `第${checkCount}次监测失败: ${error.message}`);
}
}, this.checkInterval);
// 存储interval ID以便清理
(this as any)._monitoringInterval = monitoringInterval;
}
/**
* 停止持续监测
*/
public stopContinuousMonitoring(): void {
this.isChecking = false;
if ((this as any)._monitoringInterval) {
clearInterval((this as any)._monitoringInterval);
(this as any)._monitoringInterval = null;
}
Logger.info('WifiStatusChecker', 'WiFi持续监测已停止');
}
/**
* 快速扫描周边网络(简化版本,用于实时监测)
*/
private async scanNetworksQuick(): Promise<Array<WifiScanSimple>> {
try {
await wifi.scan();
const scanResults = await wifi.getScanInfoList();
return scanResults
.slice(0, 5) // 只取前5个最强的信号
.map(result => ({
ssid: result.ssid ? result.ssid.replace(/"/g, '') : '未知',
bssid: result.bssid,
rssi: result.rssi,
level: this.calculateSignalLevel(result.rssi).level,
securityType: this.getSecurityTypeName(result.securityType),
frequency: result.frequency,
isCurrent: false // 在外部与当前连接对比后设置
}));
} catch (error) {
Logger.warn('WifiStatusChecker', '快速扫描失败,返回空列表');
return [];
}
}
/**
* 计算信号强度等级
*/
private calculateSignalLevel(rssi: number): SignalLevel {
const rssiValue = rssi || -100;
for (const [key, range] of Object.entries(WifiStatusChecker.SIGNAL_LEVELS)) {
if (rssiValue >= range.min && rssiValue <= range.max) {
return {
level: range.level,
color: range.color,
description: this.getSignalDescription(range.level)
};
}
}
// 默认返回最差等级
const poorest = WifiStatusChecker.SIGNAL_LEVELS.UNUSABLE;
return {
level: poorest.level,
color: poorest.color,
description: this.getSignalDescription(poorest.level)
};
}
/**
* 计算连接稳定性
*/
private calculateStability(checkCount: number, status: WifiBasicStatus): number {
if (checkCount <= 1) {
return 0; // 首次检查无法评估稳定性
}
// 简化稳定性计算:如果连续检查都连接良好,稳定性高
// 实际应用中应基于历史数据计算
return status.isConnected && status.level >= 2 ? 0.8 : 0.3;
}
/**
* 获取安全类型名称
*/
private getSecurityTypeName(type: number): string {
const securityTypes: Record<number, string> = {
0: '开放网络',
1: 'WEP',
2: 'PSK',
3: 'EAP',
4: 'SAE',
5: 'OWE',
6: 'SUITE_B'
};
return securityTypes[type] || '未知';
}
/**
* 获取信号描述
*/
private getSignalDescription(level: number): string {
const descriptions = [
'信号极弱,无法稳定连接',
'信号较弱,连接可能不稳定',
'信号一般,可以连接',
'信号良好,连接稳定',
'信号极佳,连接非常稳定'
];
return descriptions[level] || descriptions[0];
}
}
// 类型定义
export interface WifiBasicStatus {
isEnabled: boolean;
isConnected: boolean;
ssid?: string;
bssid?: string;
rssi?: number;
frequency?: number;
level: number;
levelColor?: string;
ipAddress?: number;
message: string;
recommendation: 'enable_wifi' | 'connect_network' | 'improve_signal' | 'ready' | 'check_permission';
}
export interface WifiMonitoringStatus extends WifiBasicStatus {
scanResults: Array<WifiScanSimple>;
checkCount: number;
timestamp: number;
stability: number;
}
export interface WifiScanSimple {
ssid: string;
bssid: string;
rssi: number;
level: number;
securityType: string;
frequency: number;
isCurrent: boolean;
}
export interface SignalLevel {
level: number; // 0-4
color: string;
description: string;
}
2.3 WiFi状态检查页面实现
// features/device/src/main/ets/pages/WifiCheckPage.ets
import { WifiStatusChecker, WifiMonitoringStatus } from '../wifi/WifiStatusChecker';
import { Logger } from '@elderly/basic';
import { Router } from '@elderly/basic';
@Component
export struct WifiCheckPage {
@State currentStatus: WifiMonitoringStatus = {
isEnabled: false,
isConnected: false,
message: '正在检查WiFi状态...',
level: 0,
recommendation: 'check_permission',
scanResults: [],
checkCount: 0,
timestamp: Date.now(),
stability: 0
};
@State isLoading: boolean = true;
@State checkProgress: number = 0;
@State showDetails: boolean = false;
private wifiChecker: WifiStatusChecker = WifiStatusChecker.getInstance();
private progressInterval: number | null = null;
aboutToAppear(): void {
this.startWifiCheck();
}
aboutToDisappear(): void {
this.wifiChecker.stopContinuousMonitoring();
if (this.progressInterval) {
clearInterval(this.progressInterval);
}
}
/**
* 开始WiFi检查
*/
private async startWifiCheck(): Promise<void> {
this.isLoading = true;
this.checkProgress = 0;
// 启动进度条动画
this.startProgressAnimation();
try {
// 首次快速检查
const initialStatus = await this.wifiChecker.checkBasicStatus();
this.currentStatus = {
...this.currentStatus,
...initialStatus,
checkCount: 1,
timestamp: Date.now()
};
this.checkProgress = 50;
// 开始持续监测
this.wifiChecker.startContinuousMonitoring((status: WifiMonitoringStatus) => {
this.currentStatus = status;
// 根据监测结果更新进度
if (status.isConnected && status.level >= 2) {
this.checkProgress = Math.min(100, this.checkProgress + 20);
}
// 检查完成条件
if (status.checkCount >= 3 || (status.isConnected && status.stability > 0.7)) {
this.isLoading = false;
this.wifiChecker.stopContinuousMonitoring();
if (this.progressInterval) {
clearInterval(this.progressInterval);
this.progressInterval = null;
}
}
});
} catch (error) {
Logger.error('WifiCheckPage', `WiFi检查启动失败: ${error.message}`);
this.currentStatus.message = `检查失败: ${error.message}`;
this.isLoading = false;
}
}
/**
* 启动进度条动画
*/
private startProgressAnimation(): void {
this.progressInterval = setInterval(() => {
if (this.checkProgress < 90 && this.isLoading) {
this.checkProgress += 5;
}
}, 500) as unknown as number;
}
/**
* 重新检查
*/
private recheck(): void {
this.startWifiCheck();
}
/**
* 前往WiFi设置
*/
private goToWifiSettings(): void {
// 鸿蒙系统中打开WiFi设置的方式
// 实际应根据系统API调整
Logger.info('WifiCheckPage', '跳转到系统WiFi设置');
// 这里可以调用系统能力打开设置
// import settings from '@ohos.settings';
// settings.openWirelessSettings();
}
/**
* 进入下一步(WiFi选择)
*/
private goToNextStep(): void {
if (this.currentStatus.isConnected && this.currentStatus.level >= 2) {
Router.pushUrl({
url: 'pages/WifiSelectPage',
params: {
currentSsid: this.currentStatus.ssid
}
});
}
}
/**
* 渲染状态指示器
*/
@Builder
private renderStatusIndicator() {
Column({ space: 15 }) {
// 状态图标
Stack({ alignContent: Alignment.Center }) {
// 背景圆环
Circle({ width: 120, height: 120 })
.fill(this.getStatusColor())
.opacity(0.1)
// 状态图标
Image(this.getStatusIcon())
.width(60)
.height(60)
// 进度环(加载时显示)
if (this.isLoading) {
Circle({ width: 130, height: 130 })
.strokeWidth(4)
.stroke(this.getStatusColor())
.fill(Color.Transparent)
.strokeDashArray([3, 5])
.strokeDashOffset(360 * (1 - this.checkProgress / 100))
}
}
// 状态文本
Column({ space: 8 }) {
Text(this.getStatusTitle())
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Black)
Text(this.currentStatus.message)
.fontSize(14)
.fontColor('#666666')
.textAlign(TextAlign.Center)
.maxLines(2)
}
.width('80%')
}
.width('100%')
.padding(30)
}
/**
* 渲染信号强度指示器
*/
@Builder
private renderSignalIndicator() {
if (!this.currentStatus.isConnected) {
return;
}
Column({ space: 10 }) {
Text('信号强度')
.fontSize(16)
.fontColor(Color.Black)
Row({ space: 4 }) {
// 5个信号条
ForEach(Array.from({ length: 5 }), (_, index: number) => {
const isActive = index < this.currentStatus.level;
const height = 10 + index * 8; // 递增高度
Rectangle()
.width(10)
.height(height)
.fill(isActive ? this.currentStatus.levelColor || '#4CAF50' : '#E0E0E0')
.borderRadius(2)
})
}
.height(50)
.justifyContent(FlexAlign.End)
.alignItems(VerticalAlign.Bottom)
Text(`${this.currentStatus.level}/4 - ${this.getSignalDescription(this.currentStatus.level)}`)
.fontSize(12)
.fontColor('#666666')
}
.width('100%')
.padding(20)
.backgroundColor('#F8F9FA')
.borderRadius(12)
.margin({ top: 10, bottom: 10 })
}
/**
* 渲染网络详情
*/
@Builder
private renderNetworkDetails() {
if (!this.currentStatus.isConnected || !this.showDetails) {
return;
}
Column({ space: 12 }) {
Text('网络详情')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
.width('100%')
this.renderDetailItem('网络名称', this.currentStatus.ssid || '未知')
this.renderDetailItem('BSSID', this.currentStatus.bssid || '未知')
this.renderDetailItem('IP地址', this.formatIpAddress(this.currentStatus.ipAddress))
this.renderDetailItem('频段', this.getFrequencyBand(this.currentStatus.frequency))
this.renderDetailItem('稳定性', `${Math.round(this.currentStatus.stability * 100)}%`)
}
.width('100%')
.padding(20)
.backgroundColor(Color.White)
.border({ width: 1, color: '#EEEEEE' })
.borderRadius(12)
.margin({ top: 10 })
}
/**
* 渲染详情项
*/
@Builder
private renderDetailItem(label: string, value: string) {
Row({ space: 15 }) {
Text(label)
.fontSize(14)
.fontColor('#666666')
.width(80)
Text(value)
.fontSize(14)
.fontColor(Color.Black)
.layoutWeight(1)
.textAlign(TextAlign.End)
}
.width('100%')
}
/**
* 渲染操作按钮
*/
@Builder
private renderActionButtons() {
Column({ space: 15 }) {
if (this.currentStatus.isConnected && this.currentStatus.level >= 2) {
Button('网络良好,继续配置设备')
.width('90%')
.height(50)
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.fontSize(16)
.enabled(!this.isLoading)
.onClick(() => this.goToNextStep())
} else {
Button('前往WiFi设置')
.width('90%')
.height(50)
.backgroundColor(Color.Blue)
.fontColor(Color.White)
.fontSize(16)
.enabled(!this.isLoading)
.onClick(() => this.goToWifiSettings())
}
Button(this.isLoading ? '检查中...' : '重新检查')
.width('90%')
.height(45)
.backgroundColor('#F0F0F0')
.fontColor(Color.Black)
.fontSize(16)
.enabled(!this.isLoading)
.onClick(() => this.recheck())
}
.width('100%')
.padding(20)
}
/**
* 获取状态颜色
*/
private getStatusColor(): Color {
if (this.isLoading) return Color.Blue;
if (!this.currentStatus.isEnabled) return Color.Gray;
if (!this.currentStatus.isConnected) return Color.Orange;
switch (this.currentStatus.level) {
case 0: case 1: return Color.Red;
case 2: return Color.Orange;
case 3: return '#8BC34A';
case 4: return '#4CAF50';
default: return Color.Gray;
}
}
/**
* 获取状态图标
*/
private getStatusIcon(): Resource {
if (this.isLoading) return $r('app.media.ic_wifi_loading');
if (!this.currentStatus.isEnabled) return $r('app.media.ic_wifi_off');
if (!this.currentStatus.isConnected) return $r('app.media.ic_wifi_disconnected');
switch (this.currentStatus.level) {
case 0: return $r('app.media.ic_wifi_0');
case 1: return $r('app.media.ic_wifi_1');
case 2: return $r('app.media.ic_wifi_2');
case 3: return $r('app.media.ic_wifi_3');
case 4: return $r('app.media.ic_wifi_4');
default: return $r('app.media.ic_wifi_unknown');
}
}
/**
* 获取状态标题
*/
private getStatusTitle(): string {
if (this.isLoading) return '检查WiFi状态中...';
if (!this.currentStatus.isEnabled) return 'WiFi未开启';
if (!this.currentStatus.isConnected) return '未连接网络';
switch (this.currentStatus.level) {
case 0: case 1: return '信号太弱';
case 2: return '信号一般';
case 3: return '信号良好';
case 4: return '信号极佳';
default: return '未知状态';
}
}
/**
* 获取信号描述
*/
private getSignalDescription(level: number): string {
const descriptions = [
'无法连接',
'非常弱',
'一般',
'良好',
'极佳'
];
return descriptions[level] || '未知';
}
/**
* 格式化IP地址
*/
private formatIpAddress(ip?: number): string {
if (!ip) return '未知';
return [
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF
].join('.');
}
/**
* 获取频段信息
*/
private getFrequencyBand(frequency?: number): string {
if (!frequency) return '未知';
if (frequency >= 2412 && frequency <= 2484) {
return `2.4GHz (${frequency}MHz)`;
} else if (frequency >= 5170 && frequency <= 5825) {
return `5GHz (${frequency}MHz)`;
} else {
return `${frequency}MHz`;
}
}
build() {
Column({ space: 0 }) {
// 顶部标题
Text('WiFi状态检查')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Black)
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 20, bottom: 20 })
.backgroundColor(Color.White)
Scroll() {
Column({ space: 20 }) {
// 状态指示器
this.renderStatusIndicator()
// 信号强度指示
if (this.currentStatus.isConnected) {
this.renderSignalIndicator()
}
// 详情切换按钮
if (this.currentStatus.isConnected) {
Row() {
Text(this.showDetails ? '隐藏详情' : '显示详情')
.fontSize(14)
.fontColor(Color.Blue)
Image($r('app.media.ic_arrow_down'))
.width(16)
.height(16)
.rotate({ angle: this.showDetails ? 180 : 0 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.showDetails = !this.showDetails;
})
.padding(10)
}
// 网络详情
if (this.showDetails) {
this.renderNetworkDetails()
}
// 操作按钮
this.renderActionButtons()
// 帮助提示
Column({ space: 8 }) {
Text('温馨提示')
.fontSize(14)
.fontWeight(FontWeight.Medium)
.fontColor('#666666')
Text('• 确保设备靠近路由器,信号强度达到3格以上')
.fontSize(12)
.fontColor('#999999')
Text('• 养老院公共WiFi可能需要网页认证')
.fontSize(12)
.fontColor('#999999')
Text('• 如遇连接问题,请联系网络管理员')
.fontSize(12)
.fontColor('#999999')
}
.width('90%')
.padding(15)
.backgroundColor('#F8F9FA')
.borderRadius(10)
.margin({ top: 20, bottom: 40 })
}
.width('100%')
.padding(20)
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F7FA')
}
}
3. WiFi密码下发模块实现
3.1 密码安全处理与加密
// features/device/src/main/ets/wifi/PasswordSecurityManager.ets
import { cryptoFramework } from '@ohos.security.cryptoFramework';
import { util } from '@ohos.util';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';
/**
* 密码安全管理器
* 负责密码的加密、安全存储和安全传输
*/
export class PasswordSecurityManager {
private static instance: PasswordSecurityManager;
private static readonly AES_ALGORITHM = 'AES256|GCM|PKCS7';
private static readonly AES_KEY_SIZE = 256; // 位
private static readonly ITERATION_COUNT = 10000;
private static readonly SALT_SIZE = 16; // 字节
// 设备特定的预共享密钥(实际应从安全芯片或安全存储获取)
private devicePreSharedKey: string = 'ELDERLY_CARE_2024_SECURE_KEY';
public static getInstance(): PasswordSecurityManager {
if (!PasswordSecurityManager.instance) {
PasswordSecurityManager.instance = new PasswordSecurityManager();
}
return PasswordSecurityManager.instance;
}
/**
* 加密WiFi密码(用于安全存储和传输)
*/
public async encryptPassword(password: string, deviceId: string): Promise<EncryptedPassword> {
try {
Logger.info('PasswordSecurityManager', `开始加密密码,设备ID: ${deviceId}`);
// 1. 生成随机的盐值
const salt = this.generateRandomBytes(PasswordSecurityManager.SALT_SIZE);
// 2. 从预共享密钥和设备ID派生加密密钥
const encryptionKey = await this.deriveEncryptionKey(
this.devicePreSharedKey,
deviceId,
salt
);
// 3. 加密密码
const encryptedData = await this.aesGcmEncrypt(password, encryptionKey, salt);
// 4. 计算完整性校验值
const integrityCheck = await this.calculateIntegrityCheck(password, deviceId);
const result: EncryptedPassword = {
encryptedData: encryptedData.cipherText,
iv: encryptedData.iv,
salt: salt,
integrityCheck,
deviceId,
timestamp: Date.now(),
algorithm: 'AES256-GCM-PKCS7',
version: '1.0'
};
Logger.info('PasswordSecurityManager', '密码加密成功');
return result;
} catch (error) {
const err: BusinessError = error as BusinessError;
Logger.error('PasswordSecurityManager', `密码加密失败: ${err.code}, ${err.message}`);
throw new Error(`密码加密失败: ${err.message}`);
}
}
/**
* 生成蓝牙传输的密码指令包
*/
public async generatePasswordPacket(
ssid: string,
password: string,
deviceId: string
): Promise<PasswordPacket> {
try {
// 1. 加密密码
const encryptedPassword = await this.encryptPassword(password, deviceId);
// 2. 构建指令数据结构
const packetData = this.buildPacketData(ssid, encryptedPassword);
// 3. 计算CRC校验
const crc32 = this.calculateCrc32(packetData);
// 4. 构建完整指令包
const packet: PasswordPacket = {
header: 0xAA55, // 包头
version: 0x01, // 协议版本
command: 0x10, // WiFi配置指令
deviceId,
ssid,
dataLength: packetData.length,
data: packetData,
crc32,
footer: 0x55AA // 包尾
};
// 5. 转换为Uint8Array用于蓝牙传输
const packetBytes = this.packetToUint8Array(packet);
return {
...packet,
rawBytes: packetBytes,
hexString: this.bytesToHex(packetBytes)
};
} catch (error) {
Logger.error('PasswordSecurityManager', `生成密码包失败: ${error.message}`);
throw error;
}
}
/**
* AES-GCM加密
*/
private async aesGcmEncrypt(
plainText: string,
key: Uint8Array,
salt: Uint8Array
): Promise<AesEncryptionResult> {
try {
// 创建对称密钥生成器
const symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
// 创建加密参数
const gcmParams = cryptoFramework.createGcmParamsSpec({
iv: salt.slice(0, 12), // GCM推荐12字节IV
aad: new Uint8Array(), // 附加认证数据
authTagLength: 128 // 认证标签长度(位)
});
// 生成对称密钥
const symKey = await symKeyGenerator.convertKey({ data: key });
// 创建加密器
const cipher = cryptoFramework.createCipher('AES256|GCM|PKCS7');
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
// 加密数据
const textEncoder = new util.TextEncoder();
const plainData = { data: textEncoder.encodeInto(plainText) };
const encryptedData = await cipher.doFinal(plainData);
return {
cipherText: Array.from(new Uint8Array(encryptedData.data)),
iv: Array.from(salt.slice(0, 12))
};
} catch (error) {
const err: BusinessError = error as BusinessError;
throw new Error(`AES加密失败: ${err.message}`);
}
}
/**
* 派生加密密钥(使用PBKDF2)
*/
private async deriveEncryptionKey(
preSharedKey: string,
deviceId: string,
salt: Uint8Array
): Promise<Uint8Array> {
try {
// 组合密钥材料
const keyMaterial = `${preSharedKey}:${deviceId}:${Date.now()}`;
const textEncoder = new util.TextEncoder();
const materialBytes = textEncoder.encodeInto(keyMaterial);
// 创建PBKDF2参数
const pbkdf2Params: cryptoFramework.PBKDF2ParamsSpec = {
algName: 'SHA256',
password: { data: materialBytes },
salt: { data: salt },
iterations: PasswordSecurityManager.ITERATION_COUNT,
keySize: PasswordSecurityManager.AES_KEY_SIZE / 8 // 字节数
};
// 派生密钥
const pbkdf2 = cryptoFramework.createPBKDF2();
const derivedKey = await pbkdf2.generateKey(pbkdf2Params);
return new Uint8Array(derivedKey.data);
} catch (error) {
const err: BusinessError = error as BusinessError;
throw new Error(`密钥派生失败: ${err.message}`);
}
}
/**
* 计算完整性校验值
*/
private async calculateIntegrityCheck(password: string, deviceId: string): Promise<string> {
try {
const textEncoder = new util.TextEncoder();
const data = textEncoder.encodeInto(`${password}:${deviceId}:${Date.now()}`);
const sha256 = cryptoFramework.createHash('SHA256');
await sha256.update({ data });
const hash = await sha256.digest();
// 取前8字节作为校验值
const hashBytes = new Uint8Array(hash.data);
const checkBytes = hashBytes.slice(0, 8);
return this.bytesToHex(checkBytes);
} catch (error) {
const err: BusinessError = error as BusinessError;
throw new Error(`完整性校验计算失败: ${err.message}`);
}
}
/**
* 构建指令包数据
*/
private buildPacketData(ssid: string, encryptedPassword: EncryptedPassword): number[] {
const data: number[] = [];
// SSID长度 + SSID内容
const ssidBytes = new TextEncoder().encode(ssid);
data.push(ssidBytes.length);
data.push(...Array.from(ssidBytes));
// 加密数据长度 + 加密数据
data.push(encryptedPassword.encryptedData.length);
data.push(...encryptedPassword.encryptedData);
// IV长度 + IV
data.push(encryptedPassword.iv.length);
data.push(...encryptedPassword.iv);
// 盐值长度 + 盐值
data.push(encryptedPassword.salt.length);
data.push(...encryptedPassword.salt);
// 完整性校验(转换为字节)
const integrityBytes = this.hexToBytes(encryptedPassword.integrityCheck);
data.push(integrityBytes.length);
data.push(...Array.from(integrityBytes));
return data;
}
/**
* 计算CRC32校验
*/
private calculateCrc32(data: number[]): number {
// 简化的CRC32计算(实际应使用标准CRC32算法)
let crc = 0xFFFFFFFF;
for (const byte of data) {
crc ^= byte;
for (let i = 0; i < 8; i++) {
crc = (crc >>> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
}
}
return crc ^ 0xFFFFFFFF;
}
/**
* 将指令包转换为Uint8Array
*/
private packetToUint8Array(packet: PasswordPacket): Uint8Array {
const bufferSize = 2 + 1 + 1 + 32 + 2 + 32 + 2 + packet.dataLength + 4 + 2;
const buffer = new ArrayBuffer(bufferSize);
const view = new DataView(buffer);
let offset = 0;
// 包头 (2字节)
view.setUint16(offset, packet.header, true);
offset += 2;
// 版本 (1字节)
view.setUint8(offset, packet.version);
offset += 1;
// 指令 (1字节)
view.setUint8(offset, packet.command);
offset += 1;
// 设备ID (32字节,固定长度)
const deviceIdBytes = new TextEncoder().encode(packet.deviceId.padEnd(32, '\0'));
for (let i = 0; i < 32; i++) {
view.setUint8(offset + i, deviceIdBytes[i]);
}
offset += 32;
// SSID长度 (2字节)
const ssidBytes = new TextEncoder().encode(packet.ssid);
view.setUint16(offset, ssidBytes.length, true);
offset += 2;
// SSID内容
for (let i = 0; i < ssidBytes.length; i++) {
view.setUint8(offset + i, ssidBytes[i]);
}
offset += ssidBytes.length;
// 数据长度 (2字节)
view.setUint16(offset, packet.dataLength, true);
offset += 2;
// 数据内容
for (let i = 0; i < packet.data.length; i++) {
view.setUint8(offset + i, packet.data[i]);
}
offset += packet.data.length;
// CRC32校验 (4字节)
view.setUint32(offset, packet.crc32, true);
offset += 4;
// 包尾 (2字节)
view.setUint16(offset, packet.footer, true);
return new Uint8Array(buffer);
}
/**
* 生成随机字节
*/
private generateRandomBytes(size: number): Uint8Array {
const bytes = new Uint8Array(size);
for (let i = 0; i < size; i++) {
bytes[i] = Math.floor(Math.random() * 256);
}
return bytes;
}
/**
* 字节数组转十六进制字符串
*/
private bytesToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* 十六进制字符串转字节数组
*/
private hexToBytes(hex: string): Uint8Array {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
}
return bytes;
}
}
// 类型定义
export interface EncryptedPassword {
encryptedData: number[];
iv: number[];
salt: Uint8Array;
integrityCheck: string;
deviceId: string;
timestamp: number;
algorithm: string;
version: string;
}
export interface PasswordPacket {
header: number;
version: number;
command: number;
deviceId: string;
ssid: string;
dataLength: number;
data: number[];
crc32: number;
footer: number;
rawBytes?: Uint8Array;
hexString?: string;
}
export interface AesEncryptionResult {
cipherText: number[];
iv: number[];
}
3.2 密码下发执行器
// features/device/src/main/ets/wifi/PasswordDeliveryExecutor.ets
import { ble, connection } from '@ohos.bluetooth';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';
import { PasswordSecurityManager, PasswordPacket } from './PasswordSecurityManager';
import { AccessControlManager } from '../bluetooth/AccessControlManager';
/**
* 密码下发执行器
* 负责通过蓝牙安全下发WiFi密码到门禁设备
*/
export class PasswordDeliveryExecutor {
private static instance: PasswordDeliveryExecutor;
private securityManager: PasswordSecurityManager;
private accessControlManager: AccessControlManager;
private isDelivering: boolean = false;
private currentDeliveryId: string | null = null;
// 蓝牙服务UUID(与门禁设备约定)
private static readonly SERVICE_UUID: string = '0000AE30-0000-1000-8000-00805F9B34FB';
// 写入特征值UUID
private static readonly WRITE_CHAR_UUID: string = '0000AE10-0000-1000-8000-00805F9B34FB';
// 通知特征值UUID(用于接收设备响应)
private static readonly NOTIFY_CHAR_UUID: string = '0000AE04-0000-1000-8000-00805F9B34FB';
// 下发状态常量
private static readonly DELIVERY_STATUS = {
PENDING: 'pending',
SENDING: 'sending',
DEVICE_ACK: 'device_ack',
CONNECTING_WIFI: 'connecting_wifi',
SUCCESS: 'success',
FAILED: 'failed',
TIMEOUT: 'timeout'
};
public static getInstance(): PasswordDeliveryExecutor {
if (!PasswordDeliveryExecutor.instance) {
PasswordDeliveryExecutor.instance = new PasswordDeliveryExecutor();
}
return PasswordDeliveryExecutor.instance;
}
private constructor() {
this.securityManager = PasswordSecurityManager.getInstance();
this.accessControlManager = AccessControlManager.getInstance();
}
/**
* 执行密码下发流程
*/
public async executeDelivery(
ssid: string,
password: string,
deviceId: string,
onProgress?: (progress: DeliveryProgress) => void
): Promise<DeliveryResult> {
if (this.isDelivering) {
return {
success: false,
message: '已有下发任务在进行中',
status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED
};
}
this.isDelivering = true;
this.currentDeliveryId = `delivery_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const startTime = Date.now();
const timeoutDuration = 60000; // 60秒超时
try {
Logger.info('PasswordDeliveryExecutor', `开始密码下发,设备: ${deviceId}, SSID: ${ssid}`);
// 1. 更新进度:准备中
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.PENDING,
progress: 10,
message: '准备下发数据...'
});
// 2. 生成加密的密码包
const passwordPacket = await this.securityManager.generatePasswordPacket(ssid, password, deviceId);
// 3. 更新进度:连接设备
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.PENDING,
progress: 20,
message: '连接门禁设备...'
});
// 4. 确保设备已连接
const isConnected = await this.ensureDeviceConnected(deviceId);
if (!isConnected) {
throw new Error('设备连接失败');
}
// 5. 更新进度:下发数据
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.SENDING,
progress: 40,
message: '下发WiFi配置...'
});
// 6. 分块发送密码包(避免超过MTU限制)
const sendResult = await this.sendPasswordPacket(deviceId, passwordPacket, onProgress);
if (!sendResult.success) {
throw new Error(`密码包发送失败: ${sendResult.message}`);
}
// 7. 等待设备响应
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.DEVICE_ACK,
progress: 70,
message: '等待设备确认...'
});
const deviceResponse = await this.waitForDeviceResponse(deviceId, 10000); // 10秒等待
if (!deviceResponse.success) {
throw new Error(`设备响应超时或失败: ${deviceResponse.message}`);
}
// 8. 更新进度:设备连接WiFi
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.CONNECTING_WIFI,
progress: 85,
message: '设备正在连接WiFi...'
});
// 9. 等待设备WiFi连接成功(通过通知监听)
const wifiResult = await this.waitForWifiConnection(deviceId, 30000); // 30秒等待
if (!wifiResult.success) {
throw new Error(`设备WiFi连接失败: ${wifiResult.message}`);
}
// 10. 更新进度:完成
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.SUCCESS,
progress: 100,
message: 'WiFi配置成功!',
details: {
ssid,
deviceIp: wifiResult.ipAddress,
connectionTime: Date.now() - startTime
}
});
Logger.info('PasswordDeliveryExecutor', `密码下发成功,总耗时: ${Date.now() - startTime}ms`);
return {
success: true,
message: 'WiFi密码下发成功',
status: PasswordDeliveryExecutor.DELIVERY_STATUS.SUCCESS,
deliveryId: this.currentDeliveryId,
timestamp: Date.now(),
details: {
ssid,
deviceIp: wifiResult.ipAddress,
duration: Date.now() - startTime
}
};
} catch (error) {
Logger.error('PasswordDeliveryExecutor', `密码下发失败: ${error.message}`);
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED,
progress: 0,
message: `下发失败: ${error.message}`
});
return {
success: false,
message: error.message,
status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED,
deliveryId: this.currentDeliveryId,
timestamp: Date.now()
};
} finally {
this.isDelivering = false;
this.currentDeliveryId = null;
}
}
/**
* 确保设备已连接
*/
private async ensureDeviceConnected(deviceId: string): Promise<boolean> {
try {
// 检查是否已连接
const devices = await connection.getConnectedDevices();
const isConnected = devices.some(device => device.deviceId === deviceId);
if (isConnected) {
return true;
}
// 重新连接
Logger.info('PasswordDeliveryExecutor', `重新连接设备: ${deviceId}`);
return await this.accessControlManager.connectDevice(deviceId);
} catch (error) {
Logger.error('PasswordDeliveryExecutor', `设备连接检查失败: ${error.message}`);
return false;
}
}
/**
* 分块发送密码包
*/
private async sendPasswordPacket(
deviceId: string,
packet: PasswordPacket,
onProgress?: (progress: DeliveryProgress) => void
): Promise<SendResult> {
try {
if (!packet.rawBytes) {
throw new Error('密码包数据为空');
}
const mtu = await this.getBleMtu(deviceId);
const chunkSize = Math.min(mtu - 3, 20); // 预留3字节ATT头,最大20字节
const totalChunks = Math.ceil(packet.rawBytes.length / chunkSize);
Logger.debug('PasswordDeliveryExecutor', `MTU: ${mtu}, 分块大小: ${chunkSize}, 总块数: ${totalChunks}`);
// 发送起始标记
await this.sendPacketStartMarker(deviceId);
// 分块发送
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, packet.rawBytes.length);
const chunk = packet.rawBytes.slice(start, end);
// 添加块序号和总块数
const chunkWithHeader = new Uint8Array(chunk.length + 2);
chunkWithHeader[0] = i; // 块序号
chunkWithHeader[1] = totalChunks; // 总块数
chunkWithHeader.set(chunk, 2);
await this.writeCharacteristic(deviceId, chunkWithHeader);
// 更新进度
const chunkProgress = 40 + Math.floor((i + 1) / totalChunks * 30); // 40% ~ 70%
this.updateProgress(onProgress, {
status: PasswordDeliveryExecutor.DELIVERY_STATUS.SENDING,
progress: chunkProgress,
message: `下发配置中... (${i + 1}/${totalChunks})`
});
// 小块延迟,避免发送过快
if (i < totalChunks - 1) {
await this.delay(50);
}
}
// 发送结束标记
await this.sendPacketEndMarker(deviceId);
return {
success: true,
message: '密码包发送完成',
chunksSent: totalChunks
};
} catch (error) {
Logger.error('PasswordDeliveryExecutor', `密码包发送失败: ${error.message}`);
return {
success: false,
message: `发送失败: ${error.message}`
};
}
}
/**
* 发送数据包起始标记
*/
private async sendPacketStartMarker(deviceId: string): Promise<void> {
const startMarker = new Uint8Array([0xAA, 0x01]); // 起始标记
await this.writeCharacteristic(deviceId, startMarker);
await this.delay(100);
}
/**
* 发送数据包结束标记
*/
private async sendPacketEndMarker(deviceId: string): Promise<void> {
const endMarker = new Uint8Array([0x55, 0x01]); // 结束标记
await this.writeCharacteristic(deviceId, endMarker);
await this.delay(100);
}
/**
* 写入特征值
*/
private async writeCharacteristic(deviceId: string, data: Uint8Array): Promise<void> {
try {
await ble.writeBLECharacteristicValue(deviceId, {
serviceUuid: PasswordDeliveryExecutor.SERVICE_UUID,
characteristicUuid: PasswordDeliveryExecutor.WRITE_CHAR_UUID,
value: data.buffer
});
Logger.debug('PasswordDeliveryExecutor', `写入特征值成功,长度: ${data.length}`);
} catch (error) {
const err: BusinessError = error as BusinessError;
throw new Error(`写入特征值失败: ${err.code}, ${err.message}`);
}
}
/**
* 获取BLE MTU
*/
private async getBleMtu(deviceId: string): Promise<number> {
try {
const mtu = await ble.getBLEMtu(deviceId);
return mtu > 0 ? mtu : 23; // 默认23字节
} catch (error) {
Logger.warn('PasswordDeliveryExecutor', `获取MTU失败,使用默认值: ${error.message}`);
return 23;
}
}
/**
* 等待设备响应
*/
private waitForDeviceResponse(deviceId: string, timeout: number): Promise<DeviceResponse> {
return new Promise((resolve) => {
let responded = false;
const startTime = Date.now();
// 监听设备响应
const responseHandler = (data: any) => {
if (data.deviceId === deviceId && data.type === 'password_ack') {
responded = true;
emitter.off(responseHandler);
resolve({
success: true,
message: '设备确认收到密码',
ackCode: data.ackCode
});
}
};
emitter.on(EventConstants.DEVICE_RESPONSE, responseHandler);
// 超时检查
const checkTimeout = () => {
if (!responded && (Date.now() - startTime) > timeout) {
emitter.off(responseHandler);
resolve({
success: false,
message: '设备响应超时'
});
} else if (!responded) {
setTimeout(checkTimeout, 100);
}
};
checkTimeout();
});
}
/**
* 等待设备WiFi连接
*/
private waitForWifiConnection(deviceId: string, timeout: number): Promise<WifiConnectionResult> {
return new Promise((resolve) => {
let connected = false;
const startTime = Date.now();
// 监听WiFi连接状态
const connectionHandler = (data: any) => {
if (data.deviceId === deviceId && data.status === 'wifi_connected') {
connected = true;
emitter.off(connectionHandler);
resolve({
success: true,
message: '设备WiFi连接成功',
ipAddress: data.ipAddress,
signalStrength: data.signalStrength
});
}
};
emitter.on(EventConstants.WIFI_CONNECTION_STATUS, connectionHandler);
// 超时检查
const checkTimeout = () => {
if (!connected && (Date.now() - startTime) > timeout) {
emitter.off(connectionHandler);
resolve({
success: false,
message: '设备WiFi连接超时'
});
} else if (!connected) {
setTimeout(checkTimeout, 100);
}
};
checkTimeout();
});
}
/**
* 更新下发进度
*/
private updateProgress(
onProgress: ((progress: DeliveryProgress) => void) | undefined,
progress: DeliveryProgress
): void {
if (onProgress) {
onProgress(progress);
}
// 同时触发全局事件
emitter.emit({
eventId: EventConstants.PASSWORD_DELIVERY_PROGRESS
}, { progress });
}
/**
* 延迟函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 取消当前下发任务
*/
public cancelDelivery(): void {
if (this.isDelivering) {
this.isDelivering = false;
Logger.info('PasswordDeliveryExecutor', '密码下发任务已取消');
}
}
}
// 类型定义
export interface DeliveryProgress {
status: string;
progress: number; // 0-100
message: string;
details?: Record<string, any>;
}
export interface DeliveryResult {
success: boolean;
message: string;
status: string;
deliveryId?: string;
timestamp: number;
details?: Record<string, any>;
}
export interface SendResult {
success: boolean;
message: string;
chunksSent?: number;
}
export interface DeviceResponse {
success: boolean;
message: string;
ackCode?: number;
}
export interface WifiConnectionResult {
success: boolean;
message: string;
ipAddress?: string;
signalStrength?: number;
}
4. 完整配网流程整合
4.1 配网流程状态机
4.2 流程执行控制器
// features/device/src/main/ets/wifi/NetworkingFlowController.ets
import { WifiStatusChecker } from './WifiStatusChecker';
import { PasswordDeliveryExecutor } from './PasswordDeliveryExecutor';
import { Logger } from '@elderly/basic';
import { emitter } from '@ohos.events.emitter';
/**
* 配网流程控制器
* 协调WiFi检查、密码下发、状态监控等各个模块
*/
export class NetworkingFlowController {
private static instance: NetworkingFlowController;
private wifiChecker: WifiStatusChecker;
private passwordExecutor: PasswordDeliveryExecutor;
private currentState: NetworkingState = NetworkingState.IDLE;
private currentDeviceId: string | null = null;
private retryCount: number = 0;
private maxRetries: number = 3;
// 配网状态定义
private static readonly NETWORKING_STATE = {
IDLE: 'idle',
CHECKING_WIFI: 'checking_wifi',
SELECTING_NETWORK: 'selecting_network',
ENTERING_PASSWORD: 'entering_password',
CONNECTING_DEVICE: 'connecting_device',
DELIVERING_PASSWORD: 'delivering_password',
WAITING_RESPONSE: 'waiting_response',
CONNECTING_WIFI: 'connecting_wifi',
SUCCESS: 'success',
FAILED: 'failed',
CANCELLED: 'cancelled'
};
public static getInstance(): NetworkingFlowController {
if (!NetworkingFlowController.instance) {
NetworkingFlowController.instance = new NetworkingFlowController();
}
return NetworkingFlowController.instance;
}
private constructor() {
this.wifiChecker = WifiStatusChecker.getInstance();
this.passwordExecutor = PasswordDeliveryExecutor.getInstance();
}
/**
* 启动完整配网流程
*/
public async startNetworkingFlow(
deviceId: string,
onStateChange?: (state: NetworkingFlowState) => void
): Promise<NetworkingResult> {
if (this.currentState !== NetworkingState.IDLE) {
return {
success: false,
message: '已有配网流程在进行中',
state: this.currentState
};
}
this.currentState = NetworkingState.CHECKING_WIFI;
this.currentDeviceId = deviceId;
this.retryCount = 0;
try {
Logger.info('NetworkingFlowController', `启动配网流程,设备: ${deviceId}`);
// 1. WiFi状态检查
await this.executeWifiCheck(onStateChange);
// 2. 网络选择(由UI层处理,这里等待用户输入)
this.updateState(NetworkingState.SELECTING_NETWORK, onStateChange);
// 流程在UI层继续,控制器提供执行方法
return {
success: true,
message: '配网流程已启动',
state: this.currentState,
nextStep: 'select_network'
};
} catch (error) {
Logger.error('NetworkingFlowController', `配网流程启动失败: ${error.message}`);
this.currentState = NetworkingState.FAILED;
this.updateState(this.currentState, onStateChange, {
error: error.message
});
return {
success: false,
message: `启动失败: ${error.message}`,
state: this.currentState
};
}
}
/**
* 执行WiFi检查步骤
*/
private async executeWifiCheck(onStateChange?: (state: NetworkingFlowState) => void): Promise<void> {
this.updateState(NetworkingState.CHECKING_WIFI, onStateChange, {
message: '正在检查WiFi状态...'
});
try {
// 执行WiFi检查
const wifiStatus = await this.wifiChecker.checkBasicStatus();
// 检查结果处理
if (!wifiStatus.isEnabled) {
throw new Error('WiFi未开启,请先打开WiFi');
}
if (!wifiStatus.isConnected) {
throw new Error('手机未连接WiFi,请先连接网络');
}
if (wifiStatus.level < 2) {
throw new Error(`WiFi信号较弱 (${wifiStatus.level}/4),请靠近路由器`);
}
// WiFi状态良好,可以继续
this.updateState(NetworkingState.CHECKING_WIFI, onStateChange, {
message: `WiFi状态良好: ${wifiStatus.ssid} (${wifiStatus.level}/4)`,
wifiStatus
});
} catch (error) {
Logger.warn('NetworkingFlowController', `WiFi检查失败: ${error.message}`);
throw error;
}
}
/**
* 执行密码下发步骤
*/
public async executePasswordDelivery(
ssid: string,
password: string,
onStateChange?: (state: NetworkingFlowState) => void
): Promise<NetworkingResult> {
if (!this.currentDeviceId) {
return {
success: false,
message: '未指定设备ID',
state: this.currentState
};
}
try {
// 1. 连接设备
this.updateState(NetworkingState.CONNECTING_DEVICE, onStateChange, {
message: '连接门禁设备...'
});
// 2. 执行密码下发
this.currentState = NetworkingState.DELIVERING_PASSWORD;
const result = await this.passwordExecutor.executeDelivery(
ssid,
password,
this.currentDeviceId,
(progress) => {
// 根据下发进度更新状态
let state = this.currentState;
let message = progress.message;
if (progress.status === 'device_ack') {
state = NetworkingState.WAITING_RESPONSE;
} else if (progress.status === 'connecting_wifi') {
state = NetworkingState.CONNECTING_WIFI;
} else if (progress.status === 'success') {
state = NetworkingState.SUCCESS;
}
this.updateState(state, onStateChange, {
message,
progress: progress.progress,
details: progress.details
});
}
);
// 3. 处理结果
if (result.success) {
this.currentState = NetworkingState.SUCCESS;
this.updateState(this.currentState, onStateChange, {
message: '配网成功!',
details: result.details
});
} else {
this.currentState = NetworkingState.FAILED;
this.updateState(this.currentState, onStateChange, {
message: result.message,
error: result.message
});
}
return {
success: result.success,
message: result.message,
state: this.currentState,
details: result.details
};
} catch (error) {
Logger.error('NetworkingFlowController', `密码下发失败: ${error.message}`);
this.currentState = NetworkingState.FAILED;
this.updateState(this.currentState, onStateChange, {
message: `密码下发失败: ${error.message}`,
error: error.message
});
return {
success: false,
message: error.message,
state: this.currentState
};
}
}
/**
* 重试当前步骤
*/
public async retryCurrentStep(onStateChange?: (state: NetworkingFlowState) => void): Promise<NetworkingResult> {
if (this.retryCount >= this.maxRetries) {
this.currentState = NetworkingState.FAILED;
this.updateState(this.currentState, onStateChange, {
message: '已达到最大重试次数'
});
return {
success: false,
message: '已达到最大重试次数',
state: this.currentState
};
}
this.retryCount++;
Logger.info('NetworkingFlowController', `第${this.retryCount}次重试`);
// 根据当前状态决定重试策略
switch (this.currentState) {
case NetworkingState.CHECKING_WIFI:
return this.retryWifiCheck(onStateChange);
case NetworkingState.CONNECTING_DEVICE:
case NetworkingState.DELIVERING_PASSWORD:
case NetworkingState.WAITING_RESPONSE:
// 重置到设备连接步骤
this.currentState = NetworkingState.CONNECTING_DEVICE;
return {
success: true,
message: '已重置到设备连接步骤',
state: this.currentState,
nextStep: 'reconnect_device'
};
default:
return {
success: false,
message: `当前状态不支持重试: ${this.currentState}`,
state: this.currentState
};
}
}
/**
* 重试WiFi检查
*/
private async retryWifiCheck(onStateChange?: (state: NetworkingFlowState) => void): Promise<NetworkingResult> {
try {
await this.executeWifiCheck(onStateChange);
return {
success: true,
message: 'WiFi检查重试成功',
state: this.currentState
};
} catch (error) {
return {
success: false,
message: `WiFi检查重试失败: ${error.message}`,
state: this.currentState
};
}
}
/**
* 取消配网流程
*/
public cancelNetworkingFlow(): void {
if (this.currentState !== NetworkingState.IDLE &&
this.currentState !== NetworkingState.SUCCESS &&
this.currentState !== NetworkingState.FAILED) {
Logger.info('NetworkingFlowController', '取消配网流程');
// 取消密码下发
this.passwordExecutor.cancelDelivery();
// 更新状态
this.currentState = NetworkingState.CANCELLED;
this.currentDeviceId = null;
this.retryCount = 0;
}
}
/**
* 重置配网流程
*/
public reset(): void {
this.currentState = NetworkingState.IDLE;
this.currentDeviceId = null;
this.retryCount = 0;
Logger.info('NetworkingFlowController', '配网流程已重置');
}
/**
* 更新状态并通知
*/
private updateState(
state: NetworkingState,
onStateChange?: (state: NetworkingFlowState) => void,
data?: Record<string, any>
): void {
this.currentState = state;
const flowState: NetworkingFlowState = {
state,
deviceId: this.currentDeviceId,
retryCount: this.retryCount,
timestamp: Date.now(),
...data
};
// 回调通知
if (onStateChange) {
onStateChange(flowState);
}
// 全局事件通知
emitter.emit({
eventId: EventConstants.NETWORKING_FLOW_STATE
}, { state: flowState });
Logger.debug('NetworkingFlowController', `状态更新: ${state}`, data);
}
/**
* 获取当前状态
*/
public getCurrentState(): NetworkingFlowState {
return {
state: this.currentState,
deviceId: this.currentDeviceId,
retryCount: this.retryCount,
timestamp: Date.now()
};
}
}
// 类型定义
type NetworkingState =
| 'idle'
| 'checking_wifi'
| 'selecting_network'
| 'entering_password'
| 'connecting_device'
| 'delivering_password'
| 'waiting_response'
| 'connecting_wifi'
| 'success'
| 'failed'
| 'cancelled';
export interface NetworkingFlowState {
state: NetworkingState;
deviceId?: string | null;
retryCount: number;
timestamp: number;
message?: string;
progress?: number;
details?: Record<string, any>;
error?: string;
}
export interface NetworkingResult {
success: boolean;
message: string;
state: NetworkingState;
nextStep?: string;
details?: Record<string, any>;
}
5. 总结与最佳实践
5.1 实现要点总结
- 权限管理:严格遵守鸿蒙权限规范,动态申请必要的WiFi、蓝牙、位置权限
- 安全设计:密码加密传输、完整性校验、防重放攻击
- 错误处理:完善的异常捕获、重试机制、用户友好的错误提示
- 用户体验:清晰的进度反馈、状态可视化、操作引导
- 性能优化:异步操作、分块传输、资源及时释放
5.2 养老场景特别优化
- 简化操作:一键式配网,减少护理员操作步骤
- 容错设计:适应养老院复杂网络环境,自动重试和降级
- 安全加固:密码本地加密,避免明文传输
- 状态持久化:配网进度保存,支持断点续传
5.3 扩展建议
- 批量配网:支持同时配置多个门禁设备
- 网络切换:设备自动切换最优WiFi网络
- 远程协助:支持管理员远程协助配网
- 数据统计:配网成功率、时长等数据收集分析
通过本文的实现方案,益康养老HarmonyOS APP能够提供稳定、安全、易用的门禁设备WiFi配网功能,显著提升养老机构的物联网设备管理效率。
更多推荐



所有评论(0)