鸿蒙跨端智能冰箱管家系统开发指南 原创

进修的泡芙
发布于 2025-6-22 16:41
浏览
0收藏

鸿蒙跨端智能冰箱管家系统开发指南

一、项目概述

本文基于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识别和分布式技术方面的强大能力,为开发者提供了智能家居应用开发的参考方案。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐