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:
2026-05-21 19:56:51 +03:00
parent c17743d829
commit 9bfbcfd8a8
+89 -14
View File
@@ -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();
if(!wsUrl) {
addEventToLog('❌ Введите WebSocket URL');
return;
}
// Если уже подключены — отключаемся
if(ws && ws.readyState === WebSocket.OPEN) {
ws.close();
return;
}
// Подключаемся
if(ws && ws.readyState === WebSocket.CONNECTING) {
ws.close();
return;
}
ws = new WebSocket(wsUrl);
// WebSocket подключение
function connectWebSocket() {
const hostInput = prompt("Введите адрес LVA WebSocket (по умолчанию ws://192.168.200.111:8675):", "ws://192.168.200.111:8675");
if(!hostInput) return;
if(ws && ws.readyState === WebSocket.OPEN) ws.close();
ws = new WebSocket(hostInput);
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(`🔌 Отключено`);
@@ -674,8 +713,44 @@
}; };
} }
// Кнопки управления // Обновление кнопки подключения
document.getElementById('connectBtn').addEventListener('click', connectWebSocket); 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', 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 }));