Install
openclaw skills install @jftech/jf-open-pro-watchdogopenclaw skills install @jftech/jf-open-pro-watchdog通过杰峰监控设备云抓图 + Agent 视觉分析,对监控画面中指定区域进行状态监控:
Agent 先通过视觉分析自动识别画面中的物品和区域,以文字交互方式让用户选择监控目标;也可通过交互式 Widget 手动框选精确区域。系统后续通过抓图对比分析区域变化,发现异常时输出报告。
看门狗 / 看门狗巡检 / 监控巡检 / 检查一下 / 巡逻 / 巡逻一次 / 添加监控 / 配置看门狗 / 设置监控区域 / 查看监控 / 定时巡检 / 自动巡检
| 依赖技能 | 用途 | 是否必需 |
|---|---|---|
jf-open-pro-device-list | 添加监控设备时查询已绑定设备列表 | 是(添加设备时) |
jf-open-pro-device-status | 添加监控设备时验证设备在线状态 | 可选 |
抓图、下载和裁剪由本技能自带的 scripts/watchdog.py 完成。
| 变量名 | 说明 | 默认值 | 必需 |
|---|---|---|---|
JF_UUID | 开放平台用户 uuid | - | 是 |
JF_APP_KEY | 开放平台应用 appKey | - | 是 |
JF_APP_SECRET | 开放平台应用密钥 | - | 是 |
JF_MOVE_CARD | 移动卡标识(用于签名) | 2 | 是 |
JF_ENDPOINT | API 接入地址 | api-cn.jftechws.com | 否 |
pip install requests Pillow
Pillow 用于图片裁剪功能。
config/monitors.json 存储所有监控配置:
{
"monitors": [
{
"sn": "设备序列号",
"name": "展厅A",
"password": "设备密码",
"location": "一楼展厅",
"channel": 0,
"regions": [
{
"id": "region_1",
"name": "灭火器位置",
"x": 100,
"y": 200,
"width": 300,
"height": 400,
"rule": "此区域应该始终有一个红色灭火器",
"alert_when": "missing",
"baseline": {
"image": "baselines/xxx_region_1_baseline.png",
"description": "画面中有一个红色灭火器,放置在墙角白色区域",
"captured_at": "2026-06-05T14:30:00+08:00"
}
}
]
}
],
"settings": {
"default_channel": 0,
"sensitivity": "moderate"
}
}
monitors 数组 — 每台监控设备:
| 字段 | 类型 | 说明 |
|---|---|---|
sn | string | 杰峰设备序列号(唯一标识) |
name | string | 用户定义的设备显示名称 |
password | string | 设备密码(用于自动获取 Token) |
location | string | 位置描述 |
channel | int | 抓图通道号(默认 0) |
regions | array | 该设备下的监控区域列表 |
regions 数组 — 每个监控区域:
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 区域唯一标识(自动生成,格式 region_{N},N 为自增序号) |
name | string | 区域名称(用户定义,如"灭火器位置") |
x | int | 矩形左上角 X 坐标(像素坐标) |
y | int | 矩形左上角 Y 坐标(像素坐标) |
width | int | 矩形宽度(像素) |
height | int | 矩形高度(像素) |
rule | string | 自然语言规则描述 |
alert_when | string | 报警触发条件:missing(应有物品消失)/ appeared(不应有物品出现)/ changed(状态发生变化)。Agent 以规则文本为主要判断依据,alert_when 作为辅助提示 |
baseline.image | string | 基线裁剪图的相对路径 |
baseline.description | string | Agent 生成的基线状态文字描述 |
baseline.captured_at | string | 基线采集时间(ISO 8601) |
settings 对象:
| 字段 | 类型 | 说明 |
|---|---|---|
default_channel | int | 默认抓图通道号 |
sensitivity | string | 分析敏感度:low / moderate / high |
当区域选择完成后(无论是 Agent 智能识别还是 Widget 手动框选):
baselines/{sn}_{regionId}_baseline.pngmonitors.json触发: 用户说"看门狗"、"看门狗怎么用"、"配置监控",或 Agent 判断用户是首次使用。
流程:
Agent 先静默检查环境变量(JF_UUID, JF_APP_KEY, JF_APP_SECRET)和 config/monitors.json 状态。
config/monitors.json,检查 monitors 列表是否为空。JF_UUID、JF_APP_KEY、JF_APP_SECRET 是否已设置(通过 echo %JF_UUID% 或 Bash 命令)。情况 A — 全新状态(无监控点、无环境变量):
你好!我来帮你搭建看门狗区域监控系统。
这个系统能做什么:
- 通过杰峰摄像头抓图,用 AI 分析指定区域的状态变化
- 支持物品防盗、通道占用检测、人员存在检测等场景
- 自动输出巡检报告,标注异常区域
- 支持定时自动巡检,结果推送到 IM 频道
搭建只需要 2 步:
1. 配置环境变量(JF_UUID, JF_APP_KEY 等)
2. 添加监控设备和区域
我们先从配置环境变量开始,好吗?说"开始"就行。
情况 B — 有环境变量,无监控点:
环境变量已就绪!接下来需要添加监控设备。
说"添加监控",我会帮你从已绑定的设备中选择并配置监控区域。
情况 C — 有监控点,就绪状态:
一切就绪!当前配置:
监控设备:{列出设备名称}
监控区域:{列出区域数量}
敏感度:{sensitivity}
现在可以:
- "巡逻一次" — 立即对所有监控区域抓图分析
- "检查 展厅A" — 只检查指定设备
- "查看监控" — 管理监控配置
- "定时巡检" — 设置自动巡检
需要做什么?
注意事项:
触发: 用户说"添加监控"、"配置看门狗"、"设置监控区域"
流程:
使用 jf-open-pro-device-list 技能查询可用设备列表。
展示设备列表让用户选择(显示 SN 和昵称)。询问用户:给设备起个名字(如"展厅A")、位置描述(如"一楼展厅")。
运行抓图脚本(设置 PYTHONIOENCODING=utf-8):
PYTHONIOENCODING=utf-8 python {skill_dir}/scripts/watchdog.py \
--action capture-single \
--device-sn {SN} \
--device-name {名称} \
--password {密码} \
--output-dir {workspace}/captures/ \
--json
脚本输出 JSON 包含抓图 URL 和本地下载路径。
Agent 使用 Read 工具查看抓图(本地路径),对画面进行全面视觉分析:
Agent 将识别结果以文字交互的方式呈现给用户:
我在画面中识别到以下可监控的目标:
① {物品名称} — {位置描述},{当前状态描述}
建议规则:「{规则描述}」
报警类型:{missing/appeared/changed}
② {物品名称} — {位置描述},{当前状态描述}
建议规则:「{规则描述}」
报警类型:{missing/appeared/changed}
③ ...
你可以:
- 选择要监控的区域(多选)
- 说"用交互页面自定义"打开 Widget 手动框选精确区域
- 说"全部都要"监控所有识别到的区域
使用 AskUserQuestion 工具让用户多选要监控的区域(multiSelect: true),同时提供"用交互页面自定义"选项。
如果用户选择"用交互页面自定义": 跳转到步骤 5b(Widget 模式)。
如果用户选择了具体区域: 进入步骤 6。
如果用户选择使用 Widget 自定义,或 Agent 无法准确识别画面内容时:
使用 show_widget 渲染交互式区域配置界面:
show_widget(
widget_path='jf-watchdog/assets/watchdog-config-widget.html',
data={
"imageUrl": "抓图URL",
"localPath": "本地抓图路径",
"deviceSn": "设备SN",
"deviceName": "设备名称",
"imageWidth": 1920,
"imageHeight": 1080,
"existingRegions": []
}
)
如果是编辑已有设备,将已有区域数据传入 existingRegions 供 Widget 回显。
用户在 Widget 中拖拽绘制矩形、设置名称和规则,点击「生成配置」输出 JSON。
文字交互模式: 根据用户选择的区域和 Agent 在步骤 4 中估计的坐标,构建区域配置 JSON。Agent 需要对每个选中区域精确估算矩形坐标(x, y, width, height),确保覆盖目标物品/区域。
Widget 模式: 直接使用 Widget 输出的 JSON。
区域配置 JSON 格式:
{
"device_sn": "xxx",
"device_name": "展厅A",
"source_image": "本地抓图路径",
"regions": [
{
"id": "region_1",
"name": "灭火器位置",
"x": 100, "y": 200, "width": 300, "height": 400,
"rule": "此区域应该始终有一个红色灭火器",
"alert_when": "missing"
}
],
"image_size": { "width": 1920, "height": 1080 }
}
对每个区域,运行基线裁剪:
PYTHONIOENCODING=utf-8 python {skill_dir}/scripts/watchdog.py \
--action crop-baselines \
--source-image {抓图本地路径} \
--device-sn {SN} \
--regions-json '{区域配置JSON}' \
--output-dir {skill_dir}/baselines/ \
--json
裁剪图保存到 baselines/{sn}_{regionId}_baseline.png。
对每个区域的基线裁剪图,Agent 使用 Read 工具查看图片,生成文字描述:
将完整配置写入 config/monitors.json,包含基线信息:
{
"sn": "设备SN",
"name": "展厅A",
"password": "设备密码",
"location": "一楼展厅",
"channel": 0,
"regions": [
{
"id": "region_1",
"name": "灭火器位置",
"x": 100, "y": 200, "width": 300, "height": 400,
"rule": "此区域应该始终有一个红色灭火器",
"alert_when": "missing",
"baseline": {
"image": "baselines/{sn}_region_1_baseline.png",
"description": "Agent生成的基线描述",
"captured_at": "ISO 8601 时间"
}
}
]
}
如果 monitors 列表中已有该设备,更新其 regions;如果是新设备,追加到列表中。
查看监控:
读取 monitors.json,展示所有监控设备和区域的摘要信息。
删除监控:
从 monitors.json 中移除对应设备或区域,删除对应的基线图片文件。
触发: 用户说"看门狗巡检"、"检查一下"、"巡逻"、"巡逻一次"
流程:
读取 config/monitors.json。如果用户指定了特定设备(如"检查 展厅A"),只巡检匹配的设备;否则巡检全部。
运行抓图脚本(设置 PYTHONIOENCODING=utf-8):
PYTHONIOENCODING=utf-8 python {skill_dir}/scripts/watchdog.py \
--action capture \
--config {skill_dir}/config/monitors.json \
--output-dir {workspace}/captures/{YYYYMMDD}/ \
--json
脚本输出 JSON 格式的结果(设备 SN、本地路径、是否成功等)。
对每台成功抓图的设备,运行区域裁剪:
PYTHONIOENCODING=utf-8 python {skill_dir}/scripts/watchdog.py \
--action crop-regions \
--config {skill_dir}/config/monitors.json \
--source-image {抓图本地路径} \
--device-sn {SN} \
--output-dir {workspace}/crops/{YYYYMMDD}/ \
--json
脚本输出每个区域的裁剪图路径。
对每个监控区域,Agent 同时读取三样内容:
baseline.image 指向的文件rule 和 baseline.description分析维度:
基线对比: 基线中描述的关键物品/状态在当前画面中是否仍然存在
规则检测: 当前画面是否出现规则中描述的异常情况
报警条件: 结合 alert_when 标签判断触发条件
missing:应有物品是否消失appeared:不应有的物品是否出现changed:状态是否发生变化敏感度调节(根据 sensitivity 设置):
low:只有明显变化才报异常moderate(默认):中等变化也报high:细微变化也报附加观察:
每个区域输出结论:正常 / 异常 / 无法判断,以及具体分析描述。
在完整抓图上为每个监控区域绘制矩形框和状态标签,生成带标注的巡检图片:
Agent 使用 Python PIL/Pillow 编写内联脚本完成标注:
ImageDraw.rectangle 按坐标 (x, y, x+width, y+height) 绘制矩形框(width=3){workspace}/crops/{YYYYMMDD}/patrol_annotated.png将所有区域的检测结论汇总,按以下格式输出:
🐕 看门狗巡检报告
━━━━━━━━━━━━━━━━━━━
巡检时间:YYYY-MM-DD HH:mm:ss
监控设备:N 台 | 监控区域:X 个 | 正常:Y 个 | 异常:Z 个
🔴 {设备名称} > {区域名称} (sn: {SN})
状态:异常 — {异常类型}
规则:{规则描述}
分析:{具体分析描述}
🟢 {设备名称} > {区域名称} (sn: {SN})
状态:正常
分析:{具体分析描述}
⚫ {设备名称} > {区域名称} (sn: {SN})
状态:无法判断
原因:{无法判断的原因}
━━━━━━━━━━━━━━━━━━━
汇总:异常 A 个 / 正常 B 个 / 无法判断 C 个
建议关注:{异常区域列表}
排列顺序: 异常 → 正常 → 无法判断(按严重程度降序)。
失败的设备和离线的设备也需在报告中标注,用"离线"或具体错误原因标注。
展示标注图片: 输出文字报告后,必须使用 present_files 工具将步骤 5 生成的标注巡检图片展示给用户,让用户在完整画面中看到每个监控区域的位置和状态。如有异常区域,额外展示基线裁剪图与当前裁剪图的对比。示例:
present_files([
{"file_path": "标注巡检图片路径(完整画面+红/绿框)"},
{"file_path": "异常区域基线裁剪图"},
{"file_path": "异常区域当前裁剪图"}
])
触发: 用户说"定时巡检"、"每天巡检"、"自动巡检"
流程:
使用 AskUserQuestion 工具确认:
生成以下自包含的巡检指令文本,其中所有变量替换为实际值:
执行以下看门狗巡检任务:
技能目录: {skill_dir}
环境变量: JF_UUID={uuid}, JF_APP_KEY={key}, JF_APP_SECRET={secret}, JF_MOVE_CARD={card}
步骤 1 — 抓图:
设置 PYTHONIOENCODING=utf-8
python {skill_dir}/scripts/watchdog.py --action capture --config {skill_dir}/config/monitors.json --output-dir {data_dir}/captures/{YYYYMMDD}/ --json
步骤 2 — 裁剪:
对每台成功抓图的设备运行:
设置 PYTHONIOENCODING=utf-8
python {skill_dir}/scripts/watchdog.py --action crop-regions --config {skill_dir}/config/monitors.json --source-image {抓图路径} --device-sn {SN} --output-dir {data_dir}/crops/{YYYYMMDD}/ --json
步骤 3 — 分析:
对每个监控区域,使用 Read 工具读取基线图片和当前裁剪图,结合规则描述和基线描述判断状态。
判断标准:基线中应有物品是否仍在、是否出现异常物品、画面状态是否符合规则。
每个区域输出结论:正常 / 异常 / 无法判断。
步骤 4 — 标注图片:
在完整抓图上用 PIL/Pillow 为每个监控区域绘制矩形框和状态标签(正常=绿色,异常=红色),保存标注图。
步骤 5 — 报告:
汇总巡检结果,输出看门狗巡检报告,展示标注图片。如有 IM 频道可用,将报告推送给相关人员。
根据当前运行环境的定时任务能力(如 cron 任务、调度系统、外部工具等)配置自动执行。
完整的巡检报告格式如下:
🐕 看门狗巡检报告
━━━━━━━━━━━━━━━━━━━
巡检时间:YYYY-MM-DD HH:mm:ss
监控设备:N 台 | 监控区域:X 个 | 正常:Y 个 | 异常:Z 个
--- 异常 ---
🔴 {设备名称} > {区域名称} (sn: {SN})
状态:异常 — {missing/appeared/changed}
规则:{规则描述}
基线:{基线描述}
分析:{具体分析,对比基线和当前画面的差异}
--- 正常 ---
🟢 {设备名称} > {区域名称} (sn: {SN})
状态:正常
分析:{具体分析,确认与基线一致}
--- 无法判断 ---
⚫ {设备名称} > {区域名称} (sn: {SN})
状态:无法判断
原因:{无法判断的原因,如图片模糊、光线不足、角度变化等}
--- 设备异常 ---
⚫ {设备名称} (sn: {SN})
状态:抓图失败
原因:{离线 / API 错误 / 下载失败等}
━━━━━━━━━━━━━━━━━━━
汇总:异常 A 个 / 正常 B 个 / 无法判断 C 个 / 设备异常 D 台
建议关注:{异常区域列表,如有}
报告必须附带标注图片: 使用 present_files 展示完整画面上标注了各区域矩形框和状态的巡检图片。异常区域额外展示基线裁剪图与当前裁剪图的对比。
| 场景 | 处理方式 |
|---|---|
| 摄像头离线 | 跳过该设备,报告中标记"离线",计入失败 |
| 抓图 API 错误 | 跳过该设备,报告中标记失败原因 |
| 图片下载失败 | 脚本自动重试一次,仍失败标记"下载失败" |
| 区域裁剪失败 | 跳过该区域,报告中标记"裁剪失败" |
| Agent 无法判断 | 标记"无法判断",说明原因 |
| 环境变量缺失 | 提示用户配置所需环境变量 |
| 配置文件为空 | 引导用户先添加监控设备 |
| 基线图片丢失 | 提示用户重新配置该区域的基线 |
| 脚本 | 用途 | 调用方式 |
|---|---|---|
scripts/watchdog.py | 抓图 + 下载 + 裁剪 | CLI: --action {capture,capture-single,crop-regions,crop-baselines} --config --output-dir --json |
scripts/crypto.py | 签名加密工具 | 被 watchdog.py 导入 |
各 action 详细说明:
| action | 说明 | 必需参数 |
|---|---|---|
capture | 批量抓图(从 monitors.json 读取设备列表) | --config, --output-dir |
capture-single | 单设备抓图(配置阶段用) | --device-sn, --device-name, --password, --output-dir |
crop-regions | 区域裁剪(巡检时从抓图裁剪各区域) | --config, --source-image, --device-sn, --output-dir |
crop-baselines | 基线裁剪(配置阶段从抓图裁剪基线) | --source-image, --device-sn, --regions-json, --output-dir |
所有 action 均支持 --json 参数输出 JSON 格式结果。
| 文件 | 说明 |
|---|---|
SKILL.md | 技能文档 |
scripts/watchdog.py | 抓图 + 下载 + 裁剪脚本 |
scripts/crypto.py | 签名/时间戳加密工具 |
assets/watchdog-config-widget.html | 交互式区域配置 Widget |
config/monitors.json | 监控配置文件 |
baselines/ | 基线图片存储目录 |