如何用Node.js在WiFi音箱Sonos Beam Gen1播放MP3实现访客姓名播报?
在Sonos Beam Gen1上播报访客姓名的Node.js实现
核心思路
Sonos Beam需通过HTTP加载音频文件完成播放,因此实现流程分为两步:将Google TTS生成的MP3文件部署到局域网可访问的HTTP服务(需与Sonos设备同网段),再通过Sonos控制库找到设备并触发播放。
步骤实现
1. 安装依赖
使用第三方Sonos控制库sonos,搭配轻量HTTP服务器托管MP3文件:
npm install sonos
2. 编写核心代码
创建doorbell-announce.js文件:
const { SonosManager, SonosDevice } = require('sonos'); const http = require('http'); const fs = require('fs'); const path = require('path'); const os = require('os'); // 配置项:替换为你的实际路径和设备信息 const MP3_PATH = './visitor-announcement.mp3'; // Google TTS生成的MP3文件路径 const SONOS_DEVICE_IP = '192.168.1.100'; // Sonos Beam的局域网IP(可选,自动发现可不填) // 启动临时HTTP服务器托管MP3 function startHttpServer() { return new Promise((resolve) => { const server = http.createServer((req, res) => { if (req.url === '/announce.mp3') { const filePath = path.resolve(MP3_PATH); const stat = fs.statSync(filePath); res.writeHead(200, { 'Content-Type': 'audio/mpeg', 'Content-Length': stat.size }); fs.createReadStream(filePath).pipe(res); } else { res.writeHead(404).end(); } }); server.listen(8080, '0.0.0.0', () => { const localIp = getLocalIP(); const audioUrl = `http://${localIp}:8080/announce.mp3`; console.log(`MP3托管服务启动:${audioUrl}`); resolve({ audioUrl, server }); }); }); } // 获取本地局域网IP(避免用127.0.0.1,确保Sonos可访问) function getLocalIP() { const interfaces = os.networkInterfaces(); for (const dev in interfaces) { const iface = interfaces[dev]; for (const alias of iface) { if (alias.family === 'IPv4' && !alias.internal && alias.address !== '127.0.0.1') { return alias.address; } } } return '127.0.0.1'; } // 控制Sonos播放播报音频 async function playAnnouncement(audioUrl) { let sonosDevice; if (SONOS_DEVICE_IP) { sonosDevice = new SonosDevice(SONOS_DEVICE_IP); } else { // 自动发现网络中的Sonos设备 const manager = new SonosManager(); await manager.InitializeWithDiscovery(); const devices = manager.Devices; if (devices.length === 0) throw new Error('未找到Sonos设备'); // 可通过设备名称筛选,比如:devices.find(d => d.Name === 'Sonos Beam') sonosDevice = devices[0]; console.log(`匹配到Sonos设备:${sonosDevice.Name}`); } // 保存当前播放状态(可选,播放完成后恢复) const [currentState, currentTrack] = await Promise.all([ sonosDevice.GetCurrentState(), sonosDevice.CurrentTrack() ]); try { // 暂停当前播放 await sonosDevice.Pause(); // 播放播报音频 await sonosDevice.Play(audioUrl); // 等待播放结束 await new Promise((resolve) => { const checkInterval = setInterval(async () => { const state = await sonosDevice.GetCurrentState(); if (state === 'stopped') { clearInterval(checkInterval); resolve(); } }, 1000); }); } finally { // 恢复原播放状态(可选) if (currentState === 'playing') { await sonosDevice.Play(currentTrack.uri); } } } // 主执行逻辑 async function main() { let server; try { const { audioUrl, server: httpServer } = await startHttpServer(); server = httpServer; await playAnnouncement(audioUrl); console.log('播报完成'); } catch (err) { console.error('播报失败:', err.message); } finally { server?.close(); } } // 导出供外部调用(比如在门铃触发API中调用) module.exports = { main };
3. 集成到自动化流程
在你的门铃触发/人脸识别API回调中,生成MP3后调用播报逻辑:
// 示例:人脸识成功后的处理函数 async function onVisitorDetected(visitorName) { // 调用Google TTS生成MP3到指定路径 await generateGoogleTTSMP3(`${visitorName}来了`, MP3_PATH); // 执行Sonos播报 await require('./doorbell-announce').main(); }
关键注意事项
- 局域网互通:运行Node.js的主机与Sonos Beam必须在同一WiFi网段,确保HTTP服务地址可被Sonos访问
- 音频格式:确保Google TTS生成的是MP3格式(Sonos原生支持MP3、AAC)
- 设备精准匹配:如果网络中有多个Sonos设备,建议通过
SONOS_DEVICE_IP或设备名称指定目标设备,避免播错 - 状态恢复:代码中默认会保存并恢复原播放状态,可根据需求删除该逻辑
内容的提问来源于stack exchange,提问作者Praveen Gaddam
相关产品推荐
相关产品推荐

