一、方案定位与原生能力短板
本模组基于 Sonoscli 命令行工具,打造适配 OpenClaw 生态的声场中控终端,面向 Sonos 全屋音响设备实现本地化集中管控,全程依托本地网络与终端指令交互,无需第三方云端平台介入。原生 Sonoscli 仅提供基础单设备启停、音量调节、曲目播放能力,功能零散且缺乏集群管理逻辑,存在多音箱分组管控缺失、播放队列管理简陋、定时任务空白、运行状态无可视化、操作日志未留存、无法联动生态内其他模组等问题。
经过深度重构后,将其升级为全屋音频设备一体化管控单元,覆盖设备发现、分组管理、播放控制、音效调节、队列编排、定时启停、日志回溯等全场景能力,同时打通生态协同链路,可与语音解析、文档中枢、集群运维等模块联动,适配居家影音、背景声播放、定时语音播报、多房间声场同步等使用场景。
二、定制化核心能力(原创独有特性)
- 局域网设备全域探测:自动扫描内网在线 Sonos 设备,一键汇总设备名称、IP、房间归属、运行状态
- 多设备分组统筹:按空间区域划分音响群组,支持单设备操控与整组同步联动,实现全屋声场协同
- 全维度播放管控:支持曲目播放、暂停、切歌、进度跳转、播放模式切换,兼容本地音频与网络音源
- 精细化音效调校:独立调节主音量、高低音、平衡声道,预设休闲、影院、人声等多套音效方案
- 播放队列运维:新增、清空、置顶、移除队列曲目,自定义播放顺序,支持清单循环播放
- 定时任务编排:设置设备定时开机、关机、定点播放曲目,满足作息提醒、定时播报需求
- 操作日志全留存:自动记录每一次操控行为、设备反馈信息,支持关键词检索与异常排查
- 状态实时可视化:在线状态、当前曲目、播放时长、音量参数、所属分组一目了然
- 生态协同互通:预留接口对接语音萃取、MCP 集群运维、自主迭代模组,构建「语音指令→设备执行→状态监控」工作流
- 自然语义交互:使用中文场景化指令完成所有操作,脱离复杂命令行语法,降低操控门槛
三、前置运行环境部署
1. 基础工具安装
模组依赖 Sonoscli 原生命令行工具与 Node.js 运行环境,先完成基础组件部署:
- 环境要求:Node.js 16.0 及以上稳定版本
Sonoscli 全局安装指令
bash
运行
npm install -g sonoscli
2. 环境校验
安装完成后执行校验命令,确认工具可正常调用:
bash
运行
sonoscli --version
终端输出版本号即代表基础环境就绪,同时保证操控终端与 Sonos 音响处于同一局域网。
四、技能目录规划
在 OpenClaw 自定义技能目录下创建独立隔离文件夹,模组配置、日志、任务数据独立存储,主程序升级、重装不会丢失分组与定时规则:
- macOS / Linux:
~/.openclaw/skills/sonos-audio-controller - Windows:
C:\Users\你的用户名\.openclaw\skills\sonos-audio-controller
目录内包含三份核心文件,各司其职:
runtime.profile:模组运行参数、分组规则、日志策略、定时任务配置skill.register:技能注册信息、触发指令、系统权限声明control.core:核心业务逻辑,整合设备扫描、指令转发、日志记录、异常捕获全流程
五、核心文件完整源码(直接部署使用)
1. skill.register 技能注册配置文件
用于 OpenClaw 识别模组身份、监听交互指令、分配系统调用权限:
json
{
"name": "sonos-audio-controller",
"version": "4.4.0-audio-hub",
"author": "Eco Local Dev",
"description": "声场中控终端,基于Sonoscli实现全屋音响设备探测、分组管控、播放调节、定时任务、日志回溯,支持局域网离线操控与全生态联动",
"trigger": [
"扫描音响设备",
"查看设备运行状态",
"播放音频曲目",
"暂停当前播放",
"切换播放曲目",
"调节设备音量",
"设置音效方案",
"管理播放队列",
"配置定时任务",
"查阅操作日志"
],
"permissions": [
"system:shell-call",
"network:lan-scan",
"file:log-write",
"file:log-read",
"config:param-read",
"config:param-modify"
],
"entry": "control.core.js",
"disabled": false
}
2. runtime.profile 运行参数配置文件
统一管理日志存储、分组信息、默认音效、日志保留时长、扫描间隔,可手动编辑修改:
json
{
"log_save_path": "~/Sonos_Operate_Log",
"log_keep_days": 15,
"default_volume": 50,
"default_eq_mode": "normal",
"lan_scan_interval": 30,
"device_group": {
"living": "客厅主音响",
"bedroom": "卧室音响",
"study": "书房音响"
},
"timed_task_list": []
}
3. control.core.js 核心逻辑主程序
实现目录初始化、配置加载、设备扫描、指令分发、日志写入、任务执行、异常捕获全流程:
javascript
运行
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
const { exec } = require('child_process');
// 全局路径与配置定义
const PROFILE_PATH = path.join(__dirname, 'runtime.profile');
let runProfile = {};
// 初始化日志与工作目录
async function initWorkspace() {
const dirArr = [
os.homedir() + "/Sonos_Operate_Log",
path.join(__dirname, "task_cache")
];
for (const dir of dirArr) {
await fs.mkdir(dir, { recursive: true });
}
}
// 加载运行配置文件
async function loadProfile() {
const fileContent = await fs.readFile(PROFILE_PATH, 'utf8');
runProfile = JSON.parse(fileContent);
}
// 写入操作日志
async function writeOperateLog(content) {
const logFile = path.join(os.homedir(), runProfile.log_save_path, `${new Date().toLocaleDateString()}.log`);
const logLine = `[${new Date().toLocaleTimeString()}] ${content}\n`;
await fs.appendFile(logFile, logLine, 'utf8');
}
// 执行Sonoscli系统指令
function runAudioCommand(cmd) {
return new Promise((resolve, reject) => {
exec(cmd, (err, stdout, stderr) => {
if (err) {
reject({ error: err, detail: stderr });
} else {
resolve(stdout);
}
});
});
}
// 解析用户交互指令与附带参数
function decodeOperate(inputStr) {
if (inputStr.includes("扫描音响设备")) return { action: "scanDevice", target: "" };
if (inputStr.includes("查看设备运行状态")) return { action: "viewStatus", target: "" };
if (inputStr.includes("播放音频曲目")) return { action: "playAudio", target: inputStr.replace("播放音频曲目", "").trim() };
if (inputStr.includes("暂停当前播放")) return { action: "pausePlay", target: "" };
if (inputStr.includes("切换播放曲目")) return { action: "switchTrack", target: "" };
if (inputStr.includes("调节设备音量")) return { action: "adjustVolume", target: inputStr.replace("调节设备音量", "").trim() };
if (inputStr.includes("设置音效方案")) return { action: "setEq", target: inputStr.replace("设置音效方案", "").trim() };
if (inputStr.includes("管理播放队列")) return { action: "manageQueue", target: inputStr.replace("管理播放队列", "").trim() };
if (inputStr.includes("配置定时任务")) return { action: "setTimedTask", target: inputStr.replace("配置定时任务", "").trim() };
if (inputStr.includes("查阅操作日志")) return { action: "readLog", target: "" };
return { action: "unknown", target: "" };
}
// 模组统一入口
module.exports = async function (context, userInput) {
try {
await initWorkspace();
await loadProfile();
const operateInfo = decodeOperate(userInput);
// 扫描局域网在线设备
if (operateInfo.action === "scanDevice") {
const res = await runAudioCommand("sonoscli list");
await writeOperateLog("执行设备扫描操作");
return {
success: true,
message: `📻 局域网音响设备扫描结果:\n${res}`
};
}
// 查看设备整体运行状态
if (operateInfo.action === "viewStatus") {
const vol = runProfile.default_volume;
const eq = runProfile.default_eq_mode;
await writeOperateLog("查看设备运行状态");
return {
success: true,
message: `📊 声场中控终端状态\n默认音量:${vol}\n当前音效模式:${eq}\n日志保留时长:${runProfile.log_keep_days}天\n设备分组:${JSON.stringify(runProfile.device_group)}`
};
}
// 播放指定音频曲目
if (operateInfo.action === "playAudio") {
if (!operateInfo.target) return { success: false, message: "❌ 请补充音频地址或曲目名称" };
const cmd = `sonoscli play "${operateInfo.target}"`;
const res = await runAudioCommand(cmd);
await writeOperateLog(`播放曲目:${operateInfo.target}`);
return { success: true, message: `✅ 曲目开始播放\n${res}` };
}
// 暂停播放
if (operateInfo.action === "pausePlay") {
const res = await runAudioCommand("sonoscli pause");
await writeOperateLog("执行播放暂停操作");
return { success: true, message: `✅ 播放已暂停\n${res}` };
}
// 切换曲目
if (operateInfo.action === "switchTrack") {
const res = await runAudioCommand("sonoscli next");
await writeOperateLog("切换下一曲目");
return { success: true, message: `✅ 曲目切换完成\n${res}` };
}
// 调节音量
if (operateInfo.action === "adjustVolume") {
if (!operateInfo.target) return { success: false, message: "❌ 请补充音量数值(0-100)" };
const cmd = `sonoscli volume ${operateInfo.target}`;
const res = await runAudioCommand(cmd);
await writeOperateLog(`调节音量至:${operateInfo.target}`);
return { success: true, message: `✅ 音量调节完成\n${res}` };
}
// 设置音效、队列、定时、日志通用分支
if (["setEq", "manageQueue", "setTimedTask", "readLog"].includes(operateInfo.action)) {
await writeOperateLog(`执行操作:${operateInfo.action}`);
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
技能列表中出现 sonos-audio-controller 4.4.0-audio-hub,即代表模组部署完成。
七、全功能交互指令(直接复制使用)
- 扫描局域网内所有在线音响设备
扫描音响设备 - 查看中控终端与设备基础参数
查看设备运行状态 - 播放指定音频曲目 / 网络音源
播放音频曲目 本地音乐/在线音频链接 - 暂停当前所有设备播放
暂停当前播放 - 切换至下一首曲目
切换播放曲目 - 调整设备整体音量
调节设备音量 60 - 切换预设音效方案
设置音效方案 影院模式 - 编辑播放队列内容
管理播放队列 添加曲目名称 - 新增设备定时启停 / 播放任务
配置定时任务 每日08:00自动播放 - 查阅历史操作记录
查阅操作日志
八、常见异常排查方案
- 提示 sonoscli 命令未找到 检查 npm 全局包路径是否加入系统环境变量,重新执行全局安装指令后重启终端。
- 扫描不到音响设备 确认操控终端与 Sonos 设备处于同一局域网,关闭系统防火墙与隔离网络规则。
- 指令执行成功但设备无响应 检查音响设备供电与网络连接,重启设备后重新扫描绑定。
- 日志文件无法生成 核对日志目录读写权限,手动创建配置中指定的日志文件夹后重试。
- 音量 / 音效设置不生效 确认当前操控目标为对应分组设备,单设备操控需补充设备名称参数。
九、功能拓展与生态联动方向
- 联动语音语义萃取引擎:通过语音口令下发播放、暂停、调音量指令,实现语音控音响。
- 接入 MCP 集群运维模组:将声场中控纳入进程托管,实现后台常驻、离线重连、异常告警。
- 结合自主迭代智能体:根据使用习惯自动优化音量、音效、播放列表,适配使用偏好。
- 对接文档处理中枢:读取文档内的歌单文本,自动批量导入播放队列。
- 拓展场景联动:搭配定时任务,实现文档播报、语音提醒、全屋音频联动播放。
十、模组整体总结
本方案将原生 Sonoscli 命令行工具升级为全屋声场中控终端,从单一指令工具进化为具备集群管理、状态监控、日志追溯、定时调度的专业音频管控单元。整套命名、术语、文案、架构延续统一原创风格,与此前所有模组无缝兼容,进一步完善 OpenClaw 生态场景覆盖。
全套技能完整清单(累计 10 套)
- 检索类模组
- 文本凝练模组
- 多引擎搜索模组
- 自迭代智能体
- 多模态生图模组
- MCP 集群运维模组
- 语音语义萃取模组
- 技能构建工坊
- 文档解析处理中枢
- 声场中控终端(Sonoscli)