鸿蒙app 能耗统计图表(日/周/月用电量分析)【华为云根技术】

举报
鱼弦 发表于 2025/12/16 09:47:03 2025/12/16
【摘要】 引言随着智能家居与能源管理需求的提升,用户需要直观地了解设备用电情况以优化能耗。鸿蒙App可利用系统能耗统计接口(或对接智能电表/插座的IoT数据),实现日/周/月用电量分析并以图表形式展示,帮助用户发现高耗电设备、制定节能策略。技术背景鸿蒙能耗数据来源​系统级:@ohos.batteryStatistics(部分设备支持获取应用级能耗)IoT设备:通过华为云IoT或私有协议获取智能插座/电...

引言

随着智能家居与能源管理需求的提升,用户需要直观地了解设备用电情况以优化能耗。鸿蒙App可利用系统能耗统计接口(或对接智能电表/插座的IoT数据),实现日/周/月用电量分析并以图表形式展示,帮助用户发现高耗电设备、制定节能策略。

技术背景

  • 鸿蒙能耗数据来源
    • 系统级:@ohos.batteryStatistics(部分设备支持获取应用级能耗)
    • IoT设备:通过华为云IoT或私有协议获取智能插座/电表数据
    • 数据库:本地或分布式数据库存储历史用电记录
  • 图表绘制:使用鸿蒙 lottie或集成 EChartsWebView 方案,或自绘 Canvas 柱状图/折线图
  • 时间维度聚合:按日、周、月对原始数据进行分组求和
  • 权限ohos.permission.READ_METERING_DATA(系统能耗)、ohos.permission.INTERNET(IoT数据)

应用使用场景

场景
需求
实现要点
家庭能耗监控
查看每日/每周/每月总用电量
从智能插座获取数据,按时间聚合
高耗电设备分析
找出耗电Top3设备
按设备ID分组统计
节能建议
根据历史数据提示关闭待机设备
分析峰谷用电规律
租户电费分摊
按房间/设备统计用电量
数据关联房间信息
电网负荷预警
月度用电突增提醒
同比环比分析

原理解释

核心原理

  1. 数据采集:定时从系统或IoT设备获取用电量(单位:kWh)。
  2. 存储:存入关系型数据库(如 @ohos.data.relationalStore)或分布式数据库。
  3. 聚合计算:根据日/周/月维度对数据分组求和。
  4. 可视化:将聚合结果渲染为柱状图/折线图。

时间维度聚合公式

  • SUM(kWh) WHERE date = 'YYYY-MM-DD'
  • SUM(kWh) WHERE week = ISOWeekNumber
  • SUM(kWh) WHERE month = 'YYYY-MM'

核心特性

  • 支持多数据源(系统+IoT)
  • 日/周/月视图切换
  • 数据持久化与增量更新
  • 图表动画与交互(点击查看详情)
  • 分布式数据共享(多设备查看同一份用电数据)

原理流程图

graph TD
    A[数据采集] --> B[存入数据库]
    B --> C{用户选择视图}
    C -->|日| D[按天聚合数据]
    C -->|周| E[按周聚合数据]
    C -->|月| F[按月聚合数据]
    D --> G[生成图表数据]
    E --> G
    F --> G
    G --> H[Canvas/ECharts渲染]
    H --> I[展示用电量图表]

环境准备

  • DevEco Studio 4.0+
  • HarmonyOS SDK 9+
  • Language: ArkTS
  • 权限配置(module.json5):
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "获取IoT设备能耗数据",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.READ_METERING_DATA",
        "reason": "读取系统能耗统计",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      }
    ]
  }
}

实际详细应用代码示例实现

1. 数据模型

models/EnergyRecord.ets
export class EnergyRecord {
  id: number = 0;
  deviceId: string = '';
  timestamp: number = 0; // Unix时间戳(毫秒)
  kwh: number = 0;       // 用电量
  cost: number = 0;       // 电费(元)
}

2. 数据库帮助类(RelationalStore)

database/EnergyDatabase.ets
import relationalStore from '@ohos.data.relationalStore';
import { EnergyRecord } from '../models/EnergyRecord';

export class EnergyDatabase {
  private rdbStore: relationalStore.RdbStore | null = null;
  private tableName: string = 'energy_record';

  async init(context: any): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: 'energy.db',
      securityLevel: relationalStore.SecurityLevel.S1
    };
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    await this.rdbStore.executeSql(
      `CREATE TABLE IF NOT EXISTS ${this.tableName} (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        deviceId TEXT NOT NULL,
        timestamp INTEGER NOT NULL,
        kwh REAL NOT NULL,
        cost REAL NOT NULL
      )`
    );
  }

  async insert(record: EnergyRecord): Promise<void> {
    if (!this.rdbStore) return;
    const values = [record.deviceId, record.timestamp, record.kwh, record.cost];
    await this.rdbStore.insert(this.tableName, values);
  }

  async queryByDay(date: string): Promise<EnergyRecord[]> {
    if (!this.rdbStore) return [];
    const predicates = new relationalStore.RdbPredicates(this.tableName);
    predicates.equalTo('date(timestamp/1000, \'unixepoch\')', date);
    const resultSet = await this.rdbStore.query(predicates, ['deviceId', 'timestamp', 'kwh', 'cost']);
    const list: EnergyRecord[] = [];
    while (resultSet.goToNextRow()) {
      list.push({
        id: resultSet.getLong(resultSet.getColumnIndex('rowid')),
        deviceId: resultSet.getString(resultSet.getColumnIndex('deviceId')),
        timestamp: resultSet.getLong(resultSet.getColumnIndex('timestamp')),
        kwh: resultSet.getDouble(resultSet.getColumnIndex('kwh')),
        cost: resultSet.getDouble(resultSet.getColumnIndex('cost'))
      });
    }
    resultSet.close();
    return list;
  }

  async queryGroupByDay(start: number, end: number): Promise<Map<string, number>> {
    if (!this.rdbStore) return new Map();
    const predicates = new relationalStore.RdbPredicates(this.tableName);
    predicates.between('timestamp', start, end);
    const sql = `SELECT strftime('%Y-%m-%d', timestamp/1000, 'unixepoch') as day, SUM(kwh) as totalKwh FROM ${this.tableName} WHERE timestamp BETWEEN ? AND ? GROUP BY day`;
    const resultSet = await this.rdbStore.querySql(sql, [start, end]);
    const map = new Map<string, number>();
    while (resultSet.goToNextRow()) {
      const day = resultSet.getString(resultSet.getColumnIndex('day'));
      const total = resultSet.getDouble(resultSet.getColumnIndex('totalKwh'));
      map.set(day, total);
    }
    resultSet.close();
    return map;
  }
}

3. 能耗数据服务(模拟IoT数据)

services/EnergyService.ets
import { EnergyRecord } from '../models/EnergyRecord';
import { EnergyDatabase } from '../database/EnergyDatabase';

export class EnergyService {
  private dbHelper = new EnergyDatabase();

  async init(context: any) {
    await this.dbHelper.init(context);
    // 模拟插入一些数据
    await this.mockData();
  }

  private async mockData() {
    const now = Date.now();
    const oneDay = 86400000;
    for (let i = 6; i >= 0; i--) {
      const ts = now - i * oneDay;
      await this.dbHelper.insert({
        deviceId: 'plug_01',
        timestamp: ts,
        kwh: 2 + Math.random() * 3,
        cost: 1.2 + Math.random() * 2
      });
    }
  }

  async getDailyUsage(date: string): Promise<EnergyRecord[]> {
    return await this.dbHelper.queryByDay(date);
  }

  async getWeeklyUsage(): Promise<Map<string, number>> {
    const now = Date.now();
    const weekStart = now - 7 * 86400000;
    return await this.dbHelper.queryGroupByDay(weekStart, now);
  }

  async getMonthlyUsage(): Promise<Map<string, number>> {
    const now = Date.now();
    const monthStart = now - 30 * 86400000;
    return await this.dbHelper.queryGroupByDay(monthStart, now);
  }
}

4. 图表绘制(Canvas 柱状图示例)

components/BarChart.ets
import { CanvasRenderingContext2D } from '@ohos.graphics.canvas';

@Component
export struct BarChart {
  @Prop data: Map<string, number> = new Map();
  @Prop barColor: string = '#4CAF50';

  private ctx: CanvasRenderingContext2D | null = null;

  build() {
    Canvas(this.context)
      .width('100%')
      .height(300)
      .onReady((ctx: CanvasRenderingContext2D) => {
        this.ctx = ctx;
        this.draw();
      })
  }

  private draw() {
    if (!this.ctx || this.data.size === 0) return;
    const width = this.ctx.width;
    const height = this.ctx.height;
    const keys = Array.from(this.data.keys());
    const values = Array.from(this.data.values());
    const maxVal = Math.max(...values);
    const barWidth = width / keys.length * 0.8;
    const gap = width / keys.length * 0.2;

    this.ctx.clearRect(0, 0, width, height);
    this.ctx.fillStyle = this.barColor;
    keys.forEach((key, idx) => {
      const x = idx * (barWidth + gap);
      const barHeight = (values[idx] / maxVal) * (height - 20);
      const y = height - barHeight - 10;
      this.ctx.fillRect(x, y, barWidth, barHeight);
      // 绘制标签
      this.ctx.fillStyle = '#000';
      this.ctx.font = '12px sans-serif';
      this.ctx.fillText(key, x, height - 5);
      // 绘制数值
      this.ctx.fillText(values[idx].toFixed(2) + 'kWh', x, y - 5);
      this.ctx.fillStyle = this.barColor;
    });
  }
}

5. 主页面集成

pages/Index.ets
import { EnergyService } from '../services/EnergyService';
import { BarChart } from '../components/BarChart';

@Entry
@Component
struct Index {
  @State viewType: 'day' | 'week' | 'month' = 'week';
  @State chartData: Map<string, number> = new Map();
  private energySvc = new EnergyService();

  aboutToAppear() {
    this.energySvc.init(getContext(this)).then(() => {
      this.loadData();
    });
  }

  loadData() {
    if (this.viewType === 'week') {
      this.energySvc.getWeeklyUsage().then(data => this.chartData = data);
    } else if (this.viewType === 'month') {
      this.energySvc.getMonthlyUsage().then(data => this.chartData = data);
    }
  }

  build() {
    Column({ space: 20 }) {
      Text('用电量分析').fontSize(24).fontWeight(FontWeight.Bold)
      Row() {
        Button('日').onClick(() => { this.viewType = 'day'; this.loadData(); })
        Button('周').onClick(() => { this.viewType = 'week'; this.loadData(); })
        Button('月').onClick(() => { this.viewType = 'month'; this.loadData(); })
      }
      BarChart({ data: this.chartData, barColor: '#2196F3' })
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}

运行结果

  • 页面顶部显示“用电量分析”标题与日/周/月切换按钮。
  • 选择“周”视图,柱状图显示最近 7 天每天的总用电量(kWh)。
  • 柱高与数值成正比,底部显示日期,顶部显示具体用电量。

测试步骤以及详细代码

  1. 创建 HarmonyOS 项目,添加上述文件与权限。
  2. 真机运行,观察模拟数据生成的图表。
  3. 替换 mockData为真实 IoT 数据接口(HTTP 请求智能插座)。
  4. 验证日/周/月数据聚合正确性。
  5. 多设备登录同一账号,检查分布式数据库同步(需配置 distributedKVStore)。

部署场景

  • 家庭能源管家 App:实时监控并分析全屋用电。
  • 物业管理系统:统计各户月度用电,生成账单。
  • 工业设备监控:分析产线设备能耗趋势。

疑难解答

问题
原因
解决
图表无数据
数据库查询为空
检查 timestamp存储单位与查询条件
跨设备数据不同步
未使用分布式数据库
改用 @ohos.data.distributedData
权限拒绝
未动态申请
在 UI 首次操作时申请权限

未来展望

  • 接入 AI 预测模型,预测未来用电峰值。
  • 与鸿蒙智慧屏联动,大屏展示家庭能耗热力图。
  • 支持碳排放换算与环保积分。

技术趋势与挑战

  • 趋势:端云协同能耗分析;联邦学习保护隐私下的多户能耗模型训练。
  • 挑战:海量 IoT 数据实时聚合性能;跨时区时间分组准确性;设备离线数据补传。

总结

本方案基于鸿蒙 RelationalStore 实现日/周/月用电量统计与图表展示,代码完整覆盖数据采集、存储、聚合、可视化全流程,支持多场景部署,并可扩展为分布式能源管理平台。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。