背景
前段时间我写了一篇《从家用功率监控到家庭数据中枢》,介绍了 HomePulse 当时已经接入的健康、用电、家电和通知数据。
那篇文章主要在讲“现在有什么”。最近为了升级 GreptimeDB,又顺手检查了一遍家里的 VM、CT、Docker 服务、数据库和代码仓库,才发现还有一个问题没有讲清楚:HomePulse 到底是怎么变成现在这样的?
这套系统并不是先有一张完整的架构图,然后照着图实现。实际过程正好相反。每次只是想解决一个眼前的问题,解决以后又发现数据可以多做一点事情,于是服务、数据库和仓库越来越多。
一开始我只是想保存自己的健康数据。最后看了一眼,家里已经多了健康接收器、通知接收器、MQTT 入库、Home Assistant 导出、场景分析、飞书反馈、Grafana、GreptimeDB、PostgreSQL 和一堆 Docker 容器。
为了一条健康曲线,差点搭出一个家庭数据中心。
一开始只是健康数据
最初的需求比较简单:手机和健康应用里已经有很多个人指标,我希望把它们长期保存下来,能够自己写 SQL 查询,也能放进 Grafana 观察趋势。
于是先做了一个健康数据接收器。手机通过 HTTPS Webhook 上传数据,接收器负责鉴权、清洗字段和统一时间,再写入 GreptimeDB 的 health_samples。
选择 GreptimeDB 也很直接:健康数据天然带时间戳,后面家里的传感器、功率和设备状态大概率也是时序数据,先放到同一个数据库里比较省事。
数据接进来以后,很快又有了新的问题:
- 最近一次数据是什么时候,采集是不是断了;
- 不同健康指标能不能放到同一条时间线上;
- 数据量增长以后怎么备份;
- 换机器或者升级数据库以后怎么恢复;
- 除了画曲线,这些数据还能解释什么。
当时还没有想过做什么“家庭智能平台”。只是觉得,数据既然已经留下来了,总不能最后只换来一张 Grafana 曲线。
健康数据还缺少生活上下文
健康指标可以描述身体发生了什么,但很多时候不能解释为什么。
例如睡眠、活动量和动态健康指标,可能同时受到空调、温湿度、作息、运动和生活场景影响。只看健康数据,很多结论只能停留在“这一天好像有变化”。
正好家里原本就在采集电表和部分家电数据,于是又把 MQTT、电表回路、冰箱、热水器等状态接了进来。后来 Home Assistant 也开始把设备、区域、实体和状态变化写入 GreptimeDB。
这时数据大致分成了几类:
- 健康指标属于个人状态;
- 温湿度和传感器属于环境状态;
- 电表和家电数据属于设备状态;
- Home Assistant 负责描述设备属于哪个房间,以及当前可以执行什么动作。
它们单独看都比较普通,放到一条时间线上以后才开始有意思。
例如某段时间热水器和水泵先后工作,卫生间湿度随后升高,照明和人体存在也发生变化。任何一个信号都不能证明“刚才洗澡了”,但几个事件按照比较稳定的顺序出现,就可以形成一个场景候选。
这里也踩过不少坑。比如电表有一路是总进线,其他是分支回路,统计时如果全部相加,就能凭空创造一部分家庭用电。又比如冰箱压缩机和其他周期性负载只看功率形状很像,不能看到一个阶跃就自信地给设备起名字。
数据多了不一定会自动变聪明,很多时候只是多了几种误判方法。
手机通知也是一种生活数据
家庭传感器只能看到家里的事情,手机通知则能补充一部分家庭之外的生活事件。
于是我又增加了通知接收器。它最早处理短信转发,后来支持 Android 应用通知、电话和交通卡消息。接收器保留原始事件,同时提取应用、事件类型、站点、时间等结构化字段。
交通卡通知是一个比较典型的例子。把进站和出站消息配对后,可以得到一次通勤的开始时间、结束时间、路线和耗时。它不能精确说明我几点走出家门,也不知道我实际上坐了哪一班车,但至少能提供一条可以长期比较的通勤记录。
这套设计里我一直希望把事实和推断分开:
- 刷卡时间、设备状态、健康样本是事实;
- 出门时间、正在洗澡、当前心情属于推断;
- 用户确认过的生活场景是另一类更可靠的事实。
如果不做这个区分,一个 Dashboard 很容易把估算值画得和真实值一样精确。图倒是会很好看,结论不一定靠得住。
通知数据本身也比较敏感。日常看板只使用结构化字段,不展示号码和通知正文;公开文章使用脱敏后的示意图。技术文章可以解释系统怎么工作,没有必要把自己的生活细节一起公开出来。
开始尝试判断家庭行为
有了健康、设备、环境和通知数据以后,HomePulse 已经能回答“发生了哪些变化”,但还不能很好地回答“这些变化可能组成了什么事情”。
于是又做了一个 scene-analyzer。
最初的想法并不是让大模型直接扫描全部原始数据。高频时序表数据量太大,噪声也很多,直接发送给模型既浪费 Token,也很难得到稳定结果。
现在采用三层处理:
本地规则提取事件
↓
组合短时间证据窗口
↓
AI 生成候选场景、解释和置信度
↓
用户确认、修正或拒绝
↓
保存人工反馈和活动画像
本地规则先处理能确定的事情,例如功率阶跃、设备状态变化和交通卡事件。AI 只读取压缩后的证据摘要,尝试生成“洗漱”“洗澡”“通勤”等候选场景,再通过飞书卡片让我确认。
确认、修正和拒绝都会保存下来。系统不只记录最后的标签,也保留原来的候选结果和证据。这样以后修改规则时,可以知道哪些类型经常误判,而不是让 AI 每次都从头猜。
后面又加了活动画像,用来记录某个场景比较稳定的个人特征。例如洗澡通常持续多久、湿度大概怎样变化、热水器和水泵一般按照什么顺序工作。
目前它还只是一个很早期的家庭行为判断系统。能发现一些候选场景,也会一本正经地猜错。所以分析链路可以继续试验,控制链路仍然必须保守。AI 可以提醒我“可能发生了什么”,不能因为它猜测正在洗澡,就直接替我操作热水器。
系统能用了,然后开始看不懂了
每增加一个数据源,通常就会多一个接收器、一个配置、一组表和几个 Dashboard。再加上公网入口、Cloudflare Tunnel、MQTT、Home Assistant、Grafana 和飞书反馈,服务逐渐分散到了不同 VM、CT 和 Docker Compose 项目中。
代码仓库也在增长。有的仓库负责采集程序,有的负责本地设备桥接,有的保存平台文档;运行环境里还有一些配置没有及时同步回 GitHub。
这次检查 GreptimeDB FDW 时,我顺便重新清点了整个部署环境,发现几个很现实的问题:
- 同一个服务曾经在新旧两个节点上出现过;
- 一个旧健康 CT 已经没有流量,但仍然开着;
- 有些运行配置与仓库内容存在漂移;
- 数据在哪台 VM、由哪个容器写入,需要重新调查才能说清楚;
- 架构变化散落在聊天、命令和提交记录中,没有完整时间线;
- 备份做过,但备份对象、位置和恢复方法没有统一入口。
旧健康 CT 后来经过连接、日志、端口和依赖检查,确认已经不再承载实际流量,于是先停止并禁用,但没有删除,历史数据也保留了下来。
这件事让我意识到,系统治理不是做完产品以后才补的附属功能。一个系统开始保存长期健康数据、家庭状态和 AI 判断以后,备份、版本、审计和架构关系本身就是产品能力。
HomePulse Ops
于是有了 HomePulse Ops 的想法。
它不是给普通家庭成员使用的设备面板,而是 HomePulse 自己的运维和治理子系统,主要回答几个问题:
- 当前有哪些主机、VM、CT、容器和服务;
- 服务运行在哪里,由哪个 GitHub 仓库管理;
- 数据从哪里来,写进哪些表,又被谁查询;
- 最近发生了什么架构变化;
- 哪些数据和配置已经备份,能不能恢复;
- 哪些常用操作可以安全执行并留下审计记录。
目前先做了三类图:部署图、部署关系数据流图和逻辑架构图。图的源文件使用结构化 JSON 保存进 Git,浏览器里展示生成的独立 HTML。以后每次 Git 提交、部署或定时扫描,都可以根据事实变化重新生成图和摘要。
这里借用了 Wiki 的版本管理思路,但不一定非要叫 Wiki。更准确的说法可能是“架构版本”:系统不仅显示当前结构,还能解释什么时候变成这样、是哪次提交或操作导致的、有没有验证和回滚方法。
Ops 的第一版仍然应该以只读为主。先把系统看清楚,再逐步加入备份、刷新和检查。重启、升级和删除之类的操作,等权限、审计和恢复链路都稳定以后再说。
HomePulse 不只是 Ops
在讨论 Ops 的过程中,我一度把 HomePulse 整体也定义成了“家庭基础设施管理平台”。这个定义能解释网络、数据库和 GitHub,却解释不了最初为什么要接健康数据,也解释不了生活场景判断最后要服务谁。
Ops 只是 HomePulse 的一个子系统。
现在我更愿意把 HomePulse 拆成五部分:
- Core:人、家庭、空间、实体、事件、权限和插件等公共模型;
- Data:健康、HA、能耗、通知等数据的接入、存储和质量;
- Intelligence:场景识别、跨域分析、异常解释和反馈学习;
- Experience:家庭总览、个人报告、对话、提醒和确认;
- Ops:部署、架构、GitHub、备份、版本和安全操作。
Home Assistant 继续负责设备和自动化,GreptimeDB 保存高频时序,PostgreSQL 保存实体关系并通过 FDW 查询 GreptimeDB。HomePulse 不需要重新实现它们,而是把这些系统组织成家庭上下文。
如果说 Data 保存“发生了什么”,Intelligence 尝试解释“这意味着什么”,Experience 负责回答“家庭成员能得到什么”,那么 Ops 解决的是“这套系统怎样长期可靠地运行”。
系统是长出来的
回头看,HomePulse 的演化顺序大致是:
个人健康数据
↓
家庭设备与环境数据
↓
手机通知和生活事件
↓
AI 家庭行为判断
↓
反馈和个人活动画像
↓
备份、治理和架构管理
它不是从一个完整的平台规划开始,而是每解决一个真实问题,就暴露出下一个问题。
健康数据带来了数据所有权和长期保存;家庭数据带来了实体关系和环境上下文;通知带来了家庭之外的生活事件;AI 判断带来了证据、置信度和反馈;服务越来越多以后,又带来了备份、配置漂移和架构治理。
如果一开始就设计一个完整的“家庭智能平台”,很可能会先写出一堆漂亮的抽象,但不知道它们具体解决什么。现在反过来,每个准备抽象成平台能力的东西,背后基本都有已经运行的服务、数据或者踩过的坑。
坏处是家里多了不少需要维护的东西。好处是这套架构至少不是从 PPT 里长出来的。
下一步
平台化并不意味着把现有服务全部重写一遍。下一步准备先整理已有能力:
- 统一人、空间、实体、状态、事件和场景的模型;
- 区分原始观测、规则推断、AI 推断和人工确认;
- 明确插件的输入、输出、权限和数据用途;
- 给已有服务补充数据质量、备份和运行状态;
- 先做 HomePulse Ops 的只读版本;
- 再做一个真正面向家庭成员的 HomePulse Experience 最小版本。
后面希望 HomePulse 能回答一些更具体的问题:
- 今天家里发生了什么;
- 最近有哪些值得注意的变化;
- 睡眠和卧室环境是否存在长期关联;
- 哪些生活建议实际带来了改善;
- 系统为什么做出这个判断,我是否同意。
最后
2019 年做家庭功率监控时,我在文章里留过一个 Todo:
加上点人工智能做智能家居。
几年后,人工智能确实加进来了,只是它没有直接变成一个会自动开关设备的“大脑”,而是先帮我解释数据、整理场景和记录系统变化。
现在新的 Todo 变成了:把这个自然生长出来的系统整理成一个可以继续生长的平台。
按照这个博客过去的规律,Todo 写出来不代表马上能填完。但至少这一次,系统已经开始替我记录坑是什么了。