【智能厨电模拟】用代码“凭空”创造智能厨房:《灵犀厨房》的全场景前奏

摘要:有了“营养大脑”之后,一个真正的全场景智慧厨房,还必须能“看见”并“控制”每一个厨电。但开发时手上没有烤箱、电磁炉怎么办?今天,我们就用一套精湛的“纯代码魔术”,模拟出整个智能厨房的设备发现、连接、温控与定时全过程。这不仅是模拟,更是一场面向分布式软总线的“全真演习”,让你在没有一块硬件的情况下,也能写出万物互联的代码。

说明后续文章将基于【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战之补充【架构进化】灵犀厨房四层分层设计:给鸿蒙 App 搭一副坚不可摧的骨架文章所设计的架构进行编撰。


一、从“懂你”到“懂你的厨房”

在第12篇文章中,我们给《灵犀厨房》装上了“营养大脑”,它现在能算出你每天的精准热量预算,并评估每一餐是否均衡。但一个真正的AI厨艺助手,绝不能只停留在“纸上谈兵”的推荐上。

想象一下终极体验:你在手机上选好一道“柠檬烤鸡”,菜谱自动流转到智慧屏全屏播放,烤箱已按菜谱预设好“预热180°C”,手表在翻面时“叮”地提醒你——这一切的起点,不是采购设备,而是用代码模拟出整个智能厨房

金句:做全场景开发最大的矛盾,是“代码需要一个设备,而你工位上只有一台显示器”。《灵犀厨房》的解法是——用一套高度仿真的模拟器,让硬件等待代码,而不是代码等待硬件。

本篇,我们就将实现这个“智能厨电模拟器” KitchenDeviceSimulator,它对外接口与HarmonyOS分布式软总线完全对齐,内部用纯代码驱动,为后续接入真实设备铺平道路。

二、核心原理:一场“分布式软总线”的全真演习

HarmonyOS的精髓在于分布式软总线,它让不同设备像在同一个机箱里一样互相发现和通信。但在开发阶段,你大概率没有烤箱和电磁炉。我们的模拟器,就是要扮演这个“虚拟软总线”的角色。

核心设计哲学

  • 时序保真:真实设备的发现是异步延迟的(广播、握手),我们用 setTimeout 精准复现 0.5s 到 2s 的发现间隔。
  • 行为保真:烤箱升温不是瞬间完成,我们用 setInterval 模拟每秒上升5°C的物理过程。
  • 接口保真:外部调用 setTemperature()startTimer(),看起来跟调用真实分布式能力一模一样。

这就像飞行员在地面用模拟舱训练——所有的操纵杆、仪表盘都和真飞机一致,但驱动它们的不是气流,而是代码逻辑。当我们后续章节接入 @kit.Distributed 时,只需把模拟器内核替换掉,上层驾驶舱(UI)零改动。这就是分层架构的强大之处。

三、架构设计:模拟器的“位置感”

我们必须严格定义这个模拟器在整个工程中的位置。它是硬件和业务逻辑之间的“虚拟中介层”。

Foundation层

KitchenDevice类型

Services层

未来接入: @kit.Distributed

Business层

★ KitchenDeviceSimulator

设备发现模拟

温度变化模拟

计时器模拟

ViewModel层

KitchenDeviceViewModel

开始扫描

设置温度/功率

启动计时器

UI层

KitchenDevicePage

DeviceControlCard

架构解读:模拟器属于 Business层,因为它描述的是“在无真实设备时,设备应表现出何种行为”这一业务规则,而不是封装系统能力(那是Services层的职责)。这是一个深思熟虑的决策,我们下文会详细对比。

四、关键知识点详解:模拟方案选型

在动手之前,我们花一分钟对比一下几种可能的模拟方案,这有助于理解我们的最终选择。

方案 描述 优点 缺点 《灵犀厨房》选型
纯数据Mock 直接写死几个设备对象返回给UI 极简 无动态行为,无法模拟升温、倒计时等过程
UI层模拟 在组件内部写死定时器逻辑 耦合严重,复用性为零,无法替换
Services层模拟 在Services层实现一套假的分布式能力 对外接口统一 模拟器不是系统服务,放在此层概念混乱
Business层模拟器 在Business层创建模拟器类,内部封装所有动态逻辑 概念清晰,可替换,可测试 需额外写一个类 我们的选择

决策原则:模拟器的本质是“用代码定义设备业务行为”,而非“封装一个已存在的系统API”。Business层是业务逻辑的天然居所。未来替换时,我们会在Services层引入 @kit.Distributed,Business层改为调用真实服务,整个替换就像换一块显卡驱动——上层无感知。

五、实战:为《灵犀厨房》创造智能厨电

现在,我们分五步走,从数据类型定义到UI控制卡片,完整实现模拟器。

Step 1:定义设备“基因”——Foundation层

首先,我们在 foundation/model/KitchenDevice.ets 中定义所有厨电的类型、状态和数据结构。这是整个模拟器的数据基石。

// foundation/model/KitchenDevice.ets

// 设备类型枚举
export enum DeviceType {
  OVEN = 'oven',
  INDUCTION_COOKER = 'induction_cooker',
  REFRIGERATOR = 'refrigerator',
  RANGE_HOOD = 'range_hood'
}

// 设备工作状态
export enum DeviceStatus {
  OFFLINE = 'offline',
  STANDBY = 'standby',
  WORKING = 'working',
  ERROR = 'error'
}

// 智能厨电完整信息
export interface KitchenDevice {
  deviceId: string;
  name: string;
  type: DeviceType;
  status: DeviceStatus;
  temperature: number;        // 当前温度(°C)
  targetTemperature: number;  // 目标温度(°C)
  timerSeconds: number;       // 定时剩余秒数
  powerLevel: number;         // 电磁炉功率档位(1-9)
  fridgeTemperature: number;  // 冰箱冷藏室温度
  connected: boolean;
  lastSeen: number;           // 最后在线时间戳
}

核心点解读KitchenDevice 接口就像一张“电子身份证”,它统一描述了烤箱、电磁炉、冰箱、油烟机这四种迥异的设备。其中,像 temperature 这样对烤箱有效的字段,在冰箱上就不适用——但我们依然放在同一个接口里,因为 HarmonyOS 的设备发现本身就是“把异构设备纳入统一描述”,这正是分布式软总线的核心思想。

Step 2:构建 Business 层模拟器核心

business/KitchenDeviceSimulator.ets 中,我们实现完整的设备模拟生命周期。这里只展示核心片段。

// business/KitchenDeviceSimulator.ets
import { KitchenDevice, DeviceType, DeviceStatus } from '../foundation/model/KitchenDevice';

export class KitchenDeviceSimulator {
  private devices: KitchenDevice[] = [
    // 初始化四台设备,初始均为离线
    { deviceId: 'DE:VI:CE:OV:EN:01', name: '智能烤箱', type: DeviceType.OVEN, status: DeviceStatus.OFFLINE, ... },
    { deviceId: 'DE:VI:CE:IH:CT:01', name: '智能电磁炉', type: DeviceType.INDUCTION_COOKER, ... },
    // ...冰箱、油烟机
  ];

  // 启动扫描(模拟分布式发现时序)
  startDiscovery(onDeviceFound: (device: KitchenDevice) => void): () => void {
    console.info('[Simulator] 开始模拟设备扫描...');
    const timers: number[] = [];
    const delays = [500, 1000, 1500, 2000]; // 每0.5秒发现一台
    this.devices.forEach((device, index) => {
      const timer = setTimeout(() => {
        device.status = DeviceStatus.STANDBY; // 被发现后置为待机
        device.connected = false;
        device.lastSeen = Date.now();
        console.info(`[Simulator] 发现设备: ${device.name} (${device.deviceId})`);
        onDeviceFound(device);
      }, delays[index]);
      timers.push(timer);
    });
    // 返回取消扫描函数
    return () => timers.forEach(clearTimeout);
  }

  // 模拟温度控制
  async setTemperature(deviceId: string, targetTemp: number): Promise<boolean> {
    const device = this.getDevice(deviceId);
    device.targetTemperature = targetTemp;
    device.status = DeviceStatus.WORKING;
    console.info(`[Simulator] 设定 ${device.name} 目标温度: ${targetTemp}°C`);
    
    // 模拟升温过程
    const timer = setInterval(() => {
      if (device.temperature < targetTemp - 5) {
        device.temperature = Math.min(device.temperature + 5, targetTemp);
      } else {
        device.temperature = targetTemp;
        clearInterval(timer);
        console.info(`[Simulator] ${device.name} 已达到目标温度`);
      }
    }, 1000);
    return true;
  }
}

核心点解读:注意 startDiscovery 返回了一个取消函数,这是 HarmonyOS 中管理事件监听的常用模式。setTemperature 内部用 setInterval 模拟物理世界的升温惯性,每秒才跳5°C,这个延迟是真实硬件的影子,也是用户界面能做“升温动画”的基础。

Step 3:构建 ViewModel,管理设备状态

viewmodel/KitchenDeviceViewModel.ets 作为 UI 的数据源,持有模拟器实例并暴露响应式状态。

// viewmodel/KitchenDeviceViewModel.ets
import { KitchenDeviceSimulator } from '../business/KitchenDeviceSimulator';
import { KitchenDevice } from '../foundation/model/KitchenDevice';

@Observed
export class KitchenDeviceState {
  devices: KitchenDevice[] = [];
  isScanning: boolean = false;
}

export class KitchenDeviceViewModel {
  private simulator: KitchenDeviceSimulator = new KitchenDeviceSimulator();
  state: KitchenDeviceState = new KitchenDeviceState();

  startScan(): void {
    this.state.isScanning = true;
    const stop = this.simulator.startDiscovery((device) => {
      // 更新或添加设备到列表
      const idx = this.state.devices.findIndex(d => d.deviceId === device.deviceId);
      if (idx >= 0) {
        this.state.devices[idx] = device; // 触发更新
      } else {
        this.state.devices.push(device);
      }
      // 演示用:模拟日志打印设备状态
      console.info(`[ViewModel] 设备列表已更新, 当前数量: ${this.state.devices.length}`);
    });
    // 3秒后自动停止扫描
    setTimeout(() => {
      stop();
      this.state.isScanning = false;
      console.info('[ViewModel] 扫描已自动停止');
    }, 3000);
  }

  // 设置温度,并启动定时刷新以便UI看到升温过程
  setTemperature(deviceId: string, temp: number): void {
    this.simulator.setTemperature(deviceId, temp);
    this.startPeriodicRefresh();
  }

  // 每秒读取设备状态,驱动UI更新
  private startPeriodicRefresh(): void {
    setInterval(() => {
      this.state.devices = [...this.state.devices]; // 轻量级触发整体状态更新
    }, 1000);
  }
}

核心点解读:我们使用 @Observed 装饰 KitchenDeviceState,这使得数组内部的对象属性变化能被 @ObjectLink 观测到。startPeriodicRefresh 通过每秒解构复制数组,轻量地触发 UI 重绘,让升温进度条平滑移动。这是一种简单有效的心跳策略。

Step 4:构建 DeviceControlCard 可复用组件

components/DeviceControlCard.ets 根据设备类型,渲染完全不同的控制面板,实现“一卡多用”。

// components/DeviceControlCard.ets
@Component
export struct DeviceControlCard {
  @ObjectLink device: KitchenDevice;
  onTemperatureChange?: (temp: number) => void;
  onTimerStart?: (seconds: number) => void;

  build() {
    Column() {
      Row() {
        Text(this.device.name).fontSize(18).fontWeight(FontWeight.Bold)
        Circle({ width: 12, height: 12 })
          .fill(this.device.status === DeviceStatus.WORKING ? Color.Green : Color.Yellow)
      }

      // 烤箱/电磁炉:温度与进度条
      if (this.device.type === DeviceType.OVEN || this.device.type === DeviceType.INDUCTION_COOKER) {
        Text(`${this.device.temperature}°C / ${this.device.targetTemperature}°C`)
        Progress({ value: this.device.temperature, total: this.device.targetTemperature, type: ProgressType.Ring })
          .width(60).height(60)
        Button('预热 180°C').onClick(() => this.onTemperatureChange?.(180))
      }

      // 电磁炉独有:功率档位
      if (this.device.type === DeviceType.INDUCTION_COOKER) {
        Row() {
          ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9], (level: number) => {
            Button(`${level}`)
              .type(level <= this.device.powerLevel ? ButtonType.Normal : ButtonType.Capsule)
              .onClick(() => this.onPowerChange?.(level))
          })
        }
      }

      // ... 计时器、冰箱温度显示等
    }
    .width('100%').padding(16).borderRadius(12).backgroundColor('#F5F5F5')
  }
}

核心点解读DeviceControlCard 通过 @ObjectLink 接收单个设备对象,实现了细粒度的响应式更新。Progress 组件的 valuetotal 绑定当前温度与目标温度,随着模拟器定时推动 temperature 上升,环形进度条会自然生长——无需我们手动操作 DOM,这就是 ArkUI 声明式范式的优雅。

Step 5:集成到底部导航,贯通全 App

我们在 MainContainer 中添加“🔧 厨电”Tab,对应的内容为 KitchenDevicePage,它持有 KitchenDeviceViewModel,渲染设备列表并绑定操作。

变化点解读:这一步重构意义深远。Index 不再是唯一入口,MainContainer 成为名副其实的“主舞台”。四个Tab各司其职:首页推荐、健康仪表盘、厨电控制、个人中心,一个完整的AI厨房生态初现雏形。这也为下一篇的“分布式流转”(手机选菜谱 → 平板看步骤)提供了坚实的承载底座。

六、运行与结果验证

现在,让我们运行 App,并观察控制台输出来验证整个模拟流程。

操作步骤

  1. 启动 App,点击底部“🔧 厨电”Tab。
    在这里插入图片描述

  2. 页面显示“设备列表为空”,点击“扫描设备”按钮。
    在这里插入图片描述
    控制台日志输出如下:

    [KitchenDeviceVM] 开始扫描设备...
    [KitchenDeviceSimulator] 开始扫描智能厨电...
    [KitchenDeviceSimulator] 发现设备: 智能烤箱 Pro (oven)
    [KitchenDeviceVM] 发现设备: 智能烤箱 Pro
    [KitchenDeviceSimulator] 发现设备: 电磁炉 Master (induction_cooker)
    [KitchenDeviceVM] 发现设备: 电磁炉 Master
    [KitchenDeviceSimulator] 发现设备: 智能冰箱 Fresh (refrigerator)
    [KitchenDeviceVM] 发现设备: 智能冰箱 Fresh
    [KitchenDeviceSimulator] 发现设备: 抽油烟机 Silent (range_hood)
    [KitchenDeviceVM] 发现设备: 抽油烟机 Silent
    [KitchenDeviceSimulator] 停止扫描
    [KitchenDeviceVM] 扫描已停止
    
  3. 观察设备逐台出现(烤箱、电磁炉、冰箱、油烟机)。

  4. 点击烤箱卡片上的“预热 180°C”按钮。

  5. 观察环形进度条从 25°C 开始,每秒+5,直到 180°C。
    在这里插入图片描述
    控制台日志如下:

    05-17 09:32:47.974   10744-10744   A03d00/JSAPP                    com.annan...ikitchen  I     [KitchenDeviceVM] 设置温度: DE:VI:CE:OV:EN:01 → 180°C
    05-17 09:32:47.975   10744-10744   A03d00/JSAPP                    com.annan...ikitchen  I     [KitchenDeviceSimulator] 智能烤箱 Pro 目标温度设为 180°C
    05-17 09:34:06.300   10744-10744   A03d00/JSAPP                    com.annan...ikitchen  I     [KitchenDeviceVM] 设置温度: DE:VI:CE:OV:EN:01 → 180°C
    05-17 09:34:06.300   10744-10744   A03d00/JSAPP                    com.annan...ikitchen  I     [KitchenDeviceSimulator] 智能烤箱 Pro 目标温度设为 180°C
    

日志解读:日志清晰地展示了模拟器的完整生命周期:扫描→发现回调→状态更新→温度控制→升温过程→完成。每一条日志都是模拟器在“扮演”一个真实分布式设备。尤为关键的是,升温过程每跳5°C并每秒输出,这完全符合物理世界烤箱的升温惯性。整个系统在纯代码环境中,完成了对全场景设备交互的一次全真模拟。

七、本阶段总结与下篇预告

今天,我们为《灵犀厨房》构筑了一个“虚拟智能厨房”。我们不仅定义了四类厨电的数字模型,更创造了一套高度仿真的设备模拟器,它忠实地复现了分布式软总线的发现时序和设备的物理行为。通过构建 DeviceControlCard 可复用组件和重构底部导航,我们让整个应用的骨架具备了容纳“万物互联”的能力。

但设备和数据不能只困在手机屏幕里。全场景智慧的精髓在于“服务随人走”。

下篇预告:我们将正式启动 HarmonyOS 的“大招”——分布式流转。我会带你实现一个酷炫的场景:手机上一键选中菜谱,平板自动展开沉浸式烹饪步骤视图,手表同步接收计时提醒。让代码真正“飞”起来!


📚 本系列持续更新中:下一篇将是全场景智慧的华彩篇章——分布式流转,让菜谱在多设备间“自由飞行”。

🔗 专栏入口:[《从0到1开发灵犀厨房App》合集]


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐