ha_data_store 是一款 Home Assistant 自定义集成:无需修改 configuration.yaml,全部通过配置界面与内置管理页面完成,把「采集 → 存储 → 分析 → 展示 → 远程交互」串成一套家庭数据平台。
| 环节 | 能力 |
|---|---|
| 采集 | 设备开关记录(含跨午夜拆分、启动恢复、用电量核算)、传感器环境数据、实体属性提取(含通讯数据采集)、扫地机轨迹、健康记录、打印机用量、小爱对话 |
| 存储 | 统一落库 SQLite(WAL 模式);支持整库备份与「排队 + 重启后原子应用」式恢复 |
| 导入导出 | 数据浏览页把 CSV / JSON 导入任意表(字段映射 + 固定值、追加 / Upsert、自动建表与补列),并把表数据或空模板导出为 CSV |
| 对外 | 完整 HTTP API(API Key 鉴权、安全沙箱下的自定义 SQL 路由)、实体→网络(读数据 / 控制设备)、实体→JSON 文件、文件源 / API 源 ↔ 实体映射 |
| 交互 | 远程 HA 设备桥接(WebSocket)、虚拟设备、原生辅助元素自管实体化、轻量自动化引擎 |
| 分析展示 | 内置数据库浏览器、可视化查询构造器、通用指标引擎、家庭洞察、今日家庭状态中文总结、系统健康监控传感器 |
| 安全 | API 访问 / 数据库浏览 / 数据库修改 / 实体网络控制 等独立开关,控制类接口另有读写凭证分离、动作白名单、参数锁定与审计日志 |
- 功能总览
- 安装
- 快速开始
- 功能详解
- 0. 今日家庭状态总结
- 1. 设备类数据采集
- 2. 传感器类数据采集
- 3. 属性提取
- 4. 自定义路由
- 5. 设备桥接
- 6. 文件源 → 实体
- 7. API源 → 实体
- 8. 虚拟设备
- 9. 实体导出为JSON
- 10. 打印机数据采集
- 11. 用户操作记录
- 12. 辅助元素
- 13. 用电计量
- 14. 设备清理
- 15. 可视化查询构造器 与 数据库新建表
- 16. 接口管理(新接口 · 免重启)
- 17. 家庭洞察(统一事件流 / 房间占用)
- 18. 通用指标引擎(元数据驱动 ·
metrics_catalog) - 19. 实体→网络(读数据 / 控制)
- 20. 整库备份
- 21. 通讯数据(采集 + 查询)
- 22. 数据导入 / 导出(CSV · JSON)
- 23. 个人账单(查询 / 分析 / 备注)
- 24. 实体编组(含前端调用说明)
- API 接口文档
- 内置数据库浏览器
- 控制开关
- 安全架构
- 数据库表结构
- 日志系统
- 常见问题
- 技术栈
- 更新日志
| 模块 | 说明 |
|---|---|
| 📡 设备类 | 监听实体 ON/OFF 状态变化,自动记录开关机时间、持续时长、用电量变化,支持午夜跨天拆分 |
| 🌡️ 传感器类 | 定时采集温湿度 / PM2.5 / CO2 / 功率 / 通用传感器数据,支持整分钟对齐 |
| 📊 属性提取 | 从实体属性的数组/嵌套字段中提取数据,独立建表存储,支持字段快照、列表展开、混合三种模式 |
| 🧹 扫地机器人 | 监听扫地机器人坐标变化,记录轨迹数据 |
| 🩺 健康数据 | 存储血压、体温、身高、体重等健康记录,支持按人员查询 |
| 🔗 设备桥接 | 通过 WebSocket 连接远程 HA,将远程实体的状态和控制在本地无缝映射(开关/灯光/气候/窗帘/风扇/门锁/数值/选择/传感器/二进制传感器) |
| 🖥️ 虚拟设备 | 动态创建自定义实体,支持多种设备类型和自定义属性;虚拟灯光支持开关/亮度/色温/颜色(HS·RGB)全功能(色温范围可配、状态可恢复,db_viewer 有「🎛 调光」面板 + POST /virtual_device/state 控制接口);支持导出/导入(配置+状态)跨机迁移 |
| 🧩 辅助元素 | 扫描原生 HA 辅助元素(input_* / counter / binary_sensor)导出,B 机导入转为本集成自管实体(switch/number/select/button/text/binary_sensor),支持前台新建、全选批量删除;汇总实体 sensor.ha_data_store_helper |
| ⚡ 用电计量 | 登记功率实体,10 秒采样积分自动生成日/月/年用电量实体并按天入库;汇总实体 sensor.ha_data_store_all_power;API 工具含查询分组 |
| 🧹 设备清理 | 一键扫描并清理本集成下无实体的空设备(安全,不误删主设备) |
| 💾 整库备份 | 顶部选项卡「💾 数据备份」:VACUUM INTO / 在线备份取一致快照,支持关闭·每小时·每天·每周计划 + 保留份数;恢复采用「校验后排队 → 重启 HA 时在数据库被打开前原子替换」,替换前自动留快照;支持备份到 NAS 挂载点(本地生成校验后再写入共享) |
| 💬 小爱对话 | 采集小爱音箱的对话记录(用户说话 / AI 回复 / 对话时间)落库,可查询历史与统计 |
| 🖨️ 打印机数据采集 | 采集打印机统计数据与当日作业明细,支持多台、配置管理、数据查询与系统监控 |
| 📁 文件源 → 实体 | 监听本地 JSON 文件变化,自动将数据映射为 HA 实体 |
| 🌐 API源 → 实体 | 定时请求外部 HTTP API,将 JSON 响应解析并映射为 HA 实体 |
| 📄 实体 → JSON 文件 | 将 HA 实体状态实时导出为 JSON 文件,供外部系统消费 |
| 🌐 实体→网络(读 / 控制) | 把实体映射成外部可直接调用的 HTTP 地址:读地址(GET /push_data/{token},只读凭证)与控制地址(POST /push_control/{token},独立写凭证;动作白名单 + 参数可锁定为固定值/限幅 + 限流 + 审计日志);同一实体可挂多套配置,总闸为开关「实体网络控制」 |
| 🔑 API 密钥 | API Key 鉴权,支持多密钥,可独立开关 |
| 🛡️ 安全控制 | 三个独立开关控制 API 访问、数据库浏览、数据库修改 |
| 📈 系统监控 | sensor.ha_data_store_info 实时展示 6 大类健康状态(设备/环境/属性/导出/文件源/API源) |
| 📋 内置数据库浏览器 | 管理页面直接浏览、编辑数据库,无需 SQL 工具;支持可视化「新建表」 |
| 🧩 查询构造器 | db_viewer 内可视化定义查询接口:选表 → 动态参数(字段/时间段/LIKE/多值 IN)→ 排序/上限 → 汇总(总条数/合计) → 试运行 → 发布;定义落库随库迁移 |
| 🔗 自定义路由 | 通过 GUI 或 API 定义自定义 HTTP 路由,绑定任意 SQL 查询;支持发布开关、来源/状态管理 |
| 🗂️ 统一泛域名动态路由 | 万能路由 /api/ha_data_store/custom/{tail} 运行时查库执行任意自定义 SQL |
| 🏠 今日家庭状态总结 | 聚合历史表生成今日家庭中文总结,sensor.today_family_status + 按钮/服务按需触发;启动后 1 分钟生成、之后每 30 秒刷新(逐台设备含运行中/实时状态/操作用户) |
| 🤖 简单自动化引擎 | 定时/间隔触发 + 多条件判断 + 顺序执行服务动作,执行记录落库(30 秒调度,db_viewer 管理) |
| 📊 自动化状态传感器 | sensor.ha_data_store_automation 实时统计自动化总数/启停/执行结果,前端自动化管理卡片数据源 |
| 🎯 用户操作记录 | 前端埋点上报每次操作(含完整 action_snapshot + config_id),ts 采用实体状态时间与 device_history 精确关联,sensor.近期使用设备 窗口聚合(number.ha_data_store_recent_days 可调天数);另含 all 节点(device_history 全量设备最近使用)与 device_last_used 查询接口,API工具支持多维度查询 |
| 📊 通用指标引擎 | 元数据驱动:新表 metrics_catalog 存放指标定义(源表 / 值列或 @value 表达式 / 聚合 / 默认分组 / 过滤),通用引擎编译成参数化 SQL 执行;内置 20+ 指标(环境/设备/用电/健康/操作/自动化/attr_*),GET /query?type=metrics_query&metric_id=xxx 查询,db_viewer「📊 指标管理」增删改与试运行,新增指标免重启 |
| 🏠 全屋用电/用时 | whole_house_usage 查询按 年/月/日 返回 总计→房间→设备 三级统计(时长/用电/开启次数/运行中设备/设备数量/单纯房间名列表),API 工具含查询分组;汇总传感器 sensor.ha_data_store_all_room_usage 输出 本年/本月/今日 三级(状态=今日用电 kWh),每分钟刷新 |
| 🗂️ 全屋实体 | 传感器 sensor.ha_data_store_all_entities 按 report_entities.entity_type 分组展示全部上报实体(支持多值逗号拆分、跨节点归属),状态值=去重实体个数,表变化才更新 |
| 🕘 家庭洞察 | 统一事件流 timeline(设备/操作/自动化/扫地机/小爱/健康/打印机合并成一条时间线,设备记录展开 on/off)+ 房间占用排行 room_occupancy(严谨区间并集、含门户事件与日/小时分解);均按天/日期/时间段查询、不分页 |
| 📞 通讯数据 | 属性提取新增 comm 采集模式:按数组展开写入 18 个固定字段(我方/对方号码、归属地、姓名、通讯时间、地点、消息类型、数据来源、呼叫类型、时长、金额、流量、流量类型、内容、图片路径、我的坐标、对方运营商、对方坐标),列名与类型由系统固定,目标列可选源字段或直接填固定值;2026/9/1 17:24 自动规范化为 2026-09-01 17:24:00,3分53秒 等中文时长自动换算为秒,1.5GB / 512MB 等流量自动换算为 MB;comm.py 提供明细/日期/排行/趋势/汇总/联系人/地点/时段 8 类查询,API 工具含专属分组 |
| 📥 数据导入 / 导出 | 数据浏览页把 CSV / JSON 导入任意表:字段映射 + 固定值、追加 / Upsert(多列唯一键)、自动建表与补列、🧪 试运行、分批进度与错误明细;基础值转换(¥1,234.5→1234.5、2026/9/1→2026-09-01);导出表数据或空模板为 CSV(UTF-8 BOM,Excel 友好) |
| 🧾 个人账单 | 独立模块 personal_bill.py + 新表 personal_bills(支付宝 / 微信账单落库):42 类查询/分析(明细、按单号/账号/多名称、多值分类汇总、数据可用性多档、分组/趋势/排行、交叉汇总、资金流向、净现金流、同比、异常、定期订阅、固定开销、金额分位数等)+ 写接口(增 / 批 / 改 / 删);支持多名称与逗号多值过滤、fields 返回字段手动点选、可编辑备注 note(POST /bill/remark 单独提交、GET /bill/notes 取候选供点击) |
| 🔗 实体编组 | 独立模块 entity_group_store.py / entity_group_views.py / entity_group_entities.py / entity_group_actions.py + 三张新表(entity_groups / entity_group_members / entity_groups_meta):把一组实体当一个设备控制,每个启用编组自动生成一个对应域的实体——灯光组 light.hds_eg_*、窗帘组 cover.hds_eg_*、媒体组 media_player.hds_eg_*、空调组 climate.hds_eg_*、其余(socket / 混编 / 设备)为聚合开关 switch.hds_eg_*(实体域只由「类型」决定,与成员域无关,域平台不可用则降级为开关;成员是 switch 的灯/窗帘也能用 turn_on/off 兜底控制;亮度/色温/颜色、位置、音量、温度/模式/风速/摆风都直接广播给成员;state 由成员实时汇总,含 on_count / partial / missing / on_entities;单监听 + 反向索引只刷受影响实体;编组 id 可自定义、改名即换实体 id、域变化自动重建);编组动作与 automations.actions 同构并支持 $group / $group:light 占位符;REST 全套 /api/ha_data_store/entity_groups(列表/新建/详情/全量·局部更新/删除/成员增删改排序/批量/排序/按实体反查/自检 _diag),db_viewer「🔗 实体编组」可视化编辑;所有编组实体挂在独立**「编组设备」**下,含两个传感器:sensor.ha_data_store_entity_groups(目录全量快照)、sensor.ha_data_store_group_entities(所有编组实体,精简投影 entity_id/name/icon,供前端与语音助手直接遍历) |
- 确保已安装 HACS
- 将本仓库(https://github.com/chjspp520/ha_data_store)添加为自定义仓库
- 搜索 "HA数据统一存储系统" 并安装
- 重启 Home Assistant
将 ha_data_store 文件夹复制到 Home Assistant 的 custom_components 目录:
# Linux / macOS
cp -r ha_data_store /path/to/config/custom_components/
# Windows
# 将 ha_data_store 文件夹复制到 %CONFIG_DIR%/custom_components/重启 Home Assistant。
配置 → 设备与服务 → 添加集成 → 搜索 "HA数据统一存储系统"
一键确认,无需任何参数。集成会自动创建所需的数据库文件。
添加集成后,点击条目下方的 "配置" 按钮进入管理菜单:
| 菜单项 | 功能 |
|---|---|
| 添加设备类实体 | 添加开关/灯/空调等 ON/OFF 设备的监控 |
| 添加传感器类实体 | 添加温湿度、功率等传感器的定时采集 |
| 删除实体 | 移除不再需要的监控实体 |
| 查看实体 | 查看当前所有已配置的监控实体 |
| 时区设置 | 设置本地时区偏移(默认 UTC+8) |
| 日志保留时长 | 设置本地日志文件保留天数(默认 7 天) |
| 路由管理 | 管理自定义 HTTP 路由 |
| 设备桥接 | 管理远程 HA 设备桥接连接 |
http://你的HA地址:8123/api/ha_data_store/db_viewer
默认密码:admin(首次使用请立即修改)
基于数据库各历史表,聚合今日家庭事实,渲染为精简中文段落(为 0 的项自动跳过)。
新增实体:
| 实体 | 类型 | 说明 |
|---|---|---|
sensor.today_family_status |
sensor | 状态值=极简一句(家中有人/无人 + 开着几盏灯 + 入户门状态及时长,≤255字符);attributes.summary=完整段落;attributes.sections=完整结构化分节;attributes.overall=normal/warning;attributes.alerts=异常提醒列表;attributes.alert_text=提醒文字;attributes.offline=离线设备数 |
button.ha_data_store_daily_summary |
button | 仪表盘放置按钮卡片,点击立即触发分析 |
新增服务: ha_data_store.generate_daily_summary
- 参数
date(可选,默认今天,格式yyyy-mm-dd) - 供自动化 / Node-RED 按需调用
自动刷新:
- HA 启动后 1 分钟 自动生成一次
- 之后每 30 秒 自动更新(设备
running/state/ 操作用户等实时字段需时效性) - 定时刷新默认强制写入(
sensor.FAMILY_STATUS_FORCE_WRITE = True),generated_at/last_updated每 30 秒确实刷新;置False则聚合内容未变化时跳过写入,减轻 recorder 压力 - 也可手动按钮 / 服务触发(手动触发始终强制写入)
聚合维度与提醒阈值:
| 节 | 数据源 | 内容 |
|---|---|---|
| 环境 | env_temperature/humidity/pm25/co2 | 今日最高/最低/平均,房间温差≥2°C 时补充房间明细 |
| 设备 | device_history + 实时 states | 共 N 台/总时长 + 运行最久亮点 + 用电 TOP3 + 开关频次 TOP3;完整逐台明细在 sections.devices.devices[](含 running/time/state/on_user/off_user) |
| 用电 | env_power | 当日自增读数(最后一条 = 今日总用电,kWh)+ 昨日用电 + 环比 |
| 家庭事件 | vacuum_history / health_records / xiaoai_conversations | 扫地机次数、健康记录条数、小爱对话条数及时段 |
| 人在/门 | device_history(name=人在/入户门) | on_time 非空且 off_time 空=该房间有人/门开;否则家中无人/门关 |
| 灯光 | device_history(name 含"灯") | 每盏灯取最新一条,on_time 非空且 off_time 空=该灯开着,统计"开着 x 盏灯" |
| 离线实体 | report_entities + 实时 states | 三态判定,unavailable 算离线、unknown 不算;有离线时 summary 末尾显示"离线设备 x 台" |
字段说明:
status_value:极简状态值(家中有人(客厅)、开着 5 盏灯、入户门开1小时8分钟)summary:完整段落(不含提醒,含"离线设备 x 台")alert_text/alerts:异常提醒(高温/运行超时/用电环比)sections.lights:开灯房间去重(开着 6 盏灯(主卧、儿童房、客厅等 5 个房间))sections.devices.times_top:开关频次 TOP(卫生间浴霸灯44 次)sections.devices.devices[]:完整逐台明细;每台除entity_id/name/room/times/duration/energy外,另含实时/最近状态字段(energy_top/times_top与devices共享同一份数据,字段同步生效):
| 字段 | 说明 |
|---|---|
running |
当日最新一条 device_history(id 最大)已开未关 = true(正在运行,历史口径) |
time |
已关闭 = 该记录 off_time(最近一次关闭时间);运行中 = null |
on_user |
运行中 = 该记录 on_user(开机操作用户);已关闭 = "" |
off_user |
已关闭 = 该记录 off_user(关机操作用户);运行中 = "" |
state |
HA 实时状态值(hass.states.get(entity_id).state);实体不在状态机中 = null |
state为实时值、running为历史记录口径,两者可能短暂不一致(如实体已关但关机事件尚未落库),分别用于"看现状"与"看记录"。
异常提醒(阈值写死): 高温 ≥30°C、低温 ≤5°C、单台连续运行 >6 小时、用电环比波动 >20%;存在任一提醒时 overall=warning,提醒文字放 alert_text 字段(alerts 为列表);summary 段落末尾单独显示"离线设备 x 台"(离线不进 alerts)。
工作原理:
用户触发 ──► 聚合(查库) ──► 渲染(拼段落) ──► 刷新传感器(状态值 + attributes)
┌──────────────────────────────────────────────────────────────┐
│ 触发层 │
│ 按钮 button.ha_data_store_daily_summary │
│ 服务 ha_data_store.generate_daily_summary(可选 date) │
└───────────────────────────────┬──────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ 聚合层 build_daily_summary_sync() │
│ 查今日数据(datetime LIKE 'yyyy-mm-dd%') │
│ 环境env_* / 设备device_history / 扫地vacuum_history │
│ 健康health_records / 小爱xiaoai_conversations │
│ + 异常提醒(高温/低温/运行>6h/用电环比>20%) │
└───────────────────────────────┬──────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ 渲染层 render_summary() │
│ 精简中文段落(为 0 的项整段跳过) │
└───────────────────────────────┬──────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ 输出层 sensor.today_family_status │
│ 状态值=极简一句;attributes={summary, sections, overall, │
│ alerts, alert_text, status_value, offline, │
│ presence, lights, date, generated_at} │
│ 自动刷新:启动后1分钟 + 每30秒(默认强制写入状态) │
└──────────────────────────────────────────────────────────────┘
核心 SQL:
- 环境:
SELECT value, room, datetime FROM env_temperature WHERE datetime LIKE '2026-08-21%' - 设备(今日,含单台用电与实时状态字段):
SELECT entity_id, name, room, duration, energy_consumed, on_power, now_kwh, on_time, off_time, on_user, off_user FROM device_history WHERE on_time LIKE '2026-08-21%'- 每实体取当日最新一条(
id最大)→running(off_time空)/time(off_time)/on_user/off_user;state另取 HA 实时状态 - 单台用电(kWh):已关闭直接用
energy_consumed;正在运行(energy_consumed空且now_kwh非空)用now_kwh - on_power
- 每实体取当日最新一条(
- 家庭总用电(今日,kWh):
SELECT entity_id, datetime, value FROM env_power WHERE datetime LIKE '2026-08-21%'——value为当日自增读数,按entity_id各取最后一条(当日累计 = 当日消耗) - 家庭总用电(昨日,环比基准):同上取
'2026-08-20%'最后一条- 用电环比:
(今日总用电 - 昨日用电) / 昨日用电 × 100%
- 用电环比:
- 扫地机:
SELECT DISTINCT datetime FROM vacuum_history WHERE datetime LIKE '2026-08-21%' - 健康记录:
SELECT name FROM health_records WHERE date_time LIKE '2026-08-21%' - 小爱对话:
SELECT conv_time FROM xiaoai_conversations WHERE conv_time LIKE '2026-08-21%' - 人在/门:
SELECT id, name, room, on_time, off_time FROM device_history WHERE name IN ('人在','入户门')—— 人在:on_time非空且off_time空=该房间有人;入户门:最新一条(id 最大)off_time空=门开,否则门关
说明:家庭总用电取自
env_power(电表当日自增读数,最后一条即当日用电,不做加法累加);device_history.energy_consumed的单位为 kWh,仅用于单台设备明细,不参与总用电求和。
监听实体 state_changed 事件,当状态从 ON 变为 OFF 时自动记录:
- on_time: 开机时间
- off_time: 关机时间
- duration: 持续时长(秒)
- on_power: 开机时电表读数
- off_power: 关机时电表读数
- energy_consumed: 本次用电量(off_power - on_power)
- room: 所属房间
- cross_day: 是否跨天
- on_user: 开机操作用户(由前端操作记录
user_actions按entity_id+ 时间关联回填) - off_user: 关机操作用户(同上,关联回填)
- icon: 该设备对应前端卡片的图标(
device_history.entity_id = report_entities.entity_id时,取report_entities中该实体最新上报(id最大)的icon写入,原样写入、不过滤空值;历史数据回填不随 HA 启动执行,由按钮button.ha_data_store_fill_device_icon按需触发,覆盖更新、不管原值是否为空,无上报记录的实体不动)
支持的 domain 状态判定:
| Domain | ON 状态 | OFF 状态 |
|---|---|---|
| switch, light, fan, lock, binary_sensor, input_boolean | on |
off |
| climate | auto, cool, dry, heat, fan_only |
off |
| cover | open |
closed |
| device_tracker | home |
not_home |
电量读取策略(优先级):
- 配置中指定的
power_entity传感器 - 设备自身属性中的
power/current_power/energy/meter_reading/energy_consumed/today_energy/total_energy - 设备 state 值(若非 unavailable/unknown/on/off 等)
配置格式(在管理界面输入):
旧格式(每行一条):
room, device_name, entity_id, power_entity
新格式(JSON,支持多条):
[
{"entity_id": "switch.fan", "device_name": "风扇", "room": "客厅"},
{"entity_id": "switch.kettle", "power_entity": "sensor.kettle_power"}
]
所有字段均可选填。
每天 00:00:00 自动执行:
- 检测所有未关闭的设备记录
- 将跨天记录拆分为两条:前一天记录在 23:59:59 关闭,当天 00:00:00 开启新记录
- 自动计算各段的持续时长和用电量
- 读取当前电表读数作为拆分的 off_power
集成会在本地 JSON 缓存文件中记录所有未关闭的设备开关事件。当 HA 重启时:
- 停机 ≤ 30 分钟:自动使用当前时间作为关机时间补全记录
- 停机 > 30 分钟:自动删除异常的未关闭记录
- 设备仍然开机:保留记录和缓存
每 10 分钟扫描今日数据,检测同一设备的多条未关闭记录异常,通过 HA recorder 查询历史状态进行修正。
定时对指定实体进行轮询,将数据写入独立的指标分表。
支持的指标类型:
| 指标 | 表名 | 值类型 | 说明 |
|---|---|---|---|
temperature |
env_temperature |
REAL | 温度 |
humidity |
env_humidity |
REAL | 湿度 |
pm25 |
env_pm25 |
REAL | PM2.5 |
co2 |
env_co2 |
REAL | 二氧化碳 |
power |
env_power |
REAL | 功率/电量 |
sensor |
env_sensor |
TEXT | 通用传感器(支持非数值,如光照度、AQI) |
配置格式(在管理界面输入):
旧格式(每行一条):
metric_type: room, entity_id, interval_minutes, [int|json|keep_blank]
示例:
power: 客厅, sensor.power, 10, int
sensor: 卧室, sensor.illuminance, 15
新格式(JSON):
[
{"entity_id": "sensor.temperature", "metric_type": "temperature", "room": "厨房", "collect_interval": 5, "round_minute": 0}
]
collect_interval:采集间隔(分钟)round_minute:整分钟对齐模式(int= 在整分钟边界采集,如 10,int 在 0/10/20/30/40/50 分采集)sensor指标的值类型为 TEXT,支持非数值 state(如bright、rainy等)- 所有指标的
value为 NULL 的记录也会记录,用于标记采集时间点
提取实体状态属性中的指定字段,独立建表存储。可在数据库浏览器中配置。
三种模式:
| 模式 | 常量 | 说明 |
|---|---|---|
| 字段快照 | fields |
提取实体属性的指定字段,每字段一列,定时快照记录 |
| 列表展开 | list |
将实体属性的某个数组展开,每个数组元素一行,可指定 key 字段做去重 |
| 混合模式 | multi |
列表展开 + 附加标量字段,extra_fields 做独立列,extra_json_nodes 合并到 JSON 列 |
配置示例(在数据库浏览器中操作):
- 在
attr_type_defs表中定义属性类型 - 在
entity_configs表中添加对应属性实体配置 - 系统自动为每种属性类型创建
attr_{type_name}表
通过 GUI 或 API 定义自定义 HTTP 路由,绑定任意 SQL 查询。
通过管理界面配置:
- 路由路径:
/api/ha_data_store/custom/{path} - SQL 语句:支持参数占位符,如
?entity_id、?date - 描述:可选的说明文字
通过 API 配置:
# 添加路由
curl -X POST /api/ha_data_store/routes \
-H "Content-Type: application/json" \
-d '{"route_path": "my_query", "sql_statement": "SELECT * FROM device_history WHERE entity_id = '?entity_id'"}'SQL 安全沙箱: 禁止 DROP、DELETE、UPDATE、INSERT、ALTER、CREATE、TRUNCATE、EXEC、EXECUTE 等危险关键字。
万能动态路由: 任何没有对应静态路由的 /api/ha_data_store/custom/{tail} 请求都会自动查询 custom_routes 表并执行 SQL 返回结果。
发布管理(v3.5.0):
- 每条路由带 启用/停用 开关,停用后外部调用返回 403;无
LIMIT的手写 SQL 执行时自动套用max_rows安全上限; - 路由列表显示来源(🧩 构造器定义 / ✍️ 手写 SQL)与状态;手写 SQL 仍是完整路由定义的一部分;
- 不想手写 SQL?用 🧩 查询构造器(见 15. 可视化查询构造器)可视化生成并发布。
通过 WebSocket 连接远程 HA 实例,将远程实体的状态和控制在本地无缝映射。
支持的实体类型:
| 类型 | 可读写 | 远程控制方式 |
|---|---|---|
| switch | 可读写 | REST API 转发 turn_on/turn_off |
| light | 可读写 | REST API 转发 turn_on/turn_off + brightness |
| climate | 可读写 | REST API 转发 set_hvac_mode + set_temperature |
| cover | 可读写 | REST API 转发 open_cover/close_cover |
| fan | 可读写 | REST API 转发 turn_on/turn_off |
| lock | 可读写 | REST API 转发 lock/unlock |
| number | 可读写 | REST API 转发 set_value |
| select | 可读写 | REST API 转发 select_option |
| sensor | 只读 | WebSocket 推送状态同步 |
| binary_sensor | 只读 | WebSocket 推送状态同步 |
配置步骤:
- 添加远程连接:输入远程 HA 地址、长期访问令牌、SSL 验证选项
- 添加桥接实体:从连接中选择一个或多个实体 ID 进行桥接
- 系统自动在本地创建对应的代理实体,操作和状态与远程同步
断线重连: 自动检测 WebSocket 断开并重连,采用指数退避(5 秒 → 5 分钟)。
监听本地 JSON 文件的变化,自动将数据映射为 HA 实体。
处理流程:
- 每 5 秒检查文件 mtime 是否变化
- 读取 JSON 文件内容
- 根据
state_field提取状态值 - 创建或更新
sensor.file_{name}实体 - JSON 中的其他字段自动映射为实体属性
配置通过 API:
POST /api/ha_data_store/file_source
{
"name": "weather_data",
"file_path": "/config/data/weather.json",
"state_field": "temperature",
"entity_prefix": "sensor.file_",
"poll_interval": 10
}定时请求外部 HTTP API,将 JSON 响应解析并映射为 HA 实体。
配置通过 API:
POST /api/ha_data_store/api_source
{
"name": "external_weather",
"url": "https://api.example.com/weather",
"method": "GET",
"headers_json": "{\"Authorization\": \"Bearer token123\"}",
"state_field": "current.temp",
"entity_prefix": "sensor.api_",
"poll_interval": 60,
"timeout": 15,
"max_retries": 5
}- 支持点号路径深度提取(如
data.temperature.value) - 自动重试(失败指数退避)
- 健康状态监控(在系统信息传感器中展示失败次数)
动态创建自定义实体,支持多种设备类型和自定义属性。
配置通过 API:
POST /api/ha_data_store/virtual_device
{
"entity_id": "sensor.virtual_room_temp",
"device_type": "sensor",
"device_name": "虚拟房间温度",
"entity_name": "Room Temp",
"extra_config": {
"unit_of_measurement": "°C",
"icon": "mdi:thermometer"
}
}虚拟设备会持久化到 virtual_devices 表,HA 重启后自动恢复。
虚拟灯光(device_type: light):支持开关 / 亮度 / 色温 / 颜色(HS·RGB)全功能。
- 建灯时可配
supported_color_modes(默认["color_temp","hs"];也可["color_temp"]/["hs"]/["brightness"]/["onoff"]——HA 规定 onoff / brightness 不能与其它模式共存)、min_color_temp_kelvin(默认 2000)、max_color_temp_kelvin(默认 6500)、default_brightness(1-255,默认 255)、default_color_temp_kelvin(默认 4000); - 这些参数会存进
virtual_devices.extra_config,重启后仍生效; supported_color_modes只声明灯真正支持的模式(color_temp+hs或单独一档),light.turn_on支持brightness/color_temp_kelvin(兼容旧式color_tempmireds)/rgb_color/hs_color,RGB↔HS 自动互转,超出范围自动夹取;- 状态通过
RestoreEntity持久化:重启后亮度 / 色温 / 颜色 / color_mode 全部恢复, 导出/导入(/virtual_device/export|import)也带这些属性; - db_viewer「虚拟设备」列表新增「🎛 调光」面板(亮度滑块 + 色温滑块 + 颜色选择器 + 开关), 以及列表里的「状态」列(亮度百分比 / 色温 K / 颜色色块);
- 控制走新接口
POST /api/ha_data_store/virtual_device/state(body{entity_id, action: on|off|toggle, brightness?, color_temp_kelvin?, rgb_color?, hs_color?}), 内部调用标准服务,所以属性、自动化、历史记录都与真实灯一致。
将指定 HA 实体的状态实时导出为 JSON 文件,供外部系统消费。
配置通过 API:
POST /api/ha_data_store/export
{
"entity_id": "sensor.power_meter",
"file_name": "power_export.json"
}每当实体的状态发生变化时,系统会自动写入对应的 JSON 文件。
采集打印机统计数据与当日作业明细,支持多台打印机、配置管理、数据查询与系统监控。
配置两个实体(通过"系统配置 → 打印机配置"):
| 实体类型 | 数据来源 | 说明 |
|---|---|---|
| 统计数据实体 | attributes.daylist |
每日汇总(print/scan/copy/fax/jam_printer)+ 墨量(ink_*) |
| 当日详细数据实体 | attributes 各类型明细数组 |
当日各类型作业明细 |
采集时机:
- 以实体状态值变化判定触发(统计实体 state 为五项累计合计、详细实体为当日作业总数,每次打印都变化)
- 当日多次打印时,每次变化都会覆盖更新当日记录(汇总 + 墨量 + 明细),保证始终为最新
- 保存配置时主动采集一次当前数据
数据存储(单张主记录表 printer_daily,每天一条):
name:打印机名称(用户设置)day:日期print/scan/copy/fax/jam_printer:当日汇总计数ink_black/ink_cyan/ink_magenta/ink_yellow:墨量printer_jobs:当日作业明细(JSON,含各类型作业数组 + total + date)
配置接口:
GET /api/ha_data_store/printer/configs # 配置列表
POST /api/ha_data_store/printer/configs # 新增/修改配置
DELETE /api/ha_data_store/printer/configs?id=xxx # 删除配置
POST /api/ha_data_store/printer/configs/recollect?name=xxx # 主动重采数据查询(详见下方"数据查询接口"的 printer_* 类型):
| 查询类型 | 功能 |
|---|---|
printer_years |
打印机有哪些年数据 |
printer_month_dates |
指定月哪些日期有数据 |
printer_total |
打印机合计数据 |
printer_monthly_total |
按年月统计合计数据 |
printer_daily_range |
指定日期区间数据 |
printer_detail |
指定日期详细数据 |
前端 room-elves-card 通过埋点记录用户每次操作(开关、调值、按钮、场景、自动化等),上报后端存储,用于分析使用习惯和将来还原设备控制面板。
每条操作记录含:
| 字段 | 说明 |
|---|---|
user_name |
当前登录用户 |
entity_id |
操作实体 |
action |
动作类型(toggle/set_value/press/call-service 等) |
name / icon |
实体显示名 / 图标 |
room_name |
所在房间 |
source |
触发位置(head/room/standalone) |
card_type |
弹窗卡片类型(预留) |
other |
预留字段 |
state_log |
操作前后状态变化(如 on→off、cool→heat) |
ts / ts_text |
时间戳 / 人类可读时间 |
config_id |
操作所属弹窗/选项卡/设备的 config_id(如 diannao),用于定位并还原完整弹窗配置 |
device_type |
设备类型(如 light/socket/ac 等) |
action_snapshot |
完整 tap_action 快照(JSON,用于还原设备面板) |
时间权威源:ts 取实体状态变化时刻(HA last_changed)而非前端点击时刻,前后端时间源统一,消除浏览器/服务器时钟偏差(前端 8 秒时间窗 + 最多 8 次轮询核对后覆盖)。后端 ts_text 由 ts 按秒精度本地化;device_history.on_time/off_time 同为实体状态时间(秒精度,毫秒自动截断),_link_device_history_to_actions 用 on_time == ts_text 精确匹配回填操作者,保证跨表时间严格一致。
user_actions 写入后,sensor.近期使用设备(sensor.ha_data_store_user_actions)实时聚合近 N 天数据:
- 状态值 = 近 N 天有操作的不同设备面板数
attributes.devices= 按 action_snapshot 归一化聚合 的设备列表,每项含:entity_id / name / icon / room_name / count(使用次数)/last_used+last_used_text(最近使用)/state_log(最近状态变化)/config_id/device_type/action_snapshot(完整还原快照),按使用次数降序- 30 秒定时刷新 + 写入后实时刷新
前端可直接订阅该 sensor,无需调 API 即可渲染"常用设备小卡片",并凭 action_snapshot 还原设备控制面板。
devices 只覆盖前端卡片埋点;all 从 device_history 取全量设备的最近使用(含自动化、定时开关等非卡片操作),每个 entity_id 一条:
| 字段 | 说明 |
|---|---|
entity_id / name / room / icon |
该实体最新一条记录的信息(icon 取 v3.6.8 新增的 device_history.icon) |
running |
最新记录 on_time 有值且 off_time 为空 → true(正在运行) |
last_used_text / last_used |
运行中 = 当前时刻;否则 = 该记录 off_time(附毫秒时间戳) |
on_time / off_time |
最新记录的开关时间 |
count |
窗口内开关次数 |
duration / duration_hour |
窗口内累计时长(秒 / 小时);运行中记录按「当前时间 − on_time」计 |
energy |
窗口内累计用电(kWh);已关闭取 energy_consumed,运行中取 now_kwh − on_power,无来源记 0 |
- 按
last_used倒序;额外属性:total_all(条数)、all_range(如"最近 30 天")、exclude_count(生效排除项数)。
number.ha_data_store_recent_days(近期使用天数):范围 1~365、默认 30、单位「天」,
可直接在仪表盘调整;缺失/非法/超范围一律回退 30。同时作用于 devices 窗口、all 节点与
device_last_used 接口;改动后立即刷新传感器(EVENT_STATE_CHANGED 监听)。
设置值通过 RestoreEntity 持久化,重启 HA 后保持(不会回到默认 30)。
同类设置实体
text.ha_data_store_ele_list(用电计量列表条数)也已支持重启恢复。
窗口过滤作用于 on_time:从「今天 − (N−1) 天 00:00:00」起算(含今天共 N 个自然日)。
被排除的 entity_id 不进入 all 节点与 device_last_used 接口。存储于
api_settings.recent_exclude_entities(JSON 数组),在 db_viewer
「系统配置 → 🕘 近期使用设备」 子页配置:已选标签(可移除)+ 手动输入(逗号/换行)+
实体选择器(搜索 entity_id / 名称 / 房间,一键「➕ 排除」)。保存后立即刷新传感器。
| 接口 | 说明 |
|---|---|
GET /api/ha_data_store/recent/exclude |
读取排除项 {success, count, exclude[]} |
POST /api/ha_data_store/recent/exclude |
保存(Body {"exclude":[...]} 或 {"text":"a,b\nc"}) |
GET /api/ha_data_store/recent/entities |
device_history 实体唯一值 {entity_id, name, room}(供选择) |
GET /api/ha_data_store/query?type=device_last_used —— 每个实体的最近使用情况(与 all 节点同源同口径),
entities 为去重后的 entity_id 列表(按最近使用倒序):
| 参数 | 说明 |
|---|---|
entities / entity_id |
逗号分隔实体(空 = 全部) |
start / end |
时间段(作用 on_time) |
date / month / year |
指定日 / 月 / 年 |
window_days |
窗口天数(仅在未传 start/end/date/month/year 时生效;缺省读设置实体;传 0 = 不限窗口/全部历史) |
filter |
是否启用「排除项过滤」,默认 1(启用,别名 use_exclude);0 = 不应用排除项(exclude 一并忽略),返回全部设备 |
exclude |
逗号分隔排除实体;不传 = 用保存的排除项,显式传空 = 不排除 |
running |
1 只返回正在运行的设备 |
detail |
0 只返回 entities 列表(不返回 items 明细),默认 1 |
limit / offset |
分页(0 = 不限) |
时间过滤优先级:start/end > date > month > year > 窗口天数 —— 即时间模式与窗口天数互斥,
选了时间段/指定日/月/年后 window_days 自动不生效。
filter=0 只关闭排除项过滤,不影响时间/实体/运行中等其它条件;
需要「全量、不做任何过滤」时用 ?type=device_last_used&window_days=0&filter=0。返回:
{ "range": "最近 30 天", "window_days": 30, "use_exclude": true, "exclude_count": 2, "total": 42, "count": 42,
"entities": ["light.a", "switch.b"],
"items": [ { "entity_id": "light.a", "name": "大灯", "room": "客厅", "icon": "mdi:lightbulb",
"running": false, "on_time": "...", "off_time": "...",
"last_used": 1789778169240, "last_used_text": "2026-09-19 08:00:00",
"count": 6, "duration": 3600.0, "duration_hour": 1.0, "energy": 0.5 } ] }db_viewer「API 工具 → 设备类 → 🕘 近期使用设备」提供可视化参数表单:多实体 + 时间模式 (全部时间 / 最近 N 天(窗口) / 时间段 / 指定日 / 指定月 / 指定年)+ 是否应用排除项过滤 + 只看正在运行 + 是否返回明细。
时间模式与窗口天数互斥:「最近 N 天(窗口)」是时间模式的一个选项(该选项只在本次查询接口可见, 切到其它接口自动隐藏并回落为「时间段」;进入本接口时默认选中),选中后出现「天数(留空 = 用设置实体)」 输入框;选「全部时间」则真正不限窗口(避免"全部时间却只返回 30 天"的歧义)。
- 系统监控 summary 新增卡片 🕘 最近使用设备(数字 = 统计到的设备数),点击展开对应区块,
表格列:实体ID / 名称 / 房间 / 状态(运行中·已关闭)/ 最近使用 / 次数 / 时长(h) / 用电(kWh),
标题右侧显示
统计范围 · 运行中 N · 排除项 M;数据来自/monitor的recent节点(最多返回前 100 台)。 - 系统配置 → 🕘 近期使用设备 子标签角标显示排除项数量的负值(如
-5表示已排除 5 个实体), 页面加载即显示,配置页内增删排除项即时反映。
POST /api/ha_data_store/action_log Body: { actions: [ {user_name, entity_id, action, ..., action_snapshot} ] }
GET /api/ha_data_store/action_log?days=30 查询近 N 天原始记录
前端本地缓冲,操作停止 5 秒后统一上报(防抖合并连续操作),满 20 条立即上报;fetch 15 秒超时(AbortController 防 pending 锁死)、失败指数退避自动重试(5s 起、上限 5 分钟)、上报前游标取 max 防多实例重复上报;fetch 前同步补核对(实体状态时间 + state_log 权威值)。
见 数据查询接口 的 user_actions_* 系列类型。
原生 HA 辅助元素(input_boolean/input_number/input_select/input_text/input_button/counter/binary_sensor)的配置存于 HA .storage,无法由本集成直接管理。本功能把 A 机原生 helper 扫描导出为 JSON(配置 + 状态),在 B 机导入后转换为本集成自管实体(RestoreEntity,无需重启、状态自动回填并在重启后保持):
| 源 | 目标 | 保留 |
|---|---|---|
input_boolean |
switch |
icon/name + on/off |
input_number |
number |
min/max/step/unit + 当前值 |
counter |
number |
min/max/step/initial + 计数 |
input_select |
select |
options + 当前选项 |
input_button |
button |
配置(无状态) |
input_text |
text |
文本值 |
binary_sensor |
binary_sensor |
on/off |
- UI:db_viewer「系统配置 → 🧩 辅助元素」——已导入列表(可删/全选批量删)、扫描并导出、导出已导入项、导入文件(可覆盖同名)、🆕 前台新建;
- 汇总实体
sensor.ha_data_store_helper:状态 = 辅助元素个数,attributesentities[]= 每个实体明细(entity_id/name/icon/source_type/source_entity_id),30s 刷新; - 接口:
GET /api/ha_data_store/helper/scan、/helper/export、POST /helper/import、GET/POST/DELETE /helper。
db_viewer「系统配置 → ⚡ 用电计量」登记功率实体(填功率实体 ID / 设备名 / 房间 / ID 段 / 单位 W·kW),系统 10 秒采样积分(功率 × 时间差)自动生成三个累计实体并按天入库:
sensor.ha_data_store_{id}_daily_ele(日,今日累计)sensor.ha_data_store_{id}_monthly_ele(月,由日数据实时聚合)sensor.ha_data_store_{id}_yearly_ele(年,由日数据实时聚合)
这三个实体由 ID 段自动派生,是登记的产物,不需要也不允许手填。库中
daily_entity_id字段即等于sensor.ha_data_store_{ID段}_daily_ele,仅用于前端关联跳转查询,永远与实际注册的实体 ID 保持一致。
特性:
- 每天一条落库
power_energy_daily(60s 落盘 + 跨日自动分账,kwh 保留 3 位小数);月/年不建表,实时聚合; - 单位 W/kW 自动识别(登记优先,其次读实体
unit_of_measurement);unavailable/unknown不累计;采样空窗 >5 分钟丢弃(防停机误算); - 全部实体归入统一设备「用电计量」;重启自动恢复,卸载前自动落盘;
- 历史列表状态属性:三个累计实体状态属性自动附带全量历史列表 —— 日用电
daylist(每日用电)、月用电monthlist(每月用电)、年用电yearlist(每年用电),元素形如{day|month|year, usage}(usage 单位 kWh,保留 3 位小数);全量不设上限、无数据日期不占位,今天/本月/当年并入实时值(与实体 state 一致),由 Manager 缓存并在 60s 落盘/跨日时重建,重启自动恢复; - 汇总实体
sensor.ha_data_store_all_power:状态 = 用电实体个数,attributes 顶层total(不受ele_list条数限制)= 合计节点{count, power, today, month, year, room[]}:power当前全屋功率(W,仅 ≥0 有效读数计入),today/month/year今日/本月/本年用电合计(kWh,直接对全部启用 meter 求和),room[]按房间汇总{room,count,power,today,month,year}(room 为空归入「未分配」);entities[]= 每个用电实体的明细(entity_id/name/icon/room/device/power_entity +period+ 对应列表 daily→daylist、monthly→monthlist、yearly→yearlist,升序保留最近 N 条),30s 刷新;明细列表条数由设置实体text.ha_data_store_ele_list(状态“日,月,年”,默认3,3,3)控制,该 text 变化时立即刷新 all_power;三个用电实体自身的列表保持全量不受影响; - 取消登记 = 软删除(回收站):点「取消登记」不会物理删除配置,而是把
power_meter_configs行置为enabled=0归档并移入「♻️ 回收站」;历史日表power_energy_daily任何情况下都不删除; - 重新登记自动沿用历史数据:对同一功率实体重新登记时,会自动沿用原有的
id_slug/ 设备名 / 房间(表单里 ID 段留空即自动带出,或点行内「编辑」一键回填),因此实体 ID 不会分叉,历史日用电数据无缝接续; - 日用电量实体自动派生:登记时按 ID 段自动生成
sensor.ha_data_store_{ID段}_daily_ele并写入daily_entity_id(配置表 + 日表),表单中该项为只读预览、随 ID 段实时变化;启动时若发现历史值缺失或曾被手填错误,会自动按 ID 段纠正(也可点 🔧 按 ID 段纠正日用电实体 手动触发); - 回收站中可 ♻️ 一键还原全部、单条 ♻ 恢复(重新注册三个用电实体,历史接续)、彻底删除(物理删配置行,日表仍保留)、清空回收站;
- 接口:
GET /api/ha_data_store/power_energy(type=configs生效中 /type=archived回收站 /type=lookup&entity_id=查单个含归档 /type=query&kind=daily|monthly|yearly|range|latest,支持 entity_id/room/date/month/year/start/end),POST(action=create|delete|restore|purge|purge_all);API 工具含「⚡ 用电计量」查询分组; - 多实体 × 多维度聚合:
GET /api/ha_data_store/query?type=power_energy_multi,参数entities(逗号分隔,空=全部)、bucket=day|month|year、view=entity|date、start/end/date/month/year(优先级 start/end > date > month > year,全不传 = 全部时间); API 工具中对应「时间模式」下拉:全部时间 / 时间段 / 指定日 / 指定月 / 指定年 (device_usage_multi同样支持)。view=entity返回entities[].series[](按实体),view=date返回dates[].devices[](按时间); 两者均含totals.kwh/day_count。API 工具「⚡ 用电计量」分组有对应可视化配置项; - 数据浏览器中
power_energy_daily为用户表(默认可见)。
集成运行过程中可能残留"没有实体"的空设备(例如删除实体后遗留)。db_viewer「系统配置 → 🧹 设备清理」可一键扫描并清理:
- 仅清理 identifiers 以本集成 domain 开头、非 entry 主设备、且按 entity_registry 统计无任何实体的空设备,安全不误删;
GET /api/ha_data_store/devices/cleanup扫描预览,POST({confirm:true})真删(删除前自动解除 config entry 关联,以最终是否仍在 registry 判定成功)。
「API 工具」内新增 🧩 查询构造器,无需手写 SQL 即可把任意数据表发布为一个受鉴权的查询接口;「数据库浏览」工具栏同时支持可视化 ➕ 新建表。
构造器流程(5 步):
- ① 选数据表与返回字段:表目录按数据类别分组(环境/属性/设备历史/其它自定义)并显示行数,返回字段默认全部、可勾选精简;
- ② 动态参数(过滤条件):每行 = 字段 + 比较方式 + 值来源。按字段类型提供合适操作符:
- 文本/ID:
=/≠;数值字段:>≥<≤ - 文本还支持 包含 LIKE / 不包含 / 多值 IN(调用时逗号分隔,如
rooms=客厅,卧室)与「为空」 - 时间/日期字段提供 「时间段(起,止)」单行 between(自动生成
start_time/end_time两个参数);可先用顶部「时间字段」下拉指定列,再点「+ 时间段」 - 值选“前端传参”即成动态参数(填参数名/默认值/必填/说明);或选“固定值”写死
- 文本/ID:
- ③ 排序:字段 + 升/降序;
- ④ 汇总信息:可选返回总条数
count与数值列合计/平均/最大/最小; - ⑤ 试运行并发布:填试运行参数值 → 执行结果表 + SQL 预览 → 保存即发布。
发布后调用:
GET /api/ha_data_store/custom/{route_path}?key=你的Key&entity_id=sensor.a&start_time=2026-09-01 00:00:00&end_time=2026-09-04 23:59:59- 响应默认:
{"success":true, "data":[...], "count":匹配总数, "summary":{合计/平均...}}; - 可选参数未传且无默认值时自动跳过该过滤(前端可只传部分条件);必填缺失返回 400 中文错误;加
&_debug=1可查看实际生成的 SQL; - 鉴权沿用「API 访问」总开关 + API Key;路由有独立 启用/停用 开关;
- 定义(
query_def)落库custom_routes,复制数据库即可迁移。
新建表: 填表名(自附加自增主键可选)与字段(名称/类型/主键/默认值),后端白名单校验后建表;新表即刻进入“用户表”下拉与查询构造器可选范围。
「API 工具」内新增 🧩 接口管理(新接口) 子页,用于发布新接口,特点是新增 / 修改 / 启停 / 删除接口均无需重启 HA。
原理:HA 的 register_view() 仅在集成 setup 时注册路由且无公开注销 API,故以往新增接口必须重启。本模块用一个固定通配路由 /api/ha_data_store/ext/{接口名}(setup 时注册一次、此后永不变),接口定义存库并在运行时读取 → 变更即时生效。
流程(不重启):
- 「API 工具 → 🧩 接口管理(新接口)」→ ➕ 新建接口:填
name(URL 用,字母开头)/ 显示名 / 描述 / 最大行数; - 粘贴
query_def(声明式定义,结构与查询构造器一致的 JSON;可在「🧩 查询构造器」里配好后复制); - ▶ 试运行(自动渲染动态参数、返回 SQL 与结果)→ 💾 保存 → 立即发布;
- 后续 编辑 / 启用停用 / 删除 同样即时生效。
调用:
GET /api/ha_data_store/ext/{接口名}?key=你的Key&参数=值- 响应:
{"success":true, "endpoint":{...}, "count":总数, "columns":[...], "rows":[...], "aggs":{...}};加&_debug=1附带实际 SQL; - 参数按定义中的动态参数自动收集;未传且无默认值时自动跳过该过滤;停用中的接口返回 403;
- 鉴权与现有接口完全一致(
api_enabled+ API Key + 局域网判断),管理操作需「管理面板登录 + 编辑开关」,未新增任何开关; - 定义落库
api_endpoints表,与旧custom_routes相互独立;旧接口保持原样、不纳入本模块。
⚠️ 边界:本模块解决的是"新增/修改接口(查询与聚合定义)不重启"。若需新增后端计算逻辑(改.py)、自定义 URL、非 JSON 响应、定时器或事件订阅、新增实体等,仍需重启 HA。
以后为 ha_data_store 新增 API 接口,一律走 v3.6.5 起的「接口管理」模块,不要要求重启 HA。 做法:接口定义存
api_endpoints表(query_def复用查询构造器 v2 结构),经固定通配路由GET/POST /api/ha_data_store/ext/{接口名}运行时加载执行;管理用/ext_manage(列表 / 新增 / 更新 / 启停)、/ext_manage/delete、/ext_manage/test(试运行);鉴权复用_check_api_enabled(执行)与_check_db_edit_enabled(管理),不新增开关;旧接口(custom_routes、/query等)不纳入、保持原样。只改db_viewer.html(布局/样式/脚本)也无需重启(HTML 热重载,刷新页面即可)。 例外(仍需重启,且需提前说明):新增后端计算逻辑(改.py,如新的行级算法/内建函数/新视图)、自定义 URL 路径、非 JSON 响应、定时器或事件订阅、新增实体等。
「只提供数据、前端负责 UI」:两个查询接口均按 天 / 日期 / 时间段 / 月 / 年 为界
(device_history 已在午夜自动拆分,不存在跨天记录),不分页(返回 count + truncated)。
GET /api/ha_data_store/query?type=timeline —— 把 7 类事件合并为一条按时间倒序的时间线:
| source | 数据表 | 事件 |
|---|---|---|
device |
device_history |
一条记录展开为 on / off 两个事件;运行中的只有 on |
user_action |
user_actions |
用户操作(含 state_log) |
automation |
automation_logs |
自动化执行结果与耗时 |
vacuum |
vacuum_history |
仅在 state 变化时产出事件(轨迹点自动折叠) |
xiaoai |
xiaoai_conversations |
小爱问答 |
health |
health_records |
健康记录 |
printer |
printer_daily |
打印机当日作业 |
| 参数 | 说明 |
|---|---|
date / start+end / month / year / today=1 |
时间窗(默认今日) |
sources |
来源过滤(逗号分隔,空 = 全部) |
events |
仅 device:on / off(空 = 两者) |
entities / rooms / users |
实体 / 房间 / 用户过滤 |
keyword |
关键词(设备名 / 操作 / 自动化 / 扫地机 ID / 小爱文本 / 健康 / 打印机) |
limit(默认 500,上限 5000)/ detail=0 |
条数上限与明细开关 |
来源能力收敛:
entities/rooms/users属"实体维度"过滤,不具备该维度的来源会被自动剔除 (如entities=会剔除 automation / health / printer),被剔除项见返回的skipped_sources。
条目结构:{ts, ts_ms, source, event, title, entity_id, name, room, icon, user, detail, extra{}}
(ts_ms 便于前端排序;extra 按来源给出 duration/energy/state_log/… 等明细)。
GET /api/ha_data_store/query?type=room_occupancy —— 数据源 device_history 中 name='人在' 的记录:
- 严谨口径:同一房间的重叠区间先做区间并集再计时(多个"人在"实体、抖动重复上报都不会重复计时),
同时给出
raw_duration_hour作为"未并集口径"对照; - 每房间输出:
duration(_hour)/count/segments/avg_hour/occupied/last_seen/share; 顶层:total_duration_hour/top_room/occupied_rooms/room_count; - 参数:时间窗(默认今日)、
rooms、include_empty=1(无数据房间也返回 0)、bucket=none|day|hour(按日序列 / 24 小时分布)、door=0、detail=0; - 门户事件
door:open_count/open_duration_hour/last_open/open_now/events[]—— 可直接与 timeline 组合出"回家 / 外出"时间线。
db_viewer:「API 工具 → 家庭洞察」分组提供上面两个接口的可视化表单(时间模式为 指定日 / 时间段 / 指定月 / 指定年,缺省今日;含来源勾选、设备事件、关键词、房间、分解粒度、 含无数据房间、门户事件等控件)。
以往每加一种统计就要新增一个内建 type= 分支;现在把「怎么查」变成一条数据(指标定义):
新增 / 修改指标无需重启 HA(定义存库,运行时读取)。
两层元数据
- schema 元数据(不落表):每张表的中文名 / 分组 / 时间列 / 实体列 / 房间列 / 值列 / 时间粒度(
datetime|date);env_*、attr_*动态生成 —— 同时是引擎的列白名单(安全边界)。 - 指标定义(新表
metrics_catalog):metric_id / name / category / source_table / value_col / value_expr / agg / unit / icon / group_by / group_col / filters / enabled / builtin / sort_order / remark。
占位符:@time @entity @value @room @name @id —— 编译时按源表解析为真实列名,同一份定义可跨表复用。
接口
| 接口 | 说明 |
|---|---|
GET /api/ha_data_store/query?type=metrics_catalog |
指标目录(可按 category / keyword 过滤,默认只返回启用项) |
GET /api/ha_data_store/query?type=metrics_query&metric_id=xxx |
执行指定指标 |
metrics_query 参数:metric_id(必填)、start/end、date、month、year、days(最近 N 天)、
entities、room、group_by(none/entity/room/name/day/hour/month/year)、
agg(avg/sum/max/min/count/count_distinct)、order、filters(JSON 字符串)、limit/offset、detail、sql。
返回:
{"metric_id":"env_temperature_avg","name":"温度均值","unit":"°C","range":"最近 7 天",
"group_by":"day","agg":"avg","row_count":842,"count":7,
"summary":{"rows":7,"samples":842,"total_count":842,"value":162.4,"avg":23.2,"min":17,"max":26.4},
"rows":[{"key":"2026-09-18","value":23.2,"count":120}],
"series":{"labels":["2026-09-18"],"values":[23.2],"counts":[120]}}示例:.../query?type=metrics_query&metric_id=env_temperature_avg&days=7&group_by=day
管理:db_viewer「系统配置 → 📊 指标管理」可新建 / 编辑 / 复制 / 启停 / 删除指标、同步内置指标,
并试运行(时间模式 + 实体 + 分组/聚合覆盖,展示生成的 SQL、summary 与结果表);
「API 工具」新增「📊 通用指标(元数据驱动)」分组(自动生成带 metric_id 的 URL)。
内置指标(启动时 seed,不覆盖用户改动):环境(温度/湿度/PM2.5/CO₂/功率 × 均值·最高·最低)、
设备(开启次数·时长合计·单次均值·用电合计·运行中设备数)、用电、健康、操作(活跃用户数)、
自动化、卡片上报,以及每个 attr_* 类型的记录数与 REAL 列均值(每类型最多 5 列)。
API工具 → 🔗 API 地址生成器 的「查询类型」下拉里有独立分组 🌐 实体→网络(token 鉴权):
- 动态列出所有已配置目标(每个目标 1~2 项:📥 读数据 / 🎛 控制),选中即生成带真实 token 的地址
- 地址不附加
?key=—— token 本身就是密钥、URL 自鉴权 - 选中控制项时会展示多行
curl -X POST示例、该 token 当前的授权动作清单, 以及push_capabilities/{token}能力清单地址;未开启控制时提示会 404
顶部选项卡 💾 数据备份 提供整库备份能力:
- 备份方式:优先
VACUUM INTO,回退 SQLite 在线备份 API,运行期取一致快照、不需停机 - 自动计划:关闭 / 每小时 / 每天 / 每周,可配置执行时刻与保留份数(默认保留 10 份自动备份)
- 恢复:不在运行期覆盖库 —— 校验后排队,重启 HA 时在数据库被打开之前原子替换, 替换前自动留一份「恢复前快照」;校验失败则取消恢复并保留现有库
- 设置存放:
api_settings表的backup_*键(不新增表,库损坏时模块仍可用) - 备份目录默认
storage/ha_data_store_backups,可改为 NAS 挂载后的绝对路径
不能直接填 smb://host/share 这类协议地址 —— 程序只能读写文件系统路径。
填了会在 HA 配置目录里静默造出一个同名垃圾目录(备份显示"成功"但没到 NAS),
因此保存时会直接报错并给出挂载指引。正确做法是先挂载,再填挂载后的路径:
| 环境 | 挂载方式 | 填写的路径示例 |
|---|---|---|
| HA OS / Supervised | 设置 → 系统 → 存储 → 添加网络存储(用途选 media / share) | /media/ha_backups、/share/backups |
| HA Container / Core | 宿主机 mount -t cifs //192.168.1.102/media /mnt/ha_backups -o username=用户,password=密码,uid=1000 |
/mnt/ha_backups |
面向共享的写入做了额外加固:备份先在本地生成并校验,再搬到共享 —— 避免 SQLite 直接在网络盘上做 journal / 依赖文件锁(官方不建议), 也避免网络中断在共享上留下半个损坏文件。挂载点需确保在 HA 启动时就已就绪。
把手机端同步过来的通讯记录(通话 / 短信 / 微信 / QQ 等)落库并查询。
「系统配置 → 📊 属性提取」的第四种模式 通讯数据采集(mode=comm):
选实体 → 选数组路径 → 选唯一键字段(通常是 id)→ 把源字段对应到 16 个固定列。
固定字段表 attr_comm_records:
| 列 | 类型 | 说明 |
|---|---|---|
my_number |
TEXT | 我方号码(微信 / QQ 场景可留空) |
party_number |
TEXT | 对方号码 |
party_place |
TEXT | 对方号码归属地 |
party_name |
TEXT | 对方姓名 |
time |
TEXT | 通讯时间 YYYY-MM-DD HH:MM:SS |
location |
TEXT | 通讯地点 |
msg_type |
TEXT | 消息类型 |
channel |
TEXT | 数据来源(call / sms / wechat / qq …) |
call_type |
TEXT | 呼叫类型 |
duration |
INTEGER | 时长(秒) |
cost |
REAL | 金额(元) |
content |
TEXT | 消息内容 |
image_path |
TEXT | 图片路径(图片消息的文件路径 / URL) |
location_coordinate |
TEXT | 我的坐标 |
party_isp |
TEXT | 对方运营商 |
party_coordinate |
TEXT | 对方坐标 |
列名与类型由系统固定(不受前端勾选影响),因此 duration 恒为整数、cost 恒为数值,
聚合查询不会因列类型被勾错而失效;建表时自动创建 time、(party_number,time)、
(party_name,time)、(location,time) 四个索引。
配置要点:
- 每个目标列既可以选数组里的字段名,也可以直接填固定值
(如
channel填wechat、my_number填自己的号码);输入框边框绿=源列 / 橙=固定值, 也可加=前缀强制指定固定值 time为唯一必填映射项;未填写的目标列保持为空- 值转换:
2026-09-01T17:24:33+08:00/2026/9/1 17:24/ 时间戳 →YYYY-MM-DD HH:MM:SS; 时长支持27秒、3分53秒、1小时2分3秒、3分53、1小时30、3:53(分:秒)、1:02:03(时:分:秒)、233、27s、3m53s等写法,自动换算为秒 - 「对比最近条数」是去重窗口:每次采集回查最近 N 条历史记录并按唯一键比对, 命中则更新、未命中才插入(通讯模式默认 1000)。采集时还会自动放大到至少 2 倍本次条数, 所以即使设小也不会重复写入本次数据
GET|POST /api/ha_data_store/comm?type=xxx&key=你的APIKey
| type | 说明 | 关键参数 |
|---|---|---|
records |
明细列表 | sort / order / limit / offset / content_len |
dates |
哪些日期有数据(每天条数 / 时长 / 金额 / 联系人数) | 任意过滤 |
stats |
★ 统计分析:按时间粒度分组汇总,每桶含条数 / 时长 / 金额 / 去重联系人数 / 活跃天数 / 平均时长,并附 total(合计)与可选 avg_per_bucket(每桶均值) |
granularity=year|quarter|month|week|day|hour|weekday、sort=time|value、order、by、limit、fill、with_total、with_avg |
crosstab |
★ 交叉汇总:行维度 × 列维度 的度量矩阵(如「按消息类型汇总各联系人的条数与时长」) | rows(1cols(0metric=count|duration|cost、limit / col_limit |
onthisday |
★ 历史上的今日:按「月日相同」筛选历年同一天的数据,再按 mode 输出(详见下节) |
mode=stats|ranking|parties|records|crosstab、date(基准日)、years / min_year / max_year / exclude_current、at / window / hour |
compare |
★ 周期对比:当前周期 vs 上一周期(环比)vs 去年同期(同比),直接返回差值与增长率 | period=day|week|month|quarter|year、date(基准日)、compare=prev|yoy|both |
contact |
★ 联系人档案:某号码 / 姓名的完整画像(总量 / 首末通讯 / 多久没联系 / 平均间隔 / 各维度分布 / 最近明细) | party_number 或 party_name(必填其一)、recent / top_days / months |
longest |
单次 Top N:最长通话 / 最高金额(按单条记录排序,区别于按人聚合的排行榜) | by=duration|cost、order、limit / offset |
quality |
数据质量检查:时间为空 / 格式异常 / 时长与金额异常 / 内容为空 / 图片缺路径 + 疑似重复 + 时间覆盖 | dup_limit |
meta |
数据概览:表结构、总量、时间跨度、去重联系人数、维度取值清单(供前端下拉与健康检查) | 任意过滤 |
ranking |
排行榜:granularity 指定粒度 + period 指定年/月/日后按维度排名;不填 period 即全部数据排名 |
dimension、by=count|duration|cost |
trend |
趋势序列(按时间桶统计,fill=1 补零) |
granularity=day|month|year|hour|weekday |
summary |
汇总统计(总量 / 首末时间 / 总时长 / 总金额 / 联系人数 / 活跃天数 + 各维度分布) | 任意过滤 |
parties |
联系人清单(按对方号码聚合,含首末通讯时间) | by、limit / offset |
places |
通讯地点清单 | limit / offset |
heatmap |
星期 × 小时分布 | 任意过滤 |
通用过滤参数(所有 type 均支持,多值参数逗号分隔):
| 参数 | 说明 |
|---|---|
date / month / year |
单日 / 单月 / 单年 |
start / end |
自定义区间(含边界,YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS) |
party_numbers / my_numbers / numbers |
对方号码 / 我方号码 / 任一方号码(多值) |
party_names / places / party_places |
姓名 / 通讯地点 / 归属地(多值,模糊匹配) |
channels / msg_types / call_types |
数据来源 / 消息类型 / 呼叫类型(多值) |
keyword |
消息内容模糊匹配 |
min_duration / max_duration / min_cost / max_cost |
时长(秒)与金额(元)区间 |
fields |
只返回指定列(逗号分隔,如 time,party_number,duration,content);留空 = 全部列。对 records / onthisday&mode=detail / longest / contact 的明细生效 |
type_name |
数据表对应的属性类型名。一般无需填写——留空时自动解析(顺序见 21.7);仅在需要明确指向某张表时才显式传参 |
示例:
# 某天某人
/api/ha_data_store/comm?type=records&date=2026-09-01&party_names=张三&key=xxx
# 指定号码在 9 月哪些日期有数据
/api/ha_data_store/comm?type=dates&month=2026-09&party_numbers=13800000000&key=xxx
# 2026-09 按姓名排行(按通话时长)
/api/ha_data_store/comm?type=ranking&granularity=month&period=2026-09&dimension=party_name&by=duration&limit=20&key=xxx
# 9 月每日趋势(缺失日期补 0)
/api/ha_data_store/comm?type=trend&granularity=day&month=2026-09&fill=1&key=xxx
# 全部数据按年汇总 / 按年月汇总
/api/ha_data_store/comm?type=stats&granularity=year&key=xxx
/api/ha_data_store/comm?type=stats&granularity=month&key=xxx
# 指定年 → 按月汇总(缺失月份补 0)
/api/ha_data_store/comm?type=stats&granularity=month&year=2026&fill=1&key=xxx
# 指定年月 → 按日汇总(并返回每桶均值)
/api/ha_data_store/comm?type=stats&granularity=day&month=2026-09&with_avg=1&key=xxx
# 指定号码 / 姓名(支持多值)按年 / 按年月 / 按年月日汇总
/api/ha_data_store/comm?type=stats&granularity=year&party_numbers=13800000000,13900000000&key=xxx
/api/ha_data_store/comm?type=stats&granularity=month&party_names=张三,李四&key=xxx
/api/ha_data_store/comm?type=stats&granularity=day&month=2026-09&party_names=张三&key=xxx
# 通讯时长最多的前 5 个月(sort=value 默认降序)
/api/ha_data_store/comm?type=stats&granularity=month&sort=value&by=duration&limit=5&key=xxx
stats 与 trend 的区别:trend 只给「时间桶 + 条数/时长/金额」,用于画折线;
stats 额外给「去重联系人数 / 活跃天数 / 平均时长」,并在响应里返回合计行与每桶均值,
sort=value 时可按指标(by=count|duration|cost)取 Top N 时间桶。
granularity 支持别名与中文(年 / 月 / 日 / 季度 / 周 / yyyy / ym / ymd),
也支持 period 参数(如 period=2026-Q3、period=2026-W35)。
按「月日相同」筛选历年同一天的记录,再按 mode 输出五种视角:
| mode | 用途 |
|---|---|
stats |
历年今日汇总:granularity=year(每年今日)/ month(每年该月)/ day(每年该日),默认 year |
detail |
★ 详细明细:返回跨年份的逐条记录(扁平列表),并附带 total(匹配总数)、year_summaries(各年汇总)与 summary(总体汇总) |
ranking |
历年今日排行榜:dimension + by |
parties |
历年今日联系人汇总:对方号码 / 姓名 + 条数 / 时长 / 金额 / 首末时间 |
records |
历年今日明细:可用 at / window / hour 限定时刻,如「此刻」 |
crosstab |
历年今日交叉汇总:rows × cols |
专用参数:
| 参数 | 说明 |
|---|---|
date |
基准日:09-29 或 2026-09-29;留空 = 今天 |
years |
只看这些年份,多值:2024,2025 |
min_year / max_year |
年份范围 |
exclude_current=1 |
排除今年,只看往年 |
at |
只看某时刻:17:24,或 now(此刻) |
align |
时刻对齐:hour(整点)/ 30 / 15 / 5 / min。at=now&align=hour → 现在 09:02 取 09:00~10:00 |
hours / minutes |
自对齐后起点向后的跨度:align=hour&hours=2 → 09:00at=09:02&minutes=30 → 09:02 |
window |
以 at 为中心的分钟窗口:at=17:24&window=30 → 17:24 ± 30 分钟 |
hour |
按时段筛选:9 / 9,10 / 9-18 |
limit / offset |
detail 模式:明细条数与偏移(默认 100 / 0,上限 1000) |
sort / order |
detail 模式:明细排序字段与方向(默认 time / desc) |
with_years=0 / years_limit |
detail 模式:不附带各年汇总 / 最多汇总年数(默认 20) |
注意:
month/start/end这类时间区间参数在「历史今日」中不生效(date已被用作基准日), 请改用years/min_year/max_year。其余通用过滤(号码 / 姓名 / 消息类型 / 地点 / 归属地 / 呼叫类型 / 关键词等)均可叠加。type=anniversary与type=onthisday等价。
# 历年今日逐条明细(rows 即每条记录,另附各年汇总与总体汇总)
/api/ha_data_store/comm?type=onthisday&mode=detail&key=xxx
# 分页取明细,每条内容截断到 200 字
/api/ha_data_store/comm?type=onthisday&mode=detail&limit=50&offset=50&content_len=200&key=xxx
# 只看和张三有关的历年今日明细
/api/ha_data_store/comm?type=onthisday&mode=detail&party_names=张三&key=xxx
# 历年今日按年汇总(每年一条)
/api/ha_data_store/comm?type=onthisday&key=xxx
# 2025 年的今日,按日看
/api/ha_data_store/comm?type=onthisday&granularity=day&year=2025&key=xxx
# 历年今日,谁联系得最多(按通话时长)
/api/ha_data_store/comm?type=onthisday&mode=ranking&dimension=party_name&by=duration&key=xxx
# 历年今日的联系人汇总(只看张李二人)
/api/ha_data_store/comm?type=onthisday&mode=parties&party_names=张三,李四&key=xxx
# 「此刻」历年今天都在做什么
/api/ha_data_store/comm?type=onthisday&mode=records&at=now&window=60&key=xxx
# 历年今日的「本小时」(现在 09:02 → 09:00~10:00)
/api/ha_data_store/comm?type=onthisday&mode=records&at=now&align=hour&key=xxx
# 历年今日「最近 2 小时」/ 指定起点起算的 30 分钟
/api/ha_data_store/comm?type=onthisday&mode=records&at=now&align=hour&hours=2&key=xxx
/api/ha_data_store/comm?type=onthisday&mode=records&at=09:02&minutes=30&key=xxx
# 历年今日:按消息类型 × 联系人 交叉汇总通话时长
/api/ha_data_store/comm?type=onthisday&mode=crosstab&rows=msg_type&cols=party_name&metric=duration&key=xxx
不限「历史今日」,对全部(或指定范围的)数据生效,用于「按某维度汇总另一维度」:
/api/ha_data_store/comm?type=crosstab&rows=msg_type&cols=party_name&metric=count&key=xxx
/api/ha_data_store/comm?type=crosstab&rows=location,call_type&metric=duration&key=xxx
可用维度(rows / cols 通用):msg_type、location、party_place、call_type、
party_name、party_number、channel、my_number。
rows 支持 1~3 个(多维度用 | 拼接为键),cols 最多 1 个(留空则只输出各行合计)。
返回 row_keys / col_keys / matrix / row_metric / col_metric / grand_total,可直接渲染表格。
周期对比 compare —— 环比与同比一次拿全:
# 本月 vs 上月 vs 去年同期
/api/ha_data_store/comm?type=compare&period=month&key=xxx
# 指定基准日、只看同比
/api/ha_data_store/comm?type=compare&period=day&date=2026-09-29&compare=yoy&key=xxx
# 只看与张三的对比
/api/ha_data_store/comm?type=compare&period=month&party_name=张三&key=xxx
返回 current / previous / yoy(各含 count / duration / cost / party_count /
active_days / label / start / end)与 diff(差值与增长率 *_pct)。
period=year 时同比不适用,返回 yoy: null。
联系人档案 contact —— 给号码或姓名,拿完整画像:
/api/ha_data_store/comm?type=contact&party_number=13800000000&key=xxx
/api/ha_data_store/comm?type=contact&party_name=张三&month=2026-09&key=xxx
返回 total(总量 / 首末通讯 / 平均时长 / 活跃天数)、days_since_last(多久没联系)、
span_days、avg_interval_days(平均联系间隔)、by_hour / by_weekday / by_month /
by_channel / by_msg_type / by_call_type、top_days(联系最多的日子)与 recent(最近明细)。
单次 Top N longest —— 单条记录排行,区别于按人聚合的排行榜:
/api/ha_data_store/comm?type=longest&by=duration&limit=20&key=xxx
/api/ha_data_store/comm?type=longest&by=cost&party_name=张三&key=xxx
数据质量 quality:
/api/ha_data_store/comm?type=quality&key=xxx
issues 每项含条数与占比:时间为空、时间格式异常、既无号码也无姓名、时长为 0 / 为负 /
超 24 小时、金额为负、内容为空、图片消息缺路径;另有 duplicates(疑似重复:同 时间 +
号码 + 内容 出现多次)、coverage(活跃天数 / 跨度天数 / 覆盖率)与 ok(严重项是否全为 0)。
数据概览 meta:
/api/ha_data_store/comm?type=meta&key=xxx
返回表名、列清单、总量、时间跨度、days_since_last,以及 values(实际存在的渠道 /
消息类型 / 呼叫类型 / 归属地 / 地点及各自条数)与 years / months 清单——
适合给前端下拉当数据源,也能快速判断采集是否正常。
「历史今日」已抽为独立模块 onthisday.py,一套代码同时服务三类数据源:
| source | 数据表 | 时间列 | 指标 | 维度 |
|---|---|---|---|---|
comm |
attr_<type_name> |
time |
条数 / 通话时长 / 金额 | 对方姓名 / 号码、地点、归属地、消息类型、呼叫类型、来源、我方号码 |
device |
device_history |
on_time |
开关次数 / 运行时长 / 用电(kWh) / 平均时长 | 实体 / 名称 / 房间 |
env |
env_<metric> |
datetime |
采样数 / 平均值 / 最大值 / 最小值 | 实体 / 名称 / 房间 |
GET|POST /api/ha_data_store/onthisday?source=comm|device|env&key=你的APIKey
通用参数(三类通用):
| 参数 | 说明 |
|---|---|
source |
数据源:comm(默认)/ device / env |
date |
基准日:09-29 或 2026-09-29;留空 = 今天 |
years / min_year / max_year / exclude_current |
只看指定年份 / 年份范围 / 排除今年 |
at / align / hours / minutes / window / hour |
时刻筛选(at=now&align=hour → 09:00~10:00) |
mode |
stats(默认)/ detail / ranking / crosstab |
limit / offset / sort / order / fields / content_len |
明细分页、排序与字段筛选 |
drop_fields |
黑名单:从明细中剔除指定列(逗号分隔)。优先级低于 fields(两者同时给出时以 fields 为准);未裁剪时默认返回全部列 |
env_by_room |
仅 env 源:1(默认)= 明细按「房间 × 时间点」聚合(一个房间一行、多种指标成列);0 = 平铺,此时每行附带 metric 字段。详见 21.7.1 |
limit |
分页条数:只设默认值、不设上限。缺省 = 默认条数(明细类 100),0 或负数 = 不限条数,其余按传入值返回 |
room_bucket |
配合 env_by_room:时间聚合精度(分钟,默认 1);0 = 精确到秒不合并 |
数据源专属:comm 的类型名一般无需填写(自动解析,见 21.7);device 用 entity_ids / rooms / names;
env 用 env_metric(temperature / humidity / pm25 / co2 / power / sensor;
留空 = 合并所有存在的指标表,均值按采样数加权)与 entity_ids / rooms。
⚠️ metric是交叉汇总的「测度」参数(count/duration/max_value…), 与环境指标不是同一个东西——环境指标请用env_metric。 (为兼容,metric的取值恰好是环境指标名时也会生效;取count之类的测度值会被忽略。)
# 去年今天设备开了多久、用了多少电(按日)
/api/ha_data_store/onthisday?source=device&date=2025-09-29&mode=stats&granularity=day
# 历年今日的设备排行(按用电)
/api/ha_data_store/onthisday?source=device&mode=ranking&dimension=name&by=energy
# 历年今日客厅温度的平均值与极值
/api/ha_data_store/onthisday?source=env&env_metric=temperature&room=客厅
# 「此刻」历年今日的设备开关明细(只取三列)
/api/ha_data_store/onthisday?source=device&mode=detail&at=now&align=hour&fields=on_time,name,duration
与通讯版的兼容:
/api/ha_data_store/comm?type=onthisday&...仍然可用,内部会委托到 同一个模块(固定source=comm)。旧的mode=records/mode=parties会自动映射为detail/ranking(dimension=party_number)。
| 实体 | 说明 |
|---|---|
sensor.ha_data_store_today_in_history |
状态 = 三类数据的记录总数。属性含 comm / device / env 三个节点,每个节点有 count(总数)、summary(总体汇总)、years(各年汇总)、detail(逐条明细,每类最多 10 条)、metrics / dimensions(可用指标与维度说明)。每整点自动刷新一次,设置实体变化时立即刷新(不参与 HA 轮询) |
text.ha_data_store_today_in_history_set |
时间范围设置,写法 <时间>,<前后分钟>:01,80 = 01:00 前后 80 分钟、now,60 = 此刻前后 60 分钟、09:02,30 = 09:02 前后 30 分钟;留空 = 全部数据。等价于接口的 at=<时间>&window=<分钟>。写入后传感器立即重算三个节点;重启后由 RestoreEntity 保持设置 |
text.ha_data_store_comm_type_name |
通讯数据表类型名(对应表名 attr_<类型名>)。留空 = 自动探测 attr_type_defs 中 mode=comm 的类型名(优先取数据表已存在者),通常无需设置;存在多张通讯表时用它明确指向其中一张。影响历史今日(comm 源)、传感器与通讯查询 API 的默认类型名 |
传感器直接复用本模块的查询实现(run_onthisday_query),因此与 API 口径完全一致。
某一类数据源的表不存在时,该节点只返回 error,不影响其它两类(warnings 会列出原因)。
明细呈现方式(只作用于 detail,summary / years 等汇总不受影响):
| 数据源 | 排序字段 | 明细形态 |
|---|---|---|
comm |
time |
剔除 id / datetime / extra_json / name / room / updated_at 六个通用元数据列,保留全部通讯业务字段(含 entity_id) |
device |
on_time |
不裁剪(保留全部列) |
env |
datetime |
按「房间 × 时间点」聚合——一个房间一行,各指标独立成列(见下) |
通讯用排除列表(drop_fields)而非白名单,因此将来表里新增列会自动保留。
环境数据分布在多张表(env_temperature / env_humidity / env_pm25 / env_co2 /
env_power / env_sensor)。若逐条平铺,不同指标的值会混在同一个 value 列里而
无法区分指标:
# 平铺(API 默认):哪个是温度、哪个是湿度看不出来
- {datetime: '2026-09-29 10:00:00', room: 客厅, value: 25.0}
- {datetime: '2026-09-29 10:00:00', room: 客厅, value: 58.0}传感器改为按「房间 × 时间点」聚合,每个指标独立成列:
env:
count: 535 # 采样总数(与聚合前口径一致)
group_count: 54 # 聚合后的行数(房间 × 时间点)
room_count: 10
rooms: [主卧, 次卧, 客厅, 厨房, ...]
metric_names: [temperature, humidity, pm25, co2]
summary: # 按指标分组——各指标量纲不同,混算没有意义
count: 535 # 采样总数(跨指标求和仍有意义:采了多少条)
by_metric:
temperature: {count: 90, avg_value: 24.5, max_value: 26.1, min_value: 19.8}
humidity: {count: 90, avg_value: 57.2, max_value: 68.0, min_value: 45.0}
pm25: {count: 90, avg_value: 35.1, max_value: 52.0, min_value: 18.0}
years:
- year: '2026'
count: 400
by_metric: {temperature: {...}, humidity: {...}, pm25: {...}}
detail:
- {room: 客厅, datetime: '2026-09-29 10:00:00', temperature: 25.0, humidity: 58.0, pm25: 35.0}
- {room: 次卧, datetime: '2026-09-29 10:00:00', temperature: 24.3, humidity: 56.0, pm25: 28.0}summary / years 同样按指标分组(无论明细是否聚合)。温度 25、湿度 58、CO₂ 800
混在一起求平均得到 46.99——这个数字不代表任何东西,所以顶层只保留 count(采样总数),
avg_value / max_value / min_value 一律放进各指标内部。comm / device 各表量纲一致,
沿用原有结构不变。
分页与截断:count 是本页行数,total 是采样总数,group_count 是聚合后的总行数
(房间 × 时间点)。响应含 truncated(是否还有未返回的行)与 remaining(未返回行数),
不必再靠数数判断。
limit只设默认值、不设上限:
- 不传 → 用默认条数(明细类 100)
- 传
limit=0(或负数)→ 不限条数,一次返回全部- 传其它值 → 按该值返回,不会被钳制
响应里的
limit_max恒为null,表示无上限。数据量大时可配合offset翻页。
⚠️ 低频指标容易被挤出窗口:温度湿度每批 12 条、power每批只有 1 条(它是「全屋」总表)。 时间倒序排列时,limit偏小的查询会先被高频指标占满,看起来像"power 少了"——实际是分页截断。 只想看某个指标时用env_metric筛选(如env_metric=power),或把limit调大(上限 1000)。
summary.by_metric 的键与 tables 一一对应——包括存在但当前筛选下无数据的指标,
它们以 {count: 0, avg_value: null, max_value: null, min_value: null} 占位。
这样一眼就能区分"这个指标没采到数据"和"它被漏统计了"(后者曾经发生过:空指标被静默跳过)。
聚合值用 null 而非 0:没有采样 ≠ 采样值恰好是 0(温度 0℃ 是有效读数)。
按年的 years[].by_metric 则只列该年有数据的指标——年份维度上补空没有意义。
room_bucket:时间聚合精度(分钟,默认 1)。同一房间同一分钟内的多条采样取平均, 因此各指标表的时间戳差几秒也能对齐到同一行;设0= 精确到秒不合并- 同一房间有多个同类传感器时,该指标取这些采样的平均值
room为空的环境记录单独成行(房间显示为空字符串)total/count仍是采样总数,与聚合前一致;returned才是聚合行数- 排序为「时间倒序,同一时刻按房间名正序」
API 与传感器默认都用聚合格式,直接请求即可:
/api/ha_data_store/onthisday?source=env&mode=detail&key=xxx
需要旧的平铺格式时传 env_by_room=0,此时每行会附带 metric 字段标明指标名,
否则同一条记录的 value 看不出是温度还是湿度:
{"datetime": "...12:30:00", "room": "次卧", "value": 24.8, "metric": "temperature"}metric 在平铺模式下总会附带(即使指定了 fields 白名单),因为缺了它就无法分辨指标。
聚合模式下 fields / drop_fields 不适用(列由数据动态决定)。
传感器的开关是类常量 DETAIL_ENV_BY_ROOM(默认 True)。
通讯类型名的解析顺序(见 comm.resolve_comm_type_name):
- 显式
type_name参数(一般无需填写,需要明确指向某张表时才传) text.ha_data_store_comm_type_name实体的设置值- 自动探测:
attr_type_defs中mode='comm'且数据表已存在的类型名(按名称排序取首个) attr_type_defs中mode='comm'的类型名(表尚未建,至少给出正确的名字)- 默认
comm_records
第 3 步意味着:在「属性提取」里用了自定义类型名(如 my_phone)也无需任何额外配置即可查询;
若表确实不存在,报错信息会列出已登记的通讯类型名,便于定位。
前两步由 传感器、通讯查询 API(/api/ha_data_store/comm)、历史今日 API
(/api/ha_data_store/onthisday)共用同一实现(comm.read_comm_type_name_setting 读实体、
comm.resolve_comm_type_name 做探测),因此三处口径必然一致——不会出现「传感器认得设置、
API 却不认」的情况。响应体里的 type_name 回显的是实际生效值(含自动探测结果)。
属性结构示例:
state: 7 # 三类记录总数
on_this_day: "09-29"
base_date: "2026-09-29"
range: "全天(不限定)" # 或 "01:00 ± 80 分钟"(随设置实体变化)
at: "" # 设置解析出的时刻("01:00" / "now")
window: 0 # 设置解析出的前后分钟数
exclude_entities: [] # 生效的排除实体(见 21.8)
exclude_count: 0
comm:
count: 3
summary: {count: 3, duration: 443, cost: 0, ...}
years: [{year: "2026", count: 1, ...}, ...]
detail: [{time: "...", party_name: "...", duration: 90, ...}, ...]
metrics: {count: "条数", duration: "通话时长(秒)", cost: "金额(元)"}
dimensions: [party_name, party_number, ...]
device:
count: 2
summary: {count: 2, duration: 7200, energy: 1.5, ...}
detail: [...]
env:
count: 2
summary: {count: 2, avg_value: 25.5, max_value: 26, min_value: 25}
detail: [...]DB 浏览器入口:API 工具 → 查询类型新增两个分组:
- 「📞 通讯数据查询」除明细 / 日期 / 排行 / 趋势 / 汇总 / 联系人 / 地点 / 时段外,另有 6 个统计分析入口、交叉汇总、周期对比、联系人档案、单次 Top N、数据质量、数据概览
- 「📜 历史上的今日」5 个入口(汇总 / 排行榜 / 联系人汇总 / 明细 / 交叉汇总)
- 「📜 历史今日 · 设备 / 环境」8 个入口(设备 / 环境 × 汇总 / 明细 / 排行 / 交叉汇总)
选中后参数区按类型(及预设)动态渲染,粒度与条件均可再改。
在 系统配置 → 📜 历史今日 中维护一份排除实体清单
(存 api_settings.today_in_history_exclude_entities),被排除的 entity_id
不参与历史今日的任何数据源、任何 mode 的统计:
- 对 API(
/api/ha_data_store/onthisday)与 传感器 (sensor.ha_data_store_today_in_history)同时生效 - 三类数据源(
comm/device/env)都按各自的entity_id列过滤 entity_id为空的历史记录不受影响(不会被误排除)
页面提供三种添加方式:手动输入(逗号 / 换行,支持一次粘贴多个)、chips 列表点 ✕ 删除、
从候选实体列表(来源为 device_history / env_* / attr_* 的 entity_id 并集)搜索后 ➕ 添加。
保存后传感器立即刷新。
单次查询还可用参数 exclude_entities 临时追加排除项(多值,逗号分隔),与设置取并集:
# 临时排除某实体(不改变持久化设置)
/api/ha_data_store/onthisday?source=device&exclude_entities=switch.ac&key=xxx
# 与 entity_ids 叠加:先按 entity_ids 收窄,再剔除排除项(取交集)
/api/ha_data_store/onthisday?source=device&entity_ids=switch.a,light.b&exclude_entities=light.b&key=xxx
配置读写接口:
GET|POST /api/ha_data_store/onthisday/exclude # 读 / 写排除清单
# POST body: {"exclude":[...]} 或 {"text":"a,b"}
GET /api/ha_data_store/onthisday/entities # 候选实体列表(供页面选择)
响应体会回显 exclude_entities / exclude_count,便于确认当前口径;传感器属性同样带这两项。
数据浏览页工具栏:📥 导入数据(把外部数据写进任意数据表)、⬇️ 导出CSV(导出当前表)。
| 步骤 | 说明 |
|---|---|
| ① 数据源 | 选择 CSV / JSON 文件(自动识别 UTF-8 与 GBK)或直接粘贴文本。CSV 自动嗅探分隔符(, ; \t |)、正确处理引号内的逗号与换行、可指定「首行是表头」;JSON 支持对象数组、{"data":[...]}、数组的数组。大文本自动按行分片提交,无需自己拆分文件 |
| ② 目标表 | 任选已有表;可勾选「表不存在时自动建表」(按数据推断列类型)与「缺少的列自动创建」 |
| ③ 字段映射 | 以目标列为基准,每行填源列名或固定值(与通讯采集同一套交互,绿=源列 / 橙=固定值)。留空的目标列不写入 |
| ④ 写入模式 | 追加 / Upsert(勾选唯一键列,可多列组合)+ 日期时间规范化、数值清洗开关 |
| ⑤ 执行 | 🧪 试运行(不写库,只回报将新增/更新多少行、会建哪些表列)/ ▶ 开始导入 / ⬇️ 下载目标表空模板 |
结果报告包含新增 / 更新 / 失败数、耗时、建表与补列明细,以及最多 50 条错误行(带行号)。
| 输入 | 输出 |
|---|---|
¥1,234.50 / 12.5元 |
1234.5 / 12.5 |
2026/9/1 17:24 / 2026年9月1日 |
2026-09-01 17:24:00 / 2026-09-01 00:00:00 |
| 空单元格 | 可空列 → NULL;NOT NULL 列 → 该列的 DDL 默认值 |
中文时长(
3分53秒)不在本模块的转换范围内,如需请用通讯采集模式。
POST /api/ha_data_store/import/parse 解析数据源文本(列名 + 全部行 + 前 20 行预览)
POST /api/ha_data_store/import 单批导入(支持 dry_run 试运行)
GET /api/ha_data_store/import/template?table=xxx 下载目标表空模板 CSV
GET /api/ha_data_store/export/csv?table=xxx 导出表数据为 CSV
大文件处理:解析只回传列名、总行数与前 20 行预览(不回传全量数据); 导入时前端把文本按行切片(每片约 200 万字符、各片自带表头)分多次提交,避免超出 HA 的请求体上限(约 16 MB), 切片时不会切断引号内的跨行字段。服务端流式解析并按每 2000 行一批写入,内存占用与总行数无关; 整批失败时自动降级为逐行重试并记录失败行,不会因一行错误丢掉整批。
安全:表名 / 列名白名单校验 + 严格参数化写入;核心配置表(api_keys、entity_configs、
attr_type_defs、push_targets 等 11 张)与 sqlite_* 系统表禁止导入;
写操作受「数据库修改」开关约束。
v5.0.0 新增,独立模块
personal_bill.py+ 新表personal_bills。 把支付宝 / 微信导出的账单落库,并提供一整套多维度查询与多维分析接口 (42 类查询 + 8 个写/辅助接口)。
| 列 | 类型 | 含义 |
|---|---|---|
source |
TEXT | 数据来源(支付宝 / 微信支付 …) |
bill_time |
TEXT | 交易时间 YYYY-MM-DD HH:MM:SS |
bill_no |
TEXT | 交易单号 |
category |
TEXT | 交易类型 |
direction |
TEXT | 收支方向(收入 / 支出 / 不计收支) |
pay_method |
TEXT | 收/支方式 |
amount |
REAL | 金额 |
counterparty |
TEXT | 对方 |
counterparty_bill_no |
TEXT | 对方单号 |
counterparty_account |
TEXT | 对方账号 |
status |
TEXT | 交易状态 |
product |
TEXT | 商品说明 |
remark |
TEXT | 备注(导入来源) |
note |
TEXT | 备注(可编辑,支持 POST 修改) |
created_at |
TEXT | 入库时间 |
year/day/month_day不单独建列,统一从bill_time用substr/strftime派生(年 / 年月 / 年月日 / 小时)。
建表:ensure_tables() 在启动时调用(幂等):CREATE TABLE IF NOT EXISTS
- 逐列
ALTER TABLE补列 + 5 个索引(bill_time、(source,bill_time)、(category,bill_time)、(direction,bill_time)、(counterparty,bill_time))。
导入账单:用「22. 数据导入 / 导出」把
CSV 映射到这些列即可(不在导入保护名单内)。整理账单时可直接把目标列名做成表头
(source,bill_time,bill_no,…,note,created_at),导入即可 1:1 映射。
GET /api/ha_data_store/bill?type=<type>&key=你的APIKey
注意:账单查询不走
/api/ha_data_store/query,而是独立路径/api/ha_data_store/bill。
公共过滤参数(所有类型通用):
| 参数 | 说明 |
|---|---|
source / category / direction / pay_method / status |
精确;逗号分隔多值(IN) |
counterparty |
对方;逗号分隔多值,每个各为一个条件(OR 模糊);counterparty_exact=1 精确 |
names |
counterparty 的等价别名(多名称) |
account |
对方账号 / 对方单号(模糊);account_exact=1 精确 |
bill_no |
本单号 / 对方单号(模糊);bill_no_exact=1 精确 |
keyword |
商品 / 单号 / 对方 / 账号 模糊 |
date / month / year / start / end |
时间维度与区间 |
month_day(MM-DD) / day_of_month(DD) / month_of_year(MM) |
跨年周期过滤(如「指定月日哪些年有数据」) |
min_amount / max_amount |
金额区间 |
fields |
返回字段设置(列白名单,逗号分隔;非法列忽略) |
类型一览:
| 分类 | type |
|---|---|
| 明细 / 定位 | bill_history、bill_recent、bill_latest、bill_by_no、bill_by_account、bill_by_names、bill_split、bill_by_day、bill_calendar、bill_periods、bill_options、bill_meta |
| 聚合 / 分布 | bill_summary、bill_group、bill_trend、bill_ranking、bill_top、bill_hour_dist、bill_weekday_dist、bill_dom_dist、bill_amount_dist、bill_status_dist、bill_product_top、bill_seasonal |
| 对比 / 画像 | bill_compare、bill_profile、bill_yoy、bill_month_day_years、bill_crosstab、bill_heatmap、bill_flow |
| 深度分析 | bill_first_last、bill_interval、bill_frequency、bill_netflow、bill_income_expense、bill_anomaly、bill_recurring、bill_fixed_cost、bill_percentile、bill_quality |
| 备注 | bill_notes |
bill_periods(数据可用性)多档返回:level = all|year|month|day,
mode 控制详细程度:
mode |
返回 |
|---|---|
minimal 极简 |
只返回有数据的键(纯字符串数组) |
simple 精简 |
键 + 笔数 |
normal 一般 |
再含收入 / 支出 / 净额(默认) |
full 完全 |
再含合计与首末时间 |
# 指定年月 → 哪些日有数据
GET /api/ha_data_store/bill?type=bill_periods&level=day&month=2025-10&mode=normal
# 指定月日 → 哪些年有数据(极简)
GET /api/ha_data_store/bill?type=bill_periods&level=year&month_day=10-09&mode=minimal常用示例:
# 明细(多对方 + 只返回指定字段)
GET /api/ha_data_store/bill?type=bill_history&counterparty=美团,滴滴,京东&fields=bill_time,amount,counterparty&limit=50
# 多值分类汇总(每个输入值各一组,可带明细)
GET /api/ha_data_store/bill?type=bill_split&dim=counterparty&values=美团,滴滴,京东&detail=1&limit=20
# 指定日交易
GET /api/ha_data_store/bill?type=bill_by_day&date=2025-10-01
# 按单号 / 账号
GET /api/ha_data_store/bill?type=bill_by_no&bill_no=2025041921001001020249316409&bill_no_exact=1
GET /api/ha_data_store/bill?type=bill_by_account&account=126.com
# 环比 / 同比(2025-10 vs 上月 / 去年同月)
GET /api/ha_data_store/bill?type=bill_compare&period=2025-10&granularity=month
GET /api/ha_data_store/bill?type=bill_compare&period=2025-10&granularity=month&yoy=1
# 交叉汇总 / 资金流向
GET /api/ha_data_store/bill?type=bill_crosstab&row_dim=category&col_dim=month
GET /api/ha_data_store/bill?type=bill_flow&from_dim=counterparty&to_dim=category
# 净现金流 / 金额分位数 / 异常大额 / 疑似订阅 / 固定开销
GET /api/ha_data_store/bill?type=bill_netflow&granularity=month
GET /api/ha_data_store/bill?type=bill_percentile
GET /api/ha_data_store/bill?type=bill_anomaly&dim=category&factor=5
GET /api/ha_data_store/bill?type=bill_recurring&dim=counterparty&min_count=6
GET /api/ha_data_store/bill?type=bill_fixed_cost&dim=counterparty&min_months=12# 单条 / 批量提交
POST /api/ha_data_store/bill/add {"source":"微信支付","bill_time":"2026-09-01 12:30:00","amount":12.34,"note":"..."}
POST /api/ha_data_store/bill/batch {"records":[ {...}, {...} ]}
# 修改(含备注)
POST /api/ha_data_store/bill/update {"id":1,"note":"已对账"}
POST /api/ha_data_store/bill/update {"ids":[1,2,3],"note":"报销中"}
POST /api/ha_data_store/bill/update {"id":1,"fields":{"category":"餐饮","amount":66.6}}
# 单独提交备注(前台点开某条详单后按 id 直接写)
POST /api/ha_data_store/bill/remark {"id":123,"note":"已对账"}
POST /api/ha_data_store/bill/remark {"id":123,"note":"发票已上传","mode":"append"}
# 删除
DELETE /api/ha_data_store/bill/delete?id=1,2,3
# 可选值 / 备注候选
GET /api/ha_data_store/bill/types
GET /api/ha_data_store/bill/notes?note_keyword=对账&limit=50备注(note):
remark= 导入来源的备注(只读语义);note= 你自己的备注(可改)- 单独提交:
POST /bill/remark,只带该条id+note;支持ids批量与mode=append追加 - 候选获取:
GET /bill/notes返回去重备注 + 使用次数 + 首末时间, 前台可渲染成可点击的备选(选中即用,不用手打) - 过滤参数用
note_keyword;bill_notes会在内部剔除通用keyword条件, 避免把结果过滤空 - 更新接口
POST /bill/update的可改列有白名单(除id/created_at外全部)
「🔗 API 地址生成器 → 查询类型」新增 4 个分组:
- 🧾 账单查询(明细 / 定位)
- 🧾 账单分析(多维度分析)
- 🧾 账单深度分析
- 🧾 账单提交(写接口)
选中后显示账单专属参数区(按类型动态渲染),其中:
- 返回字段
fields为手动点选(列复选框),无需手输列名 counterparty支持逗号分隔多个- 备注相关:
bill_notes取候选、bill_remark单独提交(提示区直接给出 POST 请求体) - 生成的 URL 自动带选中的 API Key;写接口给出的地址 + JSON 请求体可直接复制使用
把一组实体当成一个设备控制:编组状态按成员实时汇总,控制指令按成员所在域分派
(映射见 const.py 的 ENTITY_GROUP_DOMAIN_SERVICE_MAP)。每个启用编组投影成
一个 HA 实体,实体域由「类型(kind)」决定,与成员域无关。
前端有四种用法,优先用 ①②(纯前端,不经过 HTTP);只有需要读写编组配置时才用 ③④。
| 类型 kind | 实体域 | 实体 id 示例 |
|---|---|---|
lights / light |
light | light.hds_eg_ke_ting |
curtain |
cover | cover.hds_eg_ke_ting |
media |
media_player | media_player.hds_eg_ke_ting |
ac |
climate | climate.hds_eg_ke_ting |
socket |
switch | switch.hds_eg_ke_ting |
mixed(混编)/ device / 未识别 |
switch | switch.hds_eg_ke_ting |
- 编组 id 形如
eg_ke_ting;实体 object_id 固定为hds_eg_<去掉 eg_ 的后半段>。 - 改名称 / 图标不换 entity_id;改 id 或改 kind 会删旧实体、按新域重建。
- 该域平台不可用(软导入失败)时自动降级
switch。 - db_viewer 里「编组 ID」输入框只填后半段(
eg_固定显示),但 REST 请求体里的id必须带完整eg_前缀。
编组实体就是标准 HA 实体,前端卡片 / 自动化 / 脚本 / 语音都按普通实体处理:
// 开关类(socket / mixed / device)
await hass.callService('switch', 'turn_on', { entity_id: 'switch.hds_eg_ke_ting' });
// 灯光组:亮度 / 色温 / 颜色都会广播给成员
await hass.callService('light', 'turn_on', {
entity_id: 'light.hds_eg_ke_ting',
brightness_pct: 60,
color_temp_kelvin: 4000,
});
await hass.callService('light', 'turn_off', { entity_id: 'light.hds_eg_ke_ting' });
// 窗帘组
await hass.callService('cover', 'set_cover_position', {
entity_id: 'cover.hds_eg_ke_ting', position: 30,
});
await hass.callService('cover', 'open_cover', { entity_id: 'cover.hds_eg_ke_ting' });
// 空调组
await hass.callService('climate', 'set_temperature', {
entity_id: 'climate.hds_eg_ke_ting', temperature: 24,
});
await hass.callService('climate', 'set_hvac_mode', {
entity_id: 'climate.hds_eg_ke_ting', hvac_mode: 'cool',
});
// 媒体组
await hass.callService('media_player', 'volume_set', {
entity_id: 'media_player.hds_eg_ke_ting', volume_level: 0.4,
});实体属性(hass.states['light.hds_eg_ke_ting']):
| 属性 | 说明 |
|---|---|
group_id / kind / rev |
编组标识、类型、修订号 |
member_count / on_count / partial |
成员数 / 开启数 / 是否部分开启 |
missing |
成员里取不到状态的实体数 |
on_entities |
当前处于开启状态的成员 entity_id 列表 |
domains |
成员涉及的域(如 ["light"]) |
actions |
编组预设动作 id 列表 |
last_status |
最近一次控制结果 success / failed |
| 各域专有 | light:brightness / color_temp_kelvin / hs_color / color_mode / effect;cover:current_cover_position;climate:current_temperature / temperature / hvac_mode / fan_mode;media_player:volume_level / media_title |
聚合口径:任一成员 on → 编组 on,partial: true 表示只有部分成员开着。
两个传感器已经把清单放在状态属性里,前端直接读即可:
// 完整目录(含 members / kind / rev / actions / options)
const s = hass.states['sensor.ha_data_store_entity_groups'];
const groups = s.attributes.groups; // 每个启用编组一条
const version = s.attributes.version; // 脏检查:变了才重建 UI
const kinds = s.attributes.kinds; // {lights: 2, curtain: 1, ...}
const counts = s.attributes.counts; // {groups: 3, members: 12}
// 精简版(只留 entity_id / name / icon,适合卡片遍历与语音)
const slim = hass.states['sensor.ha_data_store_group_entities'];
const list = slim.attributes.groups; // [{name, entity_id, icon, entities: [...]}]sensor.ha_data_store_entity_groups 的 groups[] 结构:
注意:API / 传感器返回的 group 里没有
entity_id字段,编组实体的 id 要按 24.1 的规则用 kind 推导(sensor.ha_data_store_group_entities那个精简版里才有现成的entity_id)。这两个属性都在_unrecorded_attributes里,不进 recorder 历史 (避免 16 KB 上限),但hass.states里随时可读。
基础路径 /api/ha_data_store/entity_groups。完整接口清单、列表参数、请求体/返回结构、
curl 示例、错误码、成员 extra 可用键都在
实体编组接口 一节;这里只给前端 fetch 写法。
鉴权简版:带 ?key=<API Key>(或 Authorization 头)→ 走 Key 校验,需开「API 访问」;
不带 → 只校验总开关,HA 同源前端(已登录)可直接调;写操作另需「数据库修改」开关。
列表 + 推导实体 id:
const API = '/api/ha_data_store/entity_groups';
async function listGroups() {
const r = await fetch(`${API}?enabled=all&include_members=1`);
const j = await r.json();
if (!j.success) throw new Error(j.error + (j.detail ? ':' + j.detail : ''));
return j; // {success, groups, total, version}
}
// kind → 域(与后端 group_entity_domain 完全一致:mixed / device → switch)
const KIND_DOMAIN = {
ac: 'climate', climate: 'climate',
lights: 'light', light: 'light',
socket: 'switch', switch: 'switch',
curtain: 'cover', cover: 'cover',
media: 'media_player', media_player: 'media_player',
};
function groupEntityId(g) {
const dom = KIND_DOMAIN[g.kind] || 'switch';
return `${dom}.hds_eg_${String(g.id).replace(/^eg_/, '')}`;
}新建 / 更新 / 删除:
// 新建:id 可省略(后端自动生成 eg_xxxxxxxx)
const created = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 'eg_ke_ting', // 必须带 eg_ 前缀(和 db_viewer 输入框不同!)
name: '客厅灯',
kind: 'lights', // 决定实体域
enabled: true,
members: [
{ entity_id: 'light.ke_ting_zhu_deng', name: '主灯' },
{ entity_id: 'light.ke_ting_deng_dai', extra: { brightness_pct: 80 } },
],
}),
}).then(r => r.json()); // {success, group, version, warnings}
// 局部更新(只改名字,不动成员):带 ?rev= 做乐观锁,不匹配回 409
await fetch(`${API}/eg_ke_ting?rev=3`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '客厅主灯组' }),
});
// 全量更新(members 会整组替换)
await fetch(`${API}/eg_ke_ting`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: '客厅灯', kind: 'lights',
members: [{ entity_id: 'light.a' }, { entity_id: 'light.b' }],
}),
});
// 删除
await fetch(`${API}/eg_ke_ting`, { method: 'DELETE' });
// → {success, removed_members, version}成员级操作:
// 批量新增(幂等:已存在则更新 name/icon/extra)
await fetch(`${API}/eg_ke_ting/members`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([{ entity_id: 'light.c' }, { entity_id: 'light.d', name: '灯带' }]),
});
// 单个成员的 name/icon/extra(PATCH 对 extra 做浅合并)
await fetch(`${API}/eg_ke_ting/members?entity=light.c`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ extra: { brightness_pct: 40 } }),
});
// 移除
await fetch(`${API}/eg_ke_ting/members?entity=light.c`, { method: 'DELETE' });返回与错误:成功统一 {"success": true, ...};失败 {"success": false, "error", "detail"},
error 可能是 invalid_param(400) / not_found(404) / conflict(409) / internal_error(500,
带 traceback 最后几行,浏览器 Network 面板可直接看根因)。
# 立即同步编组实体(正常 30 秒自动同步一次;排查「新建了编组但没生成实体」用)
service: ha_data_store.sync_entity_groups
# 执行编组预设动作,或按 command 执行内置开/关
service: ha_data_store.run_entity_group_action
data:
group_id: eg_ke_ting
action_id: night # 可选:编组 actions[].id,填了则忽略 command
command: "off" # 可选:on / off,默认 on- 写接口会自动触发实体同步(视图写完调
notify()→ 重读快照 → 增量增删实体), 不必手动刷新;万一没生成,调一次ha_data_store.sync_entity_groups,日志会打印 「快照 N 个编组 / 已建实体 M 个 / 回调就绪=布尔」。 - 改 kind / 改 id 会换 entity_id:旧实体删除、新实体重建,卡片与自动化里写死的
entity_id要同步改;只改名称和图标不受影响。 rev是乐观锁:PUT/PATCH可带?rev=(或 body 里的rev),不匹配 → 409, 多个前端同时编辑时用它避免互相覆盖。enabled=all才会返回已停用的编组,默认只返回启用中的。include_members=0只取壳(更快),需要成员时再拉/members。- 不要把
groups属性塞进模板传感器:属性体积大,recorder 单状态上限 16 KB (编组类属性已_unrecorded_attributes排除历史,但仍占内存)。 - 编组实体统一挂在独立设备「编组设备」下,不是主集成设备。
所有 API 通过 /api/ha_data_store/ 路径访问。外部访问需在 URL 或 Header 中携带 API Key。
GET /api/ha_data_store/query?type=xxx&key=你的APIKey
通用参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
entity_id |
- | 实体 ID |
metric |
- | 指标类型(环境类查询) |
date |
- | 日期 YYYY-MM-DD |
month |
- | 月份 YYYY-MM |
year |
- | 年份 YYYY |
start / end |
- | 起止日期范围 |
limit |
500 | 返回条数上限(0=不限制) |
offset |
0 | 分页偏移 |
order_by |
- | 排序字段(如 datetime DESC) |
fields |
- | 返回字段(逗号分隔) |
detail |
false | 是否返回详细记录(仅汇总类查询) |
room |
- | 房间过滤 |
key |
- | API Key |
查询类型一览:
| type | 说明 | 必需参数 |
|---|---|---|
device_history |
设备开关记录(按日/月/年智能返回,内嵌汇总) | entity_id |
entity_daily_by_year |
实体按日用电(指定年,含 totals 合计与运行中 running 标记) | entity_id, year |
entity_daily_all |
实体按日用电(全部历史,含 totals 合计与运行中 running 标记) | entity_id |
entities_daily_flat |
全部实体按月按日(平铺:日×设备扁平行 + totals) | month |
entities_daily_by_day |
全部实体按月按日(按日分组:days[].devices + totals) | month |
entity_hour_dist |
实体时段分布(几点使用/分时用电,运行中设备处理) | entity_id + 可选范围 |
entity_hour_dates |
设备小时开启日期(某小时开过哪些天) | entity_id, hour + 可选范围 |
entities_period_agg |
多实体按日/月/年汇聚(每实体 series + totals;view=entity|date) | entities(可选), bucket |
entities_dates |
多实体有数据日期(group=all(0) 合并 / entity(1) 按实体 / count 按日期数量 / both 日期数量+实体 / simple 极简 list:[{date,count}] 日期倒序) |
entities(可选), start/end/date/month/year |
entities_hours_agg |
多实体时段分布(group=0 合并 / 1 按实体) | entities(可选) |
entities_weekday_hours |
多实体时段分布网格(dim=week|month|day × 小时,7/12/31×24) | entities(可选), dim |
device_summary |
纯汇总(只返回统计数字,不返回记录) | entity_id, date/month/year(可选) |
env_history |
环境历史记录(含最新日期、总条数等元数据) | entity_id, metric |
env_latest |
环境最新一条记录 | entity_id, metric |
attr_history |
属性历史 | entity_id, attr_type(可选) |
attr_latest |
属性最新 | entity_id, attr_type(可选) |
entities |
已配置实体列表 | 无 |
rooms_daily |
按房间每日汇总 | date |
rooms_multi_metric |
按房间多指标汇总 | date, room |
aggregate_daily |
所有实体按日聚合 | - |
aggregate_monthly |
所有实体按月聚合 | - |
aggregate_yearly |
所有实体按年聚合 | - |
whole_house_usage |
全屋用电/用时(总计→房间→设备 三级,含运行中设备统计与设备数量) | year(必填);month/date 可选精确 |
ranking_daily |
日排行榜 | - |
ranking_monthly |
月排行榜 | - |
ranking_yearly |
年排行榜 | - |
vacuum_history |
扫地机器人轨迹历史 | vacuum_id(可选) |
electricity_standard |
电量标准数据 | 无 |
health_history |
健康数据历史 | name(可选) |
health_latest |
最新健康数据 | name(可选) |
entity_data_dates |
实体有数据的所有日期 | entity_id |
room_data_dates |
房间指定月有数据日期(按 device/environment/attribute 多选查询) | room, month, category |
xiaoai_history |
小爱对话记录 | entity_id |
printer_years |
打印机有哪些年数据 | stats_entity |
printer_month_dates |
打印机指定月哪些日期有数据 | stats_entity, month |
printer_total |
打印机合计数据 | stats_entity |
printer_monthly_total |
打印机按年月统计合计数据 | stats_entity |
printer_daily_range |
打印机指定日期区间数据 | stats_entity, start/end |
printer_detail |
打印机指定日期详细数据 | stats_entity, date |
user_actions_daily |
用户动作·指定日期操作记录 | date |
user_actions_range |
用户动作·日期段操作记录 | start/end |
user_actions_month_dates |
用户动作·指定月哪些日期有数据 | month |
user_actions_hour_dist |
用户动作·数据点按小时分布 | entity_id(可选) |
user_actions_entity_summary |
用户动作·实体操作次数排行 | entity_id(可选) |
user_actions_user_summary |
用户动作·按用户汇总 | 无 |
user_actions_entity_last_today |
用户动作·实体当日最后一条记录 | entity_id |
查询示例:
# 查询设备开关记录
curl "http://ha:8123/api/ha_data_store/query?type=device_history&entity_id=switch.fan&date=2024-01-15&key=your_api_key"
# 查询环境历史
curl "http://ha:8123/api/ha_data_store/query?type=env_history&entity_id=sensor.temperature&metric=temperature&limit=100"
# 查询按房间的每日汇总
curl "http://ha:8123/api/ha_data_store/query?type=rooms_daily&date=2024-01-15&key=your_api_key"
# 查询月排行榜
curl "http://ha:8123/api/ha_data_store/query?type=ranking_monthly&month=2024-01&detail=true"
# 查询客厅 2025-01 哪些日期有数据
curl "http://ha:8123/api/ha_data_store/query?type=room_data_dates&room=客厅&month=2025-01&category=device&key=your_api_key"
# 用户动作:指定日期操作记录
curl "http://ha:8123/api/ha_data_store/query?type=user_actions_daily&date=2026-08-24&key=your_api_key"
# 用户动作:数据点按小时分布(指定实体)
curl "http://ha:8123/api/ha_data_store/query?type=user_actions_hour_dist&entity_id=light.living_room&key=your_api_key"
# 用户动作:实体当日最后一条记录
curl "http://ha:8123/api/ha_data_store/query?type=user_actions_entity_last_today&entity_id=light.living_room&key=your_api_key"
# 全屋用电/用时:指定年(可叠加 month/date 精确到月/日)
curl "http://ha:8123/api/ha_data_store/query?type=whole_house_usage&year=2026&month=2026-09&date=2026-09-07&key=your_api_key"whole_house_usage(全屋用电/用时)返回结构:
- 三级:
total→rooms[](每个房间含devices[])→ 设备项;year/month/date按最近一级精确匹配(date > month > year) - 每级字段:
count(开启次数) /duration_hour(小时,2位) /energy_kwh(kWh,4位) /running_count - 顶层:
room_count(房间数) +room_names(单纯房间名列表,如["客厅","餐厅",...]);total.device_count(设备总数) - 每个房间:
device_count(该房间设备数) - 运行中设备(
on_time非空、off_time空)纳入统计并带running标记:时长=当前时间−on_time;用电=有电表取now_kwh−on_power,无电表但有固定功率按power_rating(W)/1000×时长折算 - 汇总传感器
sensor.ha_data_store_all_room_usage直接输出 本年/本月/今日 三级(状态值=今日用电 kWh,每 1 分钟刷新)
通讯数据查询(独立路径):
GET /api/ha_data_store/comm?type=records&date=2026-09-01&key=你的APIKey
type 可取 records / dates / stats / crosstab / compare / contact / longest /
quality / meta / onthisday / ranking / trend / summary / parties / places / heatmap;
参数与返回结构详见 21. 通讯数据(采集 + 查询)。
个人账单接口(独立路径,不走 /query):
GET /api/ha_data_store/bill?type=<bill_xxx>&... 42 类查询 / 分析
GET /api/ha_data_store/bill/types 可选值清单
GET /api/ha_data_store/bill/notes?note_keyword=&limit= 备注候选清单
POST /api/ha_data_store/bill/add 提交单条
POST /api/ha_data_store/bill/batch 批量提交
POST /api/ha_data_store/bill/update 修改(含备注,id/ids 批量)
POST /api/ha_data_store/bill/remark 单独提交备注
POST|DELETE /api/ha_data_store/bill/delete?id=1,2,3 删除
公共过滤支持 source/category/direction/pay_method/status(逗号多值)、
counterparty(逗号多值,每个各为一个条件)/names、account、bill_no、
keyword、date/month/year/start/end、month_day/day_of_month/month_of_year、
min_amount/max_amount、fields(返回字段);
参数与返回结构详见 23. 个人账单(查询 / 分析 / 备注)。
GET /api/ha_data_store/config → 获取所有监控实体配置
POST /api/ha_data_store/config → 新增/修改监控实体配置
POST 请求体示例:
[
{
"entity_id": "switch.fan",
"category": "device",
"room": "客厅",
"device_name": "风扇",
"power_entity": "sensor.fan_power",
"enabled": 1
}
]GET /api/ha_data_store/routes → 获取所有自定义路由
POST /api/ha_data_store/routes → 新增/修改自定义路由
GET /api/ha_data_store/attr_types → 获取所有属性类型定义
GET /api/ha_data_store/entity_state?entity_id=xxx → 获取实体状态+属性树
打印机配置接口:
GET /api/ha_data_store/printer/configs → 配置列表
POST /api/ha_data_store/printer/configs → 新增/修改配置
DELETE /api/ha_data_store/printer/configs?id=xxx → 删除配置
POST /api/ha_data_store/printer/configs/recollect?name=xxx → 主动重采指定打印机
打印机配置 POST 请求体示例:
{
"name": "HP Printer",
"stats_entity": "sensor.hp_printer_yong_liang_tong_ji",
"detail_entity": "sensor.hp_printer_jin_ri_zuo_ye",
"enabled": true
}基础路径:/api/ha_data_store/entity_groups
把一组实体当成一个设备控制。建一个启用编组 = 生成一个对应域的实体(域由 kind 决定,
映射与 ID 规则见 24. 实体编组)。
鉴权
- 带
?key=<API Key>或Authorization头 → 走 API Key 校验,需开启「API 访问」开关; - 不带 → 只校验总开关,HA 同源前端(已登录)可直接调用;
- 写操作(POST / PUT / PATCH / DELETE)另需开启「数据库修改」开关。
接口一览
GET /api/ha_data_store/entity_groups → 列表(参数见下)
GET /api/ha_data_store/entity_groups/version → 只取全局版本号(脏检查)
GET /api/ha_data_store/entity_groups/{gid} → 单个编组详情
POST /api/ha_data_store/entity_groups → 新建(成功 201)
PUT /api/ha_data_store/entity_groups/{gid} → 全量更新(body 带新 id = 改名)
PATCH /api/ha_data_store/entity_groups/{gid} → 局部更新(不传 members 则成员不动)
DELETE /api/ha_data_store/entity_groups/{gid} → 删除(成员级联)
GET /api/ha_data_store/entity_groups/{gid}/members → 成员列表(?entity= / ?domain=)
POST /api/ha_data_store/entity_groups/{gid}/members → 批量新增成员(幂等 upsert)
PUT /api/ha_data_store/entity_groups/{gid}/members?entity= → 整体替换单个成员
PATCH /api/ha_data_store/entity_groups/{gid}/members?entity= → 局部更新(extra 浅合并)
DELETE /api/ha_data_store/entity_groups/{gid}/members?entity= → 移除单个成员
POST /api/ha_data_store/entity_groups/{gid}/members/remove → 批量移除 {"entities":[...]}
POST /api/ha_data_store/entity_groups/{gid}/members/order → 成员排序 {"order":[...]}
POST /api/ha_data_store/entity_groups/batch → 批量保存 / 整库导入
POST /api/ha_data_store/entity_groups/order → 编组排序
GET /api/ha_data_store/entity_groups/by-entity/{eid} → 反查实体属于哪些编组
GET /api/ha_data_store/entity_groups/_diag → 自检(逐个探测存储层方法)
列表参数(GET /entity_groups)
| 参数 | 说明 |
|---|---|
id / ids |
逗号分隔的编组 id(如 eg_a,eg_b) |
kind / kinds |
逗号分隔的类型(lights,curtain) |
entity |
只看包含该成员的编组(light.x) |
q / keyword |
按名称/id 模糊搜索 |
enabled |
1(默认,只返回启用)/ 0(只返回停用)/ all(全部) |
include_members |
默认 1;传 0 只取壳(更快) |
sort |
排序方式,可选 sort(默认,按 sort 升序)/ name(按名称)/ updated_at(最近更新优先);其它值按默认处理 |
limit / offset |
分页;limit 不传则不限制 |
编组对象结构
{
"id": "eg_ke_ting",
"name": "客厅灯",
"kind": "lights",
"icon": "mdi:lightbulb-group",
"icon_on": null,
"icon_off": null,
"color_on": null,
"color_off": null,
"options": {},
"actions": [],
"default_on_action": null,
"default_off_action": null,
"sort": 10,
"enabled": true,
"rev": 1,
"created_at": 1760800000000,
"updated_at": 1760800500000,
"members": [
{
"entity_id": "light.ke_ting_zhu_deng",
"name": "主灯",
"icon": null,
"sort": 0,
"extra": { "brightness_pct": 80 }
}
]
}返回里没有
entity_id字段:编组实体的 id 按kind推导 (<域>.hds_eg_<id 去 eg_>,mixed/device→switch);也可直接读sensor.ha_data_store_group_entities的groups[].entity_id。
新建
curl -X POST "http://homeassistant:8123/api/ha_data_store/entity_groups?key=$KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "eg_ke_ting",
"name": "客厅灯",
"kind": "lights",
"enabled": true,
"members": [
{ "entity_id": "light.ke_ting_zhu_deng", "name": "主灯" },
{ "entity_id": "light.ke_ting_deng_dai", "extra": { "brightness_pct": 80 } }
]
}'id可省略 → 后端自动生成eg_xxxxxxxx(8 位随机);自定义时必须带eg_前缀, 格式eg_+ 2~32 位小写字母/数字/下划线且不能以_开头。- 成功返回 201:
{"success": true, "group": {...}, "version": 13, "warnings": []};warnings会提示「成员实体当前不存在」这类非致命问题。
更新
# 局部更新(只改名称,成员不动)
curl -X PATCH "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting?key=$KEY&rev=3" \
-H "Content-Type: application/json" -d '{"name":"客厅主灯组","icon":"mdi:ceiling-light"}'
# 全量更新:members 会整组替换(顺序即 sort = index*10)
curl -X PUT "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting?key=$KEY" \
-H "Content-Type: application/json" \
-d '{"name":"客厅灯","kind":"lights","members":[{"entity_id":"light.a"},{"entity_id":"light.b"}]}'
# 改名(body 里带新 id):旧实体删除,按新 id 重建
curl -X PUT "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting?key=$KEY" \
-H "Content-Type: application/json" -d '{"id":"eg_ke_ting_deng"}'rev做乐观锁:?rev=(或 body 里的rev)与库里不一致 → 409 conflict; 不传则不校验。- 改
kind(域变化)或改id都会删旧实体、按新 id/域重建。
删除
curl -X DELETE "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting?key=$KEY"
# → {"success": true, "removed_members": 3, "version": 14}(成员行级联删除,被编组的设备本身不受影响。)
成员接口
# 列表(可按实体筛 / 按域筛)
curl "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting/members?key=$KEY&domain=light"
# 批量新增(幂等 upsert:已存在的按传入字段更新)
curl -X POST "http://homeassistant:8123/api/ha_data_store/entity_groups/eg_ke_ting/members?key=$KEY" \
-H "Content-Type: application/json" \
-d '[{"entity_id":"light.c"},{"entity_id":"light.d","name":"灯带"}]'
# → {"success": true, "added": ["light.c","light.d"], "updated": [], "skipped": [],
# "members": [...], "version": 15}
# 单个成员局部更新(extra 浅合并)
curl -X PATCH ".../entity_groups/eg_ke_ting/members?entity=light.c&key=$KEY" \
-H "Content-Type: application/json" -d '{"extra":{"brightness_pct":40}}'
# → {"success": true, "member": {...}, "version": 16}
# 移除单个 / 批量移除
curl -X DELETE ".../entity_groups/eg_ke_ting/members?entity=light.c&key=$KEY"
# → {"success": true, "removed": true, "member_count": 2, "version": 17}
curl -X POST ".../entity_groups/eg_ke_ting/members/remove?key=$KEY" \
-H "Content-Type: application/json" -d '{"entities":["light.c","light.d"]}'
# → {"success": true, "removed": ["light.c","light.d"], "member_count": 0, "version": 18}
# 排序
curl -X POST ".../entity_groups/eg_ke_ting/members/order?key=$KEY" \
-H "Content-Type: application/json" -d '{"order":["light.b","light.a"]}'成员 extra 可用键(写进该成员的控制参数,优先级:设备当前值 < 编组 options < 成员 extra)
brightness_pct, color_temp_kelvin, rgb_color, hs_color,
hvac_mode, temperature, fan_mode, fan_level,
position, volume_level
另外 extra 里可放 on_service / on_data / off_service / off_data
(形如 "light.turn_on")完全接管该成员的内置开/关。
批量保存 / 整库导入
curl -X POST "http://homeassistant:8123/api/ha_data_store/entity_groups/batch?key=$KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "upsert",
"groups": [ { "id": "eg_a", "name": "灯组", "kind": "lights", "members": [...] } ],
"deleted": ["eg_old"]
}'mode: "upsert"(默认)按 id 有则更新、无则新建;mode: "replace"先清空整表再写入。- 返回:
{"success": true, "groups": [...], "total": n, "version": v, "applied": {"created": 1, "updated": 0, "deleted": 1}, "warnings": []}。
排序 / 反查 / 版本
# 编组排序(sort = index*10)
curl -X POST ".../entity_groups/order?key=$KEY" \
-H "Content-Type: application/json" -d '{"order":["eg_a","eg_b"]}'
# 反查某实体属于哪些编组
curl ".../entity_groups/by-entity/light.ke_ting_zhu_deng?key=$KEY"
# → {"success": true, "groups": [{...}], "total": 1}
# 脏检查:只取版本号(几十字节)
curl ".../entity_groups/version?key=$KEY"
# → {"success": true, "version": 18, "count": 3, "updated_at": 1760800500000}返回与错误
- 成功:
{"success": true, ...};列表额外带total、version。 - 失败:
{"success": false, "error": "...", "detail": "..."}
| error | HTTP | 场景 |
|---|---|---|
invalid_param |
400 | JSON 不合法、kind 不在白名单、id/entity_id 格式错、order/entities 非数组 |
not_found |
404 | 编组不存在;成员接口里 member_not_found |
conflict |
409 | 新建时 id 已存在;改名目标 id 被占用;rev 不匹配 |
internal_error |
500 | 未捕获异常,body 带 operation 与 traceback(最后几行,浏览器 Network 面板可直接看) |
行为语义
- 任何写操作都会自动触发实体同步(重读快照 → 增量创建/删除编组实体),无需重启;
若怀疑没生效:
GET .../entity_groups/_diag自检,或调服务ha_data_store.sync_entity_groups(日志会打印快照数/已建实体数/平台回调是否就绪)。 - 编组实体域只由
kind决定(mixed/device→switch),详见 24.1。 enabled: false的编组保留配置但不投影实体。- 编组 id 与实体 object_id 是绑定关系:只改 name/icon 不换 entity_id;改 id 或改 kind 会重建。
- 上限:单编组最多 200 个成员、12 个预设动作(单个动作最多 20 条行为),
编组
name≤ 50 字符;id一旦被外部自动化引用,建议不要再改。
以下接口仅允许局域网同网段访问,需要管理员密码登录。
| 接口 | 方法 | 说明 |
|---|---|---|
/api/ha_data_store/db_viewer |
GET | 管理页面 |
/api/ha_data_store/db_viewer/data?table=xxx |
GET | 表数据分页 |
/api/ha_data_store/db_viewer/update |
POST | 编辑单元格 |
/api/ha_data_store/db_viewer/update |
DELETE | 删除行 |
/api/ha_data_store/apikey |
GET | 列出所有 API Key |
/api/ha_data_store/apikey |
POST | 创建 API Key |
/api/ha_data_store/apikey |
DELETE | 删除 API Key |
/api/ha_data_store/apikey/settings |
POST | 修改管理员密码 |
/api/ha_data_store/stats |
GET | 数据统计(各行数+磁盘大小) |
/api/ha_data_store/logs |
GET | 本地日志文件列表 |
/api/ha_data_store/logs/content?date=YYYY-MM-DD |
GET | 查看日志内容 |
/api/ha_data_store/export |
GET/POST/DELETE | 导出配置管理 |
/api/ha_data_store/file_source |
GET/POST/DELETE | 文件源配置管理 |
/api/ha_data_store/api_source |
GET/POST/DELETE | API源配置管理 |
/api/ha_data_store/push_targets |
GET/POST/DELETE | 实体→网络 目标管理(读 + 控制配置) |
/api/ha_data_store/push_data/{push_token} |
GET | 外部读取实体数据(只读,token 即密钥) |
/api/ha_data_store/push_control/{control_token} |
POST | 外部控制实体(动作模式 / raw 模式) |
/api/ha_data_store/push_capabilities/{control_token} |
GET | 该控制 token 的可用动作与参数 schema |
/api/ha_data_store/push_entity_capabilities?entity_id= |
GET | 指定实体的可控能力(配置向导用) |
/api/ha_data_store/push_control_logs |
GET | 控制审计日志(limit/entity_id/success/clear) |
/api/ha_data_store/backup |
GET/POST/DELETE | 整库备份:状态与列表 / 立即备份·保存设置·排队恢复·取消·完整性检查·清理 / 删除 |
/api/ha_data_store/backup/download?name= |
GET | 下载指定备份文件 |
/api/ha_data_store/attr_config |
GET/POST | 属性类型配置管理 |
/api/ha_data_store/attr_manual_trigger |
POST | 手动触发属性采集 |
/api/ha_data_store/vacuum_type_defs |
GET/POST | 扫地机器人类型管理 |
/api/ha_data_store/vacuum_configs |
GET/POST/DELETE | 扫地机器人配置管理 |
/api/ha_data_store/bridge_connections |
GET/POST | 桥接连接配置管理 |
/api/ha_data_store/bridge_entities |
GET/POST/DELETE | 桥接实体配置管理 |
/api/ha_data_store/bridge_reload |
POST | 重新加载所有桥接连接 |
/api/ha_data_store/virtual_device |
GET/POST/DELETE | 虚拟设备管理 |
/api/ha_data_store/virtual_device/export |
GET | 导出全部虚拟设备(配置 + 当前状态,跨机迁移用) |
/api/ha_data_store/virtual_device/import |
POST | 导入虚拟设备并重建(body {mode: skip|overwrite, devices:[...]}) |
/api/ha_data_store/helper |
GET/POST/DELETE | 辅助元素列表/新建/删除 |
/api/ha_data_store/helper/scan |
GET | 扫描原生 HA helper(可 ?include_binary_sensor=1)生成可导出 item |
/api/ha_data_store/helper/export |
GET | 导出已导入的辅助元素(配置 + 状态) |
/api/ha_data_store/helper/import |
POST | 导入辅助元素并转为本集成实体(body {mode: skip|overwrite, items:[...]}) |
/api/ha_data_store/power_energy |
GET | 用电量查询:type=configs(生效中)/ type=archived(回收站)/ type=lookup&entity_id=(单个含归档)/ type=query&kind=daily|monthly|yearly|range|latest(支持 entity_id/room/date/month/year/start/end 过滤) |
/api/ha_data_store/power_energy |
POST | 登记管理(body {action: create|delete|restore|restore_all|purge|purge_all|backfill_deid, entity_id, ...};delete 为软删除归档,日表数据保留;daily_entity_id 不接受传入,由 id_slug 派生) |
/api/ha_data_store/query?type=power_energy_multi |
GET | 用电量多实体×多维度聚合:entities(逗号分隔,空=全部)、bucket=day|month|year、view=entity|date、start/end/date/month/year、devices=0 可省明细。view=entity → entities[].series[];view=date → dates[].devices[];均含 totals.kwh/day_count |
/api/ha_data_store/query?type=device_usage_multi |
GET | 设备用时/用电多实体×多维度聚合(device_history):参数同上。指标为 count 开启次数 / duration_hour 时长 / energy_kwh 用电量(无来源为 null)/ running。运行中记录按「当前时间 − on_time」计时。额外带 room |
/api/ha_data_store/query?type=device_usage_detail |
GET | 多实体明细(不聚合):entities、时间参数(start/end 时间段、date 指定日、month 指定月、year 指定年,优先级 start/end > date > month > year,全不传 = 全部)、limit/offset、full=1、summary=0。按 on_time 倒序,返回 total/returned/records[] 与 summary 合计节点(全局 totals + 每实体合计,含 running_count;基于分页前全量记录计算,不受 limit/offset 影响)。API 工具中同样使用「时间模式」下拉。full=1 返回 device_history 全部字段(id/on_power/off_power/energy_consumed/duration/cross_day/state_attr(JSON)/now_kwh/on_user/off_user/on_snapshot/off_snapshot/power_entity/power_rating 等);不传 full 为精简字段 |
/api/ha_data_store/query?type=device_usage_total |
GET | 多实体合计(年/月/日/全部):entities、scope=all|year|month|date(配 year/month/date,month 支持 9/09/2026-09)。返回每实体合计 + 全局 totals |
/api/ha_data_store/query?type=device_usage_history |
GET | 多实体历史同期:entities、scope=today(历年同月同日)/ scope=month(历年同月),排除今年。返回 history(含 by_year 逐年)+ current(今年同期) |
/api/ha_data_store/query?type=device_usage_avg |
GET | 多实体平均指标:entities、start/end/date/month/year(默认全部)。返回 avg_daily_count 平均每日次数 / avg_daily_duration_hour 平均每日时长 / avg_per_count_hour 平均每次时长(全局按加权计算) |
/api/ha_data_store/devices/cleanup |
GET/POST | 扫描空设备 / 清理空设备(POST body {confirm:true}) |
/api/ha_data_store/health_add |
POST | 添加健康记录(body 可选 remark 备注 / description 说明) |
/api/ha_data_store/health_types |
GET/POST/DELETE | 健康数据类型管理 |
/api/ha_data_store/batch_entity_state |
POST | 批量写入实体状态 |
/api/ha_data_store/db_maintain |
POST | 数据库维护(VACUUM/REINDEX) |
/api/ha_data_store/clear_table |
POST | 清空指定表并重置自增 ID(body {table, admin_password, vacuum?},需管理员密码;核心表禁止) |
/api/ha_data_store/entity_monitor |
GET | 实体在线监控 |
/api/ha_data_store/automations |
GET/POST | 自动化配置列表/新增 |
/api/ha_data_store/automations/{id} |
PUT/DELETE | 修改/删除自动化(修改后自动重算下次运行时间) |
/api/ha_data_store/automations/{id}/run?force=1 |
POST | 手动运行(force=1 跳过条件) |
/api/ha_data_store/automation_logs |
GET/DELETE | 执行记录分页查询 / 清理(?days=N 或 ?automation_id=) |
/api/ha_data_store/automation_lookup |
GET | 按名称查询自动化详细信息(?name= 精确 / &fuzzy=1 模糊,含配置+统计) |
GET /api/ha_data_store/custom/{tail}
任何没有对应静态路由的路径会从 custom_routes 表中查找并执行 SQL。SQL 中的 ?param 格式参数会自动从 URL 查询参数中提取。
示例:
- 定义路由路径:
my_custom - SQL 语句:
SELECT * FROM device_history WHERE entity_id = '?entity_id' AND date(on_time) = '?date' - 访问:
/api/ha_data_store/custom/my_custom?entity_id=switch.fan&date=2024-01-15
高级用法 — 无路由名时使用 q 参数:
GET /api/ha_data_store/custom?q=SELECT...&key=xxx
直接传递 SQL 语句(安全沙箱限制写入操作)。
访问 http://你的HA地址:8123/api/ha_data_store/db_viewer
💡 开发提示:页面 HTML 带热重载——只修改
db_viewer.html(布局/CSS/JS)后刷新浏览器页面即可生效,无需重启 HA(建议 Ctrl+F5 强刷避开浏览器缓存);修改任何.py文件仍需重启 HA。页面标题栏右侧会显示当前集成版本号。
特性:
- 表结构查看(列名、类型、约束)
- 数据分页浏览
- 在线编辑、删除行
- 按列排序
- 📥 导入数据(CSV / JSON,字段映射 + 固定值、追加 / Upsert、自动建表与补列、试运行)
- ⬇️ 导出CSV(当前表数据;另可下载目标表空模板)
- 🧹 清空表(删除全部数据并让自增 ID 从 1 重新开始,需管理员密码 + 二次确认)
安全限制: 默认仅限同网段访问,需要管理员密码登录(默认 admin)。
集成会自动创建三个开关实体,用于控制 API 的安全访问:
| 实体 ID | 名称 | 说明 |
|---|---|---|
switch.ha_data_store_api |
API 访问 | OFF 时所有 API 请求返回 403 |
switch.ha_data_store_db_browse |
数据库浏览器 | OFF 时禁止查看数据内容 |
switch.ha_data_store_db_modify |
数据库修改 | OFF 时禁止写入操作 |
switch.ha_data_store_remote_access |
远程访问 | OFF 时仅允许同网段访问数据库浏览器;ON 时允许任意网段访问(默认关闭,重启后自动重置为关闭) |
外部网络 局域网
│ │
│ query?key=xxx │ db_viewer(管理页)
│ ✅ 允许 │ ⚠️ 同网段+密码
│ │ (远程访问开关可放行跨网段)
│ db_viewer │ 密钥管理 → 需密码
│ ❌ 拒绝 │ 修改密码 → 需旧密码
API Key 鉴权方式:
- URL 参数:
?key=your_api_key - Header:
Authorization: Bearer your_api_key
安全管理:
- 三个独立开关控制 API/浏览/修改
- 管理员密码用于管理页面登录
- API Key 可独立启用/禁用
- SQL 注入防护:禁止危险关键字
- 子网检测:管理接口仅限同 /24 子网
数据库文件位于 {config_dir}/storage/ha_data_store.db,使用 SQLite WAL 模式。
| 表名 | 说明 |
|---|---|
entity_configs |
实体配置(联合主键 entity_id + attr_type) |
device_history |
设备开关历史记录 |
custom_routes |
自定义路由定义 |
api_keys |
API 密钥 |
api_settings |
API 设置(管理员密码等) |
personal_bills |
个人账单明细(支付宝 / 微信账单,独立模块 personal_bill.py,见 23. 个人账单) |
每种指标独立建表:
| 表名 | 指标 |
|---|---|
env_temperature |
温度 |
env_humidity |
湿度 |
env_pm25 |
PM2.5 |
env_co2 |
二氧化碳 |
env_power |
功率/电量 |
env_sensor |
通用传感器(TEXT 类型) |
按类型动态创建:attr_{type_name}
通讯数据采集(mode=comm)使用 attr_comm_records,固定 16 列
(见 21. 通讯数据(采集 + 查询)),并为
time、(party_number,time)、(party_name,time)、(location,time) 建立索引。
| 表名 | 说明 |
|---|---|
attr_type_defs |
属性类型定义 |
export_configs |
导出配置 |
file_source_configs |
文件源配置(JSON→实体) |
api_source_configs |
API源配置(API→实体) |
push_targets |
实体→网络 目标配置(读 token + 控制 token,同一实体可多套配置) |
control_logs |
实体→网络 控制审计日志(保留 30 天) |
| 表名 | 说明 |
|---|---|
bridge_connections |
远程 HA 连接配置 |
bridge_entities |
桥接实体列表 |
| 表名 | 说明 |
|---|---|
vacuum_type_defs |
扫地机器人类型定义 |
vacuum_configs |
扫地机器人配置 |
vacuum_history |
扫地机器人轨迹 |
health_records |
健康记录(血压/体温/体重等,含 remark 备注 / description 说明) |
virtual_devices |
虚拟设备持久化 |
printer_configs |
打印机配置(支持多台,name 唯一) |
printer_daily |
打印机每日记录(汇总 + 墨量 + 当日明细 JSON) |
user_actions |
用户操作记录(前端埋点上报,含 action_snapshot/state_log/ts_text/config_id/device_type 等;ts 采用实体状态时间,与 device_history 精确关联) |
automations |
自动化配置(触发/条件/动作,30 秒调度执行) |
automation_logs |
自动化执行记录(时间、条件明细、动作结果、耗时、状态,保留 30 天) |
helper_entities |
辅助元素持久化(原生 helper 导入为本集成自管实体,含 source_type/source_entity_id/extra_config) |
power_meter_configs |
功率→用电计量登记表(功率实体/设备名/房间/id_slug/日用电量实体/单位/启用;enabled=0 表示已取消登记并归档到回收站,行保留以便恢复与沿用) |
power_energy_daily |
用电计量日表(每功率实体每天一条 kwh,月/年由日表实时聚合) |
集成内置每日滚动的本地日志系统,日志文件保存在集成目录的 logs/ 文件夹下:
{ha_data_store目录}/logs/2024-01-15.log
{ha_data_store目录}/logs/2024-01-16.log
特性:
- 每个自然日一个文件,格式
YYYY-MM-DD.log - 自动保留最近 N 天(默认 7 天),过期自动清理
- 线程安全,可在线程池中写入
- 日志格式:
[时间] [级别] 消息
查看日志:
- 通过 API:
GET /api/ha_data_store/logs(列表)和GET /api/ha_data_store/logs/content?date=YYYY-MM-DD - 通过管理界面:在日志保留设置中可调整保留天数
在管理界面的"添加设备类实体"或"添加传感器类实体"中,使用 JSON 格式支持一次添加多个:
[
{"entity_id": "switch.device1", "room": "客厅"},
{"entity_id": "switch.device2", "room": "卧室"}
]推荐使用 Cloudflare Tunnel(Cloudflared Add-on)进行零配置穿透:
https://ha.你的域名.com/api/ha_data_store/query?type=xxx&key=你的Key
通过 API:
curl -X POST /api/ha_data_store/apikey/settings \
-H "Content-Type: application/json" \
-d '{"old_password": "旧密码", "new_password": "新密码"}'或者直接在数据库浏览器中编辑 api_settings 表的 admin_password 记录。
- 所有数据库写操作在 HA 线程池(executor)中执行,不阻塞事件循环
- SQLite 使用 WAL 模式,读写不互斥
- 传感器轮询在整秒边界对齐,避免高频写入
使用远程 HA 的长期访问令牌(Long-Lived Access Token)。在远程 HA 的"用户资料"→"安全"→"长期访问令牌"中生成。
{HA配置目录}/storage/ha_data_store.db
- 在 HA 中删除集成
- 删除
{HA配置目录}/storage/ha_data_store.db文件 - 重新添加集成
- 运行环境: Home Assistant (Python)
- 数据库: SQLite (WAL 模式)
- API 框架: Home Assistant HTTP View (aiohttp)
- 桥接协议: WebSocket (aiohttp) + REST API
- 配置方式: Config Flow / Options Flow / REST API
注意:本集成是一个综合性数据平台,功能丰富但配置复杂度较高。建议先配置设备类和传感器类采集核心数据,再逐步探索属性提取、桥接等高级功能。
{ "id": "eg_ke_ting", "name": "客厅灯", "kind": "lights", "icon": null, "enabled": true, "rev": 3, "sort": 10, "options": {}, "actions": [], "members": [ { "entity_id": "light.ke_ting_zhu_deng", "name": "主灯", "icon": null, "sort": 0, "extra": {} } ] }