
鸿蒙跨端智能冰箱管家系统开发指南 原创
鸿蒙跨端智能冰箱管家系统开发指南
一、项目概述
本文基于HarmonyOS的AI图像识别能力和分布式技术,开发一款智能冰箱管家系统。该系统能够通过摄像头识别冰箱内食物并记录保质期,在食物临近过期时向多设备发送提醒,借鉴了《鸿蒙跨端U同步》中多设备数据同步的技术原理。
二、系统架构
±--------------------+ ±--------------------+ ±--------------------+
主设备 <-----> 分布式数据总线 <-----> 从设备
(冰箱终端) (Distributed Bus) (手机/平板等)
±---------±---------+ ±---------±---------+ ±---------±---------+
±---------v----------+ ±---------v----------+ ±---------v----------+
图像识别模块 保质期管理模块 消息提醒模块
(Image Recognition) (Expiry Manager) (Notification)
±--------------------+ ±--------------------+ ±--------------------+
三、核心代码实现
食物识别服务
// src/main/ets/service/FoodRecognitionService.ts
import { image } from ‘@ohos.multimedia.image’;
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { camera } from ‘@ohos.multimedia.camera’;
import { imageAnalysis } from ‘@ohos.ai.imageAnalysis’;
interface FoodItem {
id: string;
name: string;
category: string;
storageDate: number;
expiryDate: number;
quantity: number;
imageUri: string;
confidence: number;
interface FridgeState {
foodItems: FoodItem[];
lastUpdateTime: number;
export class FoodRecognitionService {
private static instance: FoodRecognitionService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = ‘fridge_state_store’;
private cameraInput: camera.CameraInput | null = null;
private imageAnalysisOutput: camera.ImageAnalysisOutput | null = null;
private currentState: FridgeState = {
foodItems: [],
lastUpdateTime: Date.now()
};
private imageAnalyzer: imageAnalysis.ImageAnalyzer | null = null;
private constructor() {
this.initKVStore();
this.initCamera();
this.initImageAnalyzer();
public static getInstance(): FoodRecognitionService {
if (!FoodRecognitionService.instance) {
FoodRecognitionService.instance = new FoodRecognitionService();
return FoodRecognitionService.instance;
private async initKVStore(): Promise<void> {
try {
const options: distributedData.KVManagerConfig = {
bundleName: 'com.example.smartfridge',
userInfo: {
userId: '0',
userType: distributedData.UserType.SAME_USER_ID
};
const kvManager = distributedData.createKVManager(options);
this.kvStore = await kvManager.getKVStore({
storeId: this.STORE_ID,
options: {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
});
// 注册数据变化监听
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
data.insertEntries.forEach((entry: distributedData.Entry) => {
if (entry.key === 'fridge_state') {
this.notifyStateChange(entry.value.value as FridgeState);
});
});
catch (e) {
console.error(Failed to initialize KVStore. Code: {e.code}, message: {e.message});
}
private async initCamera(): Promise<void> {
try {
const cameraManager = camera.getCameraManager();
const cameras = cameraManager.getSupportedCameras();
if (cameras.length === 0) {
console.error(‘No camera available’);
return;
// 使用后置摄像头
this.cameraInput = cameraManager.createCameraInput(cameras[0]);
await this.cameraInput.open();
// 创建图像分析输出
this.imageAnalysisOutput = cameraManager.createImageAnalysisOutput();
// 设置分析回调
this.imageAnalysisOutput.on('imageAnalysis', (analysisResult) => {
this.analyzeImage(analysisResult);
});
// 创建会话并开始分析
const captureSession = cameraManager.createCaptureSession();
await captureSession.beginConfig();
await captureSession.addInput(this.cameraInput);
await captureSession.addOutput(this.imageAnalysisOutput);
await captureSession.commitConfig();
await captureSession.start();
catch (e) {
console.error(Failed to initialize camera. Code: {e.code}, message: {e.message});
}
private async initImageAnalyzer(): Promise<void> {
try {
const config: imageAnalysis.ImageAnalyzerConfig = {
analyzerType: imageAnalysis.AnalyzerType.OBJECT,
objectAnalyzerConfig: {
isAccurateMode: true,
isAnalyzeFood: true // 特别启用食物分析
};
this.imageAnalyzer = await imageAnalysis.createImageAnalyzer(config);
catch (e) {
console.error(Failed to initialize image analyzer. Code: {e.code}, message: {e.message});
}
private async analyzeImage(analysisResult: camera.ImageAnalysisResult): Promise<void> {
if (!this.imageAnalyzer) return;
try {
const imageObj = analysisResult.image;
const analyzeResult = await this.imageAnalyzer.analyze(imageObj);
const newFoodItems: FoodItem[] = [];
const now = Date.now();
// 处理识别结果
analyzeResult.objects.forEach((obj) => {
if (obj.type === imageAnalysis.ObjectType.FOOD && obj.confidence > 0.7) {
const expiryDays = this.getDefaultExpiryDays(obj.name);
const expiryDate = now + expiryDays 24 60 60 1000;
newFoodItems.push({
id: {obj.name}_{now},
name: obj.name,
category: this.getFoodCategory(obj.name),
storageDate: now,
expiryDate: expiryDate,
quantity: 1,
imageUri: '', // 实际应用中可保存图片
confidence: obj.confidence
});
});
// 合并新旧物品
this.mergeFoodItems(newFoodItems);
// 同步状态
await this.syncState();
// 检查过期物品
this.checkExpiry();
// 释放图像资源
imageObj.release();
catch (e) {
console.error(Failed to analyze image. Code: {e.code}, message: {e.message});
}
private getDefaultExpiryDays(foodName: string): number {
// 实际应用中应该有更完善的数据库
const expiryMap: Record<string, number> = {
‘milk’: 7,
‘egg’: 30,
‘vegetable’: 5,
‘fruit’: 7,
‘meat’: 3,
‘fish’: 2,
‘cheese’: 14,
‘yogurt’: 10
};
return expiryMap[foodName.toLowerCase()] || 3;
private getFoodCategory(foodName: string): string {
// 简化的分类逻辑
const categories: Record<string, string[]> = {
'dairy': ['milk', 'cheese', 'yogurt'],
'protein': ['egg', 'meat', 'fish'],
'vegetable': ['vegetable'],
'fruit': ['fruit']
};
for (const [category, items] of Object.entries(categories)) {
if (items.includes(foodName.toLowerCase())) {
return category;
}
return 'other';
private mergeFoodItems(newItems: FoodItem[]): void {
const existingItems = [...this.currentState.foodItems];
newItems.forEach((newItem) => {
const existingItem = existingItems.find(
item => item.name === newItem.name &&
item.expiryDate > Date.now() - 24 60 60 * 1000
);
if (existingItem) {
existingItem.quantity += 1;
// 使用最新的保质期
if (newItem.expiryDate > existingItem.expiryDate) {
existingItem.expiryDate = newItem.expiryDate;
} else {
existingItems.push(newItem);
});
this.currentState.foodItems = existingItems.filter(item => item.expiryDate > Date.now());
this.currentState.lastUpdateTime = Date.now();
private async syncState(): Promise<void> {
if (this.kvStore) {
try {
await this.kvStore.put('fridge_state', { value: this.currentState });
catch (e) {
console.error(Failed to sync state. Code: {e.code}, message: {e.message});
}
private checkExpiry(): void {
const now = Date.now();
const warningThreshold = 24 60 60 * 1000; // 1天前提醒
const expiringSoon = this.currentState.foodItems.filter(
item => item.expiryDate - now < warningThreshold &&
item.expiryDate > now
);
const expired = this.currentState.foodItems.filter(
item => item.expiryDate <= now
);
if (expiringSoon.length > 0 || expired.length > 0) {
this.sendNotifications(expiringSoon, expired);
}
private sendNotifications(expiringSoon: FoodItem[], expired: FoodItem[]): void {
// 实际应用中应该使用通知服务
console.log(‘Expiring soon:’, expiringSoon.map(item => item.name));
console.log(‘Expired:’, expired.map(item => item.name));
// 同步通知状态
if (this.kvStore) {
this.kvStore.put('expiry_notifications', {
value: {
expiringSoon,
expired,
timestamp: Date.now()
});
}
public async getCurrentState(): Promise<FridgeState> {
if (!this.kvStore) return this.currentState;
try {
const entry = await this.kvStore.get('fridge_state');
return entry?.value || this.currentState;
catch (e) {
console.error(Failed to get fridge state. Code: {e.code}, message: {e.message});
return this.currentState;
}
public async manuallyAddItem(item: Omit<FoodItem, ‘id’ | ‘confidence’>): Promise<void> {
const newItem: FoodItem = {
…item,
id: {item.name}_{Date.now()},
confidence: 1 // 手动添加置信度为1
};
this.currentState.foodItems.push(newItem);
this.currentState.lastUpdateTime = Date.now();
await this.syncState();
this.checkExpiry();
public async removeItem(itemId: string): Promise<void> {
this.currentState.foodItems = this.currentState.foodItems.filter(item => item.id !== itemId);
this.currentState.lastUpdateTime = Date.now();
await this.syncState();
public async destroy(): Promise<void> {
if (this.kvStore) {
this.kvStore.off('dataChange');
if (this.cameraInput) {
await this.cameraInput.close();
if (this.imageAnalyzer) {
this.imageAnalyzer.release();
}
冰箱物品列表组件
// src/main/ets/components/FoodList.ets
@Component
export struct FoodList {
private fridgeService = FoodRecognitionService.getInstance();
@State fridgeState: FridgeState = { foodItems: [], lastUpdateTime: 0 };
@State showAddDialog: boolean = false;
@State newFoodItem: Omit<FoodItem, ‘id’ | ‘confidence’> = {
name: ‘’,
category: ‘’,
storageDate: Date.now(),
expiryDate: Date.now() + 3 24 60 60 1000,
quantity: 1,
imageUri: ‘’
};
aboutToAppear(): void {
this.loadFridgeState();
private async loadFridgeState(): Promise<void> {
this.fridgeState = await this.fridgeService.getCurrentState();
build() {
Column() {
// 标题和扫描按钮
Row() {
Text('冰箱物品清单')
.fontSize(22)
.fontWeight(FontWeight.Bold);
Button('扫描冰箱')
.type(ButtonType.Capsule)
.backgroundColor('#FF4081')
.fontColor('#FFFFFF')
.margin({ left: 20 })
.onClick(() => {
// 触发扫描
this.loadFridgeState();
});
.width(‘100%’)
.justifyContent(FlexAlign.Start)
.margin({ bottom: 20 });
// 物品列表
List({ space: 10 }) {
ForEach(this.fridgeState.foodItems, (item) => {
ListItem() {
FoodItemCard({ item: item })
.onDelete(() => {
this.fridgeService.removeItem(item.id);
});
}, item => item.id)
.width(‘100%’)
.layoutWeight(1)
.divider({ strokeWidth: 1, color: '#F0F0F0' });
// 添加物品按钮
Button('+ 添加物品')
.type(ButtonType.Circle)
.width(60)
.height(60)
.backgroundColor('#FF4081')
.fontColor('#FFFFFF')
.margin({ top: 20 })
.onClick(() => {
this.showAddDialog = true;
});
.width(‘100%’)
.height('100%')
.padding(20)
// 添加物品对话框
if (this.showAddDialog) {
Dialog.show({
title: '添加新物品',
content: this.buildAddDialogContent(),
confirm: {
value: '添加',
action: () => {
this.fridgeService.manuallyAddItem(this.newFoodItem);
this.showAddDialog = false;
this.resetNewItem();
},
cancel: () => {
this.showAddDialog = false;
this.resetNewItem();
});
}
@Builder
private buildAddDialogContent() {
Column() {
TextInput({ placeholder: ‘物品名称’ })
.width(‘100%’)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newFoodItem.name = value;
});
TextInput({ placeholder: '类别' })
.width('100%')
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newFoodItem.category = value;
});
Row() {
Text('数量:')
.fontSize(16)
.margin({ right: 10 });
TextInput({ text: '1' })
.type(InputType.Number)
.width(60)
.onChange((value: string) => {
this.newFoodItem.quantity = parseInt(value) || 1;
});
.width(‘100%’)
.margin({ bottom: 15 });
Row() {
Text('保质期至:')
.fontSize(16)
.margin({ right: 10 });
DatePicker({
start: new Date(this.newFoodItem.storageDate),
end: new Date(this.newFoodItem.storageDate + 30 24 60 60 1000),
selected: new Date(this.newFoodItem.expiryDate)
})
.onChange((date: Date) => {
this.newFoodItem.expiryDate = date.getTime();
});
.width(‘100%’);
.width(‘100%’)
.padding(10)
private resetNewItem(): void {
this.newFoodItem = {
name: '',
category: '',
storageDate: Date.now(),
expiryDate: Date.now() + 3 24 60 60 1000,
quantity: 1,
imageUri: ''
};
}
@Component
struct FoodItemCard {
@Prop item: FoodItem;
@Emit onDelete: () => void;
@State daysLeft: number = Math.ceil((this.item.expiryDate - Date.now()) / (24 60 60 * 1000));
build() {
Row() {
// 物品图片
Image($r(‘app.media.food_placeholder’))
.width(60)
.height(60)
.borderRadius(10)
.margin({ right: 15 });
// 物品信息
Column() {
Row() {
Text(this.item.name)
.fontSize(18)
.fontWeight(FontWeight.Bold);
Text(x${this.item.quantity})
.fontSize(16)
.fontColor('#999999')
.margin({ left: 10 });
.width(‘100%’);
Text(类别: ${this.item.category})
.fontSize(14)
.fontColor('#666666')
.margin({ top: 5 });
Text(保质期剩余: ${this.daysLeft}天)
.fontSize(14)
.fontColor(this.daysLeft <= 3 ? '#FF0000' : (this.daysLeft <= 7 ? '#FFA500' : '#008000'))
.margin({ top: 5 });
.layoutWeight(1);
// 删除按钮
Button($r('app.media.ic_delete'))
.width(30)
.height(30)
.backgroundColor('#00000000')
.onClick(() => {
this.onDelete();
});
.width(‘100%’)
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.shadow({ radius: 3, color: '#E0E0E0', offsetX: 0, offsetY: 2 });
}
主界面实现
// src/main/ets/pages/SmartFridgePage.ets
import { FoodRecognitionService } from ‘…/service/FoodRecognitionService’;
import { FoodList } from ‘…/components/FoodList’;
@Entry
@Component
struct SmartFridgePage {
@State activeTab: number = 0;
@State deviceList: string[] = [];
@State notifications: any = {};
private fridgeService = FoodRecognitionService.getInstance();
build() {
Column() {
// 标题
Text(‘智能冰箱管家’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 标签页
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
// 物品列表标签页
FoodList()
.tabBar(‘物品清单’);
TabContent() {
// 过期提醒标签页
this.buildNotificationsTab()
.tabBar(‘过期提醒’);
TabContent() {
// 设备管理标签页
this.buildDevicesTab()
.tabBar(‘设备管理’);
.barWidth(‘100%’)
.barHeight(50)
.width('100%')
.height('80%')
.width(‘100%’)
.height('100%')
.padding(20)
.onAppear(() => {
// 模拟获取设备列表和通知
setTimeout(() => {
this.deviceList = ['厨房冰箱', '客厅平板', '卧室手机'];
this.notifications = {
expiringSoon: [
name: ‘牛奶’, expiryDate: Date.now() + 12 60 60 * 1000 },
name: ‘鸡蛋’, expiryDate: Date.now() + 2 24 60 60 1000 }
],
expired: [
name: ‘蔬菜’, expiryDate: Date.now() - 2 24 60 60 1000 }
],
timestamp: Date.now()
};
}, 1000);
});
@Builder
private buildNotificationsTab() {
Column() {
if (this.notifications.expiringSoon && this.notifications.expiringSoon.length > 0) {
Column() {
Text(‘即将过期’)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
ForEach(this.notifications.expiringSoon, (item) => {
Text({item.name} - {new Date(item.expiryDate).toLocaleDateString()})
.fontSize(16)
.fontColor('#FFA500')
.margin({ bottom: 5 });
})
.width(‘100%’)
.padding(15)
.backgroundColor('#FFF3E0')
.borderRadius(10)
.margin({ bottom: 20 });
if (this.notifications.expired && this.notifications.expired.length > 0) {
Column() {
Text('已过期')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
ForEach(this.notifications.expired, (item) => {
Text({item.name} - {new Date(item.expiryDate).toLocaleDateString()})
.fontSize(16)
.fontColor('#FF0000')
.margin({ bottom: 5 });
})
.width(‘100%’)
.padding(15)
.backgroundColor('#FFEBEE')
.borderRadius(10);
if ((!this.notifications.expiringSoon || this.notifications.expiringSoon.length === 0) &&
(!this.notifications.expired || this.notifications.expired.length === 0)) {
Text('没有过期或即将过期的物品')
.fontSize(16)
.fontColor('#666666');
}
.width('100%')
.height('100%')
.padding(10)
@Builder
private buildDevicesTab() {
Column() {
Text(‘已连接设备’)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
if (this.deviceList.length > 0) {
List({ space: 15 }) {
ForEach(this.deviceList, (device) => {
ListItem() {
Row() {
Image($r('app.media.ic_device'))
.width(40)
.height(40)
.margin({ right: 15 });
Text(device)
.fontSize(16)
.layoutWeight(1);
if (device === '厨房冰箱') {
Text('主设备')
.fontSize(14)
.fontColor('#FF4081');
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
})
.width(‘100%’)
.layoutWeight(1);
else {
Text('没有连接的设备')
.fontSize(16)
.fontColor('#666666')
.margin({ top: 50 });
Button(‘添加设备’)
.type(ButtonType.Capsule)
.width('80%')
.margin({ top: 30 })
.backgroundColor('#FF4081')
.fontColor('#FFFFFF');
.width(‘100%’)
.height('100%')
.padding(10)
}
四、与游戏同步技术的结合点
分布式状态同步:借鉴游戏中多设备玩家状态同步机制,实现冰箱物品状态的跨设备同步
实时数据更新:类似游戏中的实时状态更新,确保多设备间物品信息的一致性
设备角色分配:类似游戏中的主机/客户端角色,确定主冰箱设备和从属设备
冲突解决策略:使用时间戳优先策略解决多设备同时更新物品信息的冲突
数据压缩传输:优化物品信息的传输效率,类似游戏中的网络优化
五、关键特性实现
食物图像识别:
const analyzeResult = await this.imageAnalyzer.analyze(imageObj);
analyzeResult.objects.forEach((obj) => {
if (obj.type === imageAnalysis.ObjectType.FOOD && obj.confidence > 0.7) {
// 处理识别到的食物
});
保质期计算:
const expiryDays = this.getDefaultExpiryDays(obj.name);
const expiryDate = now + expiryDays 24 60 60 1000;
过期检查:
const expiringSoon = this.currentState.foodItems.filter(
item => item.expiryDate - now < warningThreshold &&
item.expiryDate > now
);
状态同步:
await this.kvStore.put('fridge_state', { value: this.currentState });
六、性能优化策略
图像分析间隔控制:
private lastAnalysisTime = 0;
private readonly ANALYSIS_INTERVAL = 5000; // 5秒分析一次
private async analyzeImage(): Promise<void> {
const now = Date.now();
if (now - this.lastAnalysisTime < this.ANALYSIS_INTERVAL) return;
this.lastAnalysisTime = now;
// 分析逻辑…
数据批量更新:
private batchUpdateTimer: number | null = null;
private scheduleStateSync(): void {
if (this.batchUpdateTimer) {
clearTimeout(this.batchUpdateTimer);
this.batchUpdateTimer = setTimeout(() => {
this.syncState();
this.batchUpdateTimer = null;
}, 1000); // 1秒内多次更新只同步一次
本地缓存优先:
public async getCurrentState(): Promise<FridgeState> {
// 先返回本地缓存
const cachedState = this.currentState;
// 异步从分布式存储获取最新状态
if (this.kvStore) {
this.kvStore.get('fridge_state').then((entry) => {
if (entry?.value && entry.value.lastUpdateTime > cachedState.lastUpdateTime) {
this.currentState = entry.value;
});
return cachedState;
资源释放管理:
public async destroy(): Promise<void> {
if (this.imageAnalyzer) {
this.imageAnalyzer.release();
if (this.cameraInput) {
await this.cameraInput.close();
}
七、项目扩展方向
条形码识别:结合条形码扫描获取更准确的商品信息
智能购物清单:根据消耗情况自动生成购物清单
食谱推荐:根据现有食材推荐食谱
营养分析:分析冰箱内食物的营养组成
多冰箱管理:支持管理家庭中多个冰箱的库存
八、总结
本智能冰箱管家系统实现了以下核心功能:
基于HarmonyOS AI能力的食物图像识别
食物保质期管理和过期提醒
多设备间的状态同步
直观的物品管理和提醒界面
通过借鉴游戏中的多设备同步技术,我们构建了一个实用的智能冰箱管理系统。该项目展示了HarmonyOS在AI识别和分布式技术方面的强大能力,为开发者提供了智能家居应用开发的参考方案。
