From cd0ca8cf7c14c5348a9746b0207e2f494c065c3a Mon Sep 17 00:00:00 2001 From: Mike Johnston Date: Mon, 30 Mar 2026 23:21:23 -0400 Subject: [PATCH] web sockety things --- apps/desktop/resources/web/index.html | 49 +++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/apps/desktop/resources/web/index.html b/apps/desktop/resources/web/index.html index d863024..dd1cc00 100644 --- a/apps/desktop/resources/web/index.html +++ b/apps/desktop/resources/web/index.html @@ -606,23 +606,54 @@ async function api(method, path, body) { // ── WebSocket ────────────────────────────────────────────────────────────── function connectWS() { const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; - ws = new WebSocket(`${proto}//${location.host}`); + const wsUrl = `${proto}//${location.host}`; + + console.log(`[DWM] Connecting WebSocket to: ${wsUrl}`); + + try { + ws = new WebSocket(wsUrl); + } catch (err) { + console.error('[DWM] WebSocket creation failed:', err); + return; + } + ws.onopen = () => { + console.log('[DWM] WebSocket connected'); document.querySelectorAll('.ws-dot').forEach((d) => d.classList.add('connected')); document.querySelectorAll('[id^="ws-label"]').forEach((l) => l.textContent = 'Connected'); }; + ws.onclose = () => { + console.log('[DWM] WebSocket disconnected, reconnecting...'); document.querySelectorAll('.ws-dot').forEach((d) => d.classList.remove('connected')); document.querySelectorAll('[id^="ws-label"]').forEach((l) => l.textContent = 'Reconnecting…'); setTimeout(connectWS, 4000); }; + + ws.onerror = (error) => { + console.error('[DWM] WebSocket error:', error); + document.querySelectorAll('.ws-dot').forEach((d) => d.classList.remove('connected')); + document.querySelectorAll('[id^="ws-label"]').forEach((l) => l.textContent = 'Connection Error'); + + // Show user-friendly error message + if (error.type === 'error' && error.message.includes('SSL')) { + toast('WebSocket SSL Error: Try accessing via domain name or check certificate', 'error'); + } else if (error.message) { + toast(`WebSocket Error: ${error.message}`, 'error'); + } else { + toast('WebSocket connection failed - try refreshing the page', 'error'); + } + }; + ws.onmessage = (e) => { try { const { type, data } = JSON.parse(e.data); if (type === 'scheduler-fired') appendLog(data); if (type === 'scheduler-status') applySchedStatus(data); if (type === 'scheduler-health') appendLog({ success: data.online, msg: `[health] ${data.msg}` }); - } catch {} + } catch (err) { + console.error('[DWM] WebSocket message parse error:', err); + } }; } @@ -670,6 +701,14 @@ function renderDevices() { el.innerHTML = `
📡

No devices found.
Tap Scan to search.

`; return; } + + // Check if there are WebSocket connection issues + const wsConnected = ws && ws.readyState === WebSocket.OPEN; + if (!wsConnected) { + el.innerHTML = `
⚠️

WebSocket connection issue detected.
Device states may not be accurate.
Try refreshing the page.

`; + return; + } + el.innerHTML = devices.map((d, i) => { const name = d.friendlyName || d.name || d.host; const isDimmer = d.isDimmer || false; @@ -709,6 +748,12 @@ function renderDevices() { // fetch current state for each device devices.forEach((d, i) => { + // Skip fetching state if WebSocket is not connected + if (!ws || ws.readyState !== WebSocket.OPEN) { + console.log(`[DWM] Skipping device state fetch due to WebSocket connection issues`); + return; + } + // For dimmer devices, fetch both brightness and state if (d.isDimmer) { // Fetch brightness first