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个月内不可修改。