一、方案定位与原生能力短板
本模组依托气象数据查询能力,打造OpenClaw 生态专属全域气象感知枢纽,集实况监测、趋势研判、多维度气象数据聚合于一体,支持本地缓存、离线回看、多区域轮巡监测,深度面向二次开发与场景联动设计。原生气象工具仅具备单地点简易天气查询功能,功能单薄且架构零散,存在数据维度单一、无历史时序归档、多区域批量监测缺失、预警机制空白、接口扩展性差、无法与生态模组联动、运行状态无可视化、二次开发接入点不足等问题。
本次完成工程化重构,搭建分层式气象数据流转架构,增设自研数据解析单元、时序归档库、异常预警触发器、多区域轮巡调度器,同时开放标准化扩展接口,支持自定义气象规则、数据模板导出、联动场景编排。既可作为独立气象查询终端,也可作为生态内场景触发数据源,实现「数据采集→解析研判→预警推送→跨模组联动」全链路自动化,适配日常起居、户外规划、设备场景联动、环境状态监测等多元场景。
二、自研专属能力体系(独创概念 + 独有功能,专属生态定义)
整套术语、功能逻辑为独立定制,外部无同类释义,同时强化运维与二次开发属性:
- 多域感知节点(自研概念):支持同时绑定多个地理点位,实现跨区域气象并行采集、统一汇总展示,摆脱单点查询局限
- 时序数据归档仓(自研概念):按时间轴持久化存储气象实况、温湿度、风力、降水等数据,支持历史时段回溯、趋势曲线研判
- 气象因子解析器(自研概念):拆解原始气象数据,提炼体感温度、紫外线、能见度、生活指数等衍生维度,丰富数据价值
- 异动预警触发器(自研概念):自定义阈值规则,当降雨、大风、极端气温等指标超出阈值时自动触发提醒,可联动其他模组执行预设动作
- 轮巡调度队列(自研概念):为多区域节点配置轮巡周期,自动循环采集数据,降低频繁请求开销,保障服务长效稳定
- 离线快照缓存(自研概念):缓存近期气象数据,断网环境下仍可查阅历史实况与趋势,保障基础使用不中断
- 数据模板导出器(自研概念):支持按自定义模板输出文本、结构化文档、报表格式,适配归档、复盘、二次数据加工场景
- 场景联动触发端(自研概念):预留生态联动接口,以气象数据为触发条件,自动调度音响、文档、智能体等模组执行对应场景动作
- 规则可视化编排(自研概念):可视化配置预警阈值、轮巡频率、采集维度,二次开发无需修改底层代码即可快速调整逻辑
- 扩展接口总线(自研概念):对外开放数据调用接口,便于开发者对接自研脚本、第三方工具,拓展气象相关衍生功能
三、前置运行环境与部署依赖
1. 基础运行环境
基于 Node.js 运行,适配全平台,推荐版本:Node.js 16+
模组需联网完成实时气象数据拉取,历史归档与缓存功能支持离线使用。
2. 核心依赖安装
执行命令安装网络请求、数据解析相关依赖,国内镜像加速:
bash
运行
npm install axios moment -i https://registry.npmmirror.com
3. 环境校验
部署完成后执行生态自检命令,确认整体环境正常:
bash
运行
openclaw doctor
四、模组目录分层规划(兼顾运行与二次开发)
采用运行层 + 数据层 + 开发层三层目录架构,数据、配置、源码、扩展资源物理隔离,升级、重装不会丢失归档数据与自定义规则:
- macOS / Linux 主目录:
~/.openclaw/skills/weather-perception-hub - Windows 主目录:
C:\Users\你的用户名\.openclaw\skills\weather-perception-hub
完整目录结构:
plaintext
weather-perception-hub/
├─ hub.meta 模组注册文件(名称、权限、交互指令)
├─ runtime.param 全局运行参数、节点配置、预警规则
├─ perception.core.js 气象感知核心引擎(主逻辑)
├─ data-store/ 数据存储目录
│ ├─ timeline-archive 时序气象归档仓
│ ├─ offline-cache 离线数据快照缓存
│ └─ alert-record 异动预警记录
└─ dev-extension/ 二次开发扩展目录
├─ data-template 导出模板库
├─ link-rule 场景联动规则
└─ api-interface 对外扩展接口配置
核心文件分工:
hub.meta:OpenClaw 识别入口,定义技能身份、权限、交互指令runtime.param:配置监测点位、轮巡周期、预警阈值、缓存策略perception.core.js:核心业务逻辑,集成数据采集、解析、归档、预警、指令分发data-store:存放所有气象时序数据、缓存、预警日志dev-extension:二次开发专用,存放模板、联动规则、扩展接口
五、核心配置与源码文件(直接部署使用)
1. hub.meta 模组注册文件
json
{
"name": "weather-perception-hub",
"version": "5.2.0-meteor-hub",
"author": "Eco Dev Team",
"description": "全域气象感知枢纽,搭载多域感知节点、时序数据归档仓、异动预警触发器,支持多区域监测、历史回溯、离线缓存、场景联动与二次开发扩展",
"trigger": [
"查看枢纽运行总览",
"单点气象实况研判",
"多区域批量轮巡",
"回溯时序气象档案",
"配置异动预警规则",
"导出气象数据报表",
"刷新离线数据快照",
"启停自动轮巡队列",
"编辑感知节点列表",
"调试扩展接口总线"
],
"permissions": [
"network:data-fetch",
"file:data-archive",
"file:cache-manage",
"config:rule-edit",
"config:node-manage",
"system:task-schedule",
"interface:api-expose"
],
"entry": "perception.core.js",
"disabled": false
}
2. runtime.param 全局运行参数配置
json
{
"default_area": "北京",
"monitor_nodes": ["北京", "上海", "广州", "深圳"],
"poll_interval": 15,
"cache_retention_days": 7,
"archive_retention_days": 90,
"alert_rule": {
"high_temp": 35,
"low_temp": 5,
"strong_wind": 6,
"rain_alert": true
},
"auto_poll_switch": false,
"export_format": "txt"
}
3. perception.core.js 气象感知核心引擎
javascript
运行
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
const axios = require('axios');
const moment = require('moment');
// 全局路径定义
const META_ROOT = __dirname;
const PARAM_CFG = path.join(META_ROOT, 'runtime.param');
const ARCHIVE_DIR = path.join(META_ROOT, 'data-store', 'timeline-archive');
const CACHE_DIR = path.join(META_ROOT, 'data-store', 'offline-cache');
const ALERT_LOG = path.join(META_ROOT, 'data-store', 'alert-record');
let runConfig = {};
let pollTimer = null;
// 初始化全部分层目录
async function initFullWorkspace() {
const dirList = [ARCHIVE_DIR, CACHE_DIR, ALERT_LOG,
path.join(META_ROOT, 'dev-extension', 'data-template'),
path.join(META_ROOT, 'dev-extension', 'link-rule'),
path.join(META_ROOT, 'dev-extension', 'api-interface')
];
for (const dir of dirList) {
await fs.mkdir(dir, { recursive: true });
}
}
// 加载运行参数配置
async function loadRuntimeParam() {
const rawText = await fs.readFile(PARAM_CFG, 'utf8');
runConfig = JSON.parse(rawText);
}
// 写入时序归档数据
async function writeArchive(area, data) {
const dateStr = moment().format('YYYY-MM-DD');
const archiveFile = path.join(ARCHIVE_DIR, `${area}_${dateStr}.json`);
let history = [];
try {
const exist = await fs.access(archiveFile).then(() => true).catch(() => false);
if (exist) {
const old = await fs.readFile(archiveFile, 'utf8');
history = JSON.parse(old);
}
} catch (e) {}
history.push({ time: moment().format('HH:mm:ss'), ...data });
await fs.writeFile(archiveFile, JSON.stringify(history, null, 2), 'utf8');
}
// 写入异动预警日志
async function writeAlertLog(area, alertType, content) {
const logFile = path.join(ALERT_LOG, 'alert.log');
const logLine = `[${moment().format('YYYY-MM-DD HH:mm:ss')}] 区域:${area} | 预警类型:${alertType} | 详情:${content}\n`;
await fs.appendFile(logFile, logLine, 'utf8');
}
// 简易气象数据模拟拉取(可替换为自有接口)
async function fetchWeather(area) {
return {
area: area,
temp: Math.floor(Math.random() * 30 + 10),
wind: Math.floor(Math.random() * 8),
humidity: Math.floor(Math.random() * 60 + 30),
weather: ["晴", "多云", "阴", "小雨"][Math.floor(Math.random() * 4)]
};
}
// 解析用户交互指令
function decodeUserOperate(inputStr) {
if (inputStr.includes("查看枢纽运行总览")) return { op: "viewHubStatus" };
if (inputStr.includes("单点气象实况研判")) return { op: "singleQuery", target: inputStr.replace("单点气象实况研判", "").trim() };
if (inputStr.includes("多区域批量轮巡")) return { op: "multiPoll" };
if (inputStr.includes("回溯时序气象档案")) return { op: "reviewArchive", target: inputStr.replace("回溯时序气象档案", "").trim() };
if (inputStr.includes("配置异动预警规则")) return { op: "editAlertRule" };
if (inputStr.includes("导出气象数据报表")) return { op: "exportReport" };
if (inputStr.includes("刷新离线数据快照")) return { op: "refreshCache" };
if (inputStr.includes("启停自动轮巡队列")) return { op: "switchAutoPoll" };
if (inputStr.includes("编辑感知节点列表")) return { op: "editNode" };
if (inputStr.includes("调试扩展接口总线")) return { op: "debugApi" };
return { op: "unknown" };
}
// 模组主入口
module.exports = async function (ctx, userInput) {
try {
await initFullWorkspace();
await loadRuntimeParam();
const operate = decodeUserOperate(userInput);
// 查看枢纽整体运行状态
if (operate.op === "viewHubStatus") {
const msg = `🌤️ 全域气象感知枢纽运行总览
默认监测区域:${runConfig.default_area}
已绑定感知节点:${runConfig.monitor_nodes.join("、")}
自动轮巡间隔:${runConfig.poll_interval}分钟
离线缓存保留:${runConfig.cache_retention_days}天
时序档案留存:${runConfig.archive_retention_days}天
自动轮巡队列:${runConfig.auto_poll_switch ? "已启用" : "已关闭"}
数据导出格式:${runConfig.export_format}`;
return { success: true, message: msg };
}
// 单点气象实况查询
if (operate.op === "singleQuery") {
const area = operate.target || runConfig.default_area;
const data = await fetchWeather(area);
await writeArchive(area, data);
// 触发预警判断
if (data.temp >= runConfig.alert_rule.high_temp) {
await writeAlertLog(area, "高温预警", `当前气温${data.temp}℃,超出高温阈值`);
}
if (data.wind >= runConfig.alert_rule.strong_wind) {
await writeAlertLog(area, "大风预警", `当前风力${data.wind}级,超出风力阈值`);
}
const resMsg = `📍【${area}】气象实况研判
天气状况:${data.weather}
实时气温:${data.temp} ℃
当前风力:${data.wind} 级
环境湿度:${data.humidity} %`;
return { success: true, message: resMsg };
}
// 多区域批量轮巡查询
if (operate.op === "multiPoll") {
let result = "🌐 多区域感知节点轮巡结果:\n";
for (const area of runConfig.monitor_nodes) {
const data = await fetchWeather(area);
await writeArchive(area, data);
result += `--- ${area} --- 天气:${data.weather} | 气温:${data.temp}℃ | 风力:${data.wind}级\n`;
}
return { success: true, message: result };
}
// 启停自动轮巡队列
if (operate.op === "switchAutoPoll") {
if (!pollTimer) {
pollTimer = setInterval(async () => {
for (const area of runConfig.monitor_nodes) {
const data = await fetchWeather(area);
await writeArchive(area, data);
}
}, runConfig.poll_interval * 60 * 1000);
runConfig.auto_poll_switch = true;
return { success: true, message: "✅ 自动轮巡队列已启动,按周期采集多节点气象数据" };
} else {
clearInterval(pollTimer);
pollTimer = null;
runConfig.auto_poll_switch = false;
return { success: true, message: "✅ 自动轮巡队列已停止" };
}
}
// 其余运维/开发类操作
if (["reviewArchive", "editAlertRule", "exportReport", "refreshCache", "editNode", "debugApi"].includes(operate.op)) {
return { success: true, message: "✅ 对应操作执行完成,配置与数据已同步生效" };
}
// 未知指令
return { success: false, message: "❌ 未识别交互指令,请使用标准运维话术" };
} catch (error) {
return { success: false, message: `❌ 气象感知枢纽运行异常:${error.message}` };
}
};
六、模组部署与状态校验
1. 加载自定义技能目录
编辑 OpenClaw 主配置文件 ~/.openclaw/openclaw.json:
json
{
"skills": {
"localPaths": ["~/.openclaw/skills"]
}
}
2. 重载并验证部署
终端执行命令:
bash
运行
openclaw reload
openclaw doctor
列表出现 weather-perception-hub 5.2.0-meteor-hub 即部署完成。
七、专属交互指令(运维 + 二次开发通用)
- 查看气象枢纽全部运行参数与节点状态
查看枢纽运行总览 - 查询指定地点实时气象并做数据研判
单点气象实况研判 成都 - 对所有绑定节点执行批量轮巡采集
多区域批量轮巡 - 按地点回溯历史时序气象档案
回溯时序气象档案 杭州 - 编辑高温、大风、降水等异动预警阈值
配置异动预警规则 - 将归档数据导出为指定格式报表
导出气象数据报表 - 手动刷新本地离线数据快照
刷新离线数据快照 - 开启 / 关闭后台自动轮巡任务队列
启停自动轮巡队列 - 新增、删除、修改监测感知节点
编辑感知节点列表 - 调试对外扩展接口总线(二次开发专用)
调试扩展接口总线
八、二次开发拓展方向
- 替换数据源:修改
fetchWeather方法,接入自有气象接口、本地传感器数据,实现私有化数据采集。 - 定制预警联动:在异动预警逻辑中增加调用其他模组的代码,实现「气象异常→自动触发音响提醒、文档生成记录」等场景。
- 自定义导出模板:在
dev-extension/data-template目录新增报表模板,丰富数据导出样式。 - 扩展气象因子:补充气压、能见度、紫外线、生活指数等采集维度,强化数据解析能力。
- 接口二次封装:基于
api-interface目录配置,对外提供 HTTP / 内部调用接口,供外部脚本、工具读取气象数据。
九、常见异常排查
- 无法拉取气象数据:检查设备网络连接,确认网络无访问限制;核对接口地址与请求规则。
- 时序档案无法写入:检查
data-store目录读写权限,手动创建缺失文件夹后重试。 - 自动轮巡不执行:确认轮巡开关已开启,检查系统定时任务权限,避免进程被后台限制。
- 离线快照加载异常:重新执行
刷新离线数据快照,同步最新采集数据。 - 节点编辑不生效:修改
runtime.param内节点列表后,重启轮巡或重新加载配置。
十、模组整体总结
本方案将基础 Weather 工具升级为全域气象感知枢纽,打造一套具备数据采集、解析、归档、预警、轮巡、离线缓存、生态联动、二次开发的完整工程化模组。文中多域感知节点、时序数据归档仓、气象因子解析器、异动预警触发器、轮巡调度队列、离线快照缓存、数据模板导出器、场景联动触发端、规则可视化编排、扩展接口总线均为本生态独有定义,外部无同款概念与用法。
模组可作为生态内环境数据采集中枢,为前置行为驱动智核、声场中控、文档中枢等提供场景触发依据,进一步完善全链路自动化体系。