订阅的问题是「忘了」,账单的问题是「只知道总数」。每月交三百块电费、一百多话费,交了几年,很少有人说得清钱具体花在哪——是空调、是热水器、还是那台常年待机的旧设备。不知道构成,就没有优化的着力点。
所以这条路径的顺序和别的不同:先量化,再优化。跳过量化直接省,结果通常是牺牲了体验却没省下多少。这篇按电费、话费宽带、票据归档三块给具体做法。
电费:先找出真正的耗电大户
电费是账单里最容易出现「结构性异常」的一项,因为它对设备状态极其敏感。常见的几种情况:
- 热水器温控故障,持续加热不停机,一个月多出上百元
- 冰箱门密封条老化,制冷效率下降,悄悄多耗电
- 一台老旧设备常年待机,功率不大但一天 24 小时
- 电暖器、除湿机这类大功率设备被当成常用电器
这些都不会以「故障」的形式表现出来——设备看起来在正常工作,只是账单在涨。唯一的发现方式是按设备计量用电。
硬件门槛比想象中低。最省事的起点是带功率计量的智能插座,几十块一个,插上就能读到实时功率和累计电量,先测几个你最怀疑的设备。要做到全屋级别,才需要导轨式电表通过 Modbus 之类的方式接入。给出一个常见的精度参考:
| 方案 | 精度 | 成本 | 适合 |
|---|---|---|---|
| 带计量的智能插座 | ±5%–10% | 低 | 定位单个设备,找异常最实用 |
| 导轨式电表 + Modbus 接入 | ±1% | 中 | 全屋总表,做长期趋势 |
重要提示:插座类计量的误差可能有几个百分点,用来做「哪个设备更耗电」的相对比较没问题,用来核对电费账单不合适。核对账单看总表。
用 Home Assistant 做能耗看板
Home Assistant 是开源的家庭自动化平台,它的能源模块基本就是为这个场景准备的:设备级能耗排行、每日/每月趋势、分时电价计算、异常告警。核心是三块配置。
第一步是把实时功率换算成累计电量。很多设备只提供瞬时功率,需要一个积分传感器把它累积起来:
sensor:
- platform: integration
name: "今日用电"
source_sensor: sensor.living_room_power
unit_prefix: k
integration_method: trapezoidal
round: 2
第二步是把电价按峰谷分开配置。这一步直接决定你能不能看出「省钱空间在哪」——只配一个平均电价,峰谷优化的收益就无从计算:
energy:
grid_sources:
- source: sensor.total_grid_power
tariffs:
- name: "峰时"
start: "08:00"
end: "22:00"
price: 0.60
- name: "谷时"
start: "22:00"
end: "08:00"
price: 0.30
第三步是异常告警。持续高功率远比瞬时峰值更值得警惕——瞬时超一下可能是空调启动,持续十几分钟以上就可能是设备故障:
automation:
- alias: "功率超标告警"
trigger:
- platform: numeric_state
entity_id: sensor.total_power
above: 2000
for:
minutes: 10
action:
- service: notify.mobile_app
data:
title: "功率告警"
message: "总功率超过 2000W 已持续 10 分钟,检查是否有设备异常"
峰谷电价这块的收益最直接:把可推迟的用电(洗衣机、洗碗机、热水器、充电桩)安排到谷时,在峰谷价差三毛左右的情况下,每往谷时挪一百度电大约省三十元。关键是先知道自己的谷时占比是多少——很多人配了分时电价后才发现,谷时用电只占两成,说明大部分可推迟的用电其实没挪。
账单趋势:用脚本找出异常月份
如果没有条件上硬件,退一步也能做——把过去 12 个月的账单整理成表格,用脚本做同比环比分析。异常月份会自己浮出来,然后针对那几个月份回忆发生了什么。
import csv
import sys
def width(s):
return sum(2 if ord(c) > 0x2E80 else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - width(s))
def f(v):
v = str(v).replace(",", "").replace("¥", "").replace("¥", "").strip()
v = "".join(c for c in v if c.isdigit() or c == "." or c == "-")
return float(v) if v else None
def load(path):
with open(path, encoding="utf-8-sig") as fh:
rd = csv.DictReader(fh)
cols = {c.strip().lower(): c for c in rd.fieldnames if c}
pk = next((cols[k] for k in ("账期", "月份", "period", "month") if k in cols), None)
qk = next((cols[k] for k in ("用量", "电量", "kwh", "用量(kwh)") if k in cols), None)
ak = next((cols[k] for k in ("金额", "费用", "amount", "电费") if k in cols), None)
vk = next((cols[k] for k in ("谷时用量", "谷", "valley") if k in cols), None)
if not (pk and qk and ak):
sys.exit("CSV 需要 账期 / 用量 / 金额 三列,当前表头:%s" % rd.fieldnames)
rows = []
for r in rd:
p = str(r[pk]).strip().replace("/", "-")
q, a = f(r[qk]), f(r[ak])
if not p or q is None or a is None:
continue
v = f(r[vk]) if vk else None
rows.append({"p": p[:7], "q": q, "a": a, "v": v, "unit": a / q if q else 0})
rows.sort(key=lambda x: x["p"])
return rows
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "bill.csv"
rows = load(path)
if not rows:
sys.exit("没有解析到数据")
avg = sum(r["a"] for r in rows) / len(rows)
avgq = sum(r["q"] for r in rows) / len(rows)
print("共 %d 期,月均 %.1f 度 / %.2f 元,均价 %.3f 元/度" % (
len(rows), avgq, avg, avg / avgq if avgq else 0))
print("-" * 74)
print(pad("账期", 11) + pad("用量", 10) + pad("金额", 10) +
pad("环比", 10) + pad("同比", 10) + pad("单价", 8))
print("-" * 74)
spikes = []
for i, r in enumerate(rows):
prev = rows[i - 1] if i else None
yoy = None
for old in rows[:i]:
if old["p"][5:] == r["p"][5:] and old["p"][:4] != r["p"][:4]:
yoy = old
mom = (r["a"] - prev["a"]) / prev["a"] * 100 if prev and prev["a"] else None
yr = (r["a"] - yoy["a"]) / yoy["a"] * 100 if yoy and yoy["a"] else None
if mom is not None and mom > 30:
spikes.append((r["p"], "环比 +%.0f%%" % mom))
if r["a"] > avg * 1.5:
spikes.append((r["p"], "高于均值 50%"))
print(pad(r["p"], 11) + pad("%.1f" % r["q"], 10) + pad("%.2f" % r["a"], 10) +
pad(("%+.0f%%" % mom) if mom is not None else "-", 10) +
pad(("%+.0f%%" % yr) if yr is not None else "-", 10) +
pad("%.3f" % r["unit"], 8))
print("-" * 74)
if spikes:
print("需要回看的月份:")
for p, why in spikes:
print(" %s %s" % (p, why))
else:
print("没有明显异常月份")
valleys = [r for r in rows if r["v"] is not None]
if valleys:
cut = sum(r["v"] for r in valleys) / sum(r["q"] for r in valleys)
print("n谷时用电占比 %.0f%%" % (cut * 100))
if cut < 0.3:
print(" 占比偏低:把可推迟的用电(洗衣机、热水器、充电桩)挪到谷时,")
print(" 按峰谷价差 0.3 元/度算,每挪 100 度约省 30 元。")
if __name__ == "__main__":
main()
用一份 12 期电费数据跑出来的结果:
共 12 期,月均 312.9 度 / 178.50 元,均价 0.570 元/度
--------------------------------------------------------------------------
账期 用量 金额 环比 同比 单价
--------------------------------------------------------------------------
2026-01 320.0 182.00 - - 0.569
2026-02 290.0 165.00 -9% - 0.569
2026-03 240.0 137.00 -17% - 0.571
2026-04 205.0 117.00 -15% - 0.571
2026-05 230.0 131.00 +12% - 0.570
2026-06 410.0 234.00 +79% - 0.571
2026-07 520.0 297.00 +27% - 0.571
2026-08 480.0 274.00 -8% - 0.571
2026-09 260.0 148.00 -46% - 0.569
2026-10 210.0 120.00 -19% - 0.571
2026-11 250.0 143.00 +19% - 0.572
2026-12 340.0 194.00 +36% - 0.571
--------------------------------------------------------------------------
需要回看的月份:
2026-06 环比 +79%
2026-07 高于均值 50%
2026-08 高于均值 50%
2026-12 环比 +36%
谷时用电占比 23%
占比偏低:把可推迟的用电(洗衣机、热水器、充电桩)挪到谷时,
按峰谷价差 0.3 元/度算,每挪 100 度约省 30 元。
三个有用的视角:
单价列比总金额更有诊断价值。用量随季节变化是正常的,但如果单价开始往上走,说明要么档位进了更高阶梯,要么电价调整了——这两种情况的应对方式完全不同。
同比比环比更可信。电费天然有季节性,六月比五月高 79% 可能只是开空调了。真正要看的是「今年六月比去年六月高多少」,那才是变化。脚本在有跨年数据时会自动算这一列。
连续几个月亮黄灯比单月异常更值得查。单月异常往往有解释,连续三个月高于均值就可能是设备状态变化或者家庭用电结构变了。
话费与宽带:没有一键方案,但有明确做法
说清楚一个现实:话费和宽带套餐没有能直接替你优化的开源工具,因为改套餐涉及运营商合约、绑定业务和地区资费差异,任何脚本都碰不到这一层。
但 AI 在这件事上有两个明确用途:
第一,把账单明细贴给 AI,让它挑出「你在付费但可能不需要」的项。话费账单里常见的冗余包括:已经不用的增值业务包、超出实际需求的流量加装包、被默认开通的彩铃/视频会员、副卡长期闲置但仍在计费。这些项目名往往很含糊,人一眼扫过去会跳过,让 AI 逐条解释「这个是什么、一般多少钱、常见开通原因」,识别率会高很多。
第二,把要问的问题列成清单再去沟通。打客服电话或者去营业厅之前,先明确三件事:当前套餐的确切内容、合约期还剩多久、有没有违约金。这三条问清楚,再谈降档或者换套餐就不会被话术带走。合约期内换套餐通常要付违约金,这一点最容易在推销中被模糊掉。
票据归档:让账单变得可检索
账单类的支出还有个隐性成本:需要的时候找不到。报账、维权、保修、税务,都要翻纸质或散落的 PDF。这件事也有成熟的开源方案。
Paperless-ngx 是目前最主流的开源文档管理系统。把扫描件或 PDF 丢进一个「收件」目录,它自动 OCR、提取文字、按规则打标签和分类(比如识别到「电费」就归到水电费类),之后可以用关键词直接搜到。中文文档需要在配置里显式指定 OCR 语言(设成 chi_sim),否则默认只装了几种西文语言,中文识别会失效。
它的实际用处集中在三处:账单按年月归档后可检索、保修期内快速找到购买凭证、以及长期留存的账单本身就是趋势分析的数据源——三年后你还能查到当时的电价和用量。
几个容易踩的坑
智能插座精度不足以结算。±5%–10% 的误差用于设备间比较没问题,拿来核对电费会产生无意义的争论。要对账就用总表。
先改习惯,再改设备。很多人一上来就换电器,其实待机功耗、峰谷安排、温度设定这些零成本的调整,收益往往比换设备更快更明显。
峰谷优化要算上设备是否支持定时。老设备没有预约功能,硬挪到谷时意味着要人去按开关——这种「省」坚持不了两周。
改套餐前先确认合约期。这是话费类支出最容易吃亏的地方,违约金可能高于你一年省下的钱。
数据别裸奔。Home Assistant 和 Paperless-ngx 都涉及家庭内部数据。要远程访问就加一层反向代理和认证,不要直接把端口暴露在公网上。
国内真正能去办的三件事
量化之后要落到动作上。国内用户可以直接去办的,主要是这三件:
第一,分时电价。部分地区对居民用电提供峰谷分时的计价方式,是否可办、怎么申请、峰谷时段如何划分,各地差异较大,以当地供电公司的公告和官方渠道的说明为准。申请前建议先用本文的脚本算出自己当前谷时用电占比——占比很低的话,办了也省不了多少。
第二,话费降档。这是国内账单里最容易省下钱的一项,但流程比想象中有讲究:合约要自查、渠道要走官方、被拒之后还有两级维权路径(集团投诉专线、工信部电信用户申诉)。申诉有前置条件,必须先向运营商投诉过,完整路径和话术见话费宽带降档被拒怎么办。
第三,账单与票据归档。电费、话费的历史账单本身就是趋势数据;而维权(降档申请、押金纠纷、保修索赔)需要的截图和凭证,平时归档好,用时才拿得出来。中文票据的离线识别与自动归类见票据本地 OCR 归档。
另外,如果你考虑用开源工具替代一部分订阅来降低固定支出,可对照国产开源替代清单,里面按「值得替代」和「不值得折腾」做了区分。
常见问题
不买智能插座,能找出耗电大户吗?
能做粗略判断。方法是用总表的每日数据配合「控制变量」:某一天刻意不用某个设备,对比前后几天的用电量差异。缺点是慢、需要多次试验,而且没法区分同时段的多个设备。如果只是想知道「是不是某台设备有问题」,先租或者借一个计量插座测两天,成本几乎为零。
Home Assistant 上手难度高吗?
比一般的软件要高一些,但能源模块本身配置不复杂——核心就是三个配置文件段落(积分传感器、电价、告警)。真正的门槛在于你得先有能提供功率数据的硬件,没有硬件的话这套系统就没有数据源。所以建议的顺序是:先用一个计量插座测出值得关注的设备,再决定要不要铺开做整体监控。
这些优化一共能省多少?
没有统一答案,取决于你现在的账单结构。但有个稳定的规律:账单里的浪费通常是「少数几项占大头」,而不是平均分布。所以收益高度依赖于能不能定位到那几项。这也是为什么所有方法的第一步都是量化——不是为了省得精细,而是为了找到那几项值得动手的地方。




评论 (0)