From 4b196c1278b06127612d4703a41adab85b453c59 Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Sat, 8 Nov 2025 16:23:11 +0100 Subject: [PATCH] iphone fix --- apps/ui/templates/dashboard.html | 179 +++++++++++++++++++++++-------- 1 file changed, 132 insertions(+), 47 deletions(-) diff --git a/apps/ui/templates/dashboard.html b/apps/ui/templates/dashboard.html index dab3397..49d6caf 100644 --- a/apps/ui/templates/dashboard.html +++ b/apps/ui/templates/dashboard.html @@ -673,7 +673,6 @@ }); if (response.ok) { - thermostatTargets[deviceId] = newTarget; console.log(`Sent target ${newTarget} to ${deviceId}`); addEvent({ action: 'target_adjusted', @@ -822,61 +821,147 @@ } // Connect to SSE + let reconnectAttempts = 0; + const maxReconnectDelay = 30000; // Max 30 seconds + function connectSSE() { - eventSource = new EventSource(api('/realtime')); - - eventSource.onopen = () => { - console.log('SSE connected'); - document.getElementById('connection-status').textContent = 'Verbunden'; - document.getElementById('connection-status').className = 'status connected'; - }; - - eventSource.addEventListener('message', (e) => { - const data = JSON.parse(e.data); - console.log('SSE message:', data); - - addEvent(data); - - // Update device state - if (data.type === 'state' && data.device_id && data.payload) { - const card = document.querySelector(`[data-device-id="${data.device_id}"]`); - - // Check if it's a light - if (data.payload.power !== undefined) { - updateDeviceUI( - data.device_id, - data.payload.power, - data.payload.brightness - ); - } - - // Check if it's a thermostat - if (data.payload.mode !== undefined || data.payload.target !== undefined || data.payload.current !== undefined) { - updateThermostatUI( - data.device_id, - data.payload.current, - data.payload.target, - data.payload.mode - ); - } + // Close existing connection if any + if (eventSource) { + try { + eventSource.close(); + } catch (e) { + console.error('Error closing EventSource:', e); } - }); + eventSource = null; + } - eventSource.addEventListener('ping', (e) => { - console.log('Heartbeat received'); - }); + console.log(`Connecting to SSE... (attempt ${reconnectAttempts + 1})`); - eventSource.onerror = (error) => { - console.error('SSE error:', error); + try { + eventSource = new EventSource(api('/realtime')); + + eventSource.onopen = () => { + console.log('SSE connected successfully'); + reconnectAttempts = 0; // Reset counter on successful connection + document.getElementById('connection-status').textContent = 'Verbunden'; + document.getElementById('connection-status').className = 'status connected'; + }; + + eventSource.addEventListener('message', (e) => { + const data = JSON.parse(e.data); + console.log('SSE message:', data); + + addEvent(data); + + // Update device state + if (data.type === 'state' && data.device_id && data.payload) { + // Check if it's a light + if (data.payload.power !== undefined) { + currentState[data.device_id] = data.payload.power; + updateDeviceUI( + data.device_id, + data.payload.power, + data.payload.brightness + ); + } + + // Check if it's a thermostat + if (data.payload.mode !== undefined || data.payload.target !== undefined || data.payload.current !== undefined) { + if (data.payload.mode !== undefined) { + thermostatModes[data.device_id] = data.payload.mode; + } + if (data.payload.target !== undefined) { + thermostatTargets[data.device_id] = data.payload.target; + } + updateThermostatUI( + data.device_id, + data.payload.current, + data.payload.target, + data.payload.mode + ); + } + } + }); + + eventSource.addEventListener('ping', (e) => { + console.log('Heartbeat received'); + }); + + eventSource.onerror = (error) => { + console.error('SSE error:', error, 'readyState:', eventSource?.readyState); + document.getElementById('connection-status').textContent = 'Getrennt'; + document.getElementById('connection-status').className = 'status disconnected'; + + if (eventSource) { + try { + eventSource.close(); + } catch (e) { + console.error('Error closing EventSource on error:', e); + } + eventSource = null; + } + + // Exponential backoff with max delay + reconnectAttempts++; + const delay = Math.min( + 1000 * Math.pow(2, reconnectAttempts - 1), + maxReconnectDelay + ); + + console.log(`Reconnecting in ${delay}ms... (attempt ${reconnectAttempts})`); + setTimeout(connectSSE, delay); + }; + } catch (error) { + console.error('Failed to create EventSource:', error); document.getElementById('connection-status').textContent = 'Getrennt'; document.getElementById('connection-status').className = 'status disconnected'; - eventSource.close(); - // Reconnect after 5 seconds - setTimeout(connectSSE, 5000); - }; + reconnectAttempts++; + const delay = Math.min( + 1000 * Math.pow(2, reconnectAttempts - 1), + maxReconnectDelay + ); + + setTimeout(connectSSE, delay); + } } + // Safari/iOS specific: Reconnect when page becomes visible + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') { + console.log('Page visible, checking SSE connection...'); + // Force reconnect if connection is dead + if (!eventSource || eventSource.readyState === EventSource.CLOSED || eventSource.readyState === EventSource.CONNECTING) { + console.log('SSE connection dead or connecting, forcing reconnect...'); + reconnectAttempts = 0; // Reset for immediate reconnect + connectSSE(); + } + } + }); + + // Safari/iOS specific: Reconnect on page focus + window.addEventListener('focus', () => { + console.log('Window focused, checking SSE connection...'); + if (!eventSource || eventSource.readyState === EventSource.CLOSED) { + console.log('SSE connection dead, forcing reconnect...'); + reconnectAttempts = 0; // Reset for immediate reconnect + connectSSE(); + } + }); + + // Safari/iOS specific: Keep connection alive with periodic check + setInterval(() => { + if (!eventSource || eventSource.readyState === EventSource.CLOSED) { + console.log('Periodic check: SSE connection dead, reconnecting...'); + reconnectAttempts = 0; + connectSSE(); + } else if (eventSource.readyState === EventSource.CONNECTING) { + console.log('Periodic check: SSE still connecting, readyState:', eventSource.readyState); + } else { + console.log('Periodic check: SSE connection alive, readyState:', eventSource.readyState); + } + }, 10000); // Check every 10 seconds + // Initialize connectSSE();