鸿蒙跨端智能植物浇灌系统开发指南 原创

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

鸿蒙跨端智能植物浇灌系统开发指南

一、系统架构设计

基于HarmonyOS的物联网和分布式技术,构建智能植物浇灌系统:
传感层:土壤湿度传感器实时监测

控制层:自动控制水泵进行灌溉

决策层:根据植物类型和湿度数据智能决策

跨端同步层:多设备间同步浇灌状态和数据

!https://example.com/harmony-plant-watering-arch.png

二、核心代码实现
灌溉控制服务

// IrrigationService.ets
import iot from ‘@ohos.iot’;
import distributedData from ‘@ohos.distributedData’;
import { PlantInfo, WateringRecord } from ‘./PlantTypes’;

class IrrigationService {
private static instance: IrrigationService = null;
private sensorManager: iot.SensorManager;
private deviceManager: iot.DeviceManager;
private dataManager: distributedData.DataManager;
private listeners: IrrigationListener[] = [];
private plants: PlantInfo[] = [];

private constructor() {
this.initSensorManager();
this.initDeviceManager();
this.initDataManager();
public static getInstance(): IrrigationService {

if (!IrrigationService.instance) {
  IrrigationService.instance = new IrrigationService();

return IrrigationService.instance;

private initSensorManager(): void {

try {
  this.sensorManager = iot.createSensorManager(getContext());
  
  // 初始化湿度传感器
  this.sensorManager.registerSensor({
    sensorType: iot.SensorType.SOIL_MOISTURE,
    callback: (data) => {
      this.handleMoistureData(data);

});

catch (err) {

  console.error('初始化传感器管理器失败:', JSON.stringify(err));

}

private initDeviceManager(): void {
try {
this.deviceManager = iot.createDeviceManager(getContext());

  // 初始化水泵设备
  this.deviceManager.registerDevice({
    deviceType: iot.DeviceType.WATER_PUMP,
    deviceId: 'pump_001',
    callback: (status) => {
      this.handlePumpStatus(status);

});

catch (err) {

  console.error('初始化设备管理器失败:', JSON.stringify(err));

}

private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.irrigation’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});

this.dataManager.registerDataListener('irrigation_sync', (data) => {
  this.handleSyncData(data);
});

public async requestPermissions(): Promise<boolean> {

try {
  const permissions = [
    'ohos.permission.USE_IOT',
    'ohos.permission.DISTRIBUTED_DATASYNC'
  ];
  
  const result = await abilityAccessCtrl.requestPermissionsFromUser(
    getContext(), 
    permissions
  );
  
  return result.grantedPermissions.length === permissions.length;

catch (err) {

  console.error('请求权限失败:', JSON.stringify(err));
  return false;

}

public async addPlant(plant: PlantInfo): Promise<void> {
this.plants = […this.plants, plant];
this.syncPlant(plant);
public async removePlant(plantId: string): Promise<void> {

this.plants = this.plants.filter(p => p.id !== plantId);
this.dataManager.syncData('plant_remove', {
  type: 'plant_removed',
  data: { plantId },
  timestamp: Date.now()
});

public async startWatering(plantId: string, duration: number): Promise<void> {

const plant = this.plants.find(p => p.id === plantId);
if (!plant) return;

try {
  // 打开水泵
  await this.deviceManager.controlDevice({
    deviceId: 'pump_001',
    operation: 'turn_on',
    duration: duration
  });
  
  // 记录浇水
  const record: WateringRecord = {
    plantId: plantId,
    duration: duration,
    timestamp: Date.now()
  };
  
  this.syncWateringRecord(record);

catch (err) {

  console.error('启动浇水失败:', JSON.stringify(err));
  throw err;

}

public async stopWatering(): Promise<void> {
try {
await this.deviceManager.controlDevice({
deviceId: ‘pump_001’,
operation: ‘turn_off’
});
catch (err) {

  console.error('停止浇水失败:', JSON.stringify(err));
  throw err;

}

private handleMoistureData(data: any): void {
// 检查所有植物的湿度需求
this.plants.forEach(plant => {
if (data.moisture < plant.minMoisture) {
// 需要浇水
this.startWatering(plant.id, plant.wateringDuration);
});

this.syncMoistureData(data);

private handlePumpStatus(status: any): void {

this.notifyPumpStatusChanged(status);

private syncPlant(plant: PlantInfo): void {

this.dataManager.syncData('plant_sync', {
  type: 'plant_added',
  data: plant,
  timestamp: Date.now()
});

private syncMoistureData(data: any): void {

this.dataManager.syncData('moisture_sync', {
  type: 'moisture_data',
  data: data,
  timestamp: Date.now()
});

private syncWateringRecord(record: WateringRecord): void {

this.dataManager.syncData('watering_sync', {
  type: 'watering_record',
  data: record,
  timestamp: Date.now()
});

private handleSyncData(data: any): void {

if (!data) return;

switch (data.type) {
  case 'plant_added':
    this.handlePlantAdded(data.data);
    break;
  case 'plant_removed':
    this.handlePlantRemoved(data.data.plantId);
    break;
  case 'moisture_data':
    this.handleMoistureUpdate(data.data);
    break;
  case 'watering_record':
    this.handleWateringRecord(data.data);
    break;
  case 'pump_status':
    this.handlePumpStatusUpdate(data.data);
    break;

}

private handlePlantAdded(plant: PlantInfo): void {
if (!this.plants.some(p => p.id === plant.id)) {
this.plants = […this.plants, plant];
this.notifyPlantAdded(plant);

private handlePlantRemoved(plantId: string): void {

this.plants = this.plants.filter(p => p.id !== plantId);
this.notifyPlantRemoved(plantId);

private handleMoistureUpdate(data: any): void {

this.notifyMoistureUpdated(data);

private handleWateringRecord(record: WateringRecord): void {

this.notifyWateringRecorded(record);

private handlePumpStatusUpdate(status: any): void {

this.notifyPumpStatusChanged(status);

private notifyPlantAdded(plant: PlantInfo): void {

this.listeners.forEach(listener => {
  listener.onPlantAdded?.(plant);
});

private notifyPlantRemoved(plantId: string): void {

this.listeners.forEach(listener => {
  listener.onPlantRemoved?.(plantId);
});

private notifyMoistureUpdated(data: any): void {

this.listeners.forEach(listener => {
  listener.onMoistureUpdated?.(data);
});

private notifyWateringRecorded(record: WateringRecord): void {

this.listeners.forEach(listener => {
  listener.onWateringRecorded?.(record);
});

private notifyPumpStatusChanged(status: any): void {

this.listeners.forEach(listener => {
  listener.onPumpStatusChanged?.(status);
});

public addListener(listener: IrrigationListener): void {

if (!this.listeners.includes(listener)) {
  this.listeners.push(listener);

}

public removeListener(listener: IrrigationListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
}

interface IrrigationListener {
onPlantAdded?(plant: PlantInfo): void;
onPlantRemoved?(plantId: string): void;
onMoistureUpdated?(data: any): void;
onWateringRecorded?(record: WateringRecord): void;
onPumpStatusChanged?(status: any): void;
export const irrigationService = IrrigationService.getInstance();

主界面实现

// MainScreen.ets
import { irrigationService } from ‘./IrrigationService’;
import { PlantInfo, WateringRecord } from ‘./PlantTypes’;

@Component
export struct MainScreen {
@State hasPermission: boolean = false;
@State plants: PlantInfo[] = [];
@State moistureLevel: number = 0;
@State isPumpOn: boolean = false;
@State wateringRecords: WateringRecord[] = [];
@State showAddPlantDialog: boolean = false;
@State newPlant: PlantInfo = this.createEmptyPlant();

build() {
Column() {
// 标题栏
Row() {
Text(‘智能植物浇灌’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)

    Button(this.hasPermission ? '添加植物' : '授权')
      .width(100)
      .onClick(() => {
        if (this.hasPermission) {
          this.showAddPlantDialog = true;

else {

          this.requestPermissions();

})

.padding(10)

  .width('100%')
  
  // 湿度指示器
  Column() {
    Text('土壤湿度')
      .fontSize(18)
      .margin({ bottom: 10 })
    
    Row() {
      Progress({
        value: this.moistureLevel,
        total: 100,
        style: ProgressStyle.Ring
      })
      .width(100)
      .height(100)
      
      Column() {
        Text(${this.moistureLevel}%)
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        
        Text(this.getMoistureStatusText())
          .fontSize(16)
          .fontColor(this.getMoistureStatusColor())

.margin({ left: 20 })

}

  .padding(20)
  .width('90%')
  .backgroundColor('#FFFFFF')
  .borderRadius(8)
  .margin({ bottom: 20 })
  
  // 水泵状态
  Row() {
    Text('水泵状态:')
      .fontSize(16)
      .margin({ right: 10 })
    
    Text(this.isPumpOn ? '运行中' : '停止')
      .fontSize(16)
      .fontColor(this.isPumpOn ? '#4CAF50' : '#F44336')
    
    if (this.isPumpOn) {
      Button('停止')
        .width(80)
        .height(30)
        .margin({ left: 20 })
        .onClick(() => {
          irrigationService.stopWatering();
        })

}

  .padding(10)
  .width('90%')
  .backgroundColor('#FFFFFF')
  .borderRadius(8)
  .margin({ bottom: 20 })
  
  // 植物列表
  if (this.plants.length > 0) {
    Column() {
      Text('我的植物')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })
      
      List({ space: 10 }) {
        ForEach(this.plants, (plant) => {
          ListItem() {
            Row() {
              Image(plant.image)
                .width(60)
                .height(60)
                .borderRadius(8)
                .margin({ right: 15 })
              
              Column() {
                Text(plant.name)
                  .fontSize(16)
                  .fontWeight(FontWeight.Bold)
                  .margin({ bottom: 5 })
                
                Text(需求湿度: ${plant.minMoisture}%)
                  .fontSize(14)
                  .fontColor('#666666')

.layoutWeight(1)

              Button('浇水')
                .width(80)
                .height(30)
                .onClick(() => {
                  irrigationService.startWatering(plant.id, plant.wateringDuration);
                })

.padding(10)

            .width('100%')

})

.height(200)

.width(‘100%’)

    .margin({ bottom: 20 })

else {

    Column() {
      Text('暂无植物')
        .fontSize(18)
        .margin({ bottom: 10 })
      
      Text('点击"添加植物"按钮添加您的植物')
        .fontSize(16)
        .fontColor('#666666')

.padding(20)

    .width('90%')
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    .margin({ bottom: 20 })

// 浇水记录

  Column() {
    Text('浇水记录')
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 10 })
    
    if (this.wateringRecords.length > 0) {
      List({ space: 5 }) {
        ForEach(this.wateringRecords.slice(0, 5), (record) => {
          ListItem() {
            Row() {
              const plant = this.plants.find(p => p.id === record.plantId);
              
              Text(plant?.name || '未知植物')
                .fontSize(14)
                .layoutWeight(1)
              
              Text(${record.duration}秒)
                .fontSize(14)
                .margin({ right: 10 })
              
              Text(this.formatTime(record.timestamp))
                .fontSize(14)
                .fontColor('#666666')

.padding(10)

            .width('100%')

})

.height(150)

else {

      Text('暂无浇水记录')
        .fontSize(16)
        .fontColor('#666666')

}

  .width('100%')
  .padding(15)
  .backgroundColor('#FFFFFF')
  .borderRadius(8)

.width(‘100%’)

.height('100%')
.padding(20)

// 添加植物对话框
if (this.showAddPlantDialog) {
  DialogComponent({
    title: '添加植物',
    content: this.buildPlantDialogContent(),
    confirm: {
      value: '添加',
      action: () => this.addPlant()
    },
    cancel: {
      value: '取消',
      action: () => {
        this.showAddPlantDialog = false;
        this.newPlant = this.createEmptyPlant();

}

  })

}

private buildPlantDialogContent(): void {
Column() {
TextInput({ placeholder: ‘植物名称’, text: this.newPlant.name })
.onChange((value: string) => {
this.newPlant.name = value;
})
.margin({ bottom: 15 })

  TextInput({ placeholder: '最小湿度(%)', text: this.newPlant.minMoisture.toString() })
    .onChange((value: string) => {
      this.newPlant.minMoisture = parseInt(value) || 0;
    })
    .margin({ bottom: 15 })
  
  TextInput({ placeholder: '浇水时长(秒)', text: this.newPlant.wateringDuration.toString() })
    .onChange((value: string) => {
      this.newPlant.wateringDuration = parseInt(value) || 0;
    })
    .margin({ bottom: 15 })
  
  Button('选择图片')
    .width(200)
    .onClick(() => {
      this.pickPlantImage();
    })

.padding(20)

private createEmptyPlant(): PlantInfo {

return {
  id: '',
  name: '',
  image: $r('app.media.ic_plant_default'),
  minMoisture: 50,
  wateringDuration: 10,
  addedAt: Date.now()
};

private getMoistureStatusText(): string {

if (this.moistureLevel < 30) return '非常干燥';
if (this.moistureLevel < 50) return '干燥';
if (this.moistureLevel < 70) return '适中';
return '湿润';

private getMoistureStatusColor(): string {

if (this.moistureLevel < 30) return '#F44336';
if (this.moistureLevel < 50) return '#FF9800';
if (this.moistureLevel < 70) return '#4CAF50';
return '#2196F3';

private formatTime(timestamp: number): string {

const date = new Date(timestamp);
return {date.getHours()}:{date.getMinutes().toString().padStart(2, '0')};

private async checkPermissions(): Promise<void> {

try {
  const permissions = [
    'ohos.permission.USE_IOT',
    'ohos.permission.DISTRIBUTED_DATASYNC'
  ];
  
  const result = await abilityAccessCtrl.verifyPermissions(
    getContext(),
    permissions
  );
  
  this.hasPermission = result.every(perm => perm.granted);

catch (err) {

  console.error('检查权限失败:', JSON.stringify(err));
  this.hasPermission = false;

}

private async requestPermissions(): Promise<void> {
this.hasPermission = await irrigationService.requestPermissions();

if (!this.hasPermission) {
  prompt.showToast({ message: '授权失败,无法使用浇灌功能' });

}

private async pickPlantImage(): Promise<void> {
try {
const picker = new photo.Picker();
const result = await picker.select({
type: photo.PickerType.IMAGE,
maxSelectNumber: 1
});

  if (result.photoUris.length > 0) {
    const file = await fileIo.open(result.photoUris[0], fileIo.OpenMode.READ_ONLY);
    const buffer = await fileIo.read(file.fd, { length: 0 });
    await fileIo.close(file.fd);
    
    this.newPlant.image = buffer.buffer;

} catch (err) {

  console.error('选择图片失败:', JSON.stringify(err));
  prompt.showToast({ message: '选择图片失败,请重试' });

}

private async addPlant(): Promise<void> {
if (!this.newPlant.name.trim()) {
prompt.showToast({ message: ‘请输入植物名称’ });
return;
if (this.newPlant.minMoisture <= 0 || this.newPlant.minMoisture > 100) {

  prompt.showToast({ message: '请输入有效的湿度值(1-100)' });
  return;

if (this.newPlant.wateringDuration <= 0) {

  prompt.showToast({ message: '请输入有效的浇水时长' });
  return;

try {

  const plant: PlantInfo = {
    ...this.newPlant,
    id: Date.now().toString(),
    addedAt: Date.now()
  };
  
  await irrigationService.addPlant(plant);
  
  this.showAddPlantDialog = false;
  this.newPlant = this.createEmptyPlant();

catch (err) {

  console.error('添加植物失败:', JSON.stringify(err));
  prompt.showToast({ message: '添加植物失败,请重试' });

}

private handlePlantAdded(plant: PlantInfo): void {
this.plants = […this.plants, plant];
private handlePlantRemoved(plantId: string): void {

this.plants = this.plants.filter(p => p.id !== plantId);

private handleMoistureUpdated(data: any): void {

this.moistureLevel = data.moisture;

private handleWateringRecorded(record: WateringRecord): void {

this.wateringRecords = [record, ...this.wateringRecords];

private handlePumpStatusChanged(status: any): void {

this.isPumpOn = status.isOn;

}

类型定义

// PlantTypes.ets
export interface PlantInfo {
id: string;
name: string;
image: Resource | ArrayBuffer;
minMoisture: number; // 最小湿度百分比
wateringDuration: number; // 浇水时长(秒)
addedAt: number;
export interface WateringRecord {

plantId: string;
duration: number; // 浇水时长(秒)
timestamp: number;

三、项目配置与权限
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.USE_IOT”,

    "reason": "控制灌溉设备和传感器"
  },

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "同步灌溉数据"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true

]

}

四、总结与扩展

本智能植物浇灌系统实现了以下核心功能:
实时监测:持续监测土壤湿度变化

智能灌溉:根据植物需求自动控制浇水

多植物管理:支持多种植物不同湿度需求

远程控制:通过多设备远程查看和控制

历史记录:保存浇水记录便于分析

扩展方向:
环境监测:增加光照、温度等环境监测

植物数据库:建立植物养护知识库

生长记录:记录植物生长变化

智能预测:预测最佳浇水时间

社区分享:分享植物养护经验

AI诊断:通过图像识别植物健康状况

通过HarmonyOS的分布式技术,我们构建了一个智能化的植物养护系统,能够自动监测植物需求并进行精准灌溉,同时支持多设备间的数据同步和协同控制,让植物养护变得更加简单高效。

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