feat: 更新依赖项版本,添加新功能并重构代码,增强 ASR 服务和灯光控制逻辑
This commit is contained in:
2
assistant/src/routes/ha-api/ha.ts
Normal file
2
assistant/src/routes/ha-api/ha.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import { LightHA } from "@kevisual/ha-api";
|
||||
export const lightHA = new LightHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL });
|
||||
@@ -5,6 +5,8 @@ import './ai/index.ts';
|
||||
// TODO:
|
||||
// import './light-code/index.ts';
|
||||
import './user/index.ts';
|
||||
|
||||
// TODO: 移除
|
||||
import './hot-api/key-sender/index.ts';
|
||||
|
||||
import os from 'node:os';
|
||||
|
||||
@@ -9,6 +9,7 @@ import path from 'node:path'
|
||||
import chalk from 'chalk';
|
||||
import { AssistantApp } from './lib.ts';
|
||||
import { getBunPath } from './module/get-bun-path.ts';
|
||||
import { qwenAsr } from './services/asr/qwen-asr.ts';
|
||||
export const runServer = async (port: number = 51015, listenPath = '127.0.0.1') => {
|
||||
let _port: number | undefined;
|
||||
if (port) {
|
||||
@@ -42,7 +43,8 @@ export const runServer = async (port: number = 51015, listenPath = '127.0.0.1')
|
||||
id: 'handle-all',
|
||||
func: proxyRoute as any,
|
||||
},
|
||||
...proxyWs()
|
||||
...proxyWs(),
|
||||
qwenAsr,
|
||||
]);
|
||||
const manager = new AssistantApp(assistantConfig, app);
|
||||
setTimeout(() => {
|
||||
|
||||
107
assistant/src/services/asr/qwen-asr.ts
Normal file
107
assistant/src/services/asr/qwen-asr.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { QwenAsrRelatime } from "@kevisual/video-tools/src/asr/index.ts";
|
||||
|
||||
import { Listener, WebSocketListenerFun, WebSocketReq } from "@kevisual/router";
|
||||
import { lightHA } from "@/routes/ha-api/ha.ts";
|
||||
|
||||
const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string }>, res) => {
|
||||
const { ws, emitter, id, data } = req;
|
||||
let asr = ws.data.asr;
|
||||
|
||||
if (!id) {
|
||||
ws.send(JSON.stringify({ type: 'error', message: 'not found id' }));
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
if (!asr) {
|
||||
const token = process.env.BAILIAN_API_KEY || ''
|
||||
// 第一次请求
|
||||
asr = new QwenAsrRelatime({
|
||||
token,
|
||||
onConnect: () => {
|
||||
const asr = ws.data.asr as QwenAsrRelatime;
|
||||
ws.send(JSON.stringify({ type: 'asr', code: 200, message: 'asr服务已连接', time: Date.now() }));
|
||||
if (!asr) return;
|
||||
asr.emitter.on('message', (message) => {
|
||||
console.log('ASR message', message);
|
||||
});
|
||||
asr.emitter.on('partial', (message) => {
|
||||
// console.log('ASR message', message);
|
||||
let msgId = ws.data.msgId || Math.random().toString(36).substring(2, 10);
|
||||
ws.data.msgId = msgId;
|
||||
ws.send(JSON.stringify({
|
||||
type: 'partial',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text: message?.text,
|
||||
raw: message?.raw,
|
||||
}));
|
||||
});
|
||||
asr.emitter.on('result', async ({ text, raw }) => {
|
||||
let msgId = ws.data.msgId;
|
||||
ws.data.msgId = Math.random().toString(36).substring(2, 10);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'result',
|
||||
msgId: msgId,
|
||||
time: Date.now(),
|
||||
text,
|
||||
}));
|
||||
if (!text) return;
|
||||
const command = text?.trim().slice(0, 20);
|
||||
type ParseCommand = {
|
||||
type?: '打开' | '关闭',
|
||||
appName?: string,
|
||||
command?: string,
|
||||
}
|
||||
let obj: ParseCommand = {};
|
||||
if (command.startsWith('打开')) {
|
||||
obj.appName = command.replace('打开', '').trim();
|
||||
obj.type = '打开';
|
||||
} else if (command.startsWith('关闭')) {
|
||||
obj.appName = command.replace('关闭', '').trim();
|
||||
obj.type = '关闭';
|
||||
}
|
||||
if (obj.type) {
|
||||
try {
|
||||
const search = await lightHA.searchLight(obj.appName || '');
|
||||
if (search.id) {
|
||||
lightHA.toggleLight({ entity_id: search.id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else if (search.hasMore) {
|
||||
const [first] = search.result;
|
||||
lightHA.toggleLight({ entity_id: first.entity_id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
|
||||
} else {
|
||||
console.log('未找到对应设备:', obj.appName);
|
||||
}
|
||||
console.log('解析到控制指令', obj);
|
||||
} catch (e) {
|
||||
console.error('控制失败', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
asr.start();
|
||||
}
|
||||
})
|
||||
ws.data.asr = asr;
|
||||
}
|
||||
const isConnected = await asr.checkConnected();
|
||||
if (!isConnected) return;
|
||||
console.log('ASR receive data', 'has voice', !!data?.voice);
|
||||
if (data?.type === 'blankVoice') {
|
||||
asr.sendBlank();
|
||||
} else if (data?.voice) {
|
||||
console.log('ASR receive voice', data.voice.slice(0, 30));
|
||||
const isBrowserFormat = data.format === 'float32';
|
||||
let voice: Buffer;
|
||||
if (isBrowserFormat) {
|
||||
voice = await asr.fixBrowerBuffer(data.voice);
|
||||
} else {
|
||||
voice = Buffer.from(data.voice, 'base64');
|
||||
}
|
||||
asr.sendBuffer(voice);
|
||||
}
|
||||
}
|
||||
export const qwenAsr: Listener = {
|
||||
id: 'qwen-asr',
|
||||
path: '/ws/asr',
|
||||
io: true,
|
||||
func
|
||||
}
|
||||
Reference in New Issue
Block a user