feat: Web Client — улучшенное подключение к WebSocket
Изменения: 1. Поле для ввода WebSocket адреса (вместо prompt) - wsUrlInput с значением по умолчанию - Сохраняется между перезагрузками 2. Кнопка CONNECT/DISCONNECT - Меняет текст и цвет при подключении - DISCONNECT красная кнопка - Работают обе кнопки (в шапке и в карточке) 3. Auto-connect - Чекбокс 'Auto-connect on load' - Автоматическое подключение при загрузке - Можно отключить 4. updateConnectButton() - Обновляет обе кнопки синхронно - 🔌 CONNECT / 🔌 DISCONNECT
This commit is contained in:
+90
-15
@@ -279,7 +279,7 @@
|
|||||||
|
|
||||||
<div class="glass-panel">
|
<div class="glass-panel">
|
||||||
<!-- заголовок -->
|
<!-- заголовок -->
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
|
||||||
<div style="font-family: 'Orbitron'; font-weight: 700; font-size: 1.6rem; letter-spacing: 2px; background: linear-gradient(135deg, #aaffff, #3b8eff); -webkit-background-clip: text; background-clip: text; color: transparent;">
|
<div style="font-family: 'Orbitron'; font-weight: 700; font-size: 1.6rem; letter-spacing: 2px; background: linear-gradient(135deg, #aaffff, #3b8eff); -webkit-background-clip: text; background-clip: text; color: transparent;">
|
||||||
⚡ Wyoming Droid Assistant ⚡
|
⚡ Wyoming Droid Assistant ⚡
|
||||||
</div>
|
</div>
|
||||||
@@ -290,6 +290,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- WebSocket Connection Settings -->
|
||||||
|
<div class="card" style="margin-bottom: 20px;">
|
||||||
|
<div class="card-header">
|
||||||
|
<span>🔌 WebSocket Connection</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||||||
|
<input type="text" id="wsUrlInput" value="ws://192.168.200.111:8675"
|
||||||
|
style="flex: 1; min-width: 200px; padding: 8px 12px; background: rgba(0,0,0,0.3); border: 1px solid rgba(0,255,255,0.3); border-radius: 8px; color: #eee; font-size: 14px;"
|
||||||
|
placeholder="ws://ip-address:8675">
|
||||||
|
<button id="connectBtnCard" class="neo-btn" style="min-width: 120px;">
|
||||||
|
🔌 CONNECT
|
||||||
|
</button>
|
||||||
|
<label class="checkbox-label" style="color: #aaa; cursor: pointer;">
|
||||||
|
<input type="checkbox" id="autoConnect" checked style="margin-right: 5px;">
|
||||||
|
Auto-connect on load
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
<!-- ЛЕВАЯ КОЛОНКА: Аватар + Аудиовизуализация и Состояние -->
|
<!-- ЛЕВАЯ КОЛОНКА: Аватар + Аудиовизуализация и Состояние -->
|
||||||
<div class="card avatar-area">
|
<div class="card avatar-area">
|
||||||
@@ -643,29 +662,49 @@
|
|||||||
|
|
||||||
drawSatelliteVisualization();
|
drawSatelliteVisualization();
|
||||||
|
|
||||||
// Подключение микрофона для визуализации (опционально) — УДАЛЕНО
|
// WebSocket подключение/отключение
|
||||||
// Теперь используем RMS от satellite вместо локального микрофона
|
function toggleConnection() {
|
||||||
|
const wsUrl = document.getElementById('wsUrlInput').value.trim();
|
||||||
// WebSocket подключение
|
|
||||||
function connectWebSocket() {
|
if(!wsUrl) {
|
||||||
const hostInput = prompt("Введите адрес LVA WebSocket (по умолчанию ws://192.168.200.111:8675):", "ws://192.168.200.111:8675");
|
addEventToLog('❌ Введите WebSocket URL');
|
||||||
if(!hostInput) return;
|
return;
|
||||||
if(ws && ws.readyState === WebSocket.OPEN) ws.close();
|
}
|
||||||
ws = new WebSocket(hostInput);
|
|
||||||
|
// Если уже подключены — отключаемся
|
||||||
|
if(ws && ws.readyState === WebSocket.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подключаемся
|
||||||
|
if(ws && ws.readyState === WebSocket.CONNECTING) {
|
||||||
|
ws.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
updateConnectButton();
|
||||||
statusLed.classList.add('connected');
|
statusLed.classList.add('connected');
|
||||||
statusLabel.innerText = 'connected';
|
statusLabel.innerText = 'connected';
|
||||||
addEventToLog(`✅ Соединение установлено с ${hostInput}`);
|
addEventToLog(`✅ Соединение установлено с ${wsUrl}`);
|
||||||
stateText.innerText = 'ONLINE';
|
stateText.innerText = 'ONLINE';
|
||||||
orb.style.animation = 'pulse-glow 0.8s';
|
orb.style.animation = 'pulse-glow 0.8s';
|
||||||
setTimeout(()=> orb.style.animation='',700);
|
setTimeout(()=> orb.style.animation='',700);
|
||||||
// initMicVisualizer() — удалено, используем satellite RMS
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = handleIncomingMessage;
|
ws.onmessage = handleIncomingMessage;
|
||||||
ws.onerror = (err) => { addEventToLog(`⚠️ WebSocket ошибка`); };
|
|
||||||
|
ws.onerror = (err) => {
|
||||||
|
addEventToLog(`⚠️ WebSocket ошибка`);
|
||||||
|
};
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
|
updateConnectButton();
|
||||||
statusLed.classList.remove('connected');
|
statusLed.classList.remove('connected');
|
||||||
statusLabel.innerText = 'disconnected';
|
statusLabel.innerText = 'disconnected';
|
||||||
addEventToLog(`🔌 Отключено`);
|
addEventToLog(`🔌 Отключено`);
|
||||||
@@ -673,9 +712,45 @@
|
|||||||
document.body.classList.remove('listening-active');
|
document.body.classList.remove('listening-active');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновление кнопки подключения
|
||||||
|
function updateConnectButton() {
|
||||||
|
const btn = document.getElementById('connectBtn');
|
||||||
|
const btnCard = document.getElementById('connectBtnCard');
|
||||||
|
|
||||||
|
if(ws && ws.readyState === WebSocket.OPEN) {
|
||||||
|
if(btn) { btn.innerHTML = '🔌 DISCONNECT'; btn.classList.add('danger-btn'); }
|
||||||
|
if(btnCard) { btnCard.innerHTML = '🔌 DISCONNECT'; btnCard.classList.add('danger-btn'); }
|
||||||
|
} else {
|
||||||
|
if(btn) { btn.innerHTML = '⟳ CONNECT'; btn.classList.remove('danger-btn'); }
|
||||||
|
if(btnCard) { btnCard.innerHTML = '⟳ CONNECT'; btnCard.classList.remove('danger-btn'); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Кнопки управления
|
// Кнопки подключения
|
||||||
document.getElementById('connectBtn').addEventListener('click', connectWebSocket);
|
document.getElementById('connectBtn').addEventListener('click', toggleConnection);
|
||||||
|
document.getElementById('connectBtnCard').addEventListener('click', toggleConnection);
|
||||||
|
|
||||||
|
// Auto-connect при загрузке
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
addEventToLog('🤖 Wyoming Droid Assistant • готов к подключению');
|
||||||
|
|
||||||
|
// Инициализация RMS элементов после загрузки DOM
|
||||||
|
const rmsFillEl = document.getElementById('rmsFill');
|
||||||
|
const rmsValueEl = document.getElementById('rmsValue');
|
||||||
|
if (rmsFillEl && rmsValueEl) {
|
||||||
|
console.log('RMS meter initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-connect если включена опция
|
||||||
|
const autoConnect = document.getElementById('autoConnect');
|
||||||
|
if(autoConnect && autoConnect.checked) {
|
||||||
|
setTimeout(() => {
|
||||||
|
addEventToLog('📡 Auto-connect...');
|
||||||
|
toggleConnection();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Volume control
|
// Volume control
|
||||||
document.getElementById('startListeningBtn').addEventListener('click', () => sendCommand('set_volume', { level: 1.0 }));
|
document.getElementById('startListeningBtn').addEventListener('click', () => sendCommand('set_volume', { level: 1.0 }));
|
||||||
|
|||||||
Reference in New Issue
Block a user