import { uiController } from '../../ui/controller.js?v=0205';
|
import { log } from '../../utils/logger.js?v=0205';
|
|
let wakewordSocket = null;
|
let reconnectTimer = null;
|
let reconnectAttempts = 0;
|
let shouldReconnect = true;
|
let wakewordRequestSeq = 0;
|
let onNextBridgeConnectedCallback = null;
|
|
const pendingWakewordRequests = new Map();
|
|
export function startWakewordBridgeListener() {
|
if (wakewordSocket) {
|
return wakewordSocket;
|
}
|
|
shouldReconnect = true;
|
log('正在连接本地唤醒事件桥...', 'info');
|
tryConnect();
|
return wakewordSocket;
|
}
|
|
function tryConnect() {
|
const bridgeUrl = buildWakewordBridgeUrl();
|
|
try {
|
wakewordSocket = new WebSocket(bridgeUrl);
|
wakewordSocket.onopen = () => {
|
reconnectAttempts = 0;
|
log(`本地唤醒事件桥已连接: ${bridgeUrl}`, 'success');
|
// 连接成功后自动保存地址,刷新后仍能记住
|
localStorage.setItem('xz_tester_wakewordWsUrl', bridgeUrl);
|
const urlInput = document.getElementById('wakewordWsUrl');
|
if (urlInput) urlInput.value = bridgeUrl;
|
};
|
|
wakewordSocket.onerror = () => {
|
log(`本地唤醒事件桥连接失败: ${bridgeUrl}`, 'error');
|
};
|
|
wakewordSocket.onmessage = async (event) => {
|
try {
|
const message = parseWakewordBridgeMessage(event.data);
|
if (message.requestId && pendingWakewordRequests.has(message.requestId)) {
|
settleWakewordRequest(message);
|
return;
|
}
|
|
if (message.success === false) {
|
log(`本地唤醒事件桥返回错误: ${message.error || '未知错误'}`, 'error');
|
return;
|
}
|
|
if (message.type === 'bridge_connected') {
|
log('本地唤醒监听已就绪', 'info');
|
if (onNextBridgeConnectedCallback) {
|
const cb = onNextBridgeConnectedCallback;
|
onNextBridgeConnectedCallback = null;
|
cb(message);
|
}
|
return;
|
}
|
|
if (message.type === 'service_ready') {
|
log('本地唤醒服务已启动', 'info');
|
return;
|
}
|
|
if (message.type === 'wakeword_config') {
|
uiController.applyWakewordConfig(message.payload || {});
|
log('已同步本地唤醒词配置', 'info');
|
return;
|
}
|
|
if (message.type === 'service_stopping') {
|
log('本地唤醒服务正在停止', 'warning');
|
return;
|
}
|
|
if (message.type === 'wake_word_detected') {
|
const wakeWord = message.payload?.wake_word || '唤醒词';
|
log(`检测到本地唤醒事件: ${wakeWord}`, 'info');
|
await uiController.triggerWakewordDial(wakeWord);
|
}
|
} catch (error) {
|
log(`解析本地唤醒事件失败: ${error.message}`, 'error');
|
}
|
};
|
|
wakewordSocket.onclose = () => {
|
if (wakewordSocket) {
|
wakewordSocket = null;
|
}
|
|
rejectAllWakewordRequests('本地唤醒事件桥已断开');
|
|
if (!shouldReconnect) {
|
return;
|
}
|
|
if (reconnectTimer) {
|
return;
|
}
|
|
reconnectAttempts += 1;
|
const delay = Math.min(1000 * reconnectAttempts, 5000);
|
log(`本地唤醒事件桥将在 ${delay}ms 后重连: ${bridgeUrl}`, 'warning');
|
reconnectTimer = window.setTimeout(() => {
|
reconnectTimer = null;
|
tryConnect();
|
}, delay);
|
};
|
|
return wakewordSocket;
|
} catch (error) {
|
log(`启动本地唤醒监听失败: ${error.message}`, 'error');
|
return null;
|
}
|
}
|
|
export function stopWakewordBridgeListener() {
|
shouldReconnect = false;
|
|
if (reconnectTimer) {
|
window.clearTimeout(reconnectTimer);
|
reconnectTimer = null;
|
}
|
|
if (!wakewordSocket) {
|
return;
|
}
|
|
wakewordSocket.onclose = null;
|
wakewordSocket.close();
|
wakewordSocket = null;
|
}
|
|
export function sendWakewordBridgeMessage(type, payload = {}, requestId = null) {
|
if (!wakewordSocket || wakewordSocket.readyState !== WebSocket.OPEN) {
|
log('本地唤醒事件桥未连接,无法发送消息', 'warning');
|
return false;
|
}
|
|
wakewordSocket.send(JSON.stringify({
|
type,
|
requestId,
|
payload,
|
}));
|
return true;
|
}
|
|
export function requestWakewordBridge(type, payload = {}, timeout = 5000) {
|
const requestId = `wakeword-${Date.now()}-${++wakewordRequestSeq}`;
|
|
return new Promise((resolve, reject) => {
|
const timer = window.setTimeout(() => {
|
pendingWakewordRequests.delete(requestId);
|
reject(new Error('本地唤醒服务响应超时'));
|
}, timeout);
|
|
pendingWakewordRequests.set(requestId, { resolve, reject, timer });
|
|
if (!sendWakewordBridgeMessage(type, payload, requestId)) {
|
window.clearTimeout(timer);
|
pendingWakewordRequests.delete(requestId);
|
reject(new Error('本地唤醒事件桥未连接'));
|
}
|
});
|
}
|
|
export function getWakewordBridgeUrl() {
|
if (wakewordSocket && wakewordSocket.url) {
|
return wakewordSocket.url;
|
}
|
return buildWakewordBridgeUrl();
|
}
|
|
export function onNextBridgeConnected(callback) {
|
onNextBridgeConnectedCallback = callback;
|
}
|
|
function buildWakewordBridgeUrl() {
|
const configured = localStorage.getItem('xz_tester_wakewordWsUrl');
|
if (configured && configured.trim()) {
|
return configured.trim();
|
}
|
return 'ws://127.0.0.1:8006/wakeword-ws';
|
}
|
|
function parseWakewordBridgeMessage(rawData) {
|
const message = JSON.parse(rawData);
|
return {
|
type: message.type || '',
|
requestId: message.requestId || null,
|
success: message.success !== false,
|
payload: message.payload || {},
|
error: message.error || null,
|
};
|
}
|
|
function settleWakewordRequest(message) {
|
const pendingRequest = pendingWakewordRequests.get(message.requestId);
|
if (!pendingRequest) {
|
return;
|
}
|
|
window.clearTimeout(pendingRequest.timer);
|
pendingWakewordRequests.delete(message.requestId);
|
|
if (message.success === false) {
|
pendingRequest.reject(new Error(message.error || '本地唤醒服务返回失败'));
|
return;
|
}
|
|
pendingRequest.resolve(message);
|
}
|
|
function rejectAllWakewordRequests(errorMessage) {
|
pendingWakewordRequests.forEach((pendingRequest) => {
|
window.clearTimeout(pendingRequest.timer);
|
pendingRequest.reject(new Error(errorMessage));
|
});
|
pendingWakewordRequests.clear();
|
}
|