基于开源时序数据库的家庭能耗监控与邮件告警系统实践
2026/8/19 12:09:31 网站建设 项目流程

1. 项目概述:一个能帮你省钱的“家庭能源哨兵”

最近几年,电费账单上的数字是不是越来越让你心惊肉跳了?尤其是夏天开空调、冬天开暖气,再加上各种智能设备24小时待机,能源消耗就像个无底洞。我自己也深受其扰,直到我动手搭建了这个“家庭能源节省邮件预警系统”。这玩意儿不是什么高深莫测的科研项目,你可以把它理解为你家的“能源哨兵”。它的核心任务很简单:实时监控家里的用电情况,一旦发现异常高耗能或者浪费行为,比如空调温度设得太低却忘了关、某个老旧电器突然“偷电”,它就会立刻给你的邮箱发一封警报邮件,提醒你及时处理。

这个系统特别适合两类家庭:一类是像我这样,对科技有点兴趣,愿意花点小钱和精力来换取长期节能收益的“技术型”家庭;另一类是经常出差或者记性不太好,担心家里电器“空转”造成浪费和安全隐患的家庭。它不依赖于任何昂贵的商业智能家居中枢,核心就是几个开源的软件组件和一块几十块钱的硬件,通过巧妙的组合,实现商业级能耗监控系统80%的功能。我实测运行了半年,成功帮我揪出了家里那台待机功耗异常高的老式饮水机,单这一项每月就省下了近20度电。接下来,我就把这套从硬件选型、软件搭建到规则调优的完整方案,毫无保留地分享给你。

2. 系统核心设计与架构思路

2.1 为什么选择“邮件预警”作为核心交互?

在短信、App推送、电话等各种通知方式中,我最终选择了邮件。这背后有几个很实际的考量。首先,普适性与可靠性。几乎每个人都有邮箱,且邮件服务(如QQ邮箱、163邮箱、企业邮箱)的到达率极高,很少被运营商拦截。相比之下,短信推送涉及第三方服务商和费用,App推送则要求用户手机必须安装特定应用并保持后台运行。其次,信息承载能力强。一封邮件里可以清晰地包含时间戳、具体设备名称、当前功率、超标阈值、历史曲线图(如果系统支持生成)等丰富信息,便于你快速定位问题。最后,成本与可控性。利用现有的邮件服务器(如QQ邮箱的SMTP服务)发送警报,几乎是零成本。自己搭建邮件发送模块,也能完全掌控发送频率和内容格式,避免骚扰。

整个系统的架构可以概括为“感知-汇聚-分析-执行”四层。最底层是感知层,即部署在家里的电能监测设备,负责采集原始电流、电压、功率数据。中间是汇聚与分析层,通常是一个运行在树莓派或旧电脑上的中心服务,它持续接收数据,并运行我们设定的能耗分析规则。最上层是执行层,当规则被触发时,这个层负责调用邮件发送服务,将警报内容封装成邮件发送出去。整个数据流是单向且清晰的,确保了系统的稳定和高效。

2.2 硬件选型:从入门到精准的三种方案

硬件是系统的“眼睛”,选对了事半功倍。根据你的预算和需求精度,主要有三种选择:

方案一:总路监测(成本最低,<100元)这是最经济的入门方案。你只需要一个智能插座计量插座。把它插在墙上的主插座,再将家里的总闸空开(或主要耗电设备的插头)插到它上面。它能监测整个电路或单个设备的总功率、电量。优点是部署简单、成本极低,非常适合监测冰箱、空调、热水器等单一重型电器,或者对全屋用电总量进行阈值告警。缺点是粒度粗,无法定位到具体是哪个小电器出了问题。

方案二:分路监测(性价比之选,200-500元)如果你想监测多个电路,比如单独看客厅、卧室、厨房的用电,就需要智能配电箱模块多路电量监测仪。这类设备通常安装在家庭配电箱里,通过钳形电流互感器(CT钳子)非侵入式地夹在每一条火线上,从而实现对各个回路独立的电量计量。这是目前DIY家庭能源监控的主流选择,既能获得分项数据,又不需要改动家中原有线路,安全性高。我自家用的就是这种方案。

方案三:高精度插排监测(数据最细,成本较高)对于极客或希望研究每个设备用电行为的用户,可以选择带有独立计量功能的智能插排。每个插孔都能独立计量,数据非常精细。但成本较高,且需要每个设备都插在上面,可能影响美观和插座数量。

注意:安全第一!无论选择哪种方案,涉及强电操作(如方案二安装CT钳子),务必关闭总闸,并由具备电工知识的人员操作。如果不确定,优先选择方案一(智能插座)这种即插即用的方式。

我最终选择了方案二,使用了一款支持本地通信(如Modbus RTU或TCP)的国产多路电量监测仪。理由很充分:首先,它提供分路数据,能让我知道是“厨房”还是“客厅”的用电异常。其次,本地通信协议意味着数据不经过厂商云服务器,隐私有保障,响应速度也更快。最后,它的成本在一次性的几百元,没有后续订阅费用。

3. 软件栈搭建与核心组件解析

硬件采集到数据后,需要一套软件来接收、存储、分析和告警。我采用的是经典且强大的开源组合:Telegraf + InfluxDB + Grafana + 自研Python告警服务

3.1 数据采集与中转:Telegraf

Telegraf 是 InfluxData 出品的数据采集代理,轻量且插件丰富。它的角色是“搬运工”,定期从硬件设备(通过Modbus、HTTP API等协议)读取数据,然后格式化并发送到时序数据库 InfluxDB。

配置 Telegraf 的核心是编写一个telegraf.conf配置文件。你需要配置两部分:

  1. 输入插件:定义数据从哪里来。以我的Modbus RTU设备为例,配置片段如下。这里定义了从串口/dev/ttyUSB0读取数据,指定了设备的从机地址、寄存器地址(对应电压、电流、功率等数据的存储位置),以及每个字段的名称和数据类型。

    [[inputs.modbus]] name = "home_energy" slave_id = 1 timeout = "1s" controller = "file:///dev/ttyUSB0" baud_rate = 9600 data_bits = 8 stop_bits = 1 parity = "N" [[inputs.modbus.metric]] name = "power_metrics" byte_order = "CDAB" data_type = "INT32" scale = 0.1 address = 0 measurement = "energy" fields = [["voltage", "V"], ["current", "A"], ["power", "W"]]

    关键参数解读:scale = 0.1表示原始寄存器值需要乘以0.1才是真实值(例如,寄存器读数是2200,实际电压是220V)。address需要根据你设备的通讯协议手册来填写,这是最容易出错的地方。

  2. 输出插件:定义数据到哪里去。这里很简单,就是指向本地的 InfluxDB 服务。

    [[outputs.influxdb]] urls = ["http://127.0.0.1:8086"] database = "home_energy" skip_database_creation = false

3.2 数据存储:InfluxDB

InfluxDB 是专为时序数据优化的数据库,读写速度极快,非常适合存储按时间顺序产生的能耗数据。安装后,你需要创建一个数据库(如home_energy)来存储数据。Telegraf 会自动将数据写入这里。它的优势在于能高效处理“时间序列”数据,例如“每5秒钟的功率值”,查询和聚合(如计算每小时用电量)性能远超传统关系型数据库。

3.3 数据可视化:Grafana

Grafana 是一个强大的数据可视化平台,它从 InfluxDB 中读取数据,绘制成直观的图表和仪表盘。通过 Grafana,你可以实时看到各回路的功率曲线、当日/当月用电量统计等。虽然我们的核心是邮件告警,但一个漂亮的仪表盘能让你对家庭用电模式一目了然,也是调试系统、验证数据准确性的重要工具。你可以设置不同的面板,分别展示总功率、各分路功率、以及用电量的柱状图。

3.4 告警大脑:自研Python服务

这是整个系统的“大脑”,负责执行判断逻辑。我选择用 Python 编写,因为它库丰富、开发快捷。这个服务主要做三件事:

  1. 定时查询:每隔一段时间(如1分钟),从 InfluxDB 查询最新数据。
  2. 规则判断:根据预设的规则进行判断。规则是系统的灵魂,我设定了以下几类:
    • 绝对值阈值:当某回路功率持续超过设定值(如客厅插座>500W超过5分钟),触发告警。这用于发现异常高耗能设备。
    • 相对变化率:当功率在短时间内急剧上升或下降(如厨房功率2分钟内上升1000W),触发告警。这有助于发现设备异常启停或故障。
    • 非活跃时段活动:在夜间睡眠时段(如凌晨1点至5点),如果某个通常不工作的回路(如书房)出现持续用电,触发告警。这用于发现忘记关闭的设备。
    • 电量预算:设置每日或每周用电量预算,当实际用量接近预算时,发送提醒邮件。
  3. 触发动作:当任何规则被触发时,调用邮件发送函数。

4. 核心环节实现:邮件告警服务详解

告警服务是用户直接感知的部分,它的稳定和友好至关重要。下面我详细拆解实现过程。

4.1 邮件发送模块的封装

我使用 Python 的smtplibemail库来发送邮件。为了健壮和易用,我将其封装成一个类EmailAlertSender

import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from datetime import datetime import logging class EmailAlertSender: def __init__(self, smtp_server, smtp_port, sender_email, sender_password): """ 初始化邮件发送器 :param smtp_server: SMTP服务器地址,如 'smtp.qq.com' :param smtp_port: 端口,QQ邮箱SSL为465,STARTTLS为587 :param sender_email: 发件人邮箱 :param sender_password: 授权码(注意:不是邮箱登录密码) """ self.smtp_server = smtp_server self.smtp_port = smtp_port self.sender_email = sender_email self.sender_password = sender_password self.logger = logging.getLogger(__name__) def send_alert(self, recipient_email, subject, content, is_html=False): """ 发送告警邮件 :param recipient_email: 收件人邮箱 :param subject: 邮件主题 :param content: 邮件正文内容 :param is_html: 是否为HTML格式 """ msg = MIMEMultipart('alternative') msg['From'] = self.sender_email msg['To'] = recipient_email msg['Subject'] = f"[家庭能耗告警] {subject} - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" # 根据格式附加正文 if is_html: part = MIMEText(content, 'html') else: part = MIMEText(content, 'plain') msg.attach(part) try: # 使用SSL连接(端口465) with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server: server.login(self.sender_email, self.sender_password) server.send_message(msg) self.logger.info(f"告警邮件发送成功至 {recipient_email}: {subject}") except Exception as e: self.logger.error(f"发送告警邮件失败: {e}", exc_info=True) # 使用示例 if __name__ == "__main__": # 配置发件人信息(以QQ邮箱为例) sender = EmailAlertSender( smtp_server='smtp.qq.com', smtp_port=465, sender_email='your_email@qq.com', sender_password='your_authorization_code' # 注意这里是授权码! ) # 发送一条测试告警 test_content = """ 警报!检测到异常能耗。 位置:客厅空调回路 时间:2023-10-27 14:30:00 当前功率:2150 W 设定阈值:2000 W 已持续超标:10 分钟。 建议:检查空调温度设置是否过低,或考虑关闭。 """ sender.send_alert('recipient@example.com', '客厅空调功率超标', test_content)

实操心得:关于“发件人密码”:这里最容易踩坑。大多数邮箱服务商(如QQ、163)为了安全,不允许直接用登录密码在第三方客户端发信。你必须登录邮箱网页版,在“设置”-“账户”中找到“POP3/IMAP/SMTP服务”选项,生成一个专用的“授权码”。这个授权码才是上面代码中sender_password应该填写的。直接填登录密码一定会失败。

4.2 告警规则引擎的实现

规则引擎需要定期从 InfluxDB 拉取数据并判断。我使用influxdb-client库进行查询。

from influxdb_client import InfluxDBClient, Point from influxdb_client.client.write_api import SYNCHRONOUS import time from typing import Dict, List class EnergyAlertEngine: def __init__(self, influxdb_url, token, org, bucket, email_sender): self.client = InfluxDBClient(url=influxdb_url, token=token, org=org) self.query_api = self.client.query_api() self.bucket = bucket self.email_sender = email_sender self.rules = self._load_rules() def _load_rules(self) -> List[Dict]: """加载告警规则,这里可以改为从配置文件读取""" return [ { "name": "客厅空调持续高功率", "measurement": "energy", "field": "power", "tags": {"circuit": "living_room_ac"}, "condition": "mean > 2000", # 平均功率大于2000瓦 "duration": "5m", # 持续5分钟 "recipient": "your_family@example.com" }, { "name": "夜间书房异常用电", "measurement": "energy", "field": "power", "tags": {"circuit": "study_room"}, "condition": "mean > 50", # 功率大于50瓦(待机以上) "time_range": ["01:00", "05:00"], # 仅在凌晨1点到5点生效 "recipient": "your_phone@example.com" }, # ... 更多规则 ] def _build_query(self, rule): """根据规则构建InfluxDB Flux查询语句""" # 这里是Flux查询语言示例,核心是过滤、窗口聚合、判断 query = f''' from(bucket: "{self.bucket}") |> range(start: -{rule.get("duration", "10m")}) |> filter(fn: (r) => r._measurement == "{rule['measurement']}") |> filter(fn: (r) => r._field == "{rule['field']}") ''' # 添加标签过滤 for tag_key, tag_value in rule.get("tags", {}).items(): query += f' |> filter(fn: (r) => r["{tag_key}"] == "{tag_value}")\n' # 按时间窗口聚合(例如,每1分钟计算一个平均值) query += f' |> aggregateWindow(every: 1m, fn: mean, createEmpty: false)\n' query += f' |> yield(name: "result")' return query def check_rules(self): """检查所有规则""" current_hour = time.localtime().tm_hour current_minute = time.localtime().tm_min current_time_str = f"{current_hour:02d}:{current_minute:02d}" for rule in self.rules: # 检查时间范围限制 time_range = rule.get("time_range") if time_range: start, end = time_range if not (start <= current_time_str <= end): continue # 不在告警时段内,跳过 # 执行查询 query = self._build_query(rule) try: tables = self.query_api.query(query) # 解析查询结果,判断是否触发条件 # 这里需要根据具体的condition进行解析,例如判断最后一个平均值是否>2000 # 为简化示例,假设查询结果最后一个值就是我们要判断的 for table in tables: for record in table.records: last_value = record.get_value() # 简单演示:如果条件字符串是 "mean > 2000",这里需要解析并比较 # 实际实现中,需要更复杂的逻辑来解析 condition if last_value > 2000: # 示例判断 alert_subject = f"规则触发:{rule['name']}" alert_content = f""" 检测到能耗异常! 规则:{rule['name']} 监测点:{rule.get('tags', {})} 当前值:{last_value} W 触发条件:{rule['condition']} 时间:{time.strftime('%Y-%m-%d %H:%M:%S')} """ self.email_sender.send_alert(rule['recipient'], alert_subject, alert_content) break # 触发一次后跳出当前规则的检查 except Exception as e: logging.error(f"检查规则 {rule['name']} 时查询失败: {e}") def run_forever(self, interval_seconds=60): """以固定间隔持续运行检查""" while True: self.check_rules() time.sleep(interval_seconds) # 主程序入口 if __name__ == "__main__": # 初始化邮件发送器 email_sender = EmailAlertSender(...) # 参数省略 # 初始化告警引擎 engine = EnergyAlertEngine( influxdb_url="http://localhost:8086", token="your_influxdb_token", org="your_org", bucket="home_energy", email_sender=email_sender ) # 开始运行 engine.run_forever(interval_seconds=60)

这个引擎的核心是_build_query方法,它动态生成 InfluxDB 的 Flux 查询语句。Flux 语言功能强大但有一定学习曲线,关键在于理解如何按时间范围过滤、按标签筛选、以及进行窗口聚合(比如计算每分钟的平均功率)。规则中的condition字段在实际完整实现中,需要一个小的解析器来将其转换为对查询结果的布尔判断。

4.3 系统集成与后台运行

为了让整个系统在树莓派或旧电脑上稳定地 7x24 小时运行,我们需要将其服务化。

  1. 使用 systemd 管理服务:这是 Linux 系统下管理后台服务的最佳实践。创建一个服务文件/etc/systemd/system/energy-alert.service

    [Unit] Description=Home Energy Alert System After=network.target influxdb.service [Service] Type=simple User=pi # 替换为你的用户名 WorkingDirectory=/home/pi/energy-alert # 替换为你的代码目录 ExecStart=/usr/bin/python3 /home/pi/energy-alert/alert_engine.py Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target

    然后执行:

    sudo systemctl daemon-reload sudo systemctl enable energy-alert.service sudo systemctl start energy-alert.service sudo systemctl status energy-alert.service # 检查状态

    这样,系统重启后服务也会自动启动。

  2. 日志记录:在 Python 代码中配置 logging 模块,将日志输出到文件(如/var/log/energy-alert.log),便于后期排查问题。

    import logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('/var/log/energy-alert.log'), logging.StreamHandler() # 同时在控制台输出 ] )

5. 规则调优与高级场景实践

系统跑起来只是第一步,让告警变得“聪明”且“不烦人”才是关键。这就需要精细化的规则调优。

5.1 避免告警风暴:设置抑制与聚合

初期我最头疼的就是“告警风暴”。比如空调启动时,功率会从0飙升到2000W以上,如果规则是“功率>2000W就告警”,那么每分钟检查一次,就会连续发出好几封邮件。解决方法有两个:

  • 持续时间阈值:就像我上面规则中的"duration": "5m",要求异常状态持续一段时间才触发。这能过滤掉短暂的功率尖峰。
  • 告警抑制:在代码中实现一个简单的抑制逻辑。当一条告警发出后,标记该规则进入“冷却期”,在接下来的若干时间内(如30分钟),即使条件再次满足,也不再发送新告警。这可以防止对同一个持续性问题进行轰炸。

5.2 实现用电量预算告警

单纯的功率告警是“瞬时”的,而用电量预算是“累积”的,更有规划意义。这需要我们在 InfluxDB 中存储“电能”(单位:千瓦时kWh)字段,或者通过功率对时间积分来计算。

  1. 数据准备:确保你的电表数据能提供累计电能,或者 Telegraf 配置了计算功能(有些插件支持)。也可以每小时运行一个任务,查询过去一小时的功率平均值(kW),乘以时间(1小时),得到该小时的电量(kWh),并存入 InfluxDB 的一个专门 measurement(如energy_consumption)中。

  2. 预算规则:每天凌晨,从数据库读取当日的用电预算(可以硬编码或从配置文件读取)。然后,每隔一段时间(如每6小时)查询当日已累计的电量,计算已用比例。

    # 伪代码逻辑 daily_budget_kwh = 10 # 每日预算10度电 consumed_today = query_influxdb("SELECT sum(energy) FROM energy_consumption WHERE time > today()") usage_ratio = consumed_today / daily_budget_kwh if usage_ratio > 0.8: # 用量超过80% send_alert("当日用电量即将超标", f"已用{consumed_today:.2f}kWh,达到预算的{usage_ratio*100:.1f}%")

5.3 区分工作日与周末模式

家庭用电模式在工作日和周末差异很大。周末白天在家,用电基线自然会高。我们可以在规则引擎中引入“日期判断”。

import datetime def is_weekend(): today = datetime.datetime.now().weekday() # Monday is 0, Sunday is 6 return today >= 5 # 5=Saturday, 6=Sunday # 在检查规则时 if not is_weekend() and rule.get('name') == '白天高功率告警': # 在工作日,执行更严格的阈值判断 threshold = 1500 else: # 在周末,放宽阈值 threshold = 2500

通过这种方式,可以让告警系统更贴合实际生活场景,减少误报。

6. 常见问题排查与优化实录

在半年多的运行和维护中,我遇到了不少坑,也总结了一些优化经验。

6.1 硬件与数据采集层问题

问题1:数据读数不稳定或为0。

  • 排查:首先检查 Telegraf 日志 (sudo journalctl -u telegraf -f)。常见错误是串口权限问题或配置错误。
  • 解决
    • 权限:确保运行 Telegraf 的用户(如telegraf)有权限访问串口设备/dev/ttyUSB0。通常需要将用户加入dialout组:sudo usermod -a -G dialout telegraf
    • 配置:反复核对telegraf.conf中的slave_idaddress(寄存器地址)、byte_orderdata_typescale。这些参数必须与你的硬件设备说明书完全一致。一个字节顺序(byte_order)错误就会导致读出一个完全离谱的数字。
    • 硬件连接:检查 RS-485转USB转换器的连接是否松动,尝试更换 USB 端口。

问题2:CT钳子读数不准。

  • 排查:用钳形万用表实测电流,与系统读取的值对比。
  • 解决
    • 方向:确保 CT 钳子完全闭合,且电流方向正确(火线从钳子标注的“P1”侧流入,“P2”侧流出)。
    • 负载:CT 钳子通常需要接一个“负载电阻”,阻值需按说明书匹配。不接或接错会导致读数偏小或非线性。
    • 校准:有些高级的电量监测仪支持软件校准。在系统运行稳定后,可以对比电表总读数和系统各分路求和,计算一个校准系数,在 Telegraf 的scale参数中进行调整。

6.2 软件与服务层问题

问题3:InfluxDB 磁盘空间增长过快。

  • 现象:运行几周后,树莓派的存储空间告急。
  • 解决:InfluxDB 默认永久保存数据。我们需要设置数据保留策略。
    1. 进入 InfluxDB 命令行:influx
    2. 执行:USE home_energy(切换到你的数据库)
    3. 设置一个保留策略,例如只保留30天的原始数据:ALTER RETENTION POLICY "autogen" ON "home_energy" DURATION 30d REPLICATION 1 DEFAULT
    4. 还可以为聚合后的数据(如每小时、每天的平均值)创建更长期的保留策略,这样既节省空间,又不影响长期趋势查看。

问题4:告警邮件发送失败。

  • 排查:查看 Python 告警服务的日志文件。
  • 常见原因与解决
    • 认证失败:99%的情况是sender_password填错了。请确认使用的是邮箱的SMTP授权码,而非登录密码。
    • SMTP服务器/端口错误:不同邮箱服务商不同。QQ邮箱SSL用smtp.qq.com:465,STARTTLS用smtp.qq.com:587。163、Gmail等都有区别,需查官方文档。
    • 被当作垃圾邮件:邮件主题或内容可能触发了垃圾邮件规则。避免使用过多感叹号、红色字体等。可以在发件人邮箱设置里添加 SPF/DKIM 记录提升信誉(对个人邮箱较复杂)。最务实的办法是:将发件邮箱地址提前添加到收件人的通讯录或白名单中。

问题5:规则误报太多或漏报。

  • 优化:这是调优的核心过程,没有一劳永逸的方案。
    • 观察学习期:系统部署后,先不要急着设置严格的告警规则。让系统运行1-2周,通过 Grafana 观察各回路的正常用电基线、高峰时段、设备启停的功率曲线特征。
    • 设置合理的阈值和持续时间:不要用一个固定值(如>1000W)一刀切。结合观察期数据,为不同回路、不同时段设置差异化阈值。例如,厨房回路在做饭时段阈值可设为2000W,夜间则设为50W。持续时间从5分钟到30分钟不等,对于空调这种稳定负载,持续时间可以短一些;对于微波炉这种瞬时负载,持续时间要设长以避免误报。
    • 引入移动平均:在查询规则时,不使用最后一分钟的瞬时值,而是使用过去N分钟的平均值(mean)。这能平滑短时波动,让告警更稳定。这在我上面的 Flux 查询示例中通过aggregateWindow已经实现。

6.3 系统稳定性与维护

  • 定期检查日志:每周花一分钟看一眼/var/log/energy-alert.log和 Telegraf/InfluxDB 的日志,确认没有持续的错误。
  • 备份配置:将你的telegraf.conf、Python 脚本、Grafana 仪表盘 JSON 文件定期备份。Grafana 仪表盘可以在 Web 界面直接导出为 JSON 文件。
  • 电源保障:如果运行在树莓派上,建议使用可靠的电源适配器,并考虑配备一个小的 UPS(不间断电源),防止意外断电导致数据丢失或SD卡损坏。
  • 网络依赖:系统发送邮件需要网络。如果家庭网络中断,告警将无法发出。可以考虑增加一个本地日志记录作为备用,或者使用支持短信的硬件模块(如4G Cat.1模块)作为备用通知通道,但这会增加成本和复杂度。对于大多数家庭,网络稳定性已经足够。

这套系统搭建下来,硬件成本主要是一次性的监测设备(几百元),软件全部免费。它带给我的不仅仅是每月账单上减少的数字,更是一种对家庭能源消耗的“感知力”和“掌控感”。你知道每一度电用在了哪里,也能及时纠正浪费行为。从技术上看,它串联了硬件接口、数据采集、时序数据库、规则引擎和网络通信,是一个非常好的全栈实践项目。如果你也受困于高昂的电费,或者单纯享受动手创造的乐趣,不妨试试看。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询