目录
-
- 摘要
- [1. 引言 - 通知推送的价值](#1. 引言 - 通知推送的价值)
-
- [1.1 为什么需要通知推送?](#1.1 为什么需要通知推送?)
- [1.2 OpenClaw 通知系统架构](#1.2 OpenClaw 通知系统架构)
- [2. 通知基础操作](#2. 通知基础操作)
-
- [2.1 发送简单通知](#2.1 发送简单通知)
- [2.2 发送带声音的通知](#2.2 发送带声音的通知)
- [2.3 发送静默通知](#2.3 发送静默通知)
- [3. 通知参数详解](#3. 通知参数详解)
-
- [3.1 优先级参数(priority)](#3.1 优先级参数(priority))
- [3.2 投递方式(delivery)](#3.2 投递方式(delivery))
- [3.3 完整参数示例](#3.3 完整参数示例)
- [4. 实战案例](#4. 实战案例)
-
- [4.1 会议提醒系统](#4.1 会议提醒系统)
- [4.2 安全警报系统](#4.2 安全警报系统)
- [4.3 任务完成通知](#4.3 任务完成通知)
- [5. 高级功能](#5. 高级功能)
-
- [5.1 批量通知发送](#5.1 批量通知发送)
- [5.2 通知历史记录](#5.2 通知历史记录)
- [6. 最佳实践](#6. 最佳实践)
-
- [6.1 通知设计原则](#6.1 通知设计原则)
- [6.2 常见问题与解决方案](#6.2 常见问题与解决方案)
- [6.3 性能优化](#6.3 性能优化)
- [7. 总结](#7. 总结)
-
- [7.1 核心要点回顾](#7.1 核心要点回顾)
- [7.2 下一步学习](#7.2 下一步学习)
- 参考资料
摘要
本文深入探讨 OpenClaw Nodes 的通知推送功能,从基础概念到高级应用,全面解析如何通过 OpenClaw 向配对设备发送各类通知。文章涵盖通知类型、优先级控制、投递方式、实战案例等核心内容,帮助开发者构建完善的设备通知系统。通过丰富的代码示例和实际应用场景,让读者掌握通知推送的最佳实践。🔔
1. 引言 - 通知推送的价值
1.1 为什么需要通知推送?
在现代智能应用中,及时的通知推送是连接用户与系统的关键桥梁。想象一下这些场景:
| 场景 | 需求 | 解决方案 |
|---|---|---|
| 紧急告警 | 服务器宕机需要立即通知运维 | 高优先级推送 |
| 日程提醒 | 会议前10分钟提醒用户 | 定时通知 |
| 状态更新 | 任务完成后通知相关人员 | 状态通知 |
| 安全验证 | 异地登录需要确认 | 交互式通知 |
1.2 OpenClaw 通知系统架构
#mermaid-svg-06x24GVp8J8IkJ3k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-06x24GVp8J8IkJ3k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-06x24GVp8J8IkJ3k .error-icon{fill:#552222;}#mermaid-svg-06x24GVp8J8IkJ3k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-06x24GVp8J8IkJ3k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-06x24GVp8J8IkJ3k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-06x24GVp8J8IkJ3k .marker.cross{stroke:#333333;}#mermaid-svg-06x24GVp8J8IkJ3k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-06x24GVp8J8IkJ3k p{margin:0;}#mermaid-svg-06x24GVp8J8IkJ3k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-06x24GVp8J8IkJ3k .cluster-label text{fill:#333;}#mermaid-svg-06x24GVp8J8IkJ3k .cluster-label span{color:#333;}#mermaid-svg-06x24GVp8J8IkJ3k .cluster-label span p{background-color:transparent;}#mermaid-svg-06x24GVp8J8IkJ3k .label text,#mermaid-svg-06x24GVp8J8IkJ3k span{fill:#333;color:#333;}#mermaid-svg-06x24GVp8J8IkJ3k .node rect,#mermaid-svg-06x24GVp8J8IkJ3k .node circle,#mermaid-svg-06x24GVp8J8IkJ3k .node ellipse,#mermaid-svg-06x24GVp8J8IkJ3k .node polygon,#mermaid-svg-06x24GVp8J8IkJ3k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-06x24GVp8J8IkJ3k .rough-node .label text,#mermaid-svg-06x24GVp8J8IkJ3k .node .label text,#mermaid-svg-06x24GVp8J8IkJ3k .image-shape .label,#mermaid-svg-06x24GVp8J8IkJ3k .icon-shape .label{text-anchor:middle;}#mermaid-svg-06x24GVp8J8IkJ3k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-06x24GVp8J8IkJ3k .rough-node .label,#mermaid-svg-06x24GVp8J8IkJ3k .node .label,#mermaid-svg-06x24GVp8J8IkJ3k .image-shape .label,#mermaid-svg-06x24GVp8J8IkJ3k .icon-shape .label{text-align:center;}#mermaid-svg-06x24GVp8J8IkJ3k .node.clickable{cursor:pointer;}#mermaid-svg-06x24GVp8J8IkJ3k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-06x24GVp8J8IkJ3k .arrowheadPath{fill:#333333;}#mermaid-svg-06x24GVp8J8IkJ3k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-06x24GVp8J8IkJ3k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-06x24GVp8J8IkJ3k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-06x24GVp8J8IkJ3k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-06x24GVp8J8IkJ3k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-06x24GVp8J8IkJ3k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-06x24GVp8J8IkJ3k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-06x24GVp8J8IkJ3k .cluster text{fill:#333;}#mermaid-svg-06x24GVp8J8IkJ3k .cluster span{color:#333;}#mermaid-svg-06x24GVp8J8IkJ3k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-06x24GVp8J8IkJ3k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-06x24GVp8J8IkJ3k rect.text{fill:none;stroke-width:0;}#mermaid-svg-06x24GVp8J8IkJ3k .icon-shape,#mermaid-svg-06x24GVp8J8IkJ3k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-06x24GVp8J8IkJ3k .icon-shape p,#mermaid-svg-06x24GVp8J8IkJ3k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-06x24GVp8J8IkJ3k .icon-shape .label rect,#mermaid-svg-06x24GVp8J8IkJ3k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-06x24GVp8J8IkJ3k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-06x24GVp8J8IkJ3k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-06x24GVp8J8IkJ3k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} passive
active
timeSensitive
AI Agent
Nodes Tool
优先级判断
静默推送
普通通知
紧急通知
设备通知中心
用户设备
2. 通知基础操作
2.1 发送简单通知
最基本的通知发送方式:
python
# 基础通知
nodes(
action="notify",
node="node_001",
title="提醒",
body="会议将在10分钟后开始"
)
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| action | string | 是 | 固定值 "notify" |
| node | string | 是 | 设备节点ID |
| title | string | 是 | 通知标题 |
| body | string | 是 | 通知内容 |
2.2 发送带声音的通知
为重要通知添加提示音:
python
# 带声音的通知
nodes(
action="notify",
node="node_001",
title="重要提醒",
body="请检查您的任务",
sound="default" # 使用系统默认提示音
)
sound 参数选项:
| 值 | 说明 |
|---|---|
| "default" | 系统默认提示音 |
| "alarm" | 警报声 |
| "none" | 静音 |
2.3 发送静默通知
后台更新类通知不需要打扰用户:
python
# 静默通知 - 不显示在通知栏
nodes(
action="notify",
node="node_001",
title="后台更新",
body="数据已同步",
delivery="system" # 仅投递到系统通知中心
)
3. 通知参数详解
3.1 优先级参数(priority)
优先级决定了通知的展示方式和打断程度:
| 优先级 | 说明 | 展示方式 | 适用场景 |
|---|---|---|---|
| passive | 静默推送 | 静默显示在通知中心 | 后台更新、日志同步 |
| active | 普通提醒 | 显示通知横幅 | 一般提醒、消息通知 |
| timeSensitive | 紧急提醒 | 强制显示,可打断当前操作 | 安全警报、紧急事件 |
代码示例:
python
def send_priority_notification(node_id, priority_level, title, body):
"""
根据优先级发送通知
Args:
node_id: 目标设备ID
priority_level: 优先级 (passive/active/timeSensitive)
title: 通知标题
body: 通知内容
"""
priority_config = {
"passive": {
"sound": None,
"delivery": "system"
},
"active": {
"sound": "default",
"delivery": "auto"
},
"timeSensitive": {
"sound": "alarm",
"delivery": "overlay"
}
}
config = priority_config.get(priority_level, priority_config["active"])
return nodes(
action="notify",
node=node_id,
title=title,
body=body,
priority=priority_level,
sound=config.get("sound"),
delivery=config.get("delivery")
)
3.2 投递方式(delivery)
控制通知的展示方式:
| 方式 | 说明 | 特点 |
|---|---|---|
| system | 系统通知中心 | 静默投递,不打扰用户 |
| overlay | 弹窗覆盖 | 强制展示在最上层 |
| auto | 自动选择 | 根据优先级自动决定 |
3.3 完整参数示例
python
# 完整的通知参数
result = nodes(
action="notify",
node="node_001",
title="🔔 任务提醒",
body="您有一个待处理的任务",
priority="active",
sound="default",
delivery="auto"
)
# 检查发送结果
if result.get("success"):
print("通知发送成功")
else:
print(f"发送失败: {result.get('error')}")
4. 实战案例
4.1 会议提醒系统
python
import datetime
def schedule_meeting_reminder(node_id, meeting_info):
"""
会议提醒系统
Args:
node_id: 用户设备ID
meeting_info: 会议信息字典
{
"title": "项目评审会",
"time": datetime对象,
"location": "会议室A",
"minutes_before": 10 # 提前提醒分钟数
}
"""
minutes_before = meeting_info.get("minutes_before", 10)
meeting_time = meeting_info["time"]
# 计算提醒时间
reminder_time = meeting_time - datetime.timedelta(minutes=minutes_before)
# 立即发送预提醒(如果会议即将开始)
now = datetime.datetime.now()
time_until_meeting = (meeting_time - now).total_seconds() / 60
if time_until_meeting <= minutes_before:
# 发送紧急提醒
nodes(
action="notify",
node=node_id,
title=f"⏰ 会议提醒",
body=f"会议「{meeting_info['title']}」将在{int(time_until_meeting)}分钟后开始\n地点: {meeting_info.get('location', '待定')}",
priority="timeSensitive",
sound="default"
)
else:
# 发送普通提醒
nodes(
action="notify",
node=node_id,
title=f"📅 会议预告",
body=f"会议「{meeting_info['title']}」将于{meeting_time.strftime('%H:%M')}开始",
priority="active"
)
# 使用示例
meeting = {
"title": "产品评审会",
"time": datetime.datetime(2026, 4, 20, 14, 0),
"location": "会议室301",
"minutes_before": 15
}
schedule_meeting_reminder("node_001", meeting)
4.2 安全警报系统
python
class SecurityAlertSystem:
"""安全警报系统"""
def __init__(self):
self.alert_levels = {
"critical": {"priority": "timeSensitive", "sound": "alarm"},
"warning": {"priority": "active", "sound": "default"},
"info": {"priority": "passive", "sound": None}
}
def send_alert(self, node_id, alert_info):
"""
发送安全警报
Args:
node_id: 目标设备
alert_info: {
"level": "critical/warning/info",
"title": "警报标题",
"message": "警报详情",
"action_required": True/False
}
"""
level = alert_info.get("level", "info")
config = self.alert_levels.get(level, self.alert_levels["info"])
# 构建通知内容
emoji = {"critical": "🚨", "warning": "⚠️", "info": "ℹ️"}.get(level, "ℹ️")
title = f"{emoji} {alert_info['title']}"
body = alert_info["message"]
if alert_info.get("action_required"):
body += "\n\n[需要立即处理]"
# 发送通知
return nodes(
action="notify",
node=node_id,
title=title,
body=body,
priority=config["priority"],
sound=config["sound"],
delivery="overlay" if level == "critical" else "auto"
)
def check_device_status(self, node_id):
"""检查设备状态并发送警报"""
status = nodes(action="device_status", node=node_id)
if not status.get("online"):
self.send_alert(node_id, {
"level": "critical",
"title": "设备离线",
"message": f"设备 {node_id} 已离线",
"action_required": True
})
elif status.get("battery", 100) < 20:
self.send_alert(node_id, {
"level": "warning",
"title": "电量不足",
"message": f"设备电量仅剩 {status['battery']}%",
"action_required": False
})
# 使用示例
alert_system = SecurityAlertSystem()
alert_system.send_alert("node_001", {
"level": "critical",
"title": "异常登录检测",
"message": "检测到异地登录,IP: 192.168.1.100",
"action_required": True
})
4.3 任务完成通知
python
def send_task_complete_notification(node_id, task_info, with_summary=True):
"""
发送任务完成通知
Args:
node_id: 目标设备
task_info: 任务信息
with_summary: 是否包含摘要
"""
task_name = task_info.get("name", "未知任务")
duration = task_info.get("duration", 0)
result = task_info.get("result", "success")
# 根据结果选择图标
icon = "✅" if result == "success" else "❌"
title = f"{icon} 任务完成"
# 构建通知内容
body_lines = [f"任务「{task_name}」已完成"]
if duration > 0:
body_lines.append(f"耗时: {format_duration(duration)}")
if with_summary and "summary" in task_info:
body_lines.append(f"\n摘要: {task_info['summary']}")
body = "\n".join(body_lines)
# 根据结果选择优先级
priority = "active" if result == "success" else "timeSensitive"
return nodes(
action="notify",
node=node_id,
title=title,
body=body,
priority=priority
)
def format_duration(seconds):
"""格式化时长"""
if seconds < 60:
return f"{seconds}秒"
elif seconds < 3600:
return f"{seconds//60}分{seconds%60}秒"
else:
return f"{seconds//3600}小时{(seconds%3600)//60}分"
# 使用示例
send_task_complete_notification("node_001", {
"name": "数据备份",
"duration": 125,
"result": "success",
"summary": "成功备份 1.2GB 数据"
})
5. 高级功能
5.1 批量通知发送
python
def send_bulk_notifications(node_ids, title, body, priority="active"):
"""
向多个设备发送通知
Args:
node_ids: 设备ID列表
title: 通知标题
body: 通知内容
priority: 优先级
Returns:
发送结果统计
"""
results = {"success": 0, "failed": 0, "errors": []}
for node_id in node_ids:
try:
result = nodes(
action="notify",
node=node_id,
title=title,
body=body,
priority=priority
)
if result.get("success"):
results["success"] += 1
else:
results["failed"] += 1
results["errors"].append({
"node": node_id,
"error": result.get("error", "Unknown error")
})
except Exception as e:
results["failed"] += 1
results["errors"].append({
"node": node_id,
"error": str(e)
})
return results
# 使用示例
team_members = ["node_001", "node_002", "node_003"]
result = send_bulk_notifications(
team_members,
"📢 团队通知",
"项目里程碑已完成,感谢大家!",
priority="active"
)
print(f"成功: {result['success']}, 失败: {result['failed']}")
5.2 通知历史记录
python
class NotificationHistory:
"""通知历史管理"""
def __init__(self, storage_path="notification_history.json"):
self.storage_path = storage_path
self.history = self.load_history()
def load_history(self):
"""加载历史记录"""
try:
content = read(self.storage_path)
return json.loads(content)
except:
return []
def save_history(self):
"""保存历史记录"""
write(
file_path=self.storage_path,
content=json.dumps(self.history, ensure_ascii=False, indent=2)
)
def record(self, node_id, title, body, priority, result):
"""记录通知发送"""
entry = {
"timestamp": datetime.datetime.now().isoformat(),
"node_id": node_id,
"title": title,
"body": body,
"priority": priority,
"success": result.get("success", False)
}
self.history.append(entry)
self.save_history()
def get_history(self, node_id=None, limit=50):
"""获取历史记录"""
history = self.history
if node_id:
history = [h for h in history if h["node_id"] == node_id]
return history[-limit:]
# 使用示例
history = NotificationHistory()
6. 最佳实践
6.1 通知设计原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 简洁 | 标题简短,内容清晰 | "会议提醒"而非"关于今天下午3点会议的提醒通知" |
| 及时 | 在合适的时机发送 | 紧急事件立即发送,普通通知可合并 |
| 分级 | 根据重要性选择优先级 | 系统错误用 critical,普通更新用 passive |
| 尊重 | 避免频繁打扰用户 | 设置免打扰时段,合并相似通知 |
6.2 常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 通知不显示 | 设备通知权限未开启 | 引导用户开启权限 |
| 没有声音 | sound 参数未设置 | 添加 sound="default" |
| 被系统拦截 | 设备省电模式 | 将应用加入白名单 |
| 发送失败 | 设备离线 | 检查 device_status 后再发送 |
6.3 性能优化
python
# 批量发送时添加延迟,避免频率限制
def send_with_rate_limit(node_ids, title, body, delay_ms=100):
"""带速率限制的通知发送"""
import time
for i, node_id in enumerate(node_ids):
nodes(
action="notify",
node=node_id,
title=title,
body=body
)
# 每10个通知后暂停
if (i + 1) % 10 == 0 and i < len(node_ids) - 1:
time.sleep(delay_ms / 1000)
7. 总结
7.1 核心要点回顾
| 功能 | 关键参数 | 使用场景 |
|---|---|---|
| 基础通知 | title, body | 一般提醒 |
| 优先级控制 | priority | 重要性分级 |
| 声音控制 | sound | 需要声音提示的场景 |
| 投递方式 | delivery | 控制展示方式 |
7.2 下一步学习
- 第58篇:OpenClaw 节点摄像头:远程拍照实战
- 第59篇:OpenClaw 节点屏幕:远程截图与录屏
- 第60篇:OpenClaw 节点位置:设备定位与追踪