OpenClaw 节点通知:推送消息到设备

目录

    • 摘要
    • [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 节点位置:设备定位与追踪

参考资料


相关推荐
Flittly2 小时前
【AgentScope Java新手村系列】(1)框架简介与环境搭建
java·spring boot·笔记·spring·ai
网络与设备以及操作系统学习使用者2 小时前
三层交换机实现PC互通方案
运维·网络·学习·华为
Nile2 小时前
解密Palantir系列二:1.Foundry · 数据操作系统
大数据·人工智能·ai·ai编程·ai-native
Zhu7582 小时前
Docker环境部署Apache Hadoop3.1定制版
运维·docker·容器
梦想的旅途22 小时前
企业微信 Webhook 回调系统的工程化实践
网络·架构·自动化·企业微信
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年6月9日
人工智能·python·ai·信息可视化·自然语言处理·ai编程·灵砚智能
小此方3 小时前
Re:Linux系统篇(二十九)文件篇·二:深度解析Linux文件描述符、dup2指针覆盖与内建命令重定向完全解析
linux·运维·驱动开发
Cosolar6 小时前
LlamaIndex索引类型全解析:原理与实战指南
运维·服务器