鸿蒙跨端家庭能耗监测系统开发指南

鸿蒙跨端家庭能耗监测系统开发指南

一、系统架构设计

基于HarmonyOS的智能家居和分布式技术,构建家庭能耗监测系统:

  1. ​数据采集层​​:通过智能家居接口获取用电数据
  2. ​数据处理层​​:分析能耗数据并生成统计信息
  3. ​可视化层​​:展示实时和历史能耗数据
  4. ​跨端同步层​​:多设备间同步能耗数据和告警信息

https://example.com/harmony-energy-monitor-arch.png

二、核心代码实现

1. 能耗监测服务

// EnergyService.ets
import smartHome from '@ohos.smartHome.energy';
import distributedData from '@ohos.distributedData';
import { EnergyData, DeviceConsumption, EnergyAlert } from './EnergyTypes';

class EnergyService {
  private static instance: EnergyService = null;
  private energyManager: smartHome.EnergyManager;
  private dataManager: distributedData.DataManager;
  private listeners: EnergyListener[] = [];
  private currentConsumption: Record<string, DeviceConsumption> = {};
  private alerts: EnergyAlert[] = [];
  
  private constructor() {
    this.initEnergyManager();
    this.initDataManager();
  }
  
  public static getInstance(): EnergyService {
    if (!EnergyService.instance) {
      EnergyService.instance = new EnergyService();
    }
    return EnergyService.instance;
  }
  
  private initEnergyManager(): void {
    try {
      this.energyManager = smartHome.createEnergyManager(getContext());
      
      // 注册能耗数据监听
      this.energyManager.on('consumptionChange', (data) => {
        this.handleConsumptionData(data);
      });
      
      // 启动实时监测
      this.energyManager.startMonitoring({
        interval: 5000, // 5秒更新一次
        callback: (err, data) => {
          if (err) {
            console.error('能耗监测错误:', JSON.stringify(err));
          }
        }
      });
    } catch (err) {
      console.error('初始化能耗管理器失败:', JSON.stringify(err));
    }
  }
  
  private initDataManager(): void {
    this.dataManager = distributedData.createDataManager({
      bundleName: 'com.example.energy',
      area: distributedData.Area.GLOBAL,
      isEncrypted: true
    });
    
    this.dataManager.registerDataListener('energy_sync', (data) => {
      this.handleSyncData(data);
    });
  }
  
  public async requestPermissions(): Promise<boolean> {
    try {
      const permissions = [
        'ohos.permission.SMART_HOME_ENERGY',
        '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 getDeviceList(): Promise<smartHome.DeviceInfo[]> {
    try {
      return await this.energyManager.getDevices();
    } catch (err) {
      console.error('获取设备列表失败:', JSON.stringify(err));
      throw err;
    }
  }
  
  public async getCurrentConsumption(): Promise<Record<string, DeviceConsumption>> {
    try {
      const devices = await this.getDeviceList();
      const consumptionData: Record<string, DeviceConsumption> = {};
      
      for (const device of devices) {
        const data = await this.energyManager.getDeviceConsumption(device.deviceId);
        consumptionData[device.deviceId] = {
          deviceId: device.deviceId,
          deviceName: device.deviceName,
          power: data.power,
          current: data.current,
          voltage: data.voltage,
          timestamp: Date.now()
        };
      }
      
      this.currentConsumption = consumptionData;
      return consumptionData;
    } catch (err) {
      console.error('获取当前能耗失败:', JSON.stringify(err));
      throw err;
    }
  }
  
  public async getHistoryConsumption(period: 'day' | 'week' | 'month'): Promise<EnergyData[]> {
    try {
      const endTime = Date.now();
      let startTime = endTime;
      
      switch (period) {
        case 'day':
          startTime = endTime - 24 * 60 * 60 * 1000;
          break;
        case 'week':
          startTime = endTime - 7 * 24 * 60 * 60 * 1000;
          break;
        case 'month':
          startTime = endTime - 30 * 24 * 60 * 60 * 1000;
          break;
      }
      
      return await this.energyManager.queryHistory({
        startTime,
        endTime,
        interval: period === 'day' ? 'hour' : 'day'
      });
    } catch (err) {
      console.error('获取历史数据失败:', JSON.stringify(err));
      throw err;
    }
  }
  
  public async setAlert(deviceId: string, threshold: number): Promise<EnergyAlert> {
    try {
      const alert: EnergyAlert = {
        id: `${deviceId}_${Date.now()}`,
        deviceId,
        threshold,
        isActive: true,
        createdAt: Date.now()
      };
      
      this.alerts = [...this.alerts, alert];
      this.syncAlert(alert);
      
      return alert;
    } catch (err) {
      console.error('设置告警失败:', JSON.stringify(err));
      throw err;
    }
  }
  
  private handleConsumptionData(data: smartHome.ConsumptionData): void {
    const deviceId = data.deviceId;
    const consumption: DeviceConsumption = {
      deviceId,
      deviceName: data.deviceName || `设备_${deviceId.slice(-4)}`,
      power: data.power,
      current: data.current,
      voltage: data.voltage,
      timestamp: Date.now()
    };
    
    this.currentConsumption[deviceId] = consumption;
    this.notifyConsumptionUpdated(consumption);
    this.syncConsumption(consumption);
    
    // 检查告警
    this.checkAlerts(consumption);
  }
  
  private checkAlerts(consumption: DeviceConsumption): void {
    const deviceAlerts = this.alerts.filter(
      alert => alert.deviceId === consumption.deviceId && alert.isActive
    );
    
    deviceAlerts.forEach(alert => {
      if (consumption.power >= alert.threshold) {
        this.triggerAlert(alert, consumption);
      }
    });
  }
  
  private triggerAlert(alert: EnergyAlert, consumption: DeviceConsumption): void {
    const notification: notification.NotificationRequest = {
      id: parseInt(alert.id),
      content: {
        title: '能耗告警',
        text: `${consumption.deviceName} 能耗超过阈值 ${alert.threshold}W`,
        additionalText: `当前功率: ${consumption.power.toFixed(2)}W`,
        tapAction: {
          bundleName: 'com.example.energy',
          abilityName: 'MainAbility'
        }
      }
    };
    
    notification.publish(notification).catch(err => {
      console.error('发送通知失败:', JSON.stringify(err));
    });
    
    this.notifyAlertTriggered(alert, consumption);
  }
  
  private syncConsumption(consumption: DeviceConsumption): void {
    this.dataManager.syncData('consumption_sync', {
      type: 'consumption_update',
      data: consumption,
      timestamp: Date.now()
    });
  }
  
  private syncAlert(alert: EnergyAlert): void {
    this.dataManager.syncData('alert_sync', {
      type: 'alert_set',
      data: alert,
      timestamp: Date.now()
    });
  }
  
  private handleSyncData(data: any): void {
    if (!data) return;
    
    switch (data.type) {
      case 'consumption_update':
        this.handleConsumptionUpdate(data.data);
        break;
      case 'alert_set':
        this.handleAlertSet(data.data);
        break;
      case 'alert_triggered':
        this.handleAlertTriggered(data.data);
        break;
    }
  }
  
  private handleConsumptionUpdate(consumption: DeviceConsumption): void {
    this.currentConsumption[consumption.deviceId] = consumption;
    this.notifyConsumptionUpdated(consumption);
  }
  
  private handleAlertSet(alert: EnergyAlert): void {
    const existingIndex = this.alerts.findIndex(a => a.id === alert.id);
    
    if (existingIndex >= 0) {
      this.alerts[existingIndex] = alert;
    } else {
      this.alerts = [...this.alerts, alert];
    }
    
    this.notifyAlertSet(alert);
  }
  
  private handleAlertTriggered(data: { alert: EnergyAlert, consumption: DeviceConsumption }): void {
    this.notifyAlertTriggered(data.alert, data.consumption);
  }
  
  private notifyConsumptionUpdated(consumption: DeviceConsumption): void {
    this.listeners.forEach(listener => {
      listener.onConsumptionUpdated?.(consumption);
    });
  }
  
  private notifyAlertSet(alert: EnergyAlert): void {
    this.listeners.forEach(listener => {
      listener.onAlertSet?.(alert);
    });
  }
  
  private notifyAlertTriggered(alert: EnergyAlert, consumption: DeviceConsumption): void {
    this.listeners.forEach(listener => {
      listener.onAlertTriggered?.(alert, consumption);
    });
  }
  
  public addListener(listener: EnergyListener): void {
    if (!this.listeners.includes(listener)) {
      this.listeners.push(listener);
    }
  }
  
  public removeListener(listener: EnergyListener): void {
    this.listeners = this.listeners.filter(l => l !== listener);
  }
}

interface EnergyListener {
  onConsumptionUpdated?(consumption: DeviceConsumption): void;
  onAlertSet?(alert: EnergyAlert): void;
  onAlertTriggered?(alert: EnergyAlert, consumption: DeviceConsumption): void;
}

export const energyService = EnergyService.getInstance();

2. 主界面实现

// MainScreen.ets
import { energyService } from './EnergyService';
import { DeviceConsumption, EnergyData, EnergyAlert } from './EnergyTypes';

@Component
export struct MainScreen {
  @State hasPermission: boolean = false;
  @State currentConsumption: Record<string, DeviceConsumption> = {};
  @State historyData: EnergyData[] = [];
  @State selectedPeriod: 'day' | 'week' | 'month' = 'day';
  @State showAlertDialog: boolean = false;
  @State newAlertThreshold: number = 1000;
  @State selectedDevice: string = '';
  @State alerts: EnergyAlert[] = [];
  
  build() {
    Column() {
      // 标题栏
      Row() {
        Text('家庭能耗监测')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        
        Button(this.hasPermission ? '设置告警' : '授权')
          .width(100)
          .onClick(() => {
            if (this.hasPermission) {
              this.showAlertDialog = true;
            } else {
              this.requestPermissions();
            }
          })
      }
      .padding(10)
      .width('100%')
      
      // 实时能耗卡片
      Column() {
        Text('实时能耗')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })
        
        if (Object.keys(this.currentConsumption).length === 0) {
          Text('无能耗数据')
            .fontSize(16)
            .fontColor('#666666')
        } else {
          Grid() {
            ForEach(Object.values(this.currentConsumption), (consumption) => {
              GridItem() {
                Column() {
                  Text(consumption.deviceName)
                    .fontSize(16)
                    .fontWeight(FontWeight.Bold)
                    .margin({ bottom: 5 })
                  
                  Row() {
                    Text(`${consumption.power.toFixed(2)}`)
                      .fontSize(24)
                      .fontColor('#FF5252')
                    
                    Text('W')
                      .fontSize(16)
                      .fontColor('#666666')
                      .margin({ left: 5, top: 10 })
                  }
                  
                  Row() {
                    Text(`${consumption.voltage.toFixed(1)}V`)
                      .fontSize(14)
                      .fontColor('#666666')
                      .margin({ right: 10 })
                    
                    Text(`${consumption.current.toFixed(2)}A`)
                      .fontSize(14)
                      .fontColor('#666666')
                  }
                  .margin({ top: 5 })
                }
                .padding(10)
                .width('100%')
                .backgroundColor('#FFFFFF')
                .borderRadius(8)
                .onClick(() => {
                  this.selectedDevice = consumption.deviceId;
                  this.showAlertDialog = true;
                })
              }
            })
          }
          .columnsTemplate('1fr 1fr')
          .rowsGap(10)
          .columnsGap(10)
        }
      }
      .padding(15)
      .width('100%')
      .backgroundColor('#F5F5F5')
      .borderRadius(8)
      .margin({ bottom: 20 })
      
      // 历史能耗图表
      Column() {
        Row() {
          Text('历史能耗')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .layoutWeight(1)
          
          Segmented({
            index: this.getPeriodIndex(),
            items: ['日', '周', '月'],
            onSelect: (index: number) => {
              this.changePeriod(index);
            }
          })
          .width(200)
        }
        .margin({ bottom: 10 })
        
        if (this.historyData.length === 0) {
          Column() {
            Text('无历史数据')
              .fontSize(16)
              .margin({ bottom: 5 })
            
            Text('选择时间段查看历史能耗')
              .fontSize(14)
              .fontColor('#666666')
          }
          .padding(20)
          .width('90%')
          .backgroundColor('#FFFFFF')
          .borderRadius(8)
          .margin({ top: 20 })
        } else {
          EnergyChart({
            data: this.historyData,
            period: this.selectedPeriod
          })
          .height(250)
        }
      }
      .padding(15)
      .width('100%')
      .backgroundColor('#F5F5F5')
      .borderRadius(8)
      .margin({ bottom: 20 })
      
      // 告警列表
      Column() {
        Text('能耗告警')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .margin({ bottom: 10 })
        
        if (this.alerts.length === 0) {
          Text('无告警设置')
            .fontSize(16)
            .fontColor('#666666')
        } else {
          List({ space: 10 }) {
            ForEach(this.alerts, (alert) => {
              ListItem() {
                Row() {
                  Column() {
                    Text(this.getDeviceName(alert.deviceId))
                      .fontSize(16)
                      .fontWeight(FontWeight.Bold)
                      .margin({ bottom: 5 })
                    
                    Text(`阈值: ${alert.threshold}W`)
                      .fontSize(14)
                      .fontColor('#666666')
                  }
                  .layoutWeight(1)
                  
                  Toggle({
                    type: ToggleType.Switch,
                    isOn: alert.isActive,
                    onChange: (isOn) => {
                      this.toggleAlert(alert.id, isOn);
                    }
                  })
                }
                .padding(10)
                .width('100%')
              }
            })
          }
          .height(150)
        }
      }
      .padding(15)
      .width('100%')
      .backgroundColor('#F5F5F5')
      .borderRadius(8)
    }
    .width('100%')
    .height('100%')
    .padding(20)
    
    // 设置告警对话框
    if (this.showAlertDialog) {
      DialogComponent({
        title: '设置能耗告警',
        content: this.buildAlertDialogContent(),
        confirm: {
          value: '设置',
          action: () => this.setAlert()
        },
        cancel: {
          value: '取消',
          action: () => {
            this.showAlertDialog = false;
            this.newAlertThreshold = 1000;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值