跳转到内容

实体与设备接口

这五条都在读组:认 Bearer 或面板会话 cookie(分界见总览)。 自己写面板的话主要就用它们,怎么把页面传上去见自建面板。

设备是一台物理东西(一个插座、一台扫地机);实体是它的一项能力(开关、 功率、温度)。实体 id 形如 设备名.能力名,两段都是 snake_case。

一台小米插座能有二十多个实体,其中大部分(上电默认状态、充电保护功率、超用电量 告警阈值)你多半用不上。/api/devices 给你设备这一层,/api/entities 给你能力 这一层,两者用 entity_ids 关联。

Terminal window
# 看全部实体的当前值
curl -s -H "Authorization: Bearer $RHA_TOKEN" \
http://127.0.0.1:8420/api/entities | jq '.data[] | {id, value, stale}'
# 开一个开关
curl -s -X POST -H "Authorization: Bearer $RHA_TOKEN" \
-H 'Content-Type: application/json' -d '{"value": true}' \
http://127.0.0.1:8420/api/entities/fan.power/set | jq .data

读到一个值的时候,有三个字段答的是三个不同问题,互不替代:

  • available —— 设备现在联不联得上
  • stale —— 这个值是不是重启后从存储恢复的旧值
  • poll_tier —— 这个值刷得勤不勤(字段缺席 = 不轮询,广播型设备就是这样)

只看有没有数字会被骗:一台断电的插座,value 里可能还留着最后一次读数, available 是 false、stale 是 true。面板上该把这种值画成“未确认”的样子。

GET/api/entities

认证:Bearer token 或 面板会话 cookie

按实体 id 字典序返回。

stale = true 的值是重启后从存储恢复的旧值, 不是设备刚报上来的 —— 看到它别当成实时读数。available 答的是另一个问题(设备在不在线), poll_tier 答的是第三个(这值刷得勤不勤; 字段缺席 = 不轮询, 广播型设备就是这样)。三个字段互不替代。

返回

成功时 data 字段的形状:

数组,每个元素:
字段类型说明
available*boolean

设备现在联不联得上。false 时 value 里往往还留着最后一次读数 —— 那是旧值, 别当成当前状态。

classstring

语义类, 见 crate::class。

None 是合法且常见的状态, 含义是「不参与任何北向导出」—— 一台插座 28 个 实体里只有一两个有语义, 其余(上电默认状态、充电保护功率、超用电量告警阈值) 在任何家居生态里都没有对应物。

这让本字段的引入成本接近零: 没配 bridge 的用户全 None 也照跑。

一个实体承载的语义。

刻意窄 —— 封闭枚举加一项就要动 core 和所有 bridge, 宁可少。刻意不照抄 HomeKit 的 Service 列表: DeviceClass::Power 与 DeviceClass::Weight 在 HAP 里没有 标准对应, 照抄的话这两个语义根本进不来, 接 MQTT/HA 时又得重新发明。内核词汇表与 任何单一协议的对象模型必须分开。

domainany

数值实体的取值域, 见 crate::domain。None = 未声明, 同样是常见状态。

北向 bridge 拿它把设备刻度换算成协议刻度 —— 一台风扇的档位可能是 1-4 档 枚举、也可能是 1-100 无级, 而 HomeKit 的转速滑条固定是 0-100 的百分比。 不知道原始域就无从换算。

一个数值实体的合法取值。

id*string

实体 id, 形如 设备名.能力名。两段都是 snake_case, 首字符必须是字母。

kind*string

这个实体能被怎么用: sensor 只读, switch 可写(/set 只认这一种), trigger 是瞬时事件(按钮被按了), 没有"当前值"这回事。

last_updatedstring
date-time

这个值是什么时候收到的。缺席 = 本次启动以来没收到过。

poll_tierstring

轮询节奏: fast(被规则引用) / slow / null = 不轮询。

null 是广播、推送型设备(BLE 温湿度计、reolink 事件流): 值是设备自己送上来的, 没有"问"这个动作, 对它说"轮询得慢"是错的。用 bool 表达不了这第三种情况。

与 available / stale 是三个独立字段: 它们答的是不同问题(设备在不在线 / 这值是不是重启恢复的旧值 / 这值刷得勤不勤), 合并就会退回"光看有没有数字会被骗"。

一个实体的轮询节奏。

stale*boolean

这个值是重启后从存储恢复的旧值, 不是设备报上来的。

面板上该给它一个"未确认"的样子: 值看着有, 但没人保证设备现在真是这样。 收到第一次真实上报后它就变回 false。

unitstring

单位, 由 adapter 给, 如 °C / W / %。缺席是常见状态 —— 开关类实体本来就没有单位。

三套 adapter 各有各的词汇, 这里不做归一 —— 它是给人看的标签, 不是换算依据。要换算看 domain。

valueany

当前值, 类型见 value_type。缺席 = 从没收到过值(刚启动、且没开 历史记录, 或这台设备从没上报过), 不是"值为 0"。

value_type*string

值的类型: bool / int / float / str。写值时类型对不上会被拒。

GET/api/entities/{id}

认证:Bearer token 或 面板会话 cookie

字段含义与 /api/entities 完全一致, 只是单条。

参数

参数位置类型说明
id*路径string

实体 id, 形如 设备名.能力名

返回

成功时 data 字段的形状:

一个对象:
字段类型说明
available*boolean

设备现在联不联得上。false 时 value 里往往还留着最后一次读数 —— 那是旧值, 别当成当前状态。

classstring

语义类, 见 crate::class。

None 是合法且常见的状态, 含义是「不参与任何北向导出」—— 一台插座 28 个 实体里只有一两个有语义, 其余(上电默认状态、充电保护功率、超用电量告警阈值) 在任何家居生态里都没有对应物。

这让本字段的引入成本接近零: 没配 bridge 的用户全 None 也照跑。

一个实体承载的语义。

刻意窄 —— 封闭枚举加一项就要动 core 和所有 bridge, 宁可少。刻意不照抄 HomeKit 的 Service 列表: DeviceClass::Power 与 DeviceClass::Weight 在 HAP 里没有 标准对应, 照抄的话这两个语义根本进不来, 接 MQTT/HA 时又得重新发明。内核词汇表与 任何单一协议的对象模型必须分开。

domainany

数值实体的取值域, 见 crate::domain。None = 未声明, 同样是常见状态。

北向 bridge 拿它把设备刻度换算成协议刻度 —— 一台风扇的档位可能是 1-4 档 枚举、也可能是 1-100 无级, 而 HomeKit 的转速滑条固定是 0-100 的百分比。 不知道原始域就无从换算。

一个数值实体的合法取值。

id*string

实体 id, 形如 设备名.能力名。两段都是 snake_case, 首字符必须是字母。

kind*string

这个实体能被怎么用: sensor 只读, switch 可写(/set 只认这一种), trigger 是瞬时事件(按钮被按了), 没有"当前值"这回事。

last_updatedstring
date-time

这个值是什么时候收到的。缺席 = 本次启动以来没收到过。

poll_tierstring

轮询节奏: fast(被规则引用) / slow / null = 不轮询。

null 是广播、推送型设备(BLE 温湿度计、reolink 事件流): 值是设备自己送上来的, 没有"问"这个动作, 对它说"轮询得慢"是错的。用 bool 表达不了这第三种情况。

与 available / stale 是三个独立字段: 它们答的是不同问题(设备在不在线 / 这值是不是重启恢复的旧值 / 这值刷得勤不勤), 合并就会退回"光看有没有数字会被骗"。

一个实体的轮询节奏。

stale*boolean

这个值是重启后从存储恢复的旧值, 不是设备报上来的。

面板上该给它一个"未确认"的样子: 值看着有, 但没人保证设备现在真是这样。 收到第一次真实上报后它就变回 false。

unitstring

单位, 由 adapter 给, 如 °C / W / %。缺席是常见状态 —— 开关类实体本来就没有单位。

三套 adapter 各有各的词汇, 这里不做归一 —— 它是给人看的标签, 不是换算依据。要换算看 domain。

valueany

当前值, 类型见 value_type。缺席 = 从没收到过值(刚启动、且没开 历史记录, 或这台设备从没上报过), 不是"值为 0"。

value_type*string

值的类型: bool / int / float / str。写值时类型对不上会被拒。

错误

  • 400 —— 实体 id 格式不合法
  • 404 —— 没有这个实体
GET/api/devices

认证:Bearer token 或 面板会话 cookie

比实体高一层: 一台设备下挂若干实体。返回里的 entity_ids 是该设备的实体 id 列表, 省得前端自己按 id 前缀去 /api/entities 里筛一遍。

manufacturer / model 缺席表示那个 adapter 答不上来, 不是设备没有型号。

返回

成功时 data 字段的形状:

数组,每个元素:
字段类型说明
entity_ids*array

—

labelstring

用户在 devices.toml 里给的显示名。None = 沿用 name。

manufacturerstring

厂商与型号, 由 adapter 提供(见 DeviceIdentity)。None = 那个 adapter 答不上来。

刻意不在这里回落: HomeKit 回落成 "rha", 而 MQTT 更该是整个省略字段。 协议专属的回落归 bridge, 只有「所有协议都一样」的回落(名字, 见 DeviceMeta::display_name)才收进内核。

modelstring

—

name*string

设备 id, 也是它所有实体 id 的前缀(<name>.<能力名>)。snake_case。

POST/api/entities/{id}/set

认证:Bearer token 或 面板会话 cookie

只有 kind = switch 的实体可写。

这个接口是同步等结局的: 派发命令后最多等 6 秒, outcome 为 ok / failed / timeout 三者之一。timeout 不代表没生效, 只代表这 6 秒内没收到确认。

它在读组里 —— 一张泄漏的面板 cookie 是能开关设备的。这是有意的取舍(面板必须能按开关), 不是漏洞。

参数

参数位置类型说明
id*路径string

实体 id, 形如 设备名.能力名

请求体

字段类型说明
value*any

—

返回

成功时 data 字段的形状:

一个对象:
字段类型说明
command_id*integer

—

detailstring

—

outcome*string

—

错误

  • 400 —— id 不合法 / 实体不可写 / 值的类型对不上
  • 404 —— 没有这个实体
GET/api/entities/{id}/history

认证:Bearer token 或 面板会话 cookie

需要开着历史记录(rha.toml 的 [storage]), 关着时返回 503。

hourly = true 时按小时聚合, 返回形状变成聚合点(avg / min / max / count), 且只对数值实体有效。缺省返回原始采样点。

limit 缺省 500, 上限 5000(超了按 5000 截)。时间窗缺省是「到现在为止的 24 小时」。

参数

参数位置类型说明
id*路径string

实体 id

from查询串string

起点, RFC 3339 时间戳。缺省为 to 往前 24 小时

to查询串string

终点, RFC 3339 时间戳。缺省为现在

limit查询串integer

最多返回多少条。缺省 500, 上限 5000

hourly查询串boolean

按小时聚合。仅数值实体可用

返回

成功时 data 字段的形状 —— 有 2 种,看参数怎么传:

缺省: 原始采样点

数组,每个元素:
字段类型说明
ts*string
date-time

—

value*any

—

hourly=true: 按小时聚合

数组,每个元素:
字段类型说明
avg*number

—

bucket*string

—

count*integer

—

max*number

—

min*number

—

错误

  • 400 —— id 不合法 / 查询参数格式错 / 对非数值实体请求 hourly
  • 404 —— 没有这个实体
  • 503 —— 没开历史记录