/** * 华为交互数字人 - HwICSUiSdk 集成示例 * * 功能说明: * 1. SDK 初始化和销毁 * 2. 数字人创建和显示 * 3. 语音/文本对话交互 * 4. 事件监听和日志记录 */ // 导入 HwICSUiSdk import { HwICSUiSdk, ICSEventKey, InteractionMode, ConfigKey } from '../hwICSUiSdk/HwICSUiSdk.esm.js'; import { fetchOnceCode } from './api.js'; // ===== 全局状态 ===== const state = { isInitialized: false, isChatting: false, isSpeaking: false, isMuted: false, jobInfo: null, currentInteractionMode: InteractionMode.AUDIO }; // ===== DOM 元素引用 ===== const elements = { // 配置输入 serverAddress: document.getElementById('serverAddress'), robotId: document.getElementById('robotId'), onceCode: document.getElementById('onceCode'), // 按钮 initBtn: document.getElementById('initBtn'), destroyBtn: document.getElementById('destroyBtn'), getOnceCodeBtn: document.getElementById('getOnceCodeBtn'), startChatBtn: document.getElementById('startChatBtn'), stopChatBtn: document.getElementById('stopChatBtn'), startSpeakBtn: document.getElementById('startSpeakBtn'), stopSpeakBtn: document.getElementById('stopSpeakBtn'), sendTextBtn: document.getElementById('sendTextBtn'), interruptBtn: document.getElementById('interruptBtn'), toggleAudioBtn: document.getElementById('toggleAudioBtn'), clearLogBtn: document.getElementById('clearLogBtn'), // 输入框 textInput: document.getElementById('textInput'), logLevel: document.getElementById('logLevel'), // 显示区域 digitalHumanBox: document.getElementById('digitalHumanBox'), logContainer: document.getElementById('logContainer'), connectionStatus: document.getElementById('connectionStatus'), sdkVersion: document.getElementById('sdkVersion') }; // ===== 日志工具 ===== const logger = { log(level, message, data = null) { const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false }); const entry = document.createElement('div'); entry.className = 'log-entry'; const levelClass = level.toLowerCase(); const displayMessage = data ? `${message} ${JSON.stringify(data, null, 2)}` : message; entry.innerHTML = ` [${timestamp}] ${level} ${displayMessage} `; elements.logContainer.appendChild(entry); elements.logContainer.scrollTop = elements.logContainer.scrollHeight; console[level === 'ERROR' ? 'error' : level === 'WARN' ? 'warn' : 'log'](message, data); }, debug(message, data) { this.log('DEBUG', message, data); }, info(message, data) { this.log('INFO', message, data); }, warn(message, data) { this.log('WARN', message, data); }, error(message, data) { this.log('ERROR', message, data); } }; // ===== SDK 事件监听配置 ===== const eventListeners = { [ICSEventKey.ERROR]: (error) => { logger.error('SDK 错误', error); updateConnectionStatus(false); }, [ICSEventKey.JOB_INFO_CHANGE]: (jobInfo) => { logger.info('任务信息变更', jobInfo); state.jobInfo = jobInfo; }, [ICSEventKey.SPEAKING_START]: () => { logger.info('数字人开始说话'); state.isSpeaking = true; updateUIState(); }, [ICSEventKey.SPEAKING_STOP]: () => { logger.info('数字人停止说话'); state.isSpeaking = false; updateUIState(); }, [ICSEventKey.SPEECH_RECOGNIZED]: (data) => { logger.info('语音识别结果', data); }, [ICSEventKey.SEMANTIC_RECOGNIZED]: (data) => { logger.info('语义识别结果', data); }, [ICSEventKey.ENTER_SLEEP]: () => { logger.info('数字人进入休眠状态'); }, [ICSEventKey.ENTER_ACTIVE]: () => { logger.info('数字人进入激活状态'); }, [ICSEventKey.LANGUAGE_INFO_CHANGE]: (langInfo) => { logger.info('语言信息变更', langInfo); }, [ICSEventKey.JOB_END_MENTION]: (data) => { logger.warn('任务即将结束', data); }, [ICSEventKey.JOB_END]: (data) => { logger.info('任务已结束', data); state.isChatting = false; state.jobInfo = null; updateConnectionStatus(false); updateUIState(); } }; // ===== UI 状态更新 ===== function updateUIState() { const { isInitialized, isChatting, isSpeaking } = state; elements.initBtn.disabled = isInitialized; elements.destroyBtn.disabled = !isInitialized; elements.startChatBtn.disabled = !isInitialized || isChatting; elements.stopChatBtn.disabled = !isInitialized || !isChatting; elements.startSpeakBtn.disabled = !isInitialized || !isChatting || isSpeaking; elements.stopSpeakBtn.disabled = !isInitialized || !isChatting || !isSpeaking; elements.textInput.disabled = !isInitialized || !isChatting; elements.sendTextBtn.disabled = !isInitialized || !isChatting; elements.interruptBtn.disabled = !isInitialized || !isChatting; elements.toggleAudioBtn.disabled = !isInitialized; if (isChatting) { elements.digitalHumanBox.classList.add('active'); } else { elements.digitalHumanBox.classList.remove('active'); } } function updateConnectionStatus(connected) { const status = elements.connectionStatus; if (connected) { status.textContent = '已连接'; status.className = 'status connected'; } else { status.textContent = '未连接'; status.className = 'status disconnected'; } } // ===== SDK 操作 ===== async function initializeSDK() { try { const serverAddress = elements.serverAddress.value.trim(); const robotId = elements.robotId.value.trim(); let onceCode = elements.onceCode.value.trim(); if (!serverAddress) { logger.error('请输入服务器地址'); return; } if (!onceCode) { logger.info('授权码为空,正在自动获取...'); try { onceCode = await fetchOnceCode(); elements.onceCode.value = onceCode; logger.info('授权码自动获取成功'); } catch (err) { logger.error('授权码自动获取失败', err.message); return; } } logger.info('正在初始化 SDK...'); const isSupported = await HwICSUiSdk.checkBrowserSupport(true); if (!isSupported) { logger.error('当前浏览器不支持该功能'); return; } const logLevel = elements.logLevel.value; HwICSUiSdk.setLogLevel(logLevel); HwICSUiSdk.addEventListeners(eventListeners); const createParam = { serverAddress, containerId: 'digitalHumanBox', robotId: robotId || undefined, onceCode: onceCode || undefined, config: { [ConfigKey.ENABLE_CAPTION]: true, [ConfigKey.ENABLE_CHAT_BTN]: true, [ConfigKey.ENABLE_HOT_ISSUES]: true, [ConfigKey.ENABLE_WEAK_ERROR_INFO]: true, [ConfigKey.SHOW_LANGUAGE_UI]: true, [ConfigKey.ENABLE_DEVICE_SETTING]: true, [ConfigKey.ENABLE_NETWORK_CHECK]: true, [ConfigKey.AUTO_PLAY_REMOTE_VIDEO]: true, [ConfigKey.DEFAULT_MUTE_REMOTE_AUDIO]: false }, logLevel }; await HwICSUiSdk.create(createParam); state.isInitialized = true; updateConnectionStatus(true); updateUIState(); elements.sdkVersion.textContent = `v${HwICSUiSdk.version}`; logger.info('SDK 初始化成功', { version: HwICSUiSdk.version }); } catch (error) { logger.error('SDK 初始化失败', error); state.isInitialized = false; updateUIState(); } } async function destroySDK() { try { logger.info('正在销毁 SDK...'); await HwICSUiSdk.destroy(); state.isInitialized = false; state.isChatting = false; state.isSpeaking = false; state.jobInfo = null; updateConnectionStatus(false); updateUIState(); elements.digitalHumanBox.innerHTML = `
🤖

请先初始化 SDK 并创建数字人

`; logger.info('SDK 已销毁'); } catch (error) { logger.error('销毁 SDK 失败', error); } } async function startChat() { try { logger.info('正在开始对话...'); const result = await HwICSUiSdk.startChat({ interactionMode: InteractionMode.AUDIO, wakeupType: undefined, preCheckPermissions: true }); if (result.result) { state.isChatting = true; logger.info('对话已开始', { chatId: result.chatId, sessionId: result.sessionId }); updateUIState(); } else { logger.error('开始对话失败', result); } } catch (error) { logger.error('开始对话出错', error); } } async function stopChat() { try { logger.info('正在停止对话...'); const result = await HwICSUiSdk.stopChat(); if (result.result) { state.isChatting = false; state.isSpeaking = false; logger.info('对话已停止'); updateUIState(); } else { logger.error('停止对话失败', result); } } catch (error) { logger.error('停止对话出错', error); } } async function startUserSpeak() { try { logger.info('开始语音识别...'); const result = await HwICSUiSdk.startUserSpeak({ preCheckPermissions: true }); if (result.result) { logger.info('语音识别已启动'); } else { logger.error('启动语音识别失败', result); } } catch (error) { logger.error('启动语音识别出错', error); } } async function stopUserSpeak() { try { logger.info('停止语音识别...'); const result = await HwICSUiSdk.stopUserSpeak(); if (result.result) { logger.info('语音识别已停止'); } else { logger.error('停止语音识别失败', result); } } catch (error) { logger.error('停止语音识别出错', error); } } async function sendTextMessage() { const text = elements.textInput.value.trim(); if (!text) { logger.warn('请输入文本内容'); return; } try { logger.info('发送文本消息...', { text }); const result = await HwICSUiSdk.sendTextQuestion({ text, questionParam: undefined }); if (result.result) { logger.info('消息发送成功', { chatId: result.chatId }); elements.textInput.value = ''; } else { logger.error('发送消息失败', result); } } catch (error) { logger.error('发送消息出错', error); } } async function interruptSpeaking() { try { logger.info('打断说话...'); const result = await HwICSUiSdk.interruptSpeaking(); if (result.result) { logger.info('已打断说话'); } else { logger.error('打断说话失败', result); } } catch (error) { logger.error('打断说话出错', error); } } async function toggleAudio() { try { if (state.isMuted) { const result = await HwICSUiSdk.unmuteRemoteAudio(); if (result) { state.isMuted = false; elements.toggleAudioBtn.innerHTML = '🔊 静音'; logger.info('已取消静音'); } } else { const result = await HwICSUiSdk.muteRemoteAudio(); if (result) { state.isMuted = true; elements.toggleAudioBtn.innerHTML = '🔇 取消静音'; logger.info('已静音'); } } } catch (error) { logger.error('切换音频状态出错', error); } } async function handleGetOnceCode() { try { elements.getOnceCodeBtn.disabled = true; elements.getOnceCodeBtn.textContent = '🔄 获取中...'; logger.info('正在获取授权码...'); const onceCode = await fetchOnceCode(); elements.onceCode.value = onceCode; logger.info('授权码获取成功'); } catch (error) { logger.error('获取授权码失败', error.message); } finally { elements.getOnceCodeBtn.disabled = false; elements.getOnceCodeBtn.textContent = '🔑 获取授权码'; } } function clearLogs() { elements.logContainer.innerHTML = ''; logger.info('日志已清空'); } // ===== 事件绑定 ===== function bindEvents() { elements.initBtn.addEventListener('click', initializeSDK); elements.destroyBtn.addEventListener('click', destroySDK); elements.getOnceCodeBtn.addEventListener('click', handleGetOnceCode); elements.startChatBtn.addEventListener('click', startChat); elements.stopChatBtn.addEventListener('click', stopChat); elements.startSpeakBtn.addEventListener('click', startUserSpeak); elements.stopSpeakBtn.addEventListener('click', stopUserSpeak); elements.sendTextBtn.addEventListener('click', sendTextMessage); elements.textInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendTextMessage(); } }); elements.interruptBtn.addEventListener('click', interruptSpeaking); elements.toggleAudioBtn.addEventListener('click', toggleAudio); elements.clearLogBtn.addEventListener('click', clearLogs); elements.logLevel.addEventListener('change', () => { if (state.isInitialized) { HwICSUiSdk.setLogLevel(elements.logLevel.value); logger.info(`日志级别已切换为: ${elements.logLevel.value}`); } }); window.addEventListener('beforeunload', () => { if (state.isInitialized) { HwICSUiSdk.destroy(); } }); } // ===== 初始化 ===== function init() { logger.info('应用初始化完成'); logger.info('HwICSUiSdk 版本', { version: HwICSUiSdk.version, buildTime: HwICSUiSdk.buildTime }); bindEvents(); updateUIState(); } init();