Viseron 3.7.0 API 使用详解
Viseron 3.7.0 REST API 与 MQTT 集成实测详解:15 个处理器模块路由全景,XSRF/JWT/PAT 三种凭据链路,摄像头、事件、录像、HLS 回放、配置调参与日志端点逐条 curl 实测,负样本回执佐证路由规则。
Viseron 3.7.0 API 使用详解
目标:把 Viseron 3.7.0 的 REST API 与 MQTT 事件输出讲透——15 个处理器模块的路由全景、三种凭据(XSRF+Cookie 会话 / JWT / 个人访问令牌 PAT)的获取与调用链路、摄像头 / 事件 / 录像 / 流媒体 / 配置调参 / 日志系统六大域端点的逐条实测,全部命令输入与回显在 Ubuntu 22.04 服务器实测跑出(2026-10-30),可照抄复现。环境部署见《Viseron 3.7.0 Docker Compose 部署指南》,配置参数语义见《Viseron 3.7.0 配置参数详解》。
一、环境信息
本篇全部接口验证在下列环境中完成,所有回显均为真实输出(2026-10-30):
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04(Linux x86_64,6 核 CPU / 19GB 内存) |
| Viseron | 3.7.0(内置 PostgreSQL 16.15 / FFmpeg 8.1 / go2rtc / nginx,Tornado Web 框架) |
| Web/API 端口 | 8888(部署时映射为宿主 8151,本篇命令统一用 http://127.0.0.1:8151) |
| API 前缀 | /api/v1(全部 REST 路由挂在该前缀下) |
| 管理账号 | admin / q1w2e3r4(首次启动引导页创建,角色 admin) |
| 摄像头 | fake_camera(768x432@12fps,MediaMTX 模拟 RTSP 源) |
| 检测器 | mog2 运动检测 + darknet(YOLOv7-tiny)物体检测 |
| PAT 前缀 | vpat_(个人访问令牌,128 位十六进制) |
| JWT 有效期 | 1800 秒(expiration 字段,登录回执实测) |
Viseron 的 API 与 Web UI 同源同端口:浏览器访问 http://<服务器>:8888 打开前端页面,前端数据全部来自 /api/v1/*。命令行集成只依赖 curl(或任意 HTTP 客户端)与一个可用凭据,无需额外 SDK。
二、API 总览
2.1 路由体系:15 个处理器模块
Viseron 3.7.0 的 REST API 由 webserver 组件下 15 个处理器模块注册而成,全部挂在 /api/v1 前缀下。按模块汇总(✓ = 本篇实测,— = 源码注册、未逐一实测):
| 模块 | 路由 | 方法 | 权限 | 实测 |
|---|---|---|---|---|
| auth | /auth/enabled | GET | 免认证 | ✓ |
| auth | /auth/login | POST | 免认证(login 限流) | ✓ |
| auth | /auth/token | POST | 免认证(token 限流) | ✓ |
| auth | /auth/logout | POST | 登录用户 | ✓ |
| auth | /auth/request | GET | 登录用户 | ✓ |
| auth | /auth/users | GET | ADMIN | ✓ |
| auth | /auth/create | POST | ADMIN | — |
| auth | /auth/user/{user_id} | GET/PUT/DELETE | GET 登录用户;PUT/DELETE ADMIN | — |
| auth | /auth/users/{user_id} | PUT | ADMIN | — |
| auth | (需摄像头令牌的图像端点) | GET | 免认证 + camera token | — |
| camera | /camera/{id}/snapshot | GET | 免认证 + camera token | ✓ |
| camera | /camera/{id} | GET | 登录用户 | ✓ |
| camera | /camera/{id}/start | POST | ADMIN | ✓ |
| camera | /camera/{id}/stop | POST | ADMIN | ✓ |
| camera | /camera/{id}/manual_recording | POST | ADMIN/WRITE | ✓ |
| cameras | /cameras | GET | 登录用户 | ✓ |
| cameras | /cameras/failed | GET | 登录用户 | ✓ |
| events | /events/{camera} | GET | 登录用户 | ✓ |
| events | /events/{camera}/amount | GET | 登录用户 | ✓ |
| events | /events/amount | POST | 登录用户 | ✓ |
| events | /events/dates_of_interest | POST | 登录用户 | ✓ |
| recordings | /recordings | GET | 登录用户 | ✓ |
| recordings | /recordings/{camera} | GET | 登录用户 | ✓ |
| recordings | /recordings/{camera}/{recording_id} | DELETE | ADMIN | ✓ |
| recordings | /recordings/{camera}/available_timespans | GET | 登录用户 | — |
| recordings | /recordings/{camera} | DELETE | ADMIN(清空该摄像头录像) | — |
| hls | /hls/{camera}/available_timespans | GET | 登录用户(可 token 参数) | ✓ |
| hls | /hls/{camera}/{id}/index.m3u8 | GET | 登录用户(可 token 参数) | ✓ |
| config | /config | GET | ADMIN | ✓ |
| tune | /tune | GET | ADMIN | ✓ |
| tune | /tune/{camera} | GET | ADMIN | ✓ |
| tune | /tune/{camera} | PUT | ADMIN | ✓ |
| logger | /logger/logs | GET | ADMIN | ✓ |
| logger | /logger/config | GET | ADMIN | ✓ |
| system | /system/dispatched_events | GET | ADMIN | ✓ |
| profile | /profile/available_timezones | GET | 登录用户 | ✓ |
| profile | /profile/preferences | PUT | 登录用户 | — |
| profile | /profile/display_name | PUT | 登录用户 | — |
| profile | /profile/access_tokens | GET/POST | 登录用户 | ✓ |
| profile | /profile/access_tokens/{token_id} | DELETE | 登录用户 | ✓ |
| profile | /profile/revoke_all | POST | 登录用户 | — |
| publicimage | /publicimage | GET | 免认证(需 image token) | ✓ |
| download | /download | GET | 登录用户(需 token) | ✓ |
| onboarding | /onboarding | POST | 免认证(onboarding 限流) | — |
| compreface | /compreface/update_subjects | GET | 登录用户 | ✓ |
解读:
- 权限四层模型:每条路由在源码注册时声明四类属性——
requires_auth(默认 true,唯一例外是 enabled/login/token/onboarding/snapshot/publicimage 这几条设计上就要在无凭据时可达的路由)、requires_role(ADMIN / READ / WRITE 三级角色)、requires_camera_token(用摄像头专属 access_token 而非用户凭据)、rate_limit(login / token / onboarding 三个敏感端点单独限流)。 - 角色只分三级:admin(全部权限)、write(读写)、read(只读)。用户管理、启停摄像头、删录像、调参等写操作普遍要求 ADMIN 或 ADMIN/WRITE;查询类端点三级角色均可。
- 两个「token」不要混淆:
access_token(每摄像头一枚,GET /cameras回执里返回,用于 snapshot / publicimage / download 这类面向设备或第三方的免登录取图端点)与vpat_令牌(用户级个人访问令牌,用于模拟用户身份调任意 API)。后文分别演示。
2.2 无凭据访问的默认回执
先看反面教材——不带任何凭据直接调接口,是后面所有实测的对照基线:
curl -s http://127.0.0.1:8151/api/v1/cameras -w '\n[HTTP %{http_code}]\n'
{"status": 401, "error": "Authentication required"}
[HTTP 401]
凡是 401,正文都是同一个结构:status(HTTP 同码数字)+ error(一句话原因)。部署时启用了 webserver.auth 的环境,所有未带凭据的请求都会得到这一句,包括写操作(不会半执行)。
三、认证链路实测
3.1 认证状态探测:GET /auth/enabled
唯一不需要凭据的「元端点」,客户端启动时先调它判断要不要走登录流程:
curl -s http://127.0.0.1:8151/api/v1/auth/enabled
{"enabled": true, "onboarding_complete": true}
两个字段:enabled 表示是否启用了认证(对应 config.yaml 的 webserver.auth 段);onboarding_complete 表示首次引导(创建管理员账号)是否已完成。enabled: true 时其余接口一律要求凭据;onboarding_complete: false 时前端会强制进入 POST /onboarding 初始化流程。
3.2 JWT 登录:XSRF 防护 → POST /auth/login
浏览器场景的完整登录分三步:先拿 XSRF Cookie,再带 XSRF 头提交账密,最后组装 JWT。这是 Web UI 前端每次打开页面都在做的事,脚本场景可以完整复现:
# 第 1 步:请求任意免认证端点,让服务端种下 _xsrf cookie(-c 保存 cookie jar)
curl -s -c /tmp/jar.txt -o /dev/null http://127.0.0.1:8151/api/v1/auth/enabled
XSRF=$(awk '$6=="_xsrf"{print $7}' /tmp/jar.txt)
# 第 2 步:POST /auth/login,必须带 X-Xsrftoken 与 X-Requested-With 两个头
curl -s -b /tmp/jar.txt -c /tmp/jar.txt -X POST http://127.0.0.1:8151/api/v1/auth/login \
-H "Content-Type: application/json" \
-H "X-Xsrftoken: $XSRF" \
-H "X-Requested-With: XMLHttpRequest" \
-d '{"username":"admin","password":"q1w2e3r4","client_id":"d5f3a9c8-1111-4b2a-9c33-viseron0001"}'
实测回显:
{"header": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", "payload": "eyJpc3MiOiI4MTNhN2UyYmVhNzI0MzBlOTJkMjc5MWIzOTE2MzBlMCIsImlhdCI6MTc5MzMzMzM2NSwiZXhwIjoxNzkzMzM1MTY1fQ", "expiration": 1800, "expires_at": "2026-10-30T04:39:25.077722+00:00", "expires_at_timestamp": 1793335165, "session_expires_at": "2026-11-06T04:09:25.039988+00:00", "session_expires_at_timestamp": 1793938165}
逐字段解读:
| 字段 | 值 | 含义 |
|---|---|---|
| header | eyJhbGciOiJIUzI1NiIs... | JWT 的第一段,base64 解码是 {"alg":"HS256","typ":"JWT"} |
| payload | eyJpc3MiOiI4MTNhN2UyYmVh... | JWT 的第二段,解码见下 |
| expiration | 1800 | JWT 有效期秒数,30 分钟 |
| expires_at / expires_at_timestamp | …/1793335165 | JWT 过期时刻(ISO 与 Unix 双格式) |
| session_expires_at / …_timestamp | 2026-11-06T04:09:25… | 会话过期时刻 = 配置的 session_expiry.days(本环境 7 天) |
把 payload 段 base64 解码,能看到 JWT 的实际内容:
{"iss": "813a7e2bea72430e92d2791b391630e0", "iat": 1793333365, "exp": 1793335165}
iss(签发者标识)每次登录都不同(本环境四轮登录实测得到四个不同值),是签发时生成的会话标识而非固定用户 ID;iat/exp 是签发与过期时间戳。
关键坑:JWT 是「两段 + Cookie 签名」结构。 login 回执只给了 header 和 payload 两段,第三段签名不在回执里,而在 cookie jar 的 signature_cookie 中。缺了签名直接当 Bearer 用会被 401(本篇实测):
curl -s -H "Authorization: Bearer <header>.<payload>" \
http://127.0.0.1:8151/api/v1/cameras -w '\n[HTTP %{http_code}]\n'
{"status": 401, "error": "Authentication required"}
[HTTP 401]
正确拼法是从 cookie jar 取 signature_cookie,按 | 切分后第 5 段的 2:<base64> 就是签名:
JWT=$(python3 - <<'EOF'
import base64, json, urllib.parse
raw = None
for line in open('/tmp/jar.txt'):
if 'signature_cookie' in line:
raw = urllib.parse.unquote(line.rstrip('\n').split('\t')[-1].strip().strip('"'))
parts = raw.split('|')
sig = base64.b64decode(parts[4].split(':', 1)[1]).decode()
login = json.load(open('/tmp/login_resp.json')) # login 回执存盘
print(f"{login['header']}.{login['payload']}.{sig}")
EOF
)
echo "$JWT"
某次实测拼出的完整 JWT(三段齐全):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjOGRhZDFmNmIwYmM0ZWMwYTdhMWU2YzU3N2JmYjM0MiIsImlhdCI6MTc5MzMyNzEzMywiZXhwIjoxNzkzMzI4OTMzfQ.QWpYFsguxV6Jswdq8U2t92vVoL4J3sFvc4kpKllbLPE
拼好的 JWT 用法是「浏览器形态」:JWT 放 Authorization: Bearer 头、同时把 cookie jar 一起带上(签名 cookie 要参与校验):
curl -s -b /tmp/jar.txt -H "Authorization: Bearer $JWT" \
http://127.0.0.1:8151/api/v1/cameras -w '\n[HTTP %{http_code}]\n' | tail -1
[HTTP 200]
💡 技术拓展:为什么「JWT + cookie 签名」而不是标准单头 JWT?Viseron 的 webserver 基于 Tornado,XSRF 防护与签名 Cookie 是 Tornado 的内建安全机制——把 JWT 签名托管在 HttpOnly Cookie 里,前端 JS 无法读取,天然防 XSS 窃取令牌;代价是纯脚本调用必须维护 cookie 状态。脚本场景更省事的路径是下面的 PAT(无 Cookie 依赖),JWT 链路主要是给浏览器前端和自己写代理层用的。
3.3 JWT 刷新:POST /auth/token
JWT 30 分钟过期,浏览器前端到期前会调 POST /auth/token 静默换新。该端点免认证但单独限流(rate_limit: "token"),请求体要求 OAuth2 风格的 grant_type。用错误 grant_type 探测出的校验规则(回执即文档):
curl -s -X POST -H "Content-Type: application/json" \
-d '{"grant_type":"access_token"}' \
http://127.0.0.1:8151/api/v1/auth/token -w '\n[HTTP %{http_code}]\n'
{"status": 400, "error": "Invalid body: {\"grant_type\":\"access_token\"}. required key not provided @ data['client_id']. Got None\nvalue must be one of ['refresh_token'] for dictionary value @ data['grant_type']. Got 'access_token'"}
可见 schema 是 {grant_type: "refresh_token", client_id: <字符串>},且刷新凭据需随请求携带(把刚过期的 JWT 当凭据传入会得到明确拒绝):
curl -s -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $JWT" \
-d '{"grant_type":"refresh_token","client_id":"d5f3a9c8-1111-4b2a-9c33-viseron0001"}' \
http://127.0.0.1:8151/api/v1/auth/token -w '\n[HTTP %{http_code}]\n'
{"status": 400, "error": "Invalid refresh token"}
[HTTP 400]
Invalid refresh token 说明 refresh token 是与 JWT 独立的另一份凭据(由浏览器端随会话持有),脚本场景拿不到它,也不需要它——脚本集成的正解是 PAT。该端点的价值在浏览器静默续期与自研代理层复用,参数规则本节已用实测探明。
3.4 创建个人访问令牌(PAT):POST /profile/access_tokens
PAT 是脚本集成的首选凭据:一次创建、长期有效、随 Authorization: Bearer 头走、无 Cookie/XSRF 依赖。创建需要已登录身份(JWT+Cookie 或已有一枚 PAT),body 只要一个 name:
curl -s -X POST http://127.0.0.1:8151/api/v1/profile/access_tokens \
-H "Authorization: Bearer $JWT" -b /tmp/jar.txt \
-H "Content-Type: application/json" \
-d '{"name":"api-doc-demo"}' -w '\n[HTTP %{http_code}]\n'
{"id": "6c738dac0b6944ce85e6e5ee9e37508f", "name": "api-doc-demo", "created_at": 1793327392.88088, "expires_at": null, "last_used_at": null, "last_used_by": null, "token": "vpat_349eaef4874966c72a33893935aed8c559d3caafdfe93a809723d0d9565300623c8daf0db165fef17862b5d39304be5e973c0e8dfd9ab9844c163e9fb55e1283"}
[HTTP 201]
回执解读:
| 字段 | 值 | 含义 |
|---|---|---|
| id | 6c738dac0b6944ce85e6e5ee9e37508f | 令牌 ID(32 位十六进制),撤销时用它 |
| token | vpat_349eaef4…(共 133 字符) | 令牌明文只在创建回执出现这一次,立即妥善保存 |
| expires_at | null | 永不过期(创建时可传有效期) |
| last_used_at / last_used_by | null | 使用追踪,首次调用后回填 |
同一入口也能在 Web UI 创建:登录后右上角 Profile 页 → Access Tokens → New Token,与本接口等价。
3.5 PAT 调用:无 Cookie 直连任意接口
一枚 PAT 等价于创建者本人的身份与角色(admin 的 PAT = 全权限)。不带任何 Cookie,一个头走天下:
PAT=vpat_349eaef4874966c72a33893935aed8c559d3caafdfe93a809723d0d9565300623c8daf0db165fef17862b5d39304be5e973c0e8dfd9ab9844c163e9fb55e1283
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/cameras -w '\n[HTTP %{http_code}]\n' | tail -1
[HTTP 200]
本篇后续所有命令都基于这一形态。PAT 会让服务端记录使用轨迹,GET /profile/access_tokens 可见(last_used_by 是来源 IP):
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/profile/access_tokens
{"access_tokens": [{"id": "6c738dac0b6944ce85e6e5ee9e37508f", "name": "api-doc-demo", "created_at": 1793327392.88088, "expires_at": null, "last_used_at": 1793327393.794733, "last_used_by": "10.89.61.1"}]}
撤销单枚令牌(DELETE 按 id,幂等):
curl -s -X DELETE -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/profile/access_tokens/6c738dac0b6944ce85e6e5ee9e37508f -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
登出(浏览器会话场景)用 POST /auth/logout,需要登录态 Cookie,实测回执 {"success": true};脚本场景没有会话可登出,撤销 PAT 即可。另有一条轻量端点 GET /auth/request 用于校验当前凭据是否有效(有效性探针),PAT 调用返回:
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/auth/request -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
3.6 登录限流:429 实测
POST /auth/login 挂了 rate_limit: "login"。连续用错误密码轰它,实测第 11 次起被限流(默认 10 次 / 60 秒,对应 webserver.auth.rate_limits 配置):
attempt 1: 401
attempt 2: 401
...
attempt 10: 401
attempt 11: 429
attempt 12: 429
正确密码在限流窗口内也会被 429 拒绝,生产环境不要放宽这条——它是暴力破解的第一道闸。若误触限流,等 60 秒窗口自然过去即可。
3.7 角色与用户管理:GET /auth/users
ADMIN 专用,返回全部用户。本环境只有一个引导页创建的管理员:
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/auth/users
{"users": [{"id": "11a9ee5b86994827acd8050674387884", "name": "Administrator", "username": "admin", "role": "admin", "enabled": true, "assigned_cameras": null, "preferences": null}]}
id 是该用户的固定标识(与 JWT payload 里每次都变的 iss 会话标识是两回事);assigned_cameras 支持把用户限定到部分摄像头(多用户分权场景);role 取值 admin/read/write。源码还注册了 POST /auth/create(ADMIN 建用户)与 /auth/user/{user_id} 的 GET/PUT/DELETE(查、改、删单个用户),本篇未逐一实测,语义按 REST 约定。
四、摄像头 API
4.1 摄像头清单:GET /cameras
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/cameras
{"fake_camera": {"identifier": "fake_camera", "name": "Fake Camera", "width": 768, "height": 432, "access_token": "b4d22889c4061c2cabf4895cfd0761428a154a27f9636ae741c0536a5c99e1c4378cd815ca9f906b6a35496bb2d42ffad4fd05470b059b4660e7b2ca2dd15da0", "mainstream": {"width": 768, "height": 432}, "still_image": {"refresh_interval": 10, "available": true, "width": 768, "height": 432}, "is_on": true, "connected": true, "live_stream_available": false, "is_recording": true, "ptz_support": null}}
逐字段解读(外层键是摄像头标识符):
| 字段 | 值 | 含义 |
|---|---|---|
| identifier / name | fake_camera / Fake Camera | 标识符(config.yaml 的摄像头键名)与显示名 |
| width / height | 768 / 432 | 当前取流分辨率 |
| access_token | b4d22889…(128 hex) | 摄像头专属令牌,供 snapshot/publicimage/download 免登录取图用 |
| mainstream | 768x432 | 主码流信息 |
| still_image | refresh_interval 10, available true | 静态图每 10 秒刷新、当前可取 |
| is_on / connected | true / true | 摄像头已启用且已连上 RTSP 源 |
| is_recording | true | 此刻正在录像 |
| live_stream_available | false | Web 端实时流可用性(取决于流配置,本环境未开 live) |
| ptz_support | null | PTZ 云台能力(无则为 null) |
排查摄像头接入故障用 GET /cameras/failed,正常时是空对象:
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/cameras/failed
{}
4.2 单摄像头详情与状态翻转:GET / POST /camera/{id}
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/camera/fake_camera
{"identifier": "fake_camera", "name": "Fake Camera", "width": 768, "height": 432, "access_token": "b4d22889c4061c2cabf4895cfd0761428a154a27f9636ae741c0536a5c99e1c4378cd815ca9f906b6a35496bb2d42ffad4fd05470b059b4660e7b2ca2dd15da0", "mainstream": {"width": 768, "height": 432}, "still_image": {"refresh_interval": 10, "available": true, "width": 768, "height": 432}, "is_on": true, "connected": true, "live_stream_available": false, "is_recording": true, "ptz_support": null}
POST /camera/{id}/stop 停用摄像头(断流、暂停检测,ADMIN):
curl -s -X POST -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/camera/fake_camera/stop -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
停用后立刻回查,is_on 与 connected 双双翻转:
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/camera/fake_camera \
| python3 -c "import json,sys; d=json.load(sys.stdin); print({'is_on': d['is_on'], 'connected': d['connected']})"
{'is_on': False, 'connected': False}
POST /camera/{id}/start 恢复(ADMIN):
curl -s -X POST -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/camera/fake_camera/start -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
这对接口等价于 Web UI 里摄像头卡片的暂停/恢复按钮,适合在维护摄像头网络时脚本化摘流。
4.3 抓快照:GET /camera/{id}/snapshot
该端点设计为「摄像头令牌」免登录访问(requires_camera_token),但用用户凭据(PAT)同样可调,支持 width 参数缩放:
curl -s -o /tmp/snap.jpg -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/camera/fake_camera/snapshot?width=640" \
-w '[HTTP %{http_code}] size=%{size_download} type=%{content_type}\n'
file /tmp/snap.jpg
[HTTP 200] size=84976 type=image/jpeg
/tmp/snap.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 640x360, components 3
拿到的就是当前帧 JPEG(width=640 等比缩到 640x360)。第三方看板/传感器要拉画面又不能交出用户凭据时,改用摄像头令牌:GET /camera/fake_camera/snapshot?token=<access_token>,与下面的 publicimage 同一套 token 机制。
4.4 手动录像:POST /camera/{id}/manual_recording
不走检测触发、人工立即开录(ADMIN/WRITE),body 用 action 表达开停:
curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
-d '{"action":"start","duration":5}' \
http://127.0.0.1:8151/api/v1/camera/fake_camera/manual_recording -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
duration 单位秒。录像 5 秒后自动收尾,到第六节录像列表里能看到这条 trigger_type: "manual" 的新录像(id 87)。停录用 {"action":"stop"},同样回 {"success": true}。
4.5 公开图像与文件下载:GET /publicimage、GET /download
两个「带 token 免登录取文件」的端点。先看参数错误的实测回执(校验器把正确 schema 打在错误信息里):
curl -s "http://127.0.0.1:8151/api/v1/publicimage?camera_identifier=fake_camera" -w '\n[HTTP %{http_code}]\n'
{"status": 400, "error": "Invalid request arguments: {'camera_identifier': 'fake_camera'}. extra keys not allowed @ data['camera_identifier']. Got 'fake_camera'\nrequired key not provided @ data['token']. Got None"}
/publicimage 只接受 token 参数。但该 token 不是摄像头的 access_token,而是 config.yaml 里 public_images 段为特定图像配置的发布令牌(本环境未配置,实测 404):
CT=b4d22889c4061c2cabf4895cfd0761428a154a27f9636ae741c0536a5c99e1c4378cd815ca9f906b6a35496bb2d42ffad4fd05470b059b4660e7b2ca2dd15da0
curl -s "http://127.0.0.1:8151/api/v1/publicimage?token=$CT" -w '\n[HTTP %{http_code}]\n'
{"status": 404, "error": "Token not found"}
[HTTP 404]
/download 同理——传任意 path 会被拒(要求 token):
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/download?path=/files/thumbnails/fake_camera/86.jpg" -w '\n[HTTP %{http_code}]\n'
{"status": 400, "error": "Invalid request arguments: {'path': '/files/thumbnails/fake_camera/86.jpg'}. extra keys not allowed @ data['path']. Got '/files/thumbnails/fake_camera/86.jpg'\nrequired key not provided @ data['token']. Got None"}
两者的正确姿势:thumbnails/snapshots 等文件路径在事件与录像回执里都以 /files/... 形式给出,Web UI 的下载按钮走的就是 /files/ 静态路由(浏览器直接打开即可);/download、/publicimage 是给「不能带用户凭据的外部系统」准备的 token 化出口,token 在对应配置段(public_images 等)中生成。脚本场景直接用 PAT 请求 /files/... 静态路径或 snapshot 端点即可。
五、事件 API
5.1 事件列表:GET /events/{camera}
事件是 Viseron 的核心数据流,三类事件(motion / object / recording)共用一个列表端点,date 参数按天过滤:
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/events/fake_camera?date=2026-10-30" | head -c 1600
{"events": [{"camera_identifier": "fake_camera", "type": "recording", "id": 86, "trigger_type": "object", "start_time": "2026-10-30T02:29:48.239233+00:00", "start_timestamp": 1793327388.239233, "end_time": null, "end_timestamp": null, "duration": null, "hls_url": "/api/v1/hls/fake_camera/86/index.m3u8", "thumbnail_path": "/files/thumbnails/fake_camera/86.jpg", "created_at": "2026-10-30T02:29:48.239548+00:00", "created_at_timestamp": 1793327388.239548, "lookback": 5}, {"camera_identifier": "fake_camera", "type": "object", "id": 45, "time": "2026-10-30T02:29:48.237221+00:00", "timestamp": 1793327388.237221, "label": "bicycle", "confidence": 0.785, "created_at": "2026-10-30T02:29:48.237221+00:00", "created_at_timestamp": 1793327388.237221, "snapshot_path": "/files/snapshots/object_detector/fake_camera/2026-10-30-02-29-48-b2a7a5e5-54ac-407f-b72b-5d077320efd8.jpg", "lookback": 5}, {"camera_identifier": "fake_camera", "type": "motion", "id": 34, "start_time": "2026-10-30T02:29:38.842583+00:00", "start_timestamp": 1793327378.842583, "end_time": "2026-10-30T02:29:39.666644+00:00", "end_timestamp": 1793327379.666644, "duration": 0.824061, "snapshot_path": "/files/snapshots/motion_detector/fake_camera/2026-10-30-02-29-38-e2d2843d-03d7-4b45-a90d-dca344b13ff7.jpg", "created_at": "2026-10-30T02:29:38.843261+00:00", "created_at_timestamp": 1793327378.843261, "lookback": 5}, …
(输出过长,节选前 3 条)三类事件的结构差异一目了然:
| type | 关键字段 | 含义 |
|---|---|---|
| recording | trigger_type / hls_url / thumbnail_path / duration | 一次录像,trigger_type 说明触发源(object / motion / manual),hls_url 直接可播 |
| object | label / confidence / snapshot_path | 一次物体检测命中,含标签、置信度与目标快照 |
| motion | start_time / end_time / duration / snapshot_path | 一次运动事件,含起止与运动快照 |
所有时间字段都是 ISO 字符串 + Unix 时间戳双份,snapshot_path / thumbnail_path 是可直接下载的静态路径(拼在 http://<服务器>:8888 后即可访问)。
5.2 事件计数:按天与按摄像头
单摄像头按天计数(GET 形态,date 参数指定日期):
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/events/fake_camera/amount?date=2026-10-30" -w '\n[HTTP %{http_code}]\n'
{"events_amount": {"2026-10-30": {"motion": 124, "recording": 365, "object": 208}}}
[HTTP 200]
多摄像头批量计数用 POST 形态(body 传 camera_identifiers 数组),顺带一提 URL 写错的负样本——把 amount 当摄像头名会得到 404,回执顺带确认了路由匹配规则:
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/events/amount" -w '\n[HTTP %{http_code}]\n'
{"status": 404, "error": "Camera amount not found"}
[HTTP 404]
正确的批量形态:
curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
-d '{"camera_identifiers":["fake_camera"]}' \
http://127.0.0.1:8151/api/v1/events/amount -w '\n[HTTP %{http_code}]\n'
{"events_amount": {"2026-10-30": {"motion": 25, "recording": 65, "object": 34}}}
[HTTP 200]
5.3 活跃日期:POST /events/dates_of_interest
做时间轴/日历视图时先问「哪些天有事件」,避免逐天空查:
curl -s -X POST -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
-d '{"camera_identifiers":["fake_camera"]}' \
http://127.0.0.1:8151/api/v1/events/dates_of_interest -w '\n[HTTP %{http_code}]\n'
{"dates_of_interest": {"2026-10-30": {"events": 125, "timespanAvailable": true}}}
[HTTP 200]
timespanAvailable: true 表示该日还有可回放的时间段(对应录像/HLS 的 available_timespans)。
六、录像 API
6.1 录像列表:GET /recordings、GET /recordings/{camera}
录像按「摄像头 → 日期 → 录像 ID」两级分组。单摄像头形态:
curl -s -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/recordings/fake_camera | head -c 1000
{"2026-10-30": {"87": {"id": 87, "camera_identifier": "fake_camera", "start_time": "2026-10-30T02:29:53.985351+00:00", "start_timestamp": 1793327393.985351, "end_time": "2026-10-30T02:29:59.584679+00:00", "end_timestamp": 1793327399.584679, "trigger_type": "manual", "trigger_id": null, "thumbnail_path": "/files/thumbnails/fake_camera/87.jpg", "hls_url": "/api/v1/hls/fake_camera/87/index.m3u8"}, "86": {"id": 86, "camera_identifier": "fake_camera", "start_time": "2026-10-30T02:29:48.239233+00:00", "start_timestamp": 1793327388.239233, "end_time": "2026-10-30T02:29:53.983004+00:00", "end_timestamp": 1793327393.983004, "trigger_type": "object", "trigger_id": null, "thumbnail_path": "/files/thumbnails/fake_camera/86.jpg", "hls_url": "/api/v1/hls/fake_camera/86/index.m3u8"}, …
(节选)4.4 节手动触发的录像 87 出现了,trigger_type: "manual"、时长 5.6 秒(比 duration 5 略长是收尾对齐分片的正常现象)。多摄像头清单 GET /recordings 结构相同,外层多套一层摄像头标识符。
6.2 删除录像:DELETE /recordings/{camera}/{id}
按录像 ID 删(ADMIN)。先查列表挑一条最老的,再删,再核对总数:
# 删除 id=2 的录像
curl -s -X DELETE -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/recordings/fake_camera/2 -w '\n[HTTP %{http_code}]\n'
{"success": true}
[HTTP 200]
删除前列表总数 460、删除后回查 459,删掉的就是 id=2 那条。两个实测负样本帮我们钉死路由形态:
# 带日期段删:404(该形态不存在)
curl -s -X DELETE -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/recordings/fake_camera/2026-10-30/2 -w '\n[HTTP %{http_code}]\n'
{"status": 404, "error": "Endpoint not found"}
[HTTP 404]
即删除按录像 ID寻址(/recordings/{camera}/{recording_id}),不是「日期 + ID」。源码还注册了 GET /recordings/{camera}/available_timespans(可用时间段)与 DELETE /recordings/{camera}(清空整摄像头录像,慎用)两条,前者误用时回执同样说明路由结构:
curl -s -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/recordings/available_timespans -w '\n[HTTP %{http_code}]\n'
{"status": 404, "error": "Camera available_timespans not found"}
[HTTP 404]
available_timespans 被当成了摄像头标识符——路由是 /recordings/{camera}/... 形态,必须先给摄像头名。日常批量清理优先依赖 storage 组件的保留策略(tiers 的 max_age/max_size,见《Viseron 3.7.0 配置参数详解》5.5 节),DELETE 接口适合单条纠错。
七、流媒体回放:HLS API
7.1 可回放时间段:GET /hls/{camera}/available_timespans
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/hls/fake_camera/available_timespans?date=2026-10-30" -w '\n[HTTP %{http_code}]\n'
{"timespans": [{"start": 1793326220, "end": 1793326451, "duration": 230}, {"start": 1793326482, "end": 1793327387, "duration": 904}]}
[HTTP 200]
返回该日期有连续录像的时间段(Unix 时间戳,duration 秒)——持续录像 + 事件录像拼接成的可回放区间,前端时间轴就靠它渲染。
7.2 播放列表:GET /hls/{camera}/{id}/index.m3u8
单条录像的 HLS 播放列表,id 即录像 ID:
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/hls/fake_camera/86/index.m3u8" -w '\n[HTTP %{http_code}]\n'
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MAP:URI="/files/segments/fake_camera/init.mp4"
#EXT-X-PROGRAM-DATE-TIME:2026-10-30T02:29:42.179+00:00
#EXTINF:4.992067,
/files/segments/fake_camera/1793327383.m4s
#EXT-X-PROGRAM-DATE-TIME:2026-10-30T02:29:47.171+00:00
#EXTINF:4.993,
/files/segments/fake_camera/1793327389.m4s
#EXT-X-PROGRAM-DATE-TIME:2026-10-30T02:29:52.164+00:00
#EXTINF:5.012189,
/files/segments/fake_camera/1793327394.m4s
#EXT-X-ENDLIST
[HTTP 200]
标准 fMP4-HLS:#EXT-X-MAP 指向初始化段 init.mp4,正文是 5 秒左右的 m4s 分片,分片路径挂在 /files/segments/ 静态路由下。拿 m3u8 地址喂给任意播放器即可回放:
ffplay -autoexit \
'http://127.0.0.1:8151/api/v1/hls/fake_camera/86/index.m3u8'
播放器直接出画面即回放链路打通。事件回执里的 hls_url 字段(/api/v1/hls/{camera}/{id}/index.m3u8)与本节端点一致,拿到事件就能播。
八、配置、调参与运维 API
8.1 查看生效配置:GET /config
ADMIN 专用,返回当前生效的完整 config.yaml(纯 YAML 文本,不是 JSON):
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/config | head -16
ffmpeg:
camera:
fake_camera:
name: Fake Camera
host: mediamtx
port: 8554
path: /cam
stream_format: rtsp
fps: 12
recorder:
idle_timeout: 5
mog2:
motion_detector:
cameras:
fake_camera:
fps: 2
(节选前 16 行)它与磁盘上 config.yaml 的区别:这里返回的是「服务运行时真正生效的值」——排「改了文件没生效」时以它为准。
8.2 运行时调参:GET /tune 与 PUT /tune/{camera}
/tune 是 3.7.0 的调参利器:GET /tune 汇总所有摄像头各域实际生效的配置,GET /tune/{camera} 看单摄像头(附检测器支持的完整标签清单):
curl -s -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/tune/fake_camera | head -c 700
{"camera": {"ffmpeg": {"name": "Fake Camera", "host": "mediamtx", "port": 8554, "path": "/cam", "stream_format": "rtsp", "fps": 12, "recorder": {"idle_timeout": 5}}}, "motion_detector": {"mog2": {"fps": 2}}, "object_detector": {"darknet": {"fps": 2, "scan_on_motion_only": false, "labels": [{"label": "person", "confidence": 0.7, "trigger_event_recording": true}, {"label": "bicycle", "confidence": 0.7, "trigger_event_recording": true}, {"label": "car", "confidence": 0.7, "trigger_event_recording": true}], "available_labels": ["person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", "boat", "traffic light", …
(节选)available_labels 列出 YOLOv7 权重认识的全部 80 个 COCO 标签——labels 白名单只能从这个集合里挑,调参前先查它。运行时热调参用 PUT,body 指明 domain + component + 参数键值(ADMIN):
curl -s -X PUT -H "Authorization: Bearer $PAT" -H "Content-Type: application/json" \
-d '{"domain":"motion_detector","component":"mog2","fps":2}' \
http://127.0.0.1:8151/api/v1/tune/fake_camera -w '\n[HTTP %{http_code}]\n'
{"message": "Configuration updated successfully"}
[HTTP 200]
PUT 完立即 GET /tune/fake_camera 可见 mog2.fps: 2 生效。缺 domain/component 的 body 会被拒(回执即文档):
{"status": 400, "error": "Missing 'domain' or 'component' in request"}
/tune 的热更新不落盘——重启后仍以 config.yaml 为准,适合「先在线试参数、试好了再写回文件」的工作流。
8.3 日志:GET /logger/logs、GET /logger/config
curl -s -H "Authorization: Bearer $PAT" \
"http://127.0.0.1:8151/api/v1/logger/logs?lines=12" | head -c 700
{"logs": [{"id": "2026-10-30 10:29:45.579_info_viseron.components.nvr.nvr.fake_camera_11", "timestamp": "2026-10-30 10:29:45.579", "timestamp_unix_ms": 1793327385579, "level": "info", "name": "viseron.components.nvr.nvr.fake_camera", "message": "Pausing motion detector", "raw": "2026-10-30 10:29:45.579 [INFO ] [viseron.components.nvr.nvr.fake_camera] - Pausing motion detector"}, {"id": "2026-10-30 10:29:45.580_info_viseron.components.ffmpeg.recorder.fake_camera_10", "timestamp": "2026-10-30 10:29:45.580", "timestamp_unix_ms": 1793327385580, "level": "info", "name": "viseron.components.ffmpeg.recorder.fake_camera", "message": "Stopping recorder", "raw": "2026-10-30 10:29:45.580 [INFO ] [viseron.components.ffmpeg.recorder.fake_camera] - Stopping recorder"}, …
(节选)lines 控制条数,每条含结构化字段(timestamp/level/name/message)与 raw 原始行,接日志告警时用结构化字段,人看用 raw。当前日志配置回执:
curl -s -H "Authorization: Bearer $PAT" http://127.0.0.1:8151/api/v1/logger/config
{"default_level": "info", "logs": {}, "cameras": {}}
8.4 事件总线快照:GET /system/dispatched_events
ADMIN 专用,返回系统事件总线派发过的全部事件名,组件与域的装载状态一眼可查:
curl -s -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/system/dispatched_events | head -c 900
{"events": ["component/setup/loaded/darknet", "component/setup/loaded/data_stream", "component/setup/loaded/ffmpeg", "component/setup/loaded/logger", "component/setup/loaded/mog2", "component/setup/loaded/nvr", "component/setup/loaded/storage", "component/setup/loaded/webserver", "domain/registered/camera", "domain/registered/motion_detector", "domain/registered/nvr", "domain/registered/object_detector", "domain/setup/loaded/camera/fake_camera", "domain/setup/loaded/motion_detector/fake_camera", "domain/setup/loaded/nvr/fake_camera", "domain/setup/loaded/object_detector/fake_camera", "domain/setup/loading/camera/fake_camera", "domain/setup/loading/motion_detector/fake_camera", "domain/setup/loading/nvr/fake_camera", "domain/setup/loading/object_detector/fake_camera", "entity_added", "fake_camera/camera/frame_bytes", "fake_camera/camera/started", "fake_camera/camera/status", …
(节选)事件名三段式 对象/动作/目标:component/setup/loaded/<组件> 是组件装载成功,domain/... 是域注册与逐摄像头装载,fake_camera/... 是摄像头级运行事件。排障时比对这份清单能立刻发现哪个组件/域没有 loaded。
8.5 杂项端点:时区、初始化、人脸识别
GET /profile/available_timezones 返回全部合法时区(配置 preferences 时用,实测列表含 Africa/Abidjan…Asia/Shanghai… 数百项,节选):
{"timezones": ["Africa/Abidjan", "Africa/Accra", …, "Asia/Shanghai", …]}
POST /onboarding 是首次启动创建管理员的入口(免认证 + onboarding 限流);本环境 onboarding_complete: true,重复调用无意义。GET /compreface/update_subjects 服务于 compreface 人脸识别组件的注册表刷新,未部署该组件时的实测回执:
curl -s -H "Authorization: Bearer $PAT" \
http://127.0.0.1:8151/api/v1/compreface/update_subjects -w '\n[HTTP %{http_code}]\n'
{"status": 400, "error": "Compreface Recognition not initialized."}
[HTTP 400]
九、MQTT 集成
除 REST 外,Viseron 可启用 mqtt 组件把状态实时推给外部系统(Home Assistant 等)。本环境部署时已配置 mqtt 指向同网络的 Mosquitto,用 mosquitto_sub 抓取主题树(-v 显示主题名,image 主题是二进制会刷屏,过滤掉):
mosquitto_sub -h <MQTT服务器> -t 'viseron/#' -v -C 12 \
| grep -v '^viseron/image/'
实测抓包(消息 = 主题 + 载荷原文):
viseron/lwt alive
viseron/state online
viseron/binary_sensor/fake_camera_connected/state {"state": "on", "attributes": {"name": "Fake Camera Connected", "domain": "binary_sensor"}}
viseron/binary_sensor/fake_camera_still_image_available/state {"state": "on", "attributes": {"name": "Fake Camera Still Image Available", "domain": "binary_sensor"}}
viseron/binary_sensor/fake_camera_recorder/state {"state": "off", "attributes": {"name": "Fake Camera Recorder", "domain": "binary_sensor", "id": 301, "start_time": "2026-10-30T03:27:07.109407+00:00", "start_timestamp": 1793330827.109407, "end_time": "2026-10-30T03:27:19.979850+00:00", "end_timestamp": 1793330839.97985, "date": "2026-10-30", "thumbnail_path": "/thumbnails/fake_camera/301.jpg", "objects": [], "trigger_type": "motion"}}
viseron/binary_sensor/fake_camera_motion_detected/state {"state": "off", "attributes": {"name": "Fake Camera Motion Detected", "domain": "binary_sensor"}}
viseron/binary_sensor/fake_camera_object_detected_person/state {"state": "off", "attributes": {"name": "Fake Camera Object Detected Person", "domain": "binary_sensor", "count": 0, "objects": []}}
viseron/binary_sensor/fake_camera_object_detected_bicycle/state {"state": "off", "attributes": {"name": "Fake Camera Object Detected Bicycle", "domain": "binary_sensor", "count": 0, "objects": []}}
viseron/binary_sensor/fake_camera_object_detected_car/state {"state": "off", "attributes": {"name": "Fake Camera Object Detected Car", "domain": "binary_sensor", "count": 0, "objects": []}}
viseron/binary_sensor/fake_camera_object_detected/state {"state": "off", "attributes": {"name": "Fake Camera Object Detected", "domain": "binary_sensor", "count": 0, "objects": []}}
viseron/toggle/fake_camera_connection/state {"state": "on", "attributes": {"name": "Fake Camera Connection", "domain": "toggle"}}
viseron/toggle/fake_camera_manual_recording/state {"state": "off", "attributes": {"name": "Fake Camera Manual Recording", "domain": "toggle"}}
主题树与用途:
| 主题 | 载荷 | 用途 |
|---|---|---|
| viseron/lwt | alive | 遗嘱主题,掉线时变 offline,用于存活监测 |
| viseron/state | online / offline | 服务级状态 |
| viseron/binary_sensor/{camera}_connected/state | JSON:state + attributes | 摄像头连接状态 |
| viseron/binary_sensor/{camera}_motion_detected/state | JSON | 运动检测布尔量 |
| viseron/binary_sensor/{camera}_object_detected[_label]/state | JSON,attributes 含 count / objects | 物体检测布尔量(总开关 + 每标签一枚) |
| viseron/binary_sensor/{camera}_recorder/state | JSON,attributes 含 id / start_time / trigger_type / thumbnail_path | 录像机状态,一次录像起止都推 |
| viseron/toggle/{camera}_connection/state | JSON | 远程启停摄像头(写回该主题即控制) |
| viseron/toggle/{camera}_manual_recording/state | JSON | 远程手动录像开关 |
| viseron/image/{camera}_latest_thumbnail/image | JPEG 二进制 | 最新缩略图原图推送 |
载荷统一是 {state, attributes} JSON,attributes 把 REST 里的事件字段(objects、trigger_type、thumbnail_path)一并带上,Home Assistant 的 MQTT discovery 可直接消化这些 entity。对接 HA 时把 home_assistant.discovery_prefix 配置好,实体自动出现(配置见《Viseron 3.7.0 配置参数详解》5.6 节)。注意 image 主题是原始 JPEG 二进制,文本终端订阅会显示乱码——订阅时避开(如 mosquitto_sub -t 'viseron/binary_sensor/#' -v)或用支持二进制的客户端落盘查看。
十、常见问题
Q1:脚本调 API 总是 401?
按序检查:① 是否带了 Authorization: Bearer <token> 头;② token 是不是完整的 vpat_ 开头(创建回执里的整串);③ 如果用的是 JWT——纯 Bearer JWT 不带 Cookie 必 401(3.2 节实测),脚本场景改用 PAT;④ 凭据被撤销或过期也会 401,用 GET /auth/request 快速探活。
Q2:登录返回 429? 登录限流 10 次 / 60 秒(3.6 节实测第 11 次起 429)。等待窗口过去,且排查为什么在连续登录失败——错误密码风暴本身往往说明上游有自动化在试。
Q3:POST 报 Invalid body: … required key not provided?
Viseron 的参数校验器(voluptuous)会把期望 schema 打进错误信息,照着补字段即可。如 /auth/token 要求 grant_type(且只接受 refresh_token)+ client_id,/tune/{camera} 的 PUT 要求 domain + component。这类 400 回执是最可靠的接口文档。
Q4:/events/amount、/recordings/available_timespans 返回 404 "Camera amount not found"?
路由是 /events/{camera_identifier}/amount、/recordings/{camera_identifier}/... 形态——你把固定词写进了摄像头位(5.2、6.2 节实测负样本)。补上真实摄像头标识符即可。
Q5:删除录像 404 "Endpoint not found"?
删除按录像 ID 寻址:DELETE /recordings/{camera}/{id}。带日期段(/recordings/{camera}/{date}/{id})不是合法路由(6.2 节实测)。
Q6:改了 config.yaml,GET /config 里没变化?
GET /config 运行时生效值。配置只在启动时加载,重启服务后 GET /config 才会变;想不重启临时调参用 PUT /tune/{camera}(8.2 节实测,不落盘)。
Q7:MQTT 抓包 image 主题全是乱码? 那是原始 JPEG 二进制(第九节),不是故障。订阅时排除 image 主题或用二进制客户端。
Q8:Web UI 上传的文件(快照/缩略图/录像分片)怎么直接下载?
事件与录像回执里的 /files/... 路径是静态路由,拼在 http://<服务器>:8888 后浏览器直接打开;脚本用 PAT 请求同一路径即可。/download 与 /publicimage 是给无凭据外部系统的 token 化出口(4.5 节)。
十一、参考链接
- Viseron 官方文档:https://viseron.netlify.app/docs/documentation/configuration
- Viseron 仓库(webserver API 源码:components/webserver/api/v1/):https://github.com/roflcoopter/viseron
- MQTT 组件文档:https://viseron.netlify.app/docs/components/mqtt
- v3.7.0 发布说明:https://github.com/roflcoopter/viseron/releases/tag/v3.7.0
- 本专栏姊妹篇:《Viseron 3.7.0 介绍与选型指南》 | 《Viseron 3.7.0 Docker Compose 部署指南》 | 《Viseron 3.7.0 配置参数详解》