HarmonyOS开发:智能家居实战——完整智能家居App

举报
Jack20 发表于 2026/06/26 16:00:56 2026/06/26
【摘要】 HarmonyOS开发:智能家居实战——完整智能家居App📌 核心要点:从架构到实现的完整智能家居App开发实战,整合设备发现、控制、场景、定时、语音、家庭共享、设备分享全部功能,一个项目串起所有知识点。 背景与动机前面9篇文章,我们讲了智能家居App的每一个模块——架构设计、设备发现、设备控制、场景联动、设备分组、定时任务、语音控制、家庭共享、设备分享。每个模块都能独立工作,但它们不是...

HarmonyOS开发:智能家居实战——完整智能家居App

📌 核心要点:从架构到实现的完整智能家居App开发实战,整合设备发现、控制、场景、定时、语音、家庭共享、设备分享全部功能,一个项目串起所有知识点。

背景与动机

前面9篇文章,我们讲了智能家居App的每一个模块——架构设计、设备发现、设备控制、场景联动、设备分组、定时任务、语音控制、家庭共享、设备分享。

每个模块都能独立工作,但它们不是孤立的。一个完整的智能家居App,需要把这些模块串起来,让它们协同工作。用户从打开App到控制设备,中间经过的所有流程——发现设备、配网、控制、设场景、加定时、语音操作、分享给家人——都必须顺畅无阻。

这篇文章,我们把所有模块整合到一个完整的智能家居App中。不是简单的代码堆砌,而是从架构出发,讲清楚模块之间的依赖关系、数据流向、状态管理。

核心原理

整体架构

flowchart TD
    subgraph UI["展示层 UI Layer"]
        A1[首页 - 设备概览]
        A2[设备详情页]
        A3[场景管理页]
        A4[定时任务页]
        A5[语音控制页]
        A6[家庭管理页]
        A7[设备分享页]
        A8[设备添加页]
    end
    
    subgraph VM["ViewModel层"]
        B1[SmartHomeViewModel]
        B2[DeviceControlVM]
        B3[SceneEngineVM]
        B4[TimerTaskVM]
        B5[VoiceControlVM]
        B6[FamilyManageVM]
    end
    
    subgraph Service["服务层 Service Layer"]
        C1[DeviceDiscoveryService]
        C2[MqttService]
        C3[DeviceControlManager]
        C4[SceneEngine]
        C5[TimerTaskEngine]
        C6[VoiceRecognizer]
        C7[VoiceCommandParser]
        C8[FamilyService]
        C9[DeviceShareService]
        C10[DeviceGroupService]
        C11[BackgroundTaskKeeper]
    end
    
    subgraph Data["数据层 Data Layer"]
        D1[关系型数据库 RDB]
        D2[分布式数据 KVStore]
        D3[云端API]
    end
    
    A1 --> B1
    A2 --> B2
    A3 --> B3
    A4 --> B4
    A5 --> B5
    A6 --> B6
    
    B1 --> C3
    B1 --> C10
    B2 --> C3
    B3 --> C4
    B4 --> C5
    B5 --> C6
    B5 --> C7
    B6 --> C8
    
    C3 --> C2
    C4 --> C3
    C5 --> C3
    C7 --> C3
    C5 --> C11
    
    C1 --> D3
    C8 --> D3
    C9 --> D3
    C10 --> D1
    C5 --> D1
    C3 --> D2
    C8 --> D2
    
    classDef ui fill:#1565C0,color:#fff,stroke:#0D47A1
    classDef vm fill:#2E7D32,color:#fff,stroke:#1B5E20
    classDef service fill:#E65100,color:#fff,stroke:#BF360C
    classDef data fill:#6A1B9A,color:#fff,stroke:#4A148C

    class A1,A2,A3,A4,A5,A6,A7,A8,ui
    class B1,B2,B3,B4,B5,B6,vm
    class C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,service
    class D1,D2,D3,data

模块依赖关系

模块 依赖 被依赖
DeviceDiscoveryService BLE/WiFi API
MqttService MQTT Broker DeviceControlManager
DeviceControlManager MqttService SceneEngine, TimerTaskEngine, VoiceCommandParser
SceneEngine DeviceControlManager
TimerTaskEngine DeviceControlManager, BackgroundTaskKeeper
VoiceRecognizer SpeechRecognizer API VoiceCommandParser
VoiceCommandParser VoiceRecognizer, DeviceControlManager
FamilyService 云端API FamilyDataManager
DeviceShareService 云端API
DeviceGroupService RDB

数据流向

  1. 设备状态流:设备 → MQTT → DeviceControlManager → UI
  2. 控制指令流:UI → DeviceControlManager → MQTT → 设备
  3. 场景执行流:UI/定时器/事件 → SceneEngine → DeviceControlManager → MQTT → 设备
  4. 语音控制流:麦克风 → VoiceRecognizer → VoiceCommandParser → DeviceControlManager → MQTT → 设备
  5. 分布式同步流:DeviceControlManager → KVStore → 其他端

代码实战

基础用法:App入口与全局初始化

App启动时初始化所有服务,建立MQTT连接,注册场景引擎。

// entryability/EntryAbility.ets
// App入口 - 全局初始化

import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit'
import { window } from '@kit.ArkUI'
import MqttService from '../services/MqttService'
import DeviceControlManager from '../services/DeviceControlManager'
import SceneEngine from '../services/SceneEngine'
import TimerTaskEngine from '../services/TimerTaskEngine'
import BackgroundTaskKeeper from '../services/BackgroundTaskKeeper'
import DeviceGroupService from '../services/DeviceGroupService'
import FamilyService from '../services/FamilyService'

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    console.info('[SmartHome] App onCreate')
  }

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    // 设置主窗口
    windowStage.loadContent('pages/MainPage', (err) => {
      if (err.code) {
        console.error(`加载主页面失败: ${JSON.stringify(err)}`)
        return
      }
    })

    // 初始化所有服务
    await this.initServices()
  }

  // 全局服务初始化
  private async initServices(): Promise<void> {
    try {
      // 1. 初始化数据库
      await DeviceGroupService.init(this.context)
      await TimerTaskEngine.startScheduler()

      // 2. 初始化家庭
      const family = FamilyService.getCurrentFamily()
      if (!family) {
        // 首次使用,创建默认家庭
        await FamilyService.createFamily('我的家')
      }

      // 3. 连接MQTT
      await MqttService.connect('ssl://mqtt.example.com:8883', 'app_client', {
        onConnected: async () => {
          console.info('[SmartHome] MQTT已连接')

          // 订阅所有设备状态
          await MqttService.subscribe('device/status/+')

          // 注册场景引擎的动作执行器
          SceneEngine.setActionExecutor(async (deviceId, property, value) => {
            DeviceControlManager.controlDevice(deviceId, property, value)
            return true
          })

          // 注册定时任务的动作执行器
          TimerTaskEngine.setActionExecutor(async (deviceId, property, value) => {
            DeviceControlManager.controlDevice(deviceId, property, value)
            return true
          })
        },
        onMessage: (topic: string, payload: string) => {
          // 分发MQTT消息到设备控制管理器
          DeviceControlManager.handleMqttMessage(topic, payload)

          // 分发到场景引擎(事件触发)
          const data = JSON.parse(payload)
          if (data.type === 'state' && data.deviceId && data.property) {
            SceneEngine.onDeviceStateChange(data.deviceId, data.property, data.value)
          }
        },
        onDisconnected: () => {
          console.warn('[SmartHome] MQTT断开连接')
        },
        onError: (error: Error) => {
          console.error(`[SmartHome] MQTT错误: ${error.message}`)
        }
      })

      // 4. 申请后台保活
      await BackgroundTaskKeeper.requestContinuousTask()

      // 5. 初始化分布式数据同步
      await DeviceControlManager.initDistributedData(this.context)

      console.info('[SmartHome] 所有服务初始化完成')
    } catch (err) {
      console.error(`[SmartHome] 服务初始化失败: ${JSON.stringify(err)}`)
    }
  }

  onForeground(): void {
    console.info('[SmartHome] App onForeground')
    // App回到前台,检查MQTT连接
    if (!MqttService.isConnected()) {
      MqttService.reconnect()
    }
  }

  onBackground(): void {
    console.info('[SmartHome] App onBackground')
    // App进后台,注册后台提醒保活定时任务
    const tasks = TimerTaskEngine.getAllTasks()
    BackgroundTaskKeeper.registerReminders(tasks)
  }

  onDestroy(): void {
    // 清理资源
    MqttService.disconnect()
    TimerTaskEngine.stopScheduler()
    BackgroundTaskKeeper.cancelContinuousTask()
  }
}

进阶用法:主页面与底部导航

主页面整合所有功能模块,底部Tab导航切换不同功能。

// pages/MainPage.ets
// 智能家居主页面 - 底部Tab导航

import DeviceControlManager from '../services/DeviceControlManager'
import SceneEngine, { SceneRule } from '../services/SceneEngine'
import DeviceGroupService, { DeviceGroup } from '../services/DeviceGroupService'

// 设备信息
interface SmartDevice {
  deviceId: string
  name: string
  typeId: string
  roomName: string
  isOnline: boolean
  state: Record<string, Object>
}

@Entry
@Component
struct MainPage {
  @State currentIndex: number = 0
  @State devices: SmartDevice[] = []
  @State scenes: SceneRule[] = []
  @State groups: DeviceGroup[] = []
  @State currentRoom: string = '全部'

  // 底部Tab配置
  private tabs = [
    { title: '首页', icon: $r('sys.media.ohos_ic_public_home') },
    { title: '场景', icon: $r('sys.media.ohos_ic_public_files') },
    { title: '语音', icon: $r('sys.media.ohos_ic_public_voice') },
    { title: '家庭', icon: $r('sys.media.ohos_ic_public_contacts') },
    { title: '我的', icon: $r('sys.media.ohos_ic_public_settings') }
  ]

  aboutToAppear() {
    this.loadData()
    this.registerStateListener()
  }

  // 加载数据
  async loadData() {
    // 加载设备列表
    this.devices = [
      {
        deviceId: 'light-001', name: '客厅主灯', typeId: 'light-dimmable',
        roomName: '客厅', isOnline: true, state: { power: true, brightness: 80 }
      },
      {
        deviceId: 'ac-001', name: '卧室空调', typeId: 'air-conditioner',
        roomName: '卧室', isOnline: true, state: { power: true, temperature: 26, mode: 'cool', fanSpeed: 'auto' }
      },
      {
        deviceId: 'curtain-001', name: '客厅窗帘', typeId: 'curtain',
        roomName: '客厅', isOnline: true, state: { position: 60 }
      },
      {
        deviceId: 'sensor-001', name: '温湿度传感器', typeId: 'sensor',
        roomName: '客厅', isOnline: true, state: { temperature: 25.5, humidity: 65 }
      }
    ]

    // 加载场景
    this.scenes = SceneEngine.getAllRules()

    // 加载分组
    this.groups = await DeviceGroupService.getAllGroups()
  }

  // 注册设备状态监听
  registerStateListener() {
    DeviceControlManager.onStateChange((deviceId: string, state: Record<string, Object>) => {
      const index = this.devices.findIndex(d => d.deviceId === deviceId)
      if (index >= 0) {
        this.devices[index] = { ...this.devices[index], state: { ...state } }
      }
    })
  }

  // 控制设备
  controlDevice(deviceId: string, property: string, value: Object) {
    DeviceControlManager.controlDevice(deviceId, property, value)
  }

  // 执行场景
  async executeScene(sceneId: string) {
    await SceneEngine.executeScene(sceneId, 'manual')
  }

  build() {
    Column() {
      // 内容区域
      Stack() {
        if (this.currentIndex === 0) {
          this.HomeTab()
        } else if (this.currentIndex === 1) {
          this.SceneTab()
        } else if (this.currentIndex === 2) {
          this.VoiceTab()
        } else if (this.currentIndex === 3) {
          this.FamilyTab()
        } else {
          this.ProfileTab()
        }
      }
      .width('100%')
      .layoutWeight(1)

      // 底部导航栏
      this.BottomNavBar()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F8F8')
  }

  // 底部导航栏
  @Builder BottomNavBar() {
    Row() {
      ForEach(this.tabs, (tab: { title: string, icon: Resource }, index: number) => {
        Column() {
          Image(tab.icon)
            .width(24)
            .height(24)
            .fillColor(this.currentIndex === index ? '#007DFF' : '#999999')

          Text(tab.title)
            .fontSize(10)
            .fontColor(this.currentIndex === index ? '#007DFF' : '#999999')
            .margin({ top: 2 })
        }
        .layoutWeight(1)
        .height(56)
        .justifyContent(FlexAlign.Center)
        .onClick(() => { this.currentIndex = index })
      })
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
    .border({ width: { top: 0.5 }, color: '#E0E0E0' })
  }

  // 首页Tab
  @Builder HomeTab() {
    Navigation() {
      Column() {
        // 顶部场景快捷入口
        Row() {
          ForEach(this.scenes.filter(s => s.enabled).slice(0, 4), (scene: SceneRule) => {
            Column() {
              Text(scene.name.charAt(0))
                .fontSize(18)
                .fontColor('#FFFFFF')
                .fontWeight(FontWeight.Bold)
            }
            .width(52)
            .height(52)
            .borderRadius(14)
            .backgroundColor('#007DFF')
            .justifyContent(FlexAlign.Center)
            .margin({ right: 12 })
            .onClick(() => this.executeScene(scene.sceneId))
          })

          // 添加设备按钮
          Column() {
            Text('+')
              .fontSize(24)
              .fontColor('#007DFF')
              .fontWeight(FontWeight.Bold)
          }
          .width(52)
          .height(52)
          .borderRadius(14)
          .backgroundColor('#F0F7FF')
          .justifyContent(FlexAlign.Center)
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 12 })

        // 房间筛选
        Scroll(Scroller) {
          Row() {
            ForEach(['全部', '客厅', '卧室', '厨房', '卫生间'], (room: string) => {
              Text(room)
                .fontSize(13)
                .fontColor(this.currentRoom === room ? '#FFFFFF' : '#333333')
                .backgroundColor(this.currentRoom === room ? '#007DFF' : '#F0F0F0')
                .borderRadius(16)
                .padding({ left: 14, right: 14, top: 6, bottom: 6 })
                .margin({ right: 8 })
                .onClick(() => { this.currentRoom = room })
            })
          }
        }
        .width('100%')
        .scrollable(ScrollDirection.Horizontal)
        .padding({ left: 16, right: 16, top: 12 })

        // 设备网格
        Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
          ForEach(
            this.devices.filter(d => this.currentRoom === '全部' || d.roomName === this.currentRoom),
            (device: SmartDevice) => {
              this.DeviceCard(device)
            }
          )
        }
        .width('100%')
        .padding({ left: 12, right: 12, top: 8 })
      }
      .width('100%')
      .height('100%')
    }
    .title('智能家居')
    .titleMode(NavigationTitleMode.Mini)
  }

  // 设备卡片
  @Builder DeviceCard(device: SmartDevice) {
    Column() {
      // 设备图标和开关
      Row() {
        Column() {
          Text(device.typeId === 'light-dimmable' ? '💡' :
               device.typeId === 'air-conditioner' ? '❄️' :
               device.typeId === 'curtain' ? '🪟' : '📡')
            .fontSize(24)
        }
        .width(40)
        .height(40)
        .borderRadius(10)
        .backgroundColor(device.isOnline ? '#F0F7FF' : '#F5F5F5')
        .justifyContent(FlexAlign.Center)

        Blank()

        // 在线状态
        Circle({ width: 6, height: 6 })
          .fill(device.isOnline ? '#4CAF50' : '#BDBDBD')
      }
      .width('100%')

      // 设备名称
      Text(device.name)
        .fontSize(14)
        .fontColor('#333333')
        .fontWeight(FontWeight.Medium)
        .margin({ top: 8 })

      // 设备状态
      Text(this.getDeviceStatusText(device))
        .fontSize(12)
        .fontColor('#999999')
        .margin({ top: 2 })

      // 快捷控制
      if (device.typeId === 'light-dimmable') {
        Row() {
          Toggle({ type: ToggleType.Switch, isOn: device.state['power'] as boolean || false })
            .selectedColor('#007DFF')
            .width(44)
            .height(24)
            .onChange((isOn: boolean) => {
              this.controlDevice(device.deviceId, 'power', isOn)
            })
        }
        .margin({ top: 8 })
      }
    }
    .width('47%')
    .padding(12)
    .backgroundColor('#FFFFFF')
    .borderRadius(14)
    .margin({ left: '1.5%', right: '1.5%', bottom: 10 })
    .shadow({ radius: 2, color: '#0A000000', offsetY: 1 })
    .onClick(() => {
      // 跳转到设备详情页
    })
  }

  // 获取设备状态文本
  private getDeviceStatusText(device: SmartDevice): string {
    if (!device.isOnline) return '离线'

    switch (device.typeId) {
      case 'light-dimmable':
        return device.state['power'] ? `亮度 ${device.state['brightness']}%` : '已关闭'
      case 'air-conditioner':
        return device.state['power'] ? `${device.state['mode'] === 'cool' ? '制冷' : '制热'} ${device.state['temperature']}°C` : '已关闭'
      case 'curtain':
        return `开合 ${device.state['position']}%`
      case 'sensor':
        return `${device.state['temperature']}°C / ${device.state['humidity']}%`
      default:
        return '在线'
    }
  }

  // 场景Tab
  @Builder SceneTab() {
    Navigation() {
      Column() {
        List({ space: 12 }) {
          ForEach(this.scenes, (scene: SceneRule) => {
            ListItem() {
              Row() {
                Column() {
                  Text(scene.name)
                    .fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
                  Text(scene.conditions.map(c =>
                    c.type === 'timer' ? `定时: ${c.cronExpression}` : '事件触发'
                  ).join(' 且 '))
                    .fontSize(12).fontColor('#999999').margin({ top: 4 })
                }
                .alignItems(HorizontalAlign.Start)
                .layoutWeight(1)

                Toggle({ type: ToggleType.Switch, isOn: scene.enabled })
                  .selectedColor('#007DFF')
                  .onChange((isOn: boolean) => {
                    if (isOn) SceneEngine.enableRule(scene.sceneId)
                    else SceneEngine.disableRule(scene.sceneId)
                  })

                Button('执行')
                  .height(28).fontSize(12)
                  .fontColor('#007DFF').backgroundColor('#F0F7FF').borderRadius(14)
                  .margin({ left: 8 })
                  .onClick(() => this.executeScene(scene.sceneId))
              }
              .width('100%').padding(16)
              .backgroundColor('#FFFFFF').borderRadius(12)
            }
          })
        }
        .width('100%').layoutWeight(1)
        .padding({ left: 16, right: 16, top: 12 })
      }
      .width('100%').height('100%')
    }
    .title('场景联动')
    .titleMode(NavigationTitleMode.Mini)
  }

  // 语音Tab(简化)
  @Builder VoiceTab() {
    Navigation() {
      Column() {
        Column() {
          Circle({ width: 100, height: 100 })
            .fill('#F0F7FF')
          Image($r('sys.media.ohos_ic_public_voice'))
            .width(40).height(40).fillColor('#007DFF')
            .margin({ top: -70 })
        }
        .margin({ top: 80 })

        Text('点击开始语音控制')
          .fontSize(16).fontColor('#999999').margin({ top: 24 })

        Column() {
          Text('试试说:').fontSize(14).fontColor('#999999').margin({ bottom: 8 })
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(['打开客厅灯', '空调调到26度', '关闭窗帘'], (hint: string) => {
              Text(hint)
                .fontSize(13).fontColor('#007DFF')
                .backgroundColor('#F0F7FF').borderRadius(16)
                .padding({ left: 12, right: 12, top: 6, bottom: 6 })
                .margin({ right: 8, bottom: 8 })
            })
          }
        }
        .padding(16).margin({ top: 40 })
      }
      .width('100%').height('100%')
      .alignItems(HorizontalAlign.Center)
    }
    .title('语音控制')
    .titleMode(NavigationTitleMode.Mini)
  }

  // 家庭Tab(简化)
  @Builder FamilyTab() {
    Navigation() {
      Column() {
        Text('我的家')
          .fontSize(24).fontWeight(FontWeight.Bold).fontColor('#333333')
          .margin({ top: 24 })

        Text('3位成员 · 4个设备')
          .fontSize(14).fontColor('#999999').margin({ top: 8 })

        // 成员列表
        List({ space: 8 }) {
          ForEach([
            { name: '我', role: '管理员' },
            { name: '家人A', role: '成员' },
            { name: '家人B', role: '受限成员' }
          ], (member: { name: string, role: string }) => {
            ListItem() {
              Row() {
                Circle({ width: 36, height: 36 }).fill('#E0E0E0')
                Column() {
                  Text(member.name).fontSize(15).fontColor('#333333')
                  Text(member.role).fontSize(12).fontColor('#999999').margin({ top: 2 })
                }
                .alignItems(HorizontalAlign.Start).margin({ left: 12 })
              }
              .width('100%').padding(12)
              .backgroundColor('#FFFFFF').borderRadius(10)
            }
          })
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 24 })
        .layoutWeight(1)
      }
      .width('100%').height('100%')
      .alignItems(HorizontalAlign.Center)
    }
    .title('家庭')
    .titleMode(NavigationTitleMode.Mini)
  }

  // 我的Tab
  @Builder ProfileTab() {
    Navigation() {
      Column() {
        // 功能列表
        List({ space: 1 }) {
          ForEach([
            { title: '设备分组', icon: '📂' },
            { title: '定时任务', icon: '⏰' },
            { title: '设备分享', icon: '🔗' },
            { title: '消息通知', icon: '🔔' },
            { title: '关于', icon: 'ℹ️' }
          ], (item: { title: string, icon: string }) => {
            ListItem() {
              Row() {
                Text(item.icon).fontSize(20).margin({ right: 12 })
                Text(item.title).fontSize(15).fontColor('#333333')
                Blank()
                Image($r('sys.media.ohos_ic_public_arrow_right'))
                  .width(16).height(16).fillColor('#CCCCCC')
              }
              .width('100%').padding(16)
              .backgroundColor('#FFFFFF')
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
        .margin({ top: 16 })
      }
      .width('100%').height('100%')
    }
    .title('我的')
    .titleMode(NavigationTitleMode.Mini)
  }
}

完整示例:项目配置与模块整合

把所有服务整合到一起,确保模块间正确协作。

// services/SmartHomeOrchestrator.ets
// 智能家居编排器 - 统一管理所有服务的生命周期和协作

import MqttService from './MqttService'
import DeviceControlManager from './DeviceControlManager'
import SceneEngine from './SceneEngine'
import TimerTaskEngine from './TimerTaskEngine'
import VoiceRecognizer from './VoiceRecognizer'
import VoiceCommandParser from './VoiceCommandParser'
import DeviceGroupService from './DeviceGroupService'
import FamilyService from './FamilyService'
import DeviceShareService from './DeviceShareService'
import BackgroundTaskKeeper from './BackgroundTaskKeeper'
import { distributedData } from '@kit.ArkData'

// 编排器状态
type OrchestratorState = 'idle' | 'initializing' | 'ready' | 'error'

class SmartHomeOrchestrator {
  private state: OrchestratorState = 'idle'
  private stateListeners: Array<(state: OrchestratorState) => void> = []

  // 获取当前状态
  getState(): OrchestratorState {
    return this.state
  }

  // 注册状态监听
  onStateChange(listener: (state: OrchestratorState) => void): void {
    this.stateListeners.push(listener)
  }

  // 通知状态变更
  private notifyStateChange(state: OrchestratorState): void {
    this.state = state
    this.stateListeners.forEach(listener => listener(state))
  }

  // 初始化所有服务(按依赖顺序)
  async initialize(context: Context): Promise<void> {
    this.notifyStateChange('initializing')

    try {
      // 第一步:初始化数据层
      await DeviceGroupService.init(context)

      // 第二步:初始化家庭
      const family = FamilyService.getCurrentFamily()
      if (!family) {
        await FamilyService.createFamily('我的家')
      }

      // 第三步:初始化MQTT和设备控制
      await this.initMqttAndControl()

      // 第四步:初始化场景和定时
      this.initSceneAndTimer()

      // 第五步:初始化语音
      this.initVoice()

      // 第六步:初始化分布式数据
      await DeviceControlManager.initDistributedData(context)

      // 第七步:申请后台保活
      await BackgroundTaskKeeper.requestContinuousTask()

      this.notifyStateChange('ready')
      console.info('[Orchestrator] 所有服务初始化完成')

    } catch (err) {
      this.notifyStateChange('error')
      console.error(`[Orchestrator] 初始化失败: ${JSON.stringify(err)}`)
    }
  }

  // 初始化MQTT和设备控制
  private async initMqttAndControl(): Promise<void> {
    await MqttService.connect('ssl://mqtt.example.com:8883', 'app_client', {
      onConnected: async () => {
        await MqttService.subscribe('device/status/+')
      },
      onMessage: (topic: string, payload: string) => {
        // 分发到设备控制管理器
        DeviceControlManager.handleMqttMessage(topic, payload)

        // 分发到场景引擎
        try {
          const data = JSON.parse(payload)
          if (data.deviceId && data.property) {
            SceneEngine.onDeviceStateChange(data.deviceId, data.property, data.value)
          }
        } catch (e) {
          // 忽略解析失败
        }
      },
      onDisconnected: () => {
        console.warn('[Orchestrator] MQTT断开')
      },
      onError: (error: Error) => {
        console.error(`[Orchestrator] MQTT错误: ${error.message}`)
      }
    })
  }

  // 初始化场景和定时
  private initSceneAndTimer(): void {
    // 注入动作执行器
    SceneEngine.setActionExecutor(async (deviceId, property, value) => {
      DeviceControlManager.controlDevice(deviceId, property, value)
      return true
    })

    TimerTaskEngine.setActionExecutor(async (deviceId, property, value) => {
      DeviceControlManager.controlDevice(deviceId, property, value)
      return true
    })

    // 启动定时调度
    TimerTaskEngine.startScheduler()
  }

  // 初始化语音
  private initVoice(): void {
    VoiceRecognizer.init({
      onResult: (result) => {
        if (result.isFinal && result.text) {
          const deviceStates = new Map<string, Record<string, Object>>()
          // 获取所有设备状态
          const devices = ['light-001', 'ac-001', 'curtain-001']
          devices.forEach(id => {
            const state = DeviceControlManager.getDeviceState(id)
            if (state) deviceStates.set(id, state)
          })

          // 解析指令
          const command = VoiceCommandParser.parse(result.text, deviceStates)
          if (command.success && command.deviceId && command.property) {
            DeviceControlManager.controlDevice(command.deviceId, command.property, command.value!)
          }
        }
      },
      onError: (error) => {
        console.error(`[Orchestrator] 语音识别错误: ${error}`)
      }
    })

    // 初始化指令解析器的设备别名
    VoiceCommandParser.setDeviceAliases([
      {
        deviceId: 'light-001', name: '客厅灯',
        aliases: ['客厅的灯', '客厅主灯'],
        properties: [
          { key: 'power', name: '开关', aliases: [], type: 'switch' },
          { key: 'brightness', name: '亮度', aliases: ['亮度', '灯光'], type: 'slider' }
        ]
      },
      {
        deviceId: 'ac-001', name: '空调',
        aliases: ['卧室空调', '冷气'],
        properties: [
          { key: 'power', name: '开关', aliases: [], type: 'switch' },
          { key: 'temperature', name: '温度', aliases: ['温度'], type: 'slider' },
          { key: 'mode', name: '模式', aliases: ['模式'], type: 'enum', options: ['制冷', '制热', '自动'] }
        ]
      }
    ])
  }

  // 清理所有服务
  async cleanup(): Promise<void> {
    TimerTaskEngine.stopScheduler()
    MqttService.disconnect()
    BackgroundTaskKeeper.cancelContinuousTask()
    await BackgroundTaskKeeper.unregisterAllReminders()
    this.notifyStateChange('idle')
  }

  // App进后台
  async onBackground(): Promise<void> {
    const tasks = TimerTaskEngine.getAllTasks()
    await BackgroundTaskKeeper.registerReminders(tasks)
  }

  // App回前台
  async onForeground(): Promise<void> {
    if (!MqttService.isConnected()) {
      await MqttService.reconnect()
    }
    // 清理过期分享
    DeviceShareService.cleanExpiredShares()
  }
}

// 导出单例
export default new SmartHomeOrchestrator()

踩坑与注意事项

1. 服务初始化顺序

服务之间有依赖关系,初始化顺序不能乱。比如DeviceControlManager依赖MqttService,如果先初始化DeviceControlManager再连MQTT,控制指令发不出去。

解法: 用编排器(Orchestrator)统一管理初始化顺序。按依赖关系从底层到上层依次初始化:数据库 → MQTT → 设备控制 → 场景/定时 → 语音 → 分布式数据 → 后台保活。

2. 全局状态管理

设备状态在多个页面共享——首页显示设备状态,设备详情页控制设备,场景页引用设备。如果每个页面自己维护状态,很快就不一致了。

解法: DeviceControlManager作为唯一的状态源。所有页面从DeviceControlManager读取状态,通过DeviceControlManager控制设备。状态变更通过回调通知所有监听者。

3. MQTT消息的分发

MQTT消息到达后,需要分发给多个模块——DeviceControlManager更新状态、SceneEngine检查事件触发、TimerTaskEngine检查设备上线。如果每个模块自己订阅MQTT,会有重复订阅和重复处理的问题。

解法: MqttService只订阅一次,消息到达后分发给所有需要的模块。分发逻辑放在编排器里,集中管理。

4. 内存泄漏

页面注册了状态监听回调,页面销毁时没取消注册——回调还在执行,但页面已经不在了,导致内存泄漏。

解法: 在页面的aboutToDisappear中取消所有注册的回调。或者用弱引用(WeakRef)持有页面引用,页面销毁后回调自动失效。

5. 离线模式

MQTT断开后,用户还能控制设备吗?如果完全依赖MQTT,断网就废了。

解法: 离线模式下,通过局域网直连设备(CoAP/HTTP)。MQTT断开时自动切换到局域网模式,MQTT恢复后切换回来。用户无感知。

HarmonyOS 6适配说明

HarmonyOS 6对智能家居App做了几项系统级增强:

  1. 智能家居Kit:新增@kit.SmartHomeKit,提供设备发现、配网、控制、场景的一站式API。不需要自己实现MQTT通信和协议解析,SDK全包了。

  2. 设备模型标准化:HarmonyOS 6定义了标准设备模型(Standard Device Model),所有接入鸿蒙生态的IoT设备都遵循统一的属性定义。你的App不需要为每种设备写适配代码,直接用标准模型。

  3. 跨端迁移增强:UIAbility的迁移API新增设备控制上下文自动保存和恢复。用户在手机上控制到一半,迁移到平板后自动恢复控制状态。

  4. 系统级场景引擎:HarmonyOS 6在系统层提供场景引擎服务,App注册场景规则后,即使App被杀,系统也能执行场景。不再需要自己实现后台保活。

适配代码示例:

// HarmonyOS 6 智能家居Kit
import { smartHome } from '@kit.SmartHomeKit'

// 使用标准设备模型发现设备
const discovery = smartHome.createDeviceDiscovery()
discovery.on('deviceFound', (device: smartHome.SmartDevice) => {
  // device已经按照标准模型解析好了
  // device.deviceType, device.properties, device.capabilities
  console.info(`发现设备: ${device.name}, 类型: ${device.deviceType}`)
})

// 使用系统场景引擎
const sceneManager = smartHome.createSceneManager()
const scene: smartHome.SceneRule = {
  name: '回家模式',
  conditions: [{ type: 'geofence', action: 'enter' }],
  actions: [
    { deviceId: 'light-001', property: 'power', value: true },
    { deviceId: 'ac-001', property: 'power', value: true }
  ]
}
await sceneManager.addRule(scene)
// 场景注册到系统,即使App被杀也能执行

总结

完整的智能家居App不是10个模块的简单堆砌,而是一个有机整体。模块之间有依赖、有协作、有数据流动。编排器统一管理初始化顺序和消息分发,DeviceControlManager作为唯一的状态源,保证数据一致性。

从架构到实现,核心就三件事:分层解耦让模块独立,状态集中让数据一致,编排统一让协作顺畅。

维度 评价
学习难度 ⭐⭐⭐⭐⭐ 整合10个模块,处理依赖关系、状态管理、消息分发,复杂度最高
使用频率 ⭐⭐⭐⭐⭐ 这是最终交付物,所有功能都在这一个App里
重要程度 ⭐⭐⭐⭐⭐ 没有完整的项目实战,前面学的都是碎片

一句话:单个模块做得再好,整合不起来就是零。 完整项目的价值在于让所有模块协同工作,而不是各自为战。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。