HarmonyOS开发:智能家居实战——完整智能家居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 | 无 |
数据流向
- 设备状态流:设备 → MQTT → DeviceControlManager → UI
- 控制指令流:UI → DeviceControlManager → MQTT → 设备
- 场景执行流:UI/定时器/事件 → SceneEngine → DeviceControlManager → MQTT → 设备
- 语音控制流:麦克风 → VoiceRecognizer → VoiceCommandParser → DeviceControlManager → MQTT → 设备
- 分布式同步流: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做了几项系统级增强:
-
智能家居Kit:新增
@kit.SmartHomeKit,提供设备发现、配网、控制、场景的一站式API。不需要自己实现MQTT通信和协议解析,SDK全包了。 -
设备模型标准化:HarmonyOS 6定义了标准设备模型(Standard Device Model),所有接入鸿蒙生态的IoT设备都遵循统一的属性定义。你的App不需要为每种设备写适配代码,直接用标准模型。
-
跨端迁移增强:UIAbility的迁移API新增设备控制上下文自动保存和恢复。用户在手机上控制到一半,迁移到平板后自动恢复控制状态。
-
系统级场景引擎: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里 |
| 重要程度 | ⭐⭐⭐⭐⭐ 没有完整的项目实战,前面学的都是碎片 |
一句话:单个模块做得再好,整合不起来就是零。 完整项目的价值在于让所有模块协同工作,而不是各自为战。
- 点赞
- 收藏
- 关注作者
评论(0)