diff --git a/html/rfid.js b/html/rfid.js index d0d2f07..12b3d82 100644 --- a/html/rfid.js +++ b/html/rfid.js @@ -53,7 +53,13 @@ function initWebSocket() { // Wenn eine existierende Verbindung besteht, diese erst schließen if (socket) { - socket.close(); + try { + socket.onclose = null; // Remove onclose handler before closing + socket.onerror = null; // Remove error handler + socket.close(); + } catch (e) { + console.error('Error closing existing socket:', e); + } socket = null; } @@ -61,110 +67,80 @@ function initWebSocket() { socket = new WebSocket('ws://' + window.location.host + '/ws'); socket.onopen = function() { + console.log('WebSocket connection established'); isConnected = true; updateConnectionStatus(); startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung }; - socket.onclose = function() { + socket.onclose = function(event) { + console.log('WebSocket connection closed:', event.code, event.reason); isConnected = false; updateConnectionStatus(); - if (heartbeatTimer) clearInterval(heartbeatTimer); + if (heartbeatTimer) { + clearInterval(heartbeatTimer); + heartbeatTimer = null; + } // Nur neue Verbindung versuchen, wenn kein Timer läuft if (!reconnectTimer) { reconnectTimer = setTimeout(() => { + console.log('Attempting to reconnect...'); initWebSocket(); }, RECONNECT_INTERVAL); } }; socket.onerror = function(error) { + console.error('WebSocket error occurred:', error); isConnected = false; updateConnectionStatus(); - if (heartbeatTimer) clearInterval(heartbeatTimer); - - // Bei Fehler Verbindung schließen und neu aufbauen - if (socket) { - socket.close(); - socket = null; + if (heartbeatTimer) { + clearInterval(heartbeatTimer); + heartbeatTimer = null; } }; socket.onmessage = function(event) { - lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort - - const data = JSON.parse(event.data); - if (data.type === 'amsData') { - displayAmsData(data.payload); - } else if (data.type === 'nfcTag') { - updateNfcStatusIndicator(data.payload); - } else if (data.type === 'nfcData') { - updateNfcData(data.payload); - } else if (data.type === 'writeNfcTag') { - handleWriteNfcTagResponse(data.success); - } else if (data.type === 'heartbeat') { - // Optional: Spezifische Behandlung von Heartbeat-Antworten - // Update status dots - const bambuDot = document.getElementById('bambuDot'); - const spoolmanDot = document.getElementById('spoolmanDot'); - const ramStatus = document.getElementById('ramStatus'); + try { + lastHeartbeatResponse = Date.now(); + const data = JSON.parse(event.data); - if (bambuDot) { - bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline'); - // Add click handler only when offline - if (!data.bambu_connected) { - bambuDot.style.cursor = 'pointer'; - bambuDot.onclick = function() { - if (socket && socket.readyState === WebSocket.OPEN) { - socket.send(JSON.stringify({ - type: 'reconnect', - payload: 'bambu' - })); - } - }; - } else { - bambuDot.style.cursor = 'default'; - bambuDot.onclick = null; - } - } - if (spoolmanDot) { - spoolmanDot.className = 'status-dot ' + (data.spoolman_connected ? 'online' : 'offline'); - // Add click handler only when offline - if (!data.spoolman_connected) { - spoolmanDot.style.cursor = 'pointer'; - spoolmanDot.onclick = function() { - if (socket && socket.readyState === WebSocket.OPEN) { - socket.send(JSON.stringify({ - type: 'reconnect', - payload: 'spoolman' - })); - } - }; - } else { - spoolmanDot.style.cursor = 'default'; - spoolmanDot.onclick = null; - } - } - if (ramStatus) { - ramStatus.textContent = `${data.freeHeap}k`; - } - } - else if (data.type === 'setSpoolmanSettings') { - if (data.payload == 'success') { - showNotification(`Spoolman Settings set successfully`, true); - } else { - showNotification(`Error setting Spoolman Settings`, false); + // Handle different message types + switch(data.type) { + case 'amsData': + displayAmsData(data.payload); + break; + case 'nfcTag': + updateNfcStatusIndicator(data.payload); + break; + case 'nfcData': + updateNfcData(data.payload); + break; + case 'writeNfcTag': + handleWriteNfcTagResponse(data.success); + break; + case 'heartbeat': + handleHeartbeatResponse(data); + break; + case 'setSpoolmanSettings': + handleSpoolmanSettingsResponse(data); + break; + default: + console.warn('Unknown message type:', data.type); } + } catch (error) { + console.error('Error processing WebSocket message:', error); } }; } catch (error) { + console.error('Error initializing WebSocket:', error); isConnected = false; updateConnectionStatus(); - // Nur neue Verbindung versuchen, wenn kein Timer läuft if (!reconnectTimer) { reconnectTimer = setTimeout(() => { + console.log('Attempting to reconnect after error...'); initWebSocket(); }, RECONNECT_INTERVAL); } @@ -389,6 +365,7 @@ function updateSpoolButtons(show) { }); } +// Verbesserte Funktion zum Behandeln von Spoolman Settings function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp_min, nozzle_temp_max) { // Hole das ausgewählte Filament const selectedText = document.getElementById("selected-filament").textContent; @@ -419,6 +396,7 @@ function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp } } +// Verbesserte Funktion zum Behandeln von Spool Out function handleSpoolOut() { // Erstelle Payload const payload = { @@ -443,17 +421,21 @@ function handleSpoolOut() { } } -// Neue Funktion zum Behandeln des Spool-In-Klicks +// Verbesserte Funktion zum Behandeln des Spool-In-Klicks function handleSpoolIn(amsId, trayId) { + console.log("handleSpoolIn called with amsId:", amsId, "trayId:", trayId); + // Prüfe WebSocket Verbindung zuerst if (!socket || socket.readyState !== WebSocket.OPEN) { showNotification("No active WebSocket connection!", false); - console.error("WebSocket not connected"); + console.error("WebSocket not connected, state:", socket ? socket.readyState : "no socket"); return; } // Hole das ausgewählte Filament const selectedText = document.getElementById("selected-filament").textContent; + console.log("Selected filament:", selectedText); + if (selectedText === "Please choose...") { showNotification("Choose Filament first", false); return; @@ -466,53 +448,55 @@ function handleSpoolIn(amsId, trayId) { if (!selectedSpool) { showNotification("Selected Spool not found", false); + console.error("Selected spool not found in spoolsData"); return; } + console.log("Found spool data:", selectedSpool); + // Temperaturwerte extrahieren let minTemp = "175"; let maxTemp = "275"; - if (Array.isArray(selectedSpool.filament.nozzle_temperature) && + if (selectedSpool.filament && + Array.isArray(selectedSpool.filament.nozzle_temperature) && selectedSpool.filament.nozzle_temperature.length >= 2) { minTemp = selectedSpool.filament.nozzle_temperature[0]; maxTemp = selectedSpool.filament.nozzle_temperature[1]; } + // Extrahiere bambu_idx + let bambuIdx = "GFL99"; // Default zu Generic PLA + if (selectedSpool.filament?.extra?.bambu_idx) { + bambuIdx = selectedSpool.filament.extra.bambu_idx.replace(/['"]/g, ''); + } else if (selectedSpool.extra?.bambu_idx) { + bambuIdx = selectedSpool.extra.bambu_idx.replace(/['"]/g, ''); + } + // Erstelle Payload const payload = { type: 'setBambuSpool', payload: { amsId: amsId, trayId: trayId, - color: selectedSpool.filament.color_hex || "FFFFFF", + color: selectedSpool.filament && selectedSpool.filament.color_hex ? selectedSpool.filament.color_hex : "FFFFFF", nozzle_temp_min: parseInt(minTemp), nozzle_temp_max: parseInt(maxTemp), - type: selectedSpool.filament.material, - brand: selectedSpool.filament.vendor.name, - tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(), + type: selectedSpool.filament && selectedSpool.filament.material ? selectedSpool.filament.material : "PLA", + brand: selectedSpool.filament && selectedSpool.filament.vendor ? selectedSpool.filament.vendor.name : "", + tray_info_idx: bambuIdx, cali_idx: "-1" // Default-Wert setzen } }; - // Prüfe, ob der Key cali_idx vorhanden ist und setze ihn - if (selectedSpool.filament.extra.bambu_cali_id) { - payload.payload.cali_idx = selectedSpool.filament.extra.bambu_cali_id.replace(/['"]+/g, '').trim(); - } - - // Prüfe, ob der Key bambu_setting_id vorhanden ist - if (selectedSpool.filament.extra.bambu_setting_id) { - payload.payload.bambu_setting_id = selectedSpool.filament.extra.bambu_setting_id.replace(/['"]+/g, '').trim(); - } - - console.log("Spool-In Payload:", payload); + console.log("Sending payload:", payload); try { socket.send(JSON.stringify(payload)); - showNotification(`Spool set in AMS ${amsId} Tray ${trayId}. Pls wait`, true); + showNotification(`Spool settings sent to printer. Please wait...`, true); } catch (error) { - console.error("Fehler beim Senden der WebSocket Nachricht:", error); - showNotification("Error while sending", false); + console.error("Error sending WebSocket message:", error); + showNotification("Error sending spool settings!", false); } } @@ -687,3 +671,75 @@ function showNotification(message, isSuccess) { }, 300); }, 3000); } + +// Neue Handler-Funktionen für bessere Modularität +function handleHeartbeatResponse(data) { + const bambuDot = document.getElementById('bambuDot'); + const spoolmanDot = document.getElementById('spoolmanDot'); + const ramStatus = document.getElementById('ramStatus'); + + if (bambuDot) { + bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline'); + if (!data.bambu_connected) { + bambuDot.style.cursor = 'pointer'; + bambuDot.onclick = function() { + sendReconnectRequest('bambu'); + }; + } else { + bambuDot.style.cursor = 'default'; + bambuDot.onclick = null; + } + } + + if (spoolmanDot) { + spoolmanDot.className = 'status-dot ' + (data.spoolman_connected ? 'online' : 'offline'); + if (!data.spoolman_connected) { + spoolmanDot.style.cursor = 'pointer'; + spoolmanDot.onclick = function() { + sendReconnectRequest('spoolman'); + }; + } else { + spoolmanDot.style.cursor = 'default'; + spoolmanDot.onclick = null; + } + } + + if (ramStatus) { + ramStatus.textContent = `${data.freeHeap}k`; + } +} + +function handleSpoolmanSettingsResponse(data) { + if (data.payload === 'success') { + showNotification(`Spoolman Settings set successfully`, true); + } else { + showNotification(`Error setting Spoolman Settings`, false); + } +} + +function sendReconnectRequest(target) { + if (socket?.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify({ + type: 'reconnect', + payload: target + })); + } +} + +// Verbesserte Funktion zum Senden von WebSocket-Nachrichten +function sendWebSocketMessage(message) { + if (!socket || socket.readyState !== WebSocket.OPEN) { + console.error('WebSocket is not connected'); + showNotification("Connection error - please try again", false); + return; + } + + try { + const jsonString = JSON.stringify(message); + console.log('Sending WebSocket message:', jsonString); + socket.send(jsonString); + } catch (error) { + console.error('Error sending WebSocket message:', error); + showNotification("Error sending message", false); + } +} diff --git a/src/bambu.cpp b/src/bambu.cpp index 63d4434..0d65a94 100644 --- a/src/bambu.cpp +++ b/src/bambu.cpp @@ -18,7 +18,7 @@ PubSubClient client(sslClient); TaskHandle_t BambuMqttTask; String report_topic = ""; -//String request_topic = ""; +String request_topic = ""; const char* bambu_username = "bblp"; const char* bambu_ip = nullptr; const char* bambu_accesscode = nullptr; @@ -92,7 +92,7 @@ bool loadBambuCredentials() { bambu_serialnr = g_bambu_serialnr.c_str(); report_topic = "device/" + String(bambu_serialnr) + "/report"; - //request_topic = "device/" + String(bambu_serialnr) + "/request"; + request_topic = "device/" + String(bambu_serialnr) + "/request"; return true; } Serial.println("Keine gültigen Bambu-Credentials gefunden."); @@ -197,18 +197,69 @@ FilamentResult findFilamentIdx(String brand, String type) { } bool sendMqttMessage(const String& payload) { - Serial.println("Sending MQTT message"); - Serial.println(payload); - if (client.publish(report_topic.c_str(), payload.c_str())) - { - return true; + // Check MQTT client state first + if (!client.connected()) { + Serial.println("Error: MQTT client not connected when trying to send message"); + Serial.print("MQTT client state: "); + Serial.println(client.state()); + return false; } + + // Check if request_topic is empty + if (request_topic.isEmpty()) { + Serial.println("Error: request_topic is empty!"); + return false; + } + + // Check payload + if (payload.isEmpty()) { + Serial.println("Error: Empty payload!"); + return false; + } + + Serial.println("Attempting to send MQTT message:"); + Serial.println("Topic: " + request_topic); + Serial.println("Payload: " + payload); + Serial.println("Payload length: " + String(payload.length())); - return false; + // Try to publish with QoS 1 and retain flag false + bool published = client.publish(request_topic.c_str(), payload.c_str(), false); + + if (published) { + Serial.println("MQTT message published successfully"); + return true; + } else { + Serial.println("Failed to publish MQTT message"); + Serial.print("MQTT client state after publish attempt: "); + Serial.println(client.state()); + + // Additional error information + switch(client.state()) { + case -4: + Serial.println("Reason: Connection timeout"); + break; + case -3: + Serial.println("Reason: Connection lost"); + break; + case -2: + Serial.println("Reason: Connect failed"); + break; + case -1: + Serial.println("Reason: Disconnected"); + break; + case 0: + Serial.println("Reason: Connected but publish failed"); + break; + default: + Serial.println("Reason: Unknown error"); + break; + } + return false; + } } bool setBambuSpool(String payload) { - Serial.println("Spool settings in"); + Serial.println("Spool settings received from WebSocket"); Serial.println(payload); // Parse the JSON @@ -220,21 +271,28 @@ bool setBambuSpool(String payload) { return false; } - int amsId = doc["amsId"]; - int trayId = doc["trayId"]; + // Check if we have all required fields + if (!doc["amsId"].is() || !doc["trayId"].is()) { + Serial.println("Error: Missing or invalid required fields in payload"); + return false; + } + + int amsId = doc["amsId"].as(); + int trayId = doc["trayId"].as(); String color = doc["color"].as(); color.toUpperCase(); - int minTemp = doc["nozzle_temp_min"]; - int maxTemp = doc["nozzle_temp_max"]; - String type = doc["type"].as(); + int minTemp = doc["nozzle_temp_min"] | 0; // Default to 0 if not present + int maxTemp = doc["nozzle_temp_max"] | 0; // Default to 0 if not present + String type = doc["type"] | ""; // Default to empty string if not present (type == "PLA+") ? type = "PLA" : type; - String brand = doc["brand"].as(); + String brand = doc["brand"] | ""; // Default to empty string if not present String tray_info_idx = (doc["tray_info_idx"].as() != "-1") ? doc["tray_info_idx"].as() : ""; if (tray_info_idx == "") { if (brand != "" && type != "") { FilamentResult result = findFilamentIdx(brand, type); tray_info_idx = result.key; type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp + Serial.println("Found filament idx: " + tray_info_idx + " for type: " + type); } } String setting_id = doc["bambu_setting_id"].as(); @@ -242,6 +300,7 @@ bool setBambuSpool(String payload) { doc.clear(); + // Create MQTT message doc["print"]["sequence_id"] = "0"; doc["print"]["command"] = "ams_filament_setting"; doc["print"]["ams_id"] = amsId < 200 ? amsId : 255; @@ -250,26 +309,25 @@ bool setBambuSpool(String payload) { doc["print"]["nozzle_temp_min"] = minTemp; doc["print"]["nozzle_temp_max"] = maxTemp; doc["print"]["tray_type"] = type; - //doc["print"]["cali_idx"] = (cali_idx != "") ? cali_idx : ""; doc["print"]["tray_info_idx"] = tray_info_idx; doc["print"]["setting_id"] = setting_id; - // Serialize the JSON + // Serialize and send MQTT message String output; serializeJson(doc, output); + Serial.println("Sending to Bambu printer:"); + Serial.println(output); - if (sendMqttMessage(output)) { - Serial.println("Spool successfully set"); - } - else - { - Serial.println("Failed to set spool"); + if (!sendMqttMessage(output)) { + Serial.println("Failed to send filament settings to printer"); return false; } + Serial.println("Filament settings sent successfully"); doc.clear(); yield(); + // Send calibration if available if (cali_idx != "") { yield(); doc["print"]["sequence_id"] = "0"; @@ -278,21 +336,18 @@ bool setBambuSpool(String payload) { doc["print"]["nozzle_diameter"] = "0.4"; doc["print"]["cali_idx"] = cali_idx.toInt(); doc["print"]["tray_id"] = trayId < 200 ? trayId : 254; - //doc["print"]["ams_id"] = amsId < 200 ? amsId : 255; - // Serialize the JSON String output; serializeJson(doc, output); + Serial.println("Sending calibration to printer:"); + Serial.println(output); - if (sendMqttMessage(output)) { - Serial.println("Extrusion calibration successfully set"); - } - else - { - Serial.println("Failed to set extrusion calibration"); + if (!sendMqttMessage(output)) { + Serial.println("Failed to send calibration settings to printer"); return false; } + Serial.println("Calibration settings sent successfully"); doc.clear(); yield(); } @@ -661,8 +716,8 @@ bool setupMqtt() { String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX); // MQTT Connection Options - // client.setKeepAlive(60); - // client.setSocketTimeout(60); // Increase socket timeout + client.setKeepAlive(60); + client.setSocketTimeout(60); // Increase socket timeout Serial.print("Connecting with client ID: "); Serial.println(clientId);