说明:对方只能推 HTTP 回调、报文又对不上单次告警接口时,在终端「自定义 API」里做路径、字段拼接和级别映射。菜单与签名开关以本机「Api」页为准,文中不放站外链接。
一、痛点:对方愿意回调,但报文不是你的接口
专栏第一篇把 send_msg 讲清楚了:自己拼 led_style、color、text、sign,灯和 TTS 都听你的。现实里更多是反过来:
- 云监控、Zabbix 媒体类型、工单、OA、ERP、合规平台已经有 WebHook / HTTP 回调;
- 字段是对方定的:
host、serv、level、time,没有led_style; - 对方说"我们不能改报文规范",你也不想为每套系统再写一层网关。
博灵 Q 系列语音通知终端的 自定义 API 就是给这种异构回调准备的:在设备上声明一条用户路径,把入参拼成播报句子,再用某个字段去选通知组(红转亮 / 黄闪亮 / 绿恢复)。
本文按联调写:开接口 → 建路径 → 三类字段 → 级别映射 LED → 用脚本冒充对方 WebHook → 和 send_msg、云中继怎么分工。
二、架构:不改对方,只改终端上的"翻译层"
text
[MIS / ERP / BPM / OA / 工单 / 云监控]
|
| POST form-data 或 JSON
| 字段由对方定义
v
[博灵自定义 API]
路径:/api/api/user_msg/<你起的名字>
固定文本 + 普通字段 + 映射字段 → TTS
指定 LED 判断字段 → 通知组
|
v
[通知组 → 全彩 LED + 语音播报 + 播报队列]
和单次告警的分工:
| 能力 | 单次告警 send_msg |
自定义 API user_msg |
|---|---|---|
| 谁拼句子 | 调用方 | 终端按模板拼 |
| 谁选灯效 | 调用方传 led_style |
通知组 + LED 映射字段 |
| 适合 | 你能改脚本 | 对方回调格式锁死 |
| 签名 | 通常要 time + sign |
以页面「是否鉴权」为准 |
两者可以并存:自研脚本走 send_msg,外购平台走自定义路径。
三、上线前:启停、Key、通知组
Api → Api启停打开声光播报(以及你要用的自定义接口开关);- 设置 API Key,生产环境不要用出厂示例;
- 安全设置里确认是否开启签名;部分固件允许关闭 API 鉴权,内网也建议开着;
- 通知组先建好,后面映射才能选颜色:
| 通知组 | LED | 语音 | 对应对方级别 |
|---|---|---|---|
sev-critical |
红转亮 | 开,重复高 | level=1 严重 |
sev-warning |
黄闪亮 | 开 | level=2 警告 |
sev-ok |
绿常亮短时 | 开 | level=3 恢复 |
自定义 API 拼出的是文本 ;灯怎么转,看映射命中了哪一个通知组,而不是在回调里再传一套 color。
四、建一条用户路径
进入 Api → 自定义Api接口,新建一条。页面上的关键项:
| 项 | 建议 |
|---|---|
| 路径 | 只填一段,如 alarm。最终本机路径为 /api/api/user_msg/alarm |
| 方法 | POST |
| 数据类型 | 与对方一致,常见 form-data;设备也普遍能吃 JSON |
| 启用日志 | 联调期打开,请求进「Api调用日志」 |
| 播报模板 | 按列表顺序拼接成 TTS |
| LED 判断字段 | 例如 level,用其取值去套通知组 |
对方最终要把回调地址写成(协议与冒号拆开,避免正文被判外链):
text
主机:192.168.0.66
路径:/api/api/user_msg/alarm
方法:POST
跨 NAT 时,把「主机」换成云服务页复制的通道前缀,路径是否少一节 api 以该页高亮为准,字段映射仍然在本机自定义 API 里做。
五、播报模板:三种字段怎么拼
以对方回调为例:
| 对方字段 | 含义 |
|---|---|
host |
告警主机 |
serv |
服务或指标名 |
level |
1 严重 / 2 警告 / 3 恢复 |
time |
发生时间(对方的时间,不一定参与签名) |
模板里有三类积木:
5.1 固定文本
拼进去的字面量,不取 HTTP 参数。用来写地点、设备名、祈使句:
text
数据中心,
请值班人员处理。
5.2 普通字段
填对方参数名。请求体里有同名键,就取值为播报片段。
模板顺序示例:
text
[固定] 主机:
[普通] host
[固定] ,服务:
[普通] serv
[固定] ,级别:
[映射] level
对方 POST:
text
host=web-01
serv=CPU使用率
level=1
终端拼出:
text
主机:web-01,服务:CPU使用率,级别:严重
TTS 有长度上限(单次告警常见约 150 字)。主机名、级别放前,备注和原始 JSON 不要整段塞进去。
5.3 映射字段
对方给代码,现场要听人话。把 level 做成映射:
| 入参 | 播报 |
|---|---|
1 |
严重 |
2 |
警告 |
3 |
已恢复 |
工单系统常见 status=new/close,合规平台常见 action=deny,都用映射,不要让喇叭念英文枚举。
六、LED 判断字段:让颜色等于级别
在自定义 API 页指定 LED 判断字段 (文档示例常用 level):
- 每次请求读取该字段当前值;
- 按「LED 映射」表把值对应到某个通知组;
- 命中失败时的行为以页面为准(可能落到默认组或不亮),联调时务必覆盖 1/2/3 三条。
建议映射:
level |
通知组 | 现场 |
|---|---|---|
1 |
sev-critical |
红转亮 + 语音 |
2 |
sev-warning |
黄闪亮 + 语音 |
3 |
sev-ok |
绿短亮 + 「已恢复」 |
恢复事件必须单独组,并且不要用无限循环。否则故障恢复后喇叭还在念旧句子。
若对方没有数字级别,只用 status=firing/resolved,映射字段同样能做:firing→critical,resolved→ok。
七、用脚本冒充对方 WebHook
签名算法与本机其他 API 相同:参数加 token、time,按 key 字典序拼接后 MD5。若页面关闭了鉴权,下列 sign 可省略,但不要把关闭鉴权带进生产。
python
import hashlib
import json
import time
import requests
DEVICE_IP = "192.168.0.66"
API_KEY = "REPLACE_WITH_YOUR_KEY"
USER_PATH = "/api/api/user_msg/alarm"
def build_sign(params: dict, token: str) -> str:
payload = dict(params)
payload["token"] = token
norm = {
k: json.dumps(v, ensure_ascii=False, separators=(",", ":"))
if isinstance(v, (dict, list)) else str(v)
for k, v in payload.items()
}
sign_str = "".join(f"{k}{norm[k]}" for k in sorted(norm.keys()))
return hashlib.md5(sign_str.encode("utf-8")).hexdigest()
def push_webhook(host: str, serv: str, level: str) -> dict:
biz = {
"host": host,
"serv": serv,
"level": level,
}
biz["time"] = str(int(time.time()))
biz["sign"] = build_sign(biz, API_KEY)
proto, sep = "http", "://"
url = proto + sep + DEVICE_IP + USER_PATH
r = requests.post(url, data=biz, timeout=5)
r.raise_for_status()
return r.json()
if __name__ == "__main__":
print(push_webhook("web-01", "CPU使用率", "1"))
print(push_webhook("web-01", "CPU使用率", "3"))
对方若发 JSON 而不是 form-data,把 data=biz 改成 json=biz,并确认自定义接口的「数据类型」勾的是 JSON。签名时的序列化必须与实际提交体一致,空格和引号不一致会鉴权失败。
curl 验证时不要在文章里写带协议头的完整 URL,用「主机 + 路径」两段填进自己的终端即可。
八、三个落地例子(字段不同,模板不同)
8.1 云监控 / 主机性能
对方:instance、metric、alert_state(firing/resolved)。
- 普通字段:
instance、metric - 映射:
alert_state - LED 判断:
alert_state - 固定文本补上机房名
8.2 工单系统「新单」
对方:ticket_id、title、priority(P1/P2)。
- TTS:
新工单,标题:{title},级别:{priority映射} - P1 用高重复通知组,P2 用 warning
ticket_id放句尾,避免占满 150 字把标题截掉
8.3 OA / 合规「待办、违规」
对方:user、action、result。
- 映射
result=deny→违规操作 - 通知组不要用无限循环,避免会议室里循环念人名
- 声光免打扰按周生效,夜班可关语音只留灯
动力环境、视频平台故障回调同理:先列出对方字段表,再决定哪几个进 TTS、哪一个进 LED。
九、和签名、时间窗、队列
- 设备 NTP 必须准,签名时间窗常见 120 秒;
- 对方 WebHook 若不传你的
time/sign,只能:关鉴权(不推荐)、或中间加一层网关代签、或把自定义接口改成对方能加头/加字段的那种; - 告警风暴:在对方侧做抑制,或自定义 API 只映射恢复/严重,警告类不要进喇叭;
- 队列:连续 firing 会排队。首页可看周期/循环任务;跳过、清队列仍走基础功能接口或面板按键。
自定义 API 打开「启用日志记录」后,每条请求进 Api 调用日志,便于对照「对方以为成功」和「终端拒绝签名」。
十、联调清单
- 声光播报与自定义接口已启用,通知组试播成功;
- 路径
alarm保存后,脚本 POST 三个level,灯色和 TTS 映射都对; - 故意改错
sign或把time调出窗口,确认被拒; - 缺
host时句子仍能听懂(固定文本兜底),不会念出空白; - 恢复
level=3不会维持红转亮循环; - Api 调用日志能看到路径、参数、结果码;
- 播报日志里的文本与模板拼接一致;
- 对方正式环境改回调地址后,用一条真实 firing + 一条 resolved 收尾。
十一、常见坑
- 只配了路径,没开声光播报 → 接口 200,灯不响。
- LED 判断字段名和对方不一致 (
levelvsseverity)→ 永远默认组。 - 映射漏了恢复值 → 恢复仍走严重组。
- JSON 与 form-data 勾错 → 普通字段全空,只剩固定文本。
- 签名序列化和 body 不一致 → 本机脚本通、对方 Java 客户端不通。
- 模板把整段日志拼进去 → 超长截断,通道号被切掉。
- 多条自定义路径抢同一高重复组 → 工单和主机故障抢喇叭,按来源拆组。
- 回调打到内网 IP,平台在云上 → 应走云中继或自建公网网关,映射规则仍在本机。
十二、小结
对方不肯改 WebHook 规范时,不必再包一层微服务,也可以把翻译做在终端上。
- 博灵 Q 系列 / 博灵语音通知终端 的自定义 API 用
user_msg路径承接异构 HTTP 回调; - 模板三类积木:固定文本、普通字段、映射字段;
- LED 判断字段把级别落到通知组,完成声光告警与 TTS 语音播报;
- 自研脚本继续用
send_msg;锁死报文的 MIS / ERP / 工单 / 云监控走本篇; - 跨网段只换主机前缀,映射不用做两遍。
建议顺序:通知组 → 一条 alarm 路径 → 脚本打 1/2/3 → 再把对方正式回调切过来。
与本专栏其它篇的关系:能改报文用 HTTP 单次告警,不能改报文用本篇自定义 API;小机房探测、Modbus、邮件、云中继仍按来源各走各的,靠通知组分开现场语义。